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;