Re: Vlan Tag on Vlan Tag (l2tunneling)

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Reyk Floeter
Date: Thursday, August 21, 2008 - 12:34 pm

On Thu, Aug 21, 2008 at 04:05:50PM +0200, Claudio Jeker wrote:

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.


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 " : " ");
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
Re: Vlan Tag on Vlan Tag (l2tunneling), Stuart Henderson, (Thu Aug 21, 2:50 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Paul de Weerd, (Thu Aug 21, 4:41 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Henning Brauer, (Thu Aug 21, 4:49 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Claudio Jeker, (Thu Aug 21, 7:05 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Henning Brauer, (Thu Aug 21, 7:48 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Reyk Floeter, (Thu Aug 21, 11:31 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Reyk Floeter, (Thu Aug 21, 12:34 pm)
Re: Vlan Tag on Vlan Tag (l2tunneling), Paul de Weerd, (Thu Aug 21, 2:58 pm)
Re: Vlan Tag on Vlan Tag (l2tunneling), Henning Brauer, (Fri Aug 22, 3:38 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Insan Praja SW, (Fri Aug 22, 8:07 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Steve Shockley, (Fri Aug 22, 11:08 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Anathae Townsend, (Fri Aug 22, 12:07 pm)
Re: Vlan Tag on Vlan Tag (l2tunneling), Claudio Jeker, (Fri Aug 22, 4:09 pm)
Re: Vlan Tag on Vlan Tag (l2tunneling), Insan Praja SW, (Thu Sep 4, 7:22 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Insan Praja SW, (Fri Oct 17, 11:30 am)
Re: Vlan Tag on Vlan Tag (l2tunneling), Sam Fourman Jr., (Wed Feb 18, 12:57 pm)
Re: Vlan Tag on Vlan Tag (l2tunneling), Sam Fourman Jr., (Tue Mar 3, 4:25 pm)