Module Name:    src
Committed By:   yamaguchi
Date:           Mon Jun 20 08:14:48 UTC 2022

Modified Files:
        src/sys/net: if_bridge.c if_ether.h if_ethersubr.c if_vlan.c

Log Message:
bridge(4): support VLAN frames stripped by hardware tagging


To generate a diff of this commit:
cvs rdiff -u -r1.186 -r1.187 src/sys/net/if_bridge.c
cvs rdiff -u -r1.88 -r1.89 src/sys/net/if_ether.h
cvs rdiff -u -r1.312 -r1.313 src/sys/net/if_ethersubr.c
cvs rdiff -u -r1.169 -r1.170 src/sys/net/if_vlan.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_bridge.c
diff -u src/sys/net/if_bridge.c:1.186 src/sys/net/if_bridge.c:1.187
--- src/sys/net/if_bridge.c:1.186	Fri Dec 31 14:25:24 2021
+++ src/sys/net/if_bridge.c	Mon Jun 20 08:14:48 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridge.c,v 1.186 2021/12/31 14:25:24 riastradh Exp $	*/
+/*	$NetBSD: if_bridge.c,v 1.187 2022/06/20 08:14:48 yamaguchi Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.186 2021/12/31 14:25:24 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.187 2022/06/20 08:14:48 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1502,6 +1502,16 @@ bridge_enqueue(struct bridge_softc *sc, 
 	KERNEL_UNLOCK_ONE(NULL);
 #endif /* ALTQ */
 
+	if (vlan_has_tag(m) &&
+	    !vlan_is_hwtag_enabled(dst_ifp)) {
+		(void)ether_inject_vlantag(&m, ETHERTYPE_VLAN,
+		    vlan_get_tag(m));
+		if (m == NULL) {
+			if_statinc(&sc->sc_if, if_oerrors);
+			return;
+		}
+	}
+
 	len = m->m_pkthdr.len;
 	mflags = m->m_flags;
 
@@ -2704,6 +2714,10 @@ bridge_ipf(void *arg, struct mbuf **mp, 
 		}
 	}
 
+	/* drop VLAN traffic untagged by hardware offloading */
+	if (vlan_has_tag(*mp))
+		goto bad;
+
 	/*
 	 * If we're trying to filter bridge traffic, don't look at anything
 	 * other than IP and ARP traffic.  If the filter doesn't understand

Index: src/sys/net/if_ether.h
diff -u src/sys/net/if_ether.h:1.88 src/sys/net/if_ether.h:1.89
--- src/sys/net/if_ether.h:1.88	Mon Nov 15 07:07:05 2021
+++ src/sys/net/if_ether.h	Mon Jun 20 08:14:48 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_ether.h,v 1.88 2021/11/15 07:07:05 yamaguchi Exp $	*/
+/*	$NetBSD: if_ether.h,v 1.89 2022/06/20 08:14:48 yamaguchi Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
@@ -364,12 +364,6 @@ vlan_set_tag(struct mbuf *m, uint16_t vl
 	return;
 }
 
-static __inline bool
-vlan_has_tag(struct mbuf *m)
-{
-	return (m->m_flags & M_VLANTAG) != 0;
-}
-
 /* extract VLAN ID value from a VLAN tag */
 static __inline uint16_t
 vlan_get_tag(struct mbuf *m)
@@ -379,6 +373,23 @@ vlan_get_tag(struct mbuf *m)
 	return m->m_pkthdr.ether_vtag;
 }
 
+static __inline bool
+vlan_has_tag(struct mbuf *m)
+{
+	return (m->m_flags & M_VLANTAG) != 0;
+}
+
+static __inline bool
+vlan_is_hwtag_enabled(struct ifnet *_ifp)
+{
+	struct ethercom *ec = (void *)_ifp;
+
+	if (ec->ec_capenable & ETHERCAP_VLAN_HWTAGGING)
+		return true;
+
+	return false;
+}
+
 /* test if any VLAN is configured for this interface */
 #define VLAN_ATTACHED(ec)	((ec)->ec_nvlans > 0)
 
@@ -403,6 +414,9 @@ int	ether_enable_vlan_mtu(struct ifnet *
 int	ether_disable_vlan_mtu(struct ifnet *);
 int	ether_add_vlantag(struct ifnet *, uint16_t, bool *);
 int	ether_del_vlantag(struct ifnet *, uint16_t);
+int	ether_inject_vlantag(struct mbuf **, uint16_t, uint16_t);
+struct mbuf *
+	ether_strip_vlantag(struct mbuf *);
 #else
 /*
  * Prototype ethers(3) functions.

Index: src/sys/net/if_ethersubr.c
diff -u src/sys/net/if_ethersubr.c:1.312 src/sys/net/if_ethersubr.c:1.313
--- src/sys/net/if_ethersubr.c:1.312	Mon Jun 20 08:02:25 2022
+++ src/sys/net/if_ethersubr.c	Mon Jun 20 08:14:48 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_ethersubr.c,v 1.312 2022/06/20 08:02:25 yamaguchi Exp $	*/
+/*	$NetBSD: if_ethersubr.c,v 1.313 2022/06/20 08:14:48 yamaguchi Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.312 2022/06/20 08:02:25 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.313 2022/06/20 08:14:48 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -725,6 +725,18 @@ ether_input(struct ifnet *ifp, struct mb
 
 	if_statadd(ifp, if_ibytes, m->m_pkthdr.len);
 
+	if (!vlan_has_tag(m) && etype == ETHERTYPE_VLAN) {
+		m = ether_strip_vlantag(m);
+		if (m == NULL) {
+			if_statinc(ifp, if_ierrors);
+			return;
+		}
+
+		eh = mtod(m, struct ether_header *);
+		etype = ntohs(eh->ether_type);
+		ehlen = sizeof(*eh);
+	}
+
 	if ((m->m_flags & (M_BCAST | M_MCAST | M_PROMISC)) == 0 &&
 	    (ifp->if_flags & IFF_PROMISC) != 0 &&
 	    memcmp(CLLADDR(ifp->if_sadl), eh->ether_dhost,
@@ -763,22 +775,9 @@ ether_input(struct ifnet *ifp, struct mb
 	 * does not exist to take those frames, they're returned
 	 * to ether_input().
 	 */
-	if (vlan_has_tag(m) || etype == ETHERTYPE_VLAN) {
-		struct ether_vlan_header *evl = (void *)eh;
-		uint16_t vlan_id;
 
-		if (vlan_has_tag(m)) {
-			vlan_id = EVL_VLANOFTAG(vlan_get_tag(m));
-		} else {
-			if (m->m_len < sizeof(*evl))
-				goto error;
-
-			vlan_id = EVL_VLANOFTAG(ntohs(evl->evl_tag));
-			etype = ntohs(evl->evl_proto);
-			ehlen = sizeof(*evl);
-		}
-
-		if (vlan_id == 0) {
+	if (vlan_has_tag(m)) {
+		if (EVL_VLANOFTAG(vlan_get_tag(m)) == 0) {
 			if (etype == ETHERTYPE_VLAN ||
 			     etype == ETHERTYPE_QINQ)
 				goto drop;
@@ -1747,6 +1746,108 @@ ether_del_vlantag(struct ifnet *ifp, uin
 	return 0;
 }
 
+int
+ether_inject_vlantag(struct mbuf **mp, uint16_t etype, uint16_t tag)
+{
+	static const size_t min_data_len =
+	    ETHER_MIN_LEN - ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
+	/* Used to pad ethernet frames with < ETHER_MIN_LEN bytes */
+	static const char vlan_zero_pad_buff[ETHER_MIN_LEN] = { 0 };
+
+	struct ether_vlan_header *evl;
+	struct mbuf *m = *mp;
+	int error;
+
+	error = 0;
+
+	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
+	if (m == NULL) {
+		error = ENOBUFS;
+		goto out;
+	}
+
+	if (m->m_len < sizeof(*evl)) {
+		m = m_pullup(m, sizeof(*evl));
+		if (m == NULL) {
+			error = ENOBUFS;
+			goto out;
+		}
+	}
+
+	/*
+	 * Transform the Ethernet header into an
+	 * Ethernet header with 802.1Q encapsulation.
+	 */
+	memmove(mtod(m, void *),
+	    mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
+	    sizeof(struct ether_header));
+	evl = mtod(m, struct ether_vlan_header *);
+	evl->evl_proto = evl->evl_encap_proto;
+	evl->evl_encap_proto = htons(etype);
+	evl->evl_tag = htons(tag);
+
+	/*
+	 * To cater for VLAN-aware layer 2 ethernet
+	 * switches which may need to strip the tag
+	 * before forwarding the packet, make sure
+	 * the packet+tag is at least 68 bytes long.
+	 * This is necessary because our parent will
+	 * only pad to 64 bytes (ETHER_MIN_LEN) and
+	 * some switches will not pad by themselves
+	 * after deleting a tag.
+	 */
+	if (m->m_pkthdr.len < min_data_len) {
+		m_copyback(m, m->m_pkthdr.len,
+		    min_data_len - m->m_pkthdr.len,
+		    vlan_zero_pad_buff);
+	}
+
+	m->m_flags &= ~M_VLANTAG;
+
+out:
+	*mp = m;
+	return error;
+}
+
+struct mbuf *
+ether_strip_vlantag(struct mbuf *m)
+{
+	struct ether_vlan_header *evl;
+
+	if (m->m_len < sizeof(*evl) &&
+	    (m = m_pullup(m, sizeof(*evl))) == NULL) {
+		return NULL;
+	}
+
+	if (m_makewritable(&m, 0, sizeof(*evl), M_DONTWAIT)) {
+		m_freem(m);
+		return NULL;
+	}
+
+	evl = mtod(m, struct ether_vlan_header *);
+	KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
+
+	vlan_set_tag(m, ntohs(evl->evl_tag));
+
+	/*
+	 * Restore the original ethertype.  We'll remove
+	 * the encapsulation after we've found the vlan
+	 * interface corresponding to the tag.
+	 */
+	evl->evl_encap_proto = evl->evl_proto;
+
+	/*
+	 * Remove the encapsulation header and append tag.
+	 * The original header has already been fixed up above.
+	 */
+	vlan_set_tag(m, ntohs(evl->evl_tag));
+	memmove((char *)evl + ETHER_VLAN_ENCAP_LEN, evl,
+	    offsetof(struct ether_vlan_header, evl_encap_proto));
+	m_adj(m, ETHER_VLAN_ENCAP_LEN);
+
+	return m;
+}
+
 static int
 ether_multicast_sysctl(SYSCTLFN_ARGS)
 {

Index: src/sys/net/if_vlan.c
diff -u src/sys/net/if_vlan.c:1.169 src/sys/net/if_vlan.c:1.170
--- src/sys/net/if_vlan.c:1.169	Mon Jun 20 08:09:13 2022
+++ src/sys/net/if_vlan.c	Mon Jun 20 08:14:48 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vlan.c,v 1.169 2022/06/20 08:09:13 yamaguchi Exp $	*/
+/*	$NetBSD: if_vlan.c,v 1.170 2022/06/20 08:14:48 yamaguchi Exp $	*/
 
 /*
  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
@@ -78,7 +78,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.169 2022/06/20 08:09:13 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.170 2022/06/20 08:14:48 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -228,9 +228,6 @@ static struct psref_class *ifvm_psref_cl
 struct if_clone vlan_cloner =
     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
 
-/* Used to pad ethernet frames with < ETHER_MIN_LEN bytes */
-static char vlan_zero_pad_buff[ETHER_MIN_LEN];
-
 static uint32_t nvlanifs;
 
 static inline int
@@ -1281,57 +1278,14 @@ vlan_start(struct ifnet *ifp)
 
 			switch (p->if_type) {
 			case IFT_ETHER:
-			    {
-				struct ether_vlan_header *evl;
-
-				M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
+				(void)ether_inject_vlantag(&m,
+				    ETHERTYPE_VLAN, mib->ifvm_tag);
 				if (m == NULL) {
-					printf("%s: unable to prepend encap header",
+					printf("%s: unable to inject VLAN tag",
 					    p->if_xname);
-					if_statinc(ifp, if_oerrors);
-					continue;
-				}
-				if (m->m_len < sizeof(struct ether_vlan_header))
-					m = m_pullup(m,
-					    sizeof(struct ether_vlan_header));
-				if (m == NULL) {
-					printf("%s: unable to pullup encap "
-					    "header", p->if_xname);
-					if_statinc(ifp, if_oerrors);
 					continue;
 				}
-
-				/*
-				 * Transform the Ethernet header into an
-				 * Ethernet header with 802.1Q encapsulation.
-				 */
-				memmove(mtod(m, void *),
-				    mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
-				    sizeof(struct ether_header));
-				evl = mtod(m, struct ether_vlan_header *);
-				evl->evl_proto = evl->evl_encap_proto;
-				evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-				evl->evl_tag = htons(mib->ifvm_tag);
-
-				/*
-				 * To cater for VLAN-aware layer 2 ethernet
-				 * switches which may need to strip the tag
-				 * before forwarding the packet, make sure
-				 * the packet+tag is at least 68 bytes long.
-				 * This is necessary because our parent will
-				 * only pad to 64 bytes (ETHER_MIN_LEN) and
-				 * some switches will not pad by themselves
-				 * after deleting a tag.
-				 */
-				const size_t min_data_len = ETHER_MIN_LEN -
-				    ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
-				if (m->m_pkthdr.len < min_data_len) {
-					m_copyback(m, m->m_pkthdr.len,
-					    min_data_len - m->m_pkthdr.len,
-					    vlan_zero_pad_buff);
-				}
 				break;
-			    }
 
 			default:
 				panic("%s: impossible", __func__);
@@ -1424,59 +1378,15 @@ vlan_transmit(struct ifnet *ifp, struct 
 		 */
 		switch (p->if_type) {
 		case IFT_ETHER:
-		    {
-			struct ether_vlan_header *evl;
-
-			M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
-			if (m == NULL) {
-				printf("%s: unable to prepend encap header",
+			error = ether_inject_vlantag(&m,
+			    ETHERTYPE_VLAN, mib->ifvm_tag);
+			if (error != 0) {
+				KASSERT(m == NULL);
+				printf("%s: unable to inject VLAN tag",
 				    p->if_xname);
-				if_statinc(ifp, if_oerrors);
-				error = ENOBUFS;
 				goto out;
 			}
-			if (m->m_len < sizeof(struct ether_vlan_header))
-				m = m_pullup(m,
-				    sizeof(struct ether_vlan_header));
-			if (m == NULL) {
-				printf("%s: unable to pullup encap "
-				    "header", p->if_xname);
-				if_statinc(ifp, if_oerrors);
-				error = ENOBUFS;
-				goto out;
-			}
-
-			/*
-			 * Transform the Ethernet header into an
-			 * Ethernet header with 802.1Q encapsulation.
-			 */
-			memmove(mtod(m, void *),
-			    mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
-			    sizeof(struct ether_header));
-			evl = mtod(m, struct ether_vlan_header *);
-			evl->evl_proto = evl->evl_encap_proto;
-			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-			evl->evl_tag = htons(mib->ifvm_tag);
-
-			/*
-			 * To cater for VLAN-aware layer 2 ethernet
-			 * switches which may need to strip the tag
-			 * before forwarding the packet, make sure
-			 * the packet+tag is at least 68 bytes long.
-			 * This is necessary because our parent will
-			 * only pad to 64 bytes (ETHER_MIN_LEN) and
-			 * some switches will not pad by themselves
-			 * after deleting a tag.
-			 */
-			const size_t min_data_len = ETHER_MIN_LEN -
-			    ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
-			if (m->m_pkthdr.len < min_data_len) {
-				m_copyback(m, m->m_pkthdr.len,
-				    min_data_len - m->m_pkthdr.len,
-				    vlan_zero_pad_buff);
-			}
 			break;
-		    }
 
 		default:
 			panic("%s: impossible", __func__);
@@ -1523,53 +1433,10 @@ vlan_input(struct ifnet *ifp, struct mbu
 	struct ifvlan_linkmib *mib;
 	struct psref psref;
 
-	if (vlan_has_tag(m)) {
-		vid = EVL_VLANOFTAG(vlan_get_tag(m));
-	} else {
-		struct ether_vlan_header *evl;
-
-		if (ifp->if_type != IFT_ETHER) {
-			panic("%s: impossible", __func__);
-		}
-
-		if (m->m_len < sizeof(struct ether_vlan_header) &&
-		    (m = m_pullup(m,
-		     sizeof(struct ether_vlan_header))) == NULL) {
-			printf("%s: no memory for VLAN header, "
-			    "dropping packet.\n", ifp->if_xname);
-			return NULL;
-		}
-
-		if (m_makewritable(&m, 0,
-		    sizeof(struct ether_vlan_header), M_DONTWAIT)) {
-			m_freem(m);
-			if_statinc(ifp, if_ierrors);
-			return NULL;
-		}
-
-		evl = mtod(m, struct ether_vlan_header *);
-		KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
-
-		vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
-		vlan_set_tag(m, ntohs(evl->evl_tag));
-
-		/*
-		 * Restore the original ethertype.  We'll remove
-		 * the encapsulation after we've found the vlan
-		 * interface corresponding to the tag.
-		 */
-		evl->evl_encap_proto = evl->evl_proto;
-
-		/*
-		 * Remove the encapsulation header and append tag.
-		 * The original header has already been fixed up above.
-		 */
-		memmove((char *)evl + ETHER_VLAN_ENCAP_LEN, evl,
-		    offsetof(struct ether_vlan_header, evl_encap_proto));
-		m_adj(m, ETHER_VLAN_ENCAP_LEN);
-	}
-
+	KASSERT(vlan_has_tag(m));
+	vid = EVL_VLANOFTAG(vlan_get_tag(m));
 	KASSERT(vid != 0);
+
 	mib = vlan_lookup_tag_psref(ifp, vid, &psref);
 	if (mib == NULL) {
 		return m;

Reply via email to