This is an updated version of reyk@'s original diff
http://www.mail-archive.com/[email protected]/msg65694.html
to add 802.1QinQ support for nested VLANs:

  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

Note: If you want a full 1500-byte MTU on the inner VLAN, you'll
need to set the MTU of the physical ethernet interface to 1504.

This could use some testing by people who have an actual use for
it.


Index: sys/net/if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.177
diff -u -p -r1.177 if_bridge.c
--- sys/net/if_bridge.c 13 Jan 2010 05:25:06 -0000      1.177
+++ sys/net/if_bridge.c 13 May 2010 19:20:36 -0000
@@ -2704,7 +2704,8 @@ bridge_fragment(struct bridge_softc *sc,
 #else
        etype = ntohs(eh->ether_type);
 #if NVLAN > 0
-       if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN) {
+       if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN ||
+           etype == ETHERTYPE_QINQ) {
                int len = m->m_pkthdr.len;
 
                if (m->m_flags & M_VLANTAG)
Index: sys/net/if_ethersubr.c
===================================================================
RCS file: /cvs/src/sys/net/if_ethersubr.c,v
retrieving revision 1.142
diff -u -p -r1.142 if_ethersubr.c
--- sys/net/if_ethersubr.c      7 May 2010 13:33:16 -0000       1.142
+++ sys/net/if_ethersubr.c      13 May 2010 19:50:22 -0000
@@ -622,8 +622,8 @@ ether_input(ifp0, eh, m)
        }
 
 #if NVLAN > 0
-       if (((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN)
-           && (vlan_input(eh, m) == 0))
+       if (((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN ||
+           etype == ETHERTYPE_QINQ) && (vlan_input(eh, m, etype) == 0))
                return;
 #endif
 
@@ -648,7 +648,8 @@ ether_input(ifp0, eh, m)
 #endif
 
 #if NVLAN > 0
-       if ((m->m_flags & M_VLANTAG) || etype == ETHERTYPE_VLAN) {
+       if ((m->m_flags & M_VLANTAG) || 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.83
diff -u -p -r1.83 if_vlan.c
--- sys/net/if_vlan.c   20 Apr 2010 22:05:43 -0000      1.83
+++ sys/net/if_vlan.c   25 May 2010 15:23:09 -0000
@@ -77,19 +77,19 @@
 #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;
-
-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);
+#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);
 void   vlan_vlandev_state(void *);
-void   vlanattach (int count);
-int    vlan_set_promisc (struct ifnet *ifp);
+void   vlanattach(int count);
+int    vlan_set_promisc(struct ifnet *ifp);
 int    vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
 int    vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
 void   vlan_ether_purgemulti(struct ifvlan *);
@@ -99,16 +99,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
@@ -129,6 +139,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;
        IFQ_SET_MAXLEN(&ifp->if_snd, 1);
@@ -206,7 +222,8 @@ vlan_start(struct ifnet *ifp)
                 * to create a special header for it. In this case, we just pass
                 * the packet along.
                 */
-               if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
+               if ((p->if_capabilities & IFCAP_VLAN_HWTAGGING) &&
+                   (ifv->ifv_type == ETHERTYPE_VLAN)) {
                        m->m_pkthdr.ether_vtag = ifv->ifv_tag +
                            (ifv->ifv_prio << EVL_PRIO_BITS);
                        m->m_flags |= M_VLANTAG;
@@ -215,7 +232,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));
 
@@ -255,15 +272,16 @@ 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_flags & M_VLANTAG) {
+               etype = ETHERTYPE_VLAN;
+               tagh = vlan_tagh;
                tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
        } else {
                if (m->m_len < EVL_ENCAPLEN &&
@@ -272,11 +290,13 @@ 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)
@@ -336,6 +356,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)
@@ -395,6 +416,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.
         */
@@ -409,7 +437,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.19
diff -u -p -r1.19 if_vlan_var.h
--- sys/net/if_vlan_var.h       7 Nov 2008 00:52:40 -0000       1.19
+++ sys/net/if_vlan_var.h       13 May 2010 19:17:38 -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: 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       13 May 2010 19:44:17 -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].
-- 
Christian "naddy" Weisgerber                          [email protected]

Reply via email to