The branch main has been updated by ivy: URL: https://cgit.FreeBSD.org/src/commit/?id=f94c370de6e7d32cd3b635893870438b744214e7
commit f94c370de6e7d32cd3b635893870438b744214e7 Author: Lexi Winter <i...@freebsd.org> AuthorDate: 2025-08-05 17:51:11 +0000 Commit: Lexi Winter <i...@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.