git: f94c370de6e7 - main - bridge: Allow VLAN protocol to be configured
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 05 Aug 2025 19:26:02 UTC
The branch main has been updated by ivy:
URL: https://cgit.FreeBSD.org/src/commit/?id=f94c370de6e7d32cd3b635893870438b744214e7
commit f94c370de6e7d32cd3b635893870438b744214e7
Author: Lexi Winter <ivy@FreeBSD.org>
AuthorDate: 2025-08-05 17:51:11 +0000
Commit: Lexi Winter <ivy@FreeBSD.org>
CommitDate: 2025-08-05 18:35:30 +0000
bridge: Allow VLAN protocol to be configured
Add a new per-interface option "ifvlanproto", which can be either
"802.1q" (the default) or "802.1ad". This controls what type of
tag we attach to outgoing packets on the interface.
Reviewed by: pauamma_gundo.com (manpages)
Differential Revision: https://reviews.freebsd.org/D51231
---
sbin/ifconfig/ifbridge.c | 38 ++++++++++++++++++++++++++++++++++++++
sbin/ifconfig/ifconfig.8 | 11 +++++++++++
sys/net/if_bridge.c | 36 ++++++++++++++++++++++++++++++++----
sys/net/if_bridgevar.h | 3 +++
4 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
index 3566acdcf54c..d02d92d10b59 100644
--- a/sbin/ifconfig/ifbridge.c
+++ b/sbin/ifconfig/ifbridge.c
@@ -190,6 +190,21 @@ print_vlans(ifbvlan_set_t *vlans)
}
}
+static char const *
+vlan_proto_name(uint16_t proto)
+{
+ switch (proto) {
+ case 0:
+ return "none";
+ case ETHERTYPE_VLAN:
+ return "802.1q";
+ case ETHERTYPE_QINQ:
+ return "802.1ad";
+ default:
+ return "unknown";
+ }
+}
+
static void
bridge_status(if_ctx *ctx)
{
@@ -261,6 +276,9 @@ bridge_status(if_ctx *ctx)
else
printf(" <unknown state %d>", state);
}
+ if (member->ifbr_vlanproto != 0)
+ printf(" vlan protocol %s",
+ vlan_proto_name(member->ifbr_vlanproto));
if (member->ifbr_pvid != 0)
printf(" untagged %u", (unsigned)member->ifbr_pvid);
print_vlans(&bridge->member_vlans[i]);
@@ -895,6 +913,25 @@ unsetbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused)
do_bridgeflag(ctx, val, IFBIF_QINQ, 0);
}
+static void
+setbridge_ifvlanproto(if_ctx *ctx, const char *ifname, const char *proto)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
+
+ if (strcmp(proto, "802.1q") == 0)
+ req.ifbr_vlanproto = ETHERTYPE_VLAN;
+ else if (strcmp(proto, "802.1ad") == 0)
+ req.ifbr_vlanproto = ETHERTYPE_QINQ;
+ else
+ errx(1, "unrecognised VLAN protocol: %s", proto);
+
+ if (do_cmd(ctx, BRDGSIFVLANPROTO, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFVLANPROTO");
+}
+
static struct cmd bridge_cmds[] = {
DEF_CMD_ARG("addm", setbridge_add),
DEF_CMD_ARG("deletem", setbridge_delete),
@@ -936,6 +973,7 @@ static struct cmd bridge_cmds[] = {
DEF_CMD_ARG2("tagged", setbridge_tagged),
DEF_CMD_ARG2("+tagged", addbridge_tagged),
DEF_CMD_ARG2("-tagged", delbridge_tagged),
+ DEF_CMD_ARG2("ifvlanproto", setbridge_ifvlanproto),
DEF_CMD_ARG("timeout", setbridge_timeout),
DEF_CMD_ARG("private", setbridge_private),
DEF_CMD_ARG("-private", unsetbridge_private),
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 69a81f72421f..d7b2570ab20d 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -2765,6 +2765,17 @@ Do not enable the
.Cm qinq
option by default on newly added members.
This is the default behavior.
+.It Cm ifvlanproto Ar interface Ar proto
+Set the VLAN encapsulation protocol on
+.Ar interface
+to
+.Ar proto ,
+which must be either
+.Dq 802.1q
+or
+.Dq 802.1ad .
+The default is
+.Dq 802.1q .
.El
.Ss Link Aggregation and Link Failover Parameters
The following parameters are specific to lagg interfaces:
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 26ea4400e67d..945318c5af1a 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -259,6 +259,7 @@ struct bridge_iflist {
struct epoch_context bif_epoch_ctx;
ether_vlanid_t bif_pvid; /* port vlan id */
ifbvlan_set_t bif_vlan_set; /* if allowed tagged vlans */
+ uint16_t bif_vlanproto; /* vlan protocol */
};
/*
@@ -423,6 +424,7 @@ static int bridge_ioctl_gflags(struct bridge_softc *, void *);
static int bridge_ioctl_sflags(struct bridge_softc *, void *);
static int bridge_ioctl_gdefpvid(struct bridge_softc *, void *);
static int bridge_ioctl_sdefpvid(struct bridge_softc *, void *);
+static int bridge_ioctl_svlanproto(struct bridge_softc *, void *);
static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *,
int);
#ifdef INET
@@ -654,6 +656,9 @@ static const struct bridge_control bridge_control_table[] = {
{ bridge_ioctl_sdefpvid, sizeof(struct ifbrparam),
BC_F_COPYIN|BC_F_SUSER },
+
+ { bridge_ioctl_svlanproto, sizeof(struct ifbreq),
+ BC_F_COPYIN|BC_F_SUSER },
};
static const int bridge_control_table_size = nitems(bridge_control_table);
@@ -1494,6 +1499,7 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
bif->bif_ifp = ifs;
bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
bif->bif_savedcaps = ifs->if_capenable;
+ bif->bif_vlanproto = ETHERTYPE_VLAN;
if (sc->sc_flags & IFBRF_VLANFILTER)
bif->bif_pvid = sc->sc_defpvid;
if (sc->sc_flags & IFBRF_DEFQINQ)
@@ -1579,6 +1585,7 @@ bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg)
req->ifbr_addrmax = bif->bif_addrmax;
req->ifbr_addrexceeded = bif->bif_addrexceeded;
req->ifbr_pvid = bif->bif_pvid;
+ req->ifbr_vlanproto = bif->bif_vlanproto;
/* Copy STP state options as flags */
if (bp->bp_operedge)
@@ -2254,6 +2261,24 @@ bridge_ioctl_sdefpvid(struct bridge_softc *sc, void *arg)
return (0);
}
+static int
+bridge_ioctl_svlanproto(struct bridge_softc *sc, void *arg)
+{
+ struct ifbreq *req = arg;
+ struct bridge_iflist *bif;
+
+ bif = bridge_lookup_member(sc, req->ifbr_ifsname);
+ if (bif == NULL)
+ return (EXTERROR(ENOENT, "Interface is not a bridge member"));
+
+ if (req->ifbr_vlanproto != ETHERTYPE_VLAN &&
+ req->ifbr_vlanproto != ETHERTYPE_QINQ)
+ return (EXTERROR(EINVAL, "Invalid VLAN protocol"));
+
+ bif->bif_vlanproto = req->ifbr_vlanproto;
+
+ return (0);
+}
/*
* bridge_ifdetach:
*
@@ -2397,12 +2422,15 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m,
}
/*
- * If underlying interface can not do VLAN tag insertion itself
- * then attach a packet tag that holds it.
+ * There are two cases where we have to insert our own tag:
+ * if the member interface doesn't support hardware tagging,
+ * or if the tag proto is not 802.1q.
*/
if ((m->m_flags & M_VLANTAG) &&
- (dst_ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) {
- m = ether_vlanencap(m, m->m_pkthdr.ether_vtag);
+ ((dst_ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0 ||
+ bif->bif_vlanproto != ETHERTYPE_VLAN)) {
+ m = ether_vlanencap_proto(m, m->m_pkthdr.ether_vtag,
+ bif->bif_vlanproto);
if (m == NULL) {
if_printf(dst_ifp,
"unable to prepend VLAN header\n");
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
index 15194fecff7c..b0f579f688ac 100644
--- a/sys/net/if_bridgevar.h
+++ b/sys/net/if_bridgevar.h
@@ -131,6 +131,7 @@
#define BRDGSFLAGS 35 /* set bridge flags (ifbrparam) */
#define BRDGGDEFPVID 36 /* get default pvid (ifbrparam) */
#define BRDGSDEFPVID 37 /* set default pvid (ifbrparam) */
+#define BRDGSIFVLANPROTO 38 /* set if vlan protocol (ifbreq) */
/* BRDGSFLAGS, Bridge flags (non-interface-specific) */
typedef uint32_t ifbr_flags_t;
@@ -157,6 +158,7 @@ struct ifbreq {
uint32_t ifbr_addrmax; /* member if addr max */
uint32_t ifbr_addrexceeded; /* member if addr violations */
ether_vlanid_t ifbr_pvid; /* member if PVID */
+ uint16_t ifbr_vlanproto; /* member if VLAN protocol */
uint8_t pad[32];
};
@@ -252,6 +254,7 @@ struct ifbrparam {
* addresses */
#define ifbrp_flags ifbrp_ifbrpu.ifbrpu_int32 /* bridge flags */
#define ifbrp_defpvid ifbrp_ifbrpu.ifbrpu_int16 /* default pvid */
+#define ifbrp_vlanproto ifbrp_ifbrpu.ifbrpu_int8 /* vlan protocol */
/*
* Bridge current operational parameters structure.