The branch main has been updated by ivy:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=65ed1a035ceb8f7c323c0bc0c7344a7fa7cadc9f

commit 65ed1a035ceb8f7c323c0bc0c7344a7fa7cadc9f
Author:     Lexi Winter <i...@freebsd.org>
AuthorDate: 2025-07-05 04:27:25 +0000
Commit:     Lexi Winter <i...@freebsd.org>
CommitDate: 2025-07-05 07:04:22 +0000

    bridge: allow member interface vlan to be configured
    
    Add two new bridge(4) interface options, 'vlanfilter' and 'untagged':
    
            # ifconfig bridge0 vlanfilter ix0
            # ifconfig bridge0 -vlanfilter ix0
            # ifconfig bridge0 untagged ix0 20
            # ifconfig bridge0 -untagged ix0
    
    Setting 'vlanfilter' causes the bridge to filter ingress and egress
    traffic on that interface based on the frame's VLAN, rather than simply
    passing all frames.  By default, an interface is not permitted on any
    VLANs, so all frames will be dropped.
    
    Setting 'untagged' allows the interface to send and receive untagged
    traffic in the given VLAN, allowing two (or more) interfaces in the
    same VLAN to communicate with each other, but not with any other
    interface.
    
    Setting 'untagged' on an interface automatically enables 'vlanfilter'
    as well.  The untagged VLAN may be removed using the '-untagged'
    option, but this does not disable VLAN filtering automatically.
    
    Tagged frames may not be sent or received on a port with VLAN filtering
    enabled.
    
    Update bridge.4 to document this change, and also add an overview of the
    existing vlan/.1q support in if_bridge.
    
    Basic tests for the new functionality are included.
    
    Bump __FreeBSD_version for struct ibfreq ABI change.
    
    Reviewed by:    kevans, kp
    Approved by:    kevans (mentor)
    Differential Revision:  https://reviews.freebsd.org/D49993
---
 sbin/ifconfig/ifbridge.c        |  59 +++++++++++++++++-
 sbin/ifconfig/ifconfig.8        |  21 ++++++-
 share/man/man4/bridge.4         |  31 ++++++++-
 sys/net/if_bridge.c             | 135 ++++++++++++++++++++++++++++++++++++----
 sys/net/if_bridgevar.h          |   5 +-
 sys/sys/param.h                 |   2 +-
 tests/sys/net/if_bridge_test.sh | 126 +++++++++++++++++++++++++++++++++++++
 7 files changed, 362 insertions(+), 17 deletions(-)

diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
index 2d0af1255a73..a60ddabcbdd4 100644
--- a/sbin/ifconfig/ifbridge.c
+++ b/sbin/ifconfig/ifbridge.c
@@ -211,6 +211,8 @@ bridge_status(if_ctx *ctx)
                        else
                                printf(" <unknown state %d>", state);
                }
+               if (member->ifbr_untagged != 0)
+                       printf(" untagged %u", (unsigned)member->ifbr_untagged);
                printf("\n");
        }
 
@@ -576,6 +578,45 @@ setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const 
char *cost)
                err(1, "BRDGSIFCOST %s",  cost);
 }
 
+static void
+setbridge_untagged(if_ctx *ctx, const char *ifn, const char *vlanid)
+{
+       struct ifbreq req;
+       u_long val;
+
+       memset(&req, 0, sizeof(req));
+
+       if (get_val(vlanid, &val) < 0)
+               errx(1, "invalid VLAN identifier: %s", vlanid);
+
+       /*
+        * Reject vlan 0, since it's not a valid vlan identifier and has a
+        * special meaning in the kernel interface.
+        */
+       if (val == 0)
+               errx(1, "invalid VLAN identifier: %lu", val);
+
+       strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
+       req.ifbr_untagged = val;
+
+       if (do_cmd(ctx, BRDGSIFUNTAGGED, &req, sizeof(req), 1) < 0)
+               err(1, "BRDGSIFUNTAGGED %s", vlanid);
+}
+
+static void
+unsetbridge_untagged(if_ctx *ctx, const char *ifn, int dummy __unused)
+{
+       struct ifbreq req;
+
+       memset(&req, 0, sizeof(req));
+
+       strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
+       req.ifbr_untagged = 0;
+
+       if (do_cmd(ctx, BRDGSIFUNTAGGED, &req, sizeof(req), 1) < 0)
+               err(1, "BRDGSIFUNTAGGED");
+}
+
 static void
 setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg)
 {
@@ -612,17 +653,27 @@ setbridge_timeout(if_ctx *ctx, const char *arg, int dummy 
__unused)
 static void
 setbridge_private(if_ctx *ctx, const char *val, int dummy __unused)
 {
-
        do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1);
 }
 
 static void
 unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused)
 {
-
        do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0);
 }
 
+static void
+setbridge_vlanfilter(if_ctx *ctx, const char *val, int dummy __unused)
+{
+       do_bridgeflag(ctx, val, IFBIF_VLANFILTER, 1);
+}
+
+static void
+unsetbridge_vlanfilter(if_ctx *ctx, const char *val, int dummy __unused)
+{
+       do_bridgeflag(ctx, val, IFBIF_VLANFILTER, 0);
+}
+
 static struct cmd bridge_cmds[] = {
        DEF_CMD_ARG("addm",             setbridge_add),
        DEF_CMD_ARG("deletem",          setbridge_delete),
@@ -659,6 +710,10 @@ static struct cmd bridge_cmds[] = {
        DEF_CMD_ARG2("ifpriority",      setbridge_ifpriority),
        DEF_CMD_ARG2("ifpathcost",      setbridge_ifpathcost),
        DEF_CMD_ARG2("ifmaxaddr",       setbridge_ifmaxaddr),
+       DEF_CMD_ARG("vlanfilter",       setbridge_vlanfilter),
+       DEF_CMD_ARG("-vlanfilter",      unsetbridge_vlanfilter),
+       DEF_CMD_ARG2("untagged",        setbridge_untagged),
+       DEF_CMD_ARG("-untagged",        unsetbridge_untagged),
        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 e3f094a336fb..490da7b2ce2c 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd April 24, 2025
+.Dd July 5, 2025
 .Dt IFCONFIG 8
 .Os
 .Sh NAME
@@ -2696,6 +2696,25 @@ source addresses are dropped until an existing host 
cache entry expires or is
 removed.
 Set to 0 to disable.
 .El
+.Ss Bridge VLAN Filtering Parameters
+The behaviour of these options is described in the
+.Dq VLAN SUPPORT
+section of
+.Xr bridge 4 .
+.Bl -tag -width indent
+.It Cm vlanfilter Ar interface
+Enable VLAN filtering on an interface.
+.It Cm -vlanfilter Ar interface
+Disable VLAN filtering on an interface.
+.It Cm untagged Ar interface Ar vlan-id
+Set the untagged VLAN identifier for an interface.
+.Pp
+Setting
+.Cm untagged
+will automatically enable VLAN filtering on the interface.
+.It Cm -untagged Ar interface Ar vlan-id
+Clear the untagged VLAN identifier for an interface.
+.El
 .Ss Link Aggregation and Link Failover Parameters
 The following parameters are specific to lagg interfaces:
 .Bl -tag -width indent
diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
index 7ce734ae87eb..73e7fd56af78 100644
--- a/share/man/man4/bridge.4
+++ b/share/man/man4/bridge.4
@@ -36,7 +36,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 28, 2025
+.Dd July 5, 2025
 .Dt IF_BRIDGE 4
 .Os
 .Sh NAME
@@ -271,6 +271,35 @@ by setting the
 .Va net.link.bridge.log_stp
 node using
 .Xr sysctl 8 .
+.Sh VLAN SUPPORT
+The
+.Nm
+driver has limited support for virtual LANs (VLANs).
+The bridge implements independent VLAN learning, i.e. MAC addresses are
+learned on a per-VLAN basis, and the same MAC address may be learned on
+multiple interfaces on different VLANs.
+Incoming frames with an 802.1Q tag will be assigned to the appropriate
+VLAN.
+.Pp
+By default no access control is enabled, so any interface may
+participate in any VLAN.
+.Pp
+VLAN filtering may be enabled on an interface using the
+.Xr ifconfig 8
+.Cm vlanfilter
+option.
+When VLAN filtering is enabled, an interface may only send and receive
+untagged frames.
+The interface's untagged VLAN ID may be configured using the
+.Xr ifconfig 8
+.Cm untagged
+option.
+If an untagged VLAN ID is configured, incoming frames will be assigned
+to that VLAN, and the interface may receive outgoing untagged frames
+in that VLAN.
+.Pp
+There is no support for adding or removing 802.1Q tags from frames
+processed by the bridge.
 .Sh PACKET FILTERING
 Packet filtering can be used with any firewall package that hooks in via the
 .Xr pfil 9
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index bc421a8e156d..d8eff929e47b 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -254,6 +254,7 @@ struct bridge_iflist {
        uint32_t                bif_addrcnt;    /* cur. # of addresses */
        uint32_t                bif_addrexceeded;/* # of address violations */
        struct epoch_context    bif_epoch_ctx;
+       ether_vlanid_t          bif_untagged;   /* untagged vlan id */
 };
 
 /*
@@ -335,13 +336,12 @@ static int        bridge_enqueue(struct bridge_softc *, 
struct ifnet *,
 static void    bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int);
 
 static void    bridge_forward(struct bridge_softc *, struct bridge_iflist *,
-                   struct mbuf *m);
+                   struct mbuf *m, ether_vlanid_t vlan);
 static bool    bridge_member_ifaddrs(void);
-
 static void    bridge_timer(void *);
 
 static void    bridge_broadcast(struct bridge_softc *, struct ifnet *,
-                   struct mbuf *, int);
+                   struct mbuf *, int, ether_vlanid_t);
 static void    bridge_span(struct bridge_softc *, struct mbuf *);
 
 static int     bridge_rtupdate(struct bridge_softc *, const uint8_t *,
@@ -353,6 +353,8 @@ static void bridge_rtage(struct bridge_softc *);
 static void    bridge_rtflush(struct bridge_softc *, int);
 static int     bridge_rtdaddr(struct bridge_softc *, const uint8_t *,
                    ether_vlanid_t);
+static bool    bridge_vfilter_out(const struct bridge_iflist *,
+                   const struct mbuf *, ether_vlanid_t);
 
 static void    bridge_rtable_init(struct bridge_softc *);
 static void    bridge_rtable_fini(struct bridge_softc *);
@@ -400,6 +402,7 @@ static int  bridge_ioctl_sma(struct bridge_softc *, void *);
 static int     bridge_ioctl_sifprio(struct bridge_softc *, void *);
 static int     bridge_ioctl_sifcost(struct bridge_softc *, void *);
 static int     bridge_ioctl_sifmaxaddr(struct bridge_softc *, void *);
+static int     bridge_ioctl_sifuntagged(struct bridge_softc *, void *);
 static int     bridge_ioctl_addspan(struct bridge_softc *, void *);
 static int     bridge_ioctl_delspan(struct bridge_softc *, void *);
 static int     bridge_ioctl_gbparam(struct bridge_softc *, void *);
@@ -618,6 +621,8 @@ static const struct bridge_control bridge_control_table[] = 
{
        { bridge_ioctl_sifmaxaddr,      sizeof(struct ifbreq),
          BC_F_COPYIN|BC_F_SUSER },
 
+       { bridge_ioctl_sifuntagged,     sizeof(struct ifbreq),
+         BC_F_COPYIN|BC_F_SUSER },
 };
 static const int bridge_control_table_size = nitems(bridge_control_table);
 
@@ -1495,6 +1500,7 @@ bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg)
        req->ifbr_addrcnt = bif->bif_addrcnt;
        req->ifbr_addrmax = bif->bif_addrmax;
        req->ifbr_addrexceeded = bif->bif_addrexceeded;
+       req->ifbr_untagged = bif->bif_untagged;
 
        /* Copy STP state options as flags */
        if (bp->bp_operedge)
@@ -1872,6 +1878,25 @@ bridge_ioctl_sifmaxaddr(struct bridge_softc *sc, void 
*arg)
        return (0);
 }
 
+static int
+bridge_ioctl_sifuntagged(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 (ENOENT);
+
+       if (req->ifbr_untagged > DOT1Q_VID_MAX)
+               return (EINVAL);
+
+       if (req->ifbr_untagged != DOT1Q_VID_NULL)
+               bif->bif_flags |= IFBIF_VLANFILTER;
+       bif->bif_untagged = req->ifbr_untagged;
+       return (0);
+}
+
 static int
 bridge_ioctl_addspan(struct bridge_softc *sc, void *arg)
 {
@@ -2376,7 +2401,7 @@ bridge_transmit(struct ifnet *ifp, struct mbuf *m)
            NULL) {
                error = bridge_enqueue(sc, dst_if, m);
        } else
-               bridge_broadcast(sc, ifp, m, 0);
+               bridge_broadcast(sc, ifp, m, 0, DOT1Q_VID_NULL);
 
        return (error);
 }
@@ -2430,12 +2455,11 @@ bridge_qflush(struct ifnet *ifp __unused)
  */
 static void
 bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
-    struct mbuf *m)
+    struct mbuf *m, ether_vlanid_t vlan)
 {
        struct bridge_iflist *dbif;
        struct ifnet *src_if, *dst_if, *ifp;
        struct ether_header *eh;
-       uint16_t vlan;
        uint8_t *dst;
        int error;
 
@@ -2446,7 +2470,6 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
 
        if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
        if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
-       vlan = VLANTAGOF(m);
 
        if ((sbif->bif_flags & IFBIF_STP) &&
            sbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING)
@@ -2535,7 +2558,7 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
        }
 
        if (dst_if == NULL) {
-               bridge_broadcast(sc, src_if, m, 1);
+               bridge_broadcast(sc, src_if, m, 1, vlan);
                return;
        }
 
@@ -2555,6 +2578,10 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
        if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)
                goto drop;
 
+       /* Do VLAN filtering. */
+       if (!bridge_vfilter_out(dbif, m, vlan))
+               goto drop;
+
        if ((dbif->bif_flags & IFBIF_STP) &&
            dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING)
                goto drop;
@@ -2636,6 +2663,27 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                return (NULL);
        }
 
+       /* Do VLAN filtering. */
+       if (bif->bif_flags & IFBIF_VLANFILTER) {
+               /*
+                * If the frame was received with a tag, drop it, since we only
+                * support untagged ports which shouldn't be receiving tagged
+                * frames.
+                *
+                * If the frame was received without a tag, and the port doesn't
+                * have an untagged vlan configured, drop it.
+                */
+               if (vlan != DOT1Q_VID_NULL ||
+                   bif->bif_untagged == DOT1Q_VID_NULL) {
+                       if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
+                       m_freem(m);
+                       return (NULL);
+               }
+
+               /* Otherwise, assign the untagged frame to the correct vlan. */
+               vlan = bif->bif_untagged;
+       }
+
        bridge_span(sc, m);
 
        if (m->m_flags & (M_BCAST|M_MCAST)) {
@@ -2662,7 +2710,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                }
 
                /* Perform the bridge forwarding function with the copy. */
-               bridge_forward(sc, bif, mc);
+               bridge_forward(sc, bif, mc, vlan);
 
 #ifdef DEV_NETMAP
                /*
@@ -2801,7 +2849,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 #undef GRAB_OUR_PACKETS
 
        /* Perform the bridge forwarding function. */
-       bridge_forward(sc, bif, m);
+       bridge_forward(sc, bif, m, vlan);
 
        return (NULL);
 }
@@ -2839,7 +2887,7 @@ bridge_inject(struct ifnet *ifp, struct mbuf *m)
  */
 static void
 bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
-    struct mbuf *m, int runfilt)
+    struct mbuf *m, int runfilt, ether_vlanid_t vlan)
 {
        struct bridge_iflist *dbif, *sbif;
        struct mbuf *mc;
@@ -2867,6 +2915,10 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet 
*src_if,
                if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE))
                        continue;
 
+               /* Do VLAN filtering. */
+               if (!bridge_vfilter_out(dbif, m, vlan))
+                       continue;
+
                if ((dbif->bif_flags & IFBIF_STP) &&
                    dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING)
                        continue;
@@ -2950,6 +3002,67 @@ bridge_span(struct bridge_softc *sc, struct mbuf *m)
        }
 }
 
+/*
+ * Outgoing VLAN filtering.  Given a frame, its vlan, and the member interface
+ * we intend to send it to, decide whether the port configuration allows it to
+ * be sent.
+ */
+static bool
+bridge_vfilter_out(const struct bridge_iflist *dbif, const struct mbuf *m,
+    ether_vlanid_t vlan)
+{
+       struct ether_header *eh;
+
+       NET_EPOCH_ASSERT();
+
+       /* If VLAN filtering isn't enabled, pass everything. */
+       if ((dbif->bif_flags & IFBIF_VLANFILTER) == 0)
+               return (true);
+
+       /*
+        * Always allow untagged 802.1D STP frames, even if they would
+        * otherwise be dropped.  This is required for STP to work on
+        * a filtering bridge.
+        *
+        * Tagged STP (Cisco PVST+) is a non-standard extension, so
+        * handle those frames via the normal filtering path.
+        */
+       eh = mtod(m, struct ether_header *);
+       if (vlan == DOT1Q_VID_NULL &&
+           memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0)
+               return (true);
+
+       /*
+        * If the frame wasn't assigned to a vlan at ingress, drop it.
+        * We can't forward these frames to filtering ports because we
+        * don't know what VLAN they're supposed to be in.
+        */
+       if (vlan == DOT1Q_VID_NULL)
+               return (false);
+
+       /*
+        * If the frame was received with a vlan tag then drop it,
+        * since we only support untagged ports.
+        *
+        * If the egress port doesn't have an untagged vlan configured,
+        * it doesn't want untagged frames, so drop it.
+        */
+       if (VLANTAGOF(m) != DOT1Q_VID_NULL ||
+           dbif->bif_untagged == DOT1Q_VID_NULL)
+               return (false);
+
+       /*
+        * Make sure the frame's vlan matches the port's untagged vlan.
+        */
+       if (vlan != dbif->bif_untagged)
+               return (false);
+
+       /*
+        * Everything looks fine, so pass this frame.
+        */
+       return (true);
+}
+
 /*
  * bridge_rtupdate:
  *
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
index 90beb6c96d82..c72afcc1785d 100644
--- a/sys/net/if_bridgevar.h
+++ b/sys/net/if_bridgevar.h
@@ -122,6 +122,7 @@
 #define        BRDGSPROTO              28      /* set protocol (ifbrparam) */
 #define        BRDGSTXHC               29      /* set tx hold count 
(ifbrparam) */
 #define        BRDGSIFAMAX             30      /* set max interface addrs 
(ifbreq) */
+#define        BRDGSIFUNTAGGED         31      /* set if untagged vlan */
 
 /*
  * Generic bridge control request.
@@ -139,6 +140,7 @@ struct ifbreq {
        uint32_t        ifbr_addrcnt;           /* member if addr number */
        uint32_t        ifbr_addrmax;           /* member if addr max */
        uint32_t        ifbr_addrexceeded;      /* member if addr violations */
+       ether_vlanid_t  ifbr_untagged;          /* member if untagged vlan */
        uint8_t         pad[32];
 };
 
@@ -155,10 +157,11 @@ struct ifbreq {
 #define        IFBIF_BSTP_ADMEDGE      0x0200  /* member stp admin edge 
enabled */
 #define        IFBIF_BSTP_ADMCOST      0x0400  /* member stp admin path cost */
 #define        IFBIF_PRIVATE           0x0800  /* if is a private segment */
+#define        IFBIF_VLANFILTER        0x1000  /* if does vlan filtering */
 
 #define        IFBIFBITS       "\020\001LEARNING\002DISCOVER\003STP\004SPAN" \
                        "\005STICKY\014PRIVATE\006EDGE\007AUTOEDGE\010PTP" \
-                       "\011AUTOPTP"
+                       "\011AUTOPTP\015VLANFILTER"
 #define        IFBIFMASK       
~(IFBIF_BSTP_EDGE|IFBIF_BSTP_AUTOEDGE|IFBIF_BSTP_PTP| \
                        IFBIF_BSTP_AUTOPTP|IFBIF_BSTP_ADMEDGE| \
                        IFBIF_BSTP_ADMCOST)     /* not saved */
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 57eb8ebcf12c..af116d6e3f7a 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -74,7 +74,7 @@
  * cannot include sys/param.h and should only be updated here.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1500050
+#define __FreeBSD_version 1500051
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
index 2c6b039048e3..c6fa0a69ea7c 100755
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -829,6 +829,129 @@ member_ifaddrs_vlan_cleanup()
        vnet_cleanup
 }
 
+atf_test_case "vlan_pvid" "cleanup"
+vlan_pvid_head()
+{
+       atf_set descr 'bridge with two ports with pvid set'
+       atf_set require.user root
+}
+
+vlan_pvid_body()
+{
+       vnet_init
+       vnet_init_bridge
+
+       epone=$(vnet_mkepair)
+       eptwo=$(vnet_mkepair)
+
+       vnet_mkjail one ${epone}b
+       vnet_mkjail two ${eptwo}b
+
+       jexec one ifconfig ${epone}b 192.0.2.1/24 up
+       jexec two ifconfig ${eptwo}b 192.0.2.2/24 up
+
+       bridge=$(vnet_mkbridge)
+
+       ifconfig ${bridge} up
+       ifconfig ${epone}a up
+       ifconfig ${eptwo}a up
+       ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+       ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 20
+
+       # With VLAN filtering enabled, traffic should be passed.
+       atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+       atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+       # Removed the untagged VLAN on one port; traffic should not be passed.
+       ifconfig ${bridge} -untagged ${epone}a
+       atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+       atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_cleanup()
+{
+       vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_filtered" "cleanup"
+vlan_pvid_filtered_head()
+{
+       atf_set descr 'bridge with two ports with different pvids'
+       atf_set require.user root
+}
+
+vlan_pvid_filtered_body()
+{
+       vnet_init
+       vnet_init_bridge
+
+       epone=$(vnet_mkepair)
+       eptwo=$(vnet_mkepair)
+
+       vnet_mkjail one ${epone}b
+       vnet_mkjail two ${eptwo}b
+
+       jexec one ifconfig ${epone}b 192.0.2.1/24 up
+       jexec two ifconfig ${eptwo}b 192.0.2.2/24 up
+
+       bridge=$(vnet_mkbridge)
+
+       ifconfig ${bridge} up
+       ifconfig ${epone}a up
+       ifconfig ${eptwo}a up
+       ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+       ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 30
+
+       atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+       atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_filtered_cleanup()
+{
+       vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_tagged" "cleanup"
+vlan_pvid_tagged_head()
+{
+       atf_set descr 'bridge pvid with tagged frames for pvid'
+       atf_set require.user root
+}
+
+vlan_pvid_tagged_body()
+{
+       vnet_init
+       vnet_init_bridge
+
+       epone=$(vnet_mkepair)
+       eptwo=$(vnet_mkepair)
+
+       vnet_mkjail one ${epone}b
+       vnet_mkjail two ${eptwo}b
+
+       # Create two tagged interfaces on the appropriate VLANs
+       jexec one ifconfig ${epone}b up
+       jexec one ifconfig ${epone}b.20 create 192.0.2.1/24 up
+       jexec two ifconfig ${eptwo}b up
+       jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up
+
+       bridge=$(vnet_mkbridge)
+
+       ifconfig ${bridge} up
+       ifconfig ${epone}a up
+       ifconfig ${eptwo}a up
+       ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+       ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 20
+
+       # Tagged frames should not be passed.
+       atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+       atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_tagged_cleanup()
+{
+       vnet_cleanup
+}
 atf_init_test_cases()
 {
        atf_add_test_case "bridge_transmit_ipv4_unicast"
@@ -847,4 +970,7 @@ atf_init_test_cases()
        atf_add_test_case "member_ifaddrs_enabled"
        atf_add_test_case "member_ifaddrs_disabled"
        atf_add_test_case "member_ifaddrs_vlan"
+       atf_add_test_case "vlan_pvid"
+       atf_add_test_case "vlan_pvid_filtered"
+       atf_add_test_case "vlan_pvid_tagged"
 }

Reply via email to