> On Thu, Aug 21, 2008 at 04:05:50PM +0200, Claudio Jeker wrote:
>> > no point in just doing that.
>> >
>> > a button to change the ether type would make sense.
>> >
>>
>
> this is not trivial because it would require a change in the Rx path
> where it is currently matching the ethertype in ether_input() before
> calling vlan_input(). do you want to call vlan_input() for every
> other packet or do a configured type lookup all the time? and what if
> the user specifies an ethernet type that is conflicting with something
> else? i think it should really only be 0x8100 or 0x88a8.
>
>> If we stack vlan interfaces I don't see a real need for such a button.
>> This could be figured out either at configuration time or on runtime.
>> E.g. just check if the ethertype is 0x8100 and add the next vlan tag as
>> 0x88a8. This would also allow to use a bridge for qinq setups. Because
>> of
>> this I think doing it on runtime is the best.
>>
>
> here is another approach defining QinQ-compliant interfaces as a new
> cloner type; so you can stack 0x88a8 devices as you wish and it
> doesn't need a new button in ifconfig. it also uses a dedicated vlan
> tag hash for "Service VLANs" to avoid tag/Id conflicts.
>
> # ifconfig em0 up
> # ifconfig svlan100 vlandev em0
> # ifconfig vlan200 vlandev svlan100 192.168.2.100
>
> reyk
>
> Index: share/man/man4/vlan.4
> ===================================================================
> RCS file: /cvs/src/share/man/man4/vlan.4,v
> retrieving revision 1.31
> diff -u -p -r1.31 vlan.4
> --- share/man/man4/vlan.4 26 Jun 2008 05:42:07 -0000 1.31
> +++ share/man/man4/vlan.4 21 Aug 2008 19:18:42 -0000
> @@ -31,8 +31,9 @@
> .Dt VLAN 4
> .Os
> .Sh NAME
> -.Nm vlan
> -.Nd "IEEE 802.1Q encapsulation/decapsulation pseudo-device"
> +.Nm vlan ,
> +.Nm svlan
> +.Nd "IEEE 802.1Q/1AD encapsulation/decapsulation pseudo-devices"
> .Sh SYNOPSIS
> .Cd "pseudo-device vlan"
> .Sh DESCRIPTION
> @@ -40,6 +41,10 @@ The
> .Nm
> Ethernet interface allows construction of virtual LANs when used in
> conjunction with IEEE 802.1Q-compliant Ethernet devices.
> +The
> +.Ic svlan
> +Ethernet interface allows contruction of IEEE 802.1AD-compliant
> +provider bridges.
> .Pp
> A
> .Nm
> @@ -83,6 +88,24 @@ option for more information.
> Following the vlan header is the actual ether type for the frame and
> length
> information.
> .Pp
> +An
> +.Ic svlan
> +interface is normally used for QinQ in 802.1AD-compliant provider
> bridges to
> +stack other
> +.Nm
> +interfaces on top of it.
> +It can be created using the
> +.Ic ifconfig svlan Ns Ar N Ic create
> +command or by setting up a
> +.Xr hostname.if 5
> +configuration file for
> +.Xr netstart 8 .
> +The configuration is identical to the
> +.Nm
> +interface, the only differences are that it uses a different Ethernet
> +type (0x88a8) and an independent VLAN Id space on the parent
> +interface.
> +.Pp
> .Nm
> interfaces support the following unique
> .Xr ioctl 2 Ns s :
> @@ -104,7 +127,10 @@ interfaces use the following interface c
> The parent interface can handle full sized frames, plus the size
> of the vlan tag.
> .It IFCAP_VLAN_HWTAGGING
> -The parent interface will participate in the tagging of frames.
> +The parent interface will participate in the tagging of frames
> +(This is not supported by
> +.Ic svlan
> +interfaces).
> .El
> .Sh DIAGNOSTICS
> .Bl -diag
> @@ -150,6 +176,10 @@ and
> .Rs
> .%T IEEE 802.1Q standard
> .%O
http://standards.ieee.org/getieee802/802.1.html
> +.Re
> +.Rs
> +.%T IEEE 802.1AD standard
> +.%O Provider Bridges, QinQ
> .Re
> .Sh AUTHORS
> Originally
wollman@freebsd.org.
> Index: sys/net/ethertypes.h
> ===================================================================
> RCS file: /cvs/src/sys/net/ethertypes.h,v
> retrieving revision 1.9
> diff -u -p -r1.9 ethertypes.h
> --- sys/net/ethertypes.h 5 May 2008 13:40:17 -0000 1.9
> +++ sys/net/ethertypes.h 21 Aug 2008 19:18:42 -0000
> @@ -300,6 +300,7 @@
> #define ETHERTYPE_LANPROBE 0x8888 /* HP LanProbe test? */
> #define ETHERTYPE_PAE 0x888E /* 802.1X Port Access Entity */
> #define ETHERTYPE_AOE 0x88A2 /* ATA over Ethernet */
> +#define ETHERTYPE_QINQ 0x88A8 /* 802.1ad VLAN stacking */
> #define ETHERTYPE_LLDP 0x88CC /* Link Layer Discovery Protocol */
> #define ETHERTYPE_LOOPBACK 0x9000 /* Loopback */
> #define ETHERTYPE_LBACK ETHERTYPE_LOOPBACK /* DEC MOP loopback */
> Index: sys/net/if_bridge.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_bridge.c,v
> retrieving revision 1.170
> diff -u -p -r1.170 if_bridge.c
> --- sys/net/if_bridge.c 14 Jun 2008 21:46:22 -0000 1.170
> +++ sys/net/if_bridge.c 21 Aug 2008 19:18:42 -0000
> @@ -2601,7 +2601,7 @@ bridge_fragment(struct bridge_softc *sc,
> goto dropit;
> #else
> etype = ntohs(eh->ether_type);
> - if (etype == ETHERTYPE_VLAN &&
> + if ((etype == ETHERTYPE_VLAN || etype == ETHERTYPE_QINQ) &&
> (ifp->if_capabilities & IFCAP_VLAN_MTU) &&
> ((m->m_pkthdr.len - sizeof(struct ether_vlan_header)) <=
> ifp->if_mtu)) {
> Index: sys/net/if_ethersubr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_ethersubr.c,v
> retrieving revision 1.123
> diff -u -p -r1.123 if_ethersubr.c
> --- sys/net/if_ethersubr.c 4 Aug 2008 18:55:08 -0000 1.123
> +++ sys/net/if_ethersubr.c 21 Aug 2008 19:18:42 -0000
> @@ -573,7 +573,8 @@ ether_input(ifp0, eh, m)
> }
> #if NVLAN > 0
> - if (etype == ETHERTYPE_VLAN && (vlan_input(eh, m) == 0))
> + if ((etype == ETHERTYPE_VLAN || etype == ETHERTYPE_QINQ) &&
> + (vlan_input(eh, m, etype) == 0))
> return;
> #endif
> @@ -598,7 +599,7 @@ ether_input(ifp0, eh, m)
> #endif
> #if NVLAN > 0
> - if (etype == ETHERTYPE_VLAN) {
> + if (etype == ETHERTYPE_VLAN || etype == ETHERTYPE_QINQ) {
> /* The bridge did not want the vlan frame either, drop it. */
> ifp->if_noproto++;
> m_freem(m);
> Index: sys/net/if_vlan.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_vlan.c,v
> retrieving revision 1.73
> diff -u -p -r1.73 if_vlan.c
> --- sys/net/if_vlan.c 7 May 2008 13:45:35 -0000 1.73
> +++ sys/net/if_vlan.c 21 Aug 2008 19:18:42 -0000
> @@ -78,16 +78,16 @@
> #include <net/if_vlan_var.h>
> extern struct ifaddr **ifnet_addrs;
> -u_long vlan_tagmask;
> +u_long vlan_tagmask, svlan_tagmask;
> -#define TAG_HASH_SIZE 32
> -#define TAG_HASH(tag) (tag & vlan_tagmask)
> -LIST_HEAD(, ifvlan) *vlan_tagh;
> +#define TAG_HASH_SIZE 32
> +#define TAG_HASH(tag) (tag & vlan_tagmask)
> +LIST_HEAD(vlan_taghash, ifvlan) *vlan_tagh, *svlan_tagh;
> void vlan_start (struct ifnet *ifp);
> int vlan_ioctl (struct ifnet *ifp, u_long cmd, caddr_t addr);
> int vlan_unconfig (struct ifnet *ifp);
> -int vlan_config (struct ifvlan *, struct ifnet *, u_int16_t);
> +int vlan_config(struct ifvlan *, struct ifnet *, u_int16_t);
> void vlan_vlandev_state(void *);
> void vlanattach (int count);
> int vlan_set_promisc (struct ifnet *ifp);
> @@ -100,16 +100,26 @@ void vlan_ifdetach(void *);
> struct if_clone vlan_cloner =
> IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
> +struct if_clone svlan_cloner =
> + IF_CLONE_INITIALIZER("svlan", vlan_clone_create,
> vlan_clone_destroy);
> /* ARGSUSED */
> void
> vlanattach(int count)
> {
> - vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT, &vlan_tagmask);
> + /* Normal VLAN */
> + vlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT,
> + &vlan_tagmask);
> if (vlan_tagh == NULL)
> panic("vlanattach: hashinit");
> -
> if_clone_attach(&vlan_cloner);
> +
> + /* Service-VLAN for QinQ/802.1ad provider bridges */
> + svlan_tagh = hashinit(TAG_HASH_SIZE, M_DEVBUF, M_NOWAIT,
> + &svlan_tagmask);
> + if (svlan_tagh == NULL)
> + panic("vlanattach: hashinit");
> + if_clone_attach(&svlan_cloner);
> }
> int
> @@ -130,6 +140,12 @@ vlan_clone_create(struct if_clone *ifc,
> /* NB: flags are not set here */
> /* NB: mtu is not set here */
> + /* Special handling for the IEEE 802.1ad QinQ variant */
> + if (strcmp("svlan", ifc->ifc_name) == 0)
> + ifv->ifv_type = ETHERTYPE_QINQ;
> + else
> + ifv->ifv_type = ETHERTYPE_VLAN;
> +
> ifp->if_start = vlan_start;
> ifp->if_ioctl = vlan_ioctl;
> ifp->if_output = ether_output;
> @@ -220,7 +236,8 @@ vlan_start(struct ifnet *ifp)
> * following potentially bogus rcvif pointers off into
> * never-never land.
> */
> - if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
> + if ((p->if_capabilities & IFCAP_VLAN_HWTAGGING) &&
> + (ifv->ifv_type == ETHERTYPE_VLAN)) {
> m->m_pkthdr.rcvif = ifp;
> m->m_flags |= M_PROTO1;
> } else {
> @@ -228,7 +245,7 @@ vlan_start(struct ifnet *ifp)
> m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
> evh.evl_proto = evh.evl_encap_proto;
> - evh.evl_encap_proto = htons(ETHERTYPE_VLAN);
> + evh.evl_encap_proto = htons(ifv->ifv_type);
> evh.evl_tag = htons(ifv->ifv_tag +
> (ifv->ifv_prio << EVL_PRIO_BITS));
> @@ -268,13 +285,12 @@ vlan_start(struct ifnet *ifp)
> * vlan_input() returns 0 if it has consumed the packet, 1 otherwise.
> */
> int
> -vlan_input(eh, m)
> - struct ether_header *eh;
> - struct mbuf *m;
> +vlan_input(struct ether_header *eh, struct mbuf *m, u_int16_t etype)
> {
> struct ifvlan *ifv;
> u_int tag;
> struct ifnet *ifp = m->m_pkthdr.rcvif;
> + struct vlan_taghash *tagh;
> if (m->m_len < EVL_ENCAPLEN &&
> (m = m_pullup(m, EVL_ENCAPLEN)) == NULL) {
> @@ -282,10 +298,12 @@ vlan_input(eh, m)
> return (0);
> }
> + tagh = etype == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
> tag = EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)));
> - LIST_FOREACH(ifv, &vlan_tagh[TAG_HASH(tag)], ifv_list) {
> - if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag)
> + LIST_FOREACH(ifv, &tagh[TAG_HASH(tag)], ifv_list) {
> + if (m->m_pkthdr.rcvif == ifv->ifv_p && tag == ifv->ifv_tag &&
> + etype == ifv->ifv_type)
> break;
> }
> if (ifv == NULL)
> @@ -326,6 +344,7 @@ vlan_config(struct ifvlan *ifv, struct i
> {
> struct ifaddr *ifa1, *ifa2;
> struct sockaddr_dl *sdl1, *sdl2;
> + struct vlan_taghash *tagh;
> int s;
> if (p->if_type != IFT_ETHER)
> @@ -385,6 +404,13 @@ vlan_config(struct ifvlan *ifv, struct i
> /* (IFCAP_CSUM_TCPv6|IFCAP_CSUM_UDPv6); */
> /*
> + * Hardware VLAN tagging only works with the default VLAN
> + * ethernet type (0x8100).
> + */
> + if (ifv->ifv_type != ETHERTYPE_VLAN)
> + ifv->ifv_if.if_capabilities &= ~IFCAP_VLAN_HWTAGGING;
> +
> + /*
> * Set up our ``Ethernet address'' to reflect the underlying
> * physical interface's.
> */
> @@ -399,7 +425,8 @@ vlan_config(struct ifvlan *ifv, struct i
> ifv->ifv_tag = tag;
> s = splnet();
> - LIST_INSERT_HEAD(&vlan_tagh[TAG_HASH(tag)], ifv, ifv_list);
> + tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
> + LIST_INSERT_HEAD(&tagh[TAG_HASH(tag)], ifv, ifv_list);
> /* Register callback for physical link state changes */
> ifv->lh_cookie = hook_establish(p->if_linkstatehooks, 1,
> Index: sys/net/if_vlan_var.h
> ===================================================================
> RCS file: /cvs/src/sys/net/if_vlan_var.h,v
> retrieving revision 1.18
> diff -u -p -r1.18 if_vlan_var.h
> --- sys/net/if_vlan_var.h 9 Feb 2006 00:05:55 -0000 1.18
> +++ sys/net/if_vlan_var.h 21 Aug 2008 19:18:42 -0000
> @@ -53,6 +53,7 @@ struct ifvlan {
> u_int16_t ifvm_proto; /* encapsulation ethertype */
> u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
> u_int16_t ifvm_prio; /* prio to apply on packet leaving if */
> + u_int16_t ifvm_type; /* non-standard ethertype or 0x8100 */
> } ifv_mib;
> LIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead;
> LIST_ENTRY(ifvlan) ifv_list;
> @@ -64,6 +65,7 @@ struct ifvlan {
> #define ifv_if ifv_ac.ac_if
> #define ifv_tag ifv_mib.ifvm_tag
> #define ifv_prio ifv_mib.ifvm_prio
> +#define ifv_type ifv_mib.ifvm_type
> #define IFVF_PROMISC 0x01
> #endif /* _KERNEL */
> @@ -97,6 +99,6 @@ struct vlanreq {
> #define SIOCGETVLAN SIOCGIFGENERIC
> #ifdef _KERNEL
> -extern int vlan_input(struct ether_header *eh, struct mbuf *m);
> +extern int vlan_input(struct ether_header *eh, struct mbuf *m,
> u_int16_t);
> #endif /* _KERNEL */
> #endif /* _NET_IF_VLAN_VAR_H_ */
> Index: usr.sbin/tcpdump/ethertype.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/tcpdump/ethertype.h,v
> retrieving revision 1.13
> diff -u -p -r1.13 ethertype.h
> --- usr.sbin/tcpdump/ethertype.h 7 Oct 2007 16:41:05 -0000 1.13
> +++ usr.sbin/tcpdump/ethertype.h 21 Aug 2008 19:18:42 -0000
> @@ -102,6 +102,9 @@
> #ifndef ETHERTYPE_8021Q
> #define ETHERTYPE_8021Q 0x8100
> #endif
> +#ifndef ETHERTYPE_QINQ
> +#define ETHERTYPE_QINQ 0x88a8
> +#endif
> #ifndef ETHERTYPE_IPX
> #define ETHERTYPE_IPX 0x8137
> #endif
> Index: usr.sbin/tcpdump/print-ether.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v
> retrieving revision 1.23
> diff -u -p -r1.23 print-ether.c
> --- usr.sbin/tcpdump/print-ether.c 7 Oct 2007 16:41:05 -0000 1.23
> +++ usr.sbin/tcpdump/print-ether.c 21 Aug 2008 19:18:42 -0000
> @@ -204,7 +204,11 @@ recurse:
> return (1);
> case ETHERTYPE_8021Q:
> - printf("802.1Q vid %d pri %d%s",
> + printf("802.1Q ");
> + case ETHERTYPE_QINQ:
> + if (ethertype == ETHERTYPE_QINQ)
> + printf("QinQ s");
> + printf("vid %d pri %d%s",
> ntohs(*(unsigned short*)p)&0xFFF,
> ntohs(*(unsigned short*)p)>>13,
> (ntohs(*(unsigned short*)p)&0x1000) ? " cfi " : " ");
>