bridge(4): support VLAN frames stripped by hardware tagging
This commit is contained in:
parent
ca0626726d
commit
f17fac5794
|
@ -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, struct ifnet *dst_ifp, struct mbuf *m,
|
|||
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, struct ifnet *ifp, int dir)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -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 vlantag)
|
|||
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.
|
||||
|
|
|
@ -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 mbuf *m)
|
|||
|
||||
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 mbuf *m)
|
|||
* 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, uint16_t vtag)
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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_class __read_mostly;
|
|||
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 mbuf *m)
|
|||
*/
|
||||
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 mbuf *m)
|
|||
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;
|
||||
|
|
Loading…
Reference in New Issue