Works for me. (haven't tested this very extensively yet, and only OpenBSD <-> OpenBSD ... nor did I try the tcpdump patches .. will do so later)
Thanks Reyk, cool stuff ;) Paul 'WEiRD' de Weerd On Thu, Aug 21, 2008 at 09:34:12PM +0200, Reyk Floeter wrote: | 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 " : " "); | -- >++++++++[<++++++++++>-]<+++++++.>+++[<------>-]<.>+++[<+ +++++++++++>-]<.>++[<------------>-]<+.--------------.[-] http://www.weirdnet.nl/