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;