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.

Reply via email to