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
This commit is contained in:
Lexi Winter
2025-08-05 18:51:11 +01:00
parent d43ac3b76e
commit f94c370de6
4 changed files with 84 additions and 4 deletions
+38
View File
@@ -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),
+11
View File
@@ -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:
+32 -4
View File
@@ -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");
+3
View File
@@ -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.