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 [EMAIL PROTECTED] 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 " : " ");