Page MenuHomeFreeBSD

D49993.id155904.diff
No OneTemporary

D49993.id155904.diff

diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
--- a/sbin/ifconfig/ifbridge.c
+++ b/sbin/ifconfig/ifbridge.c
@@ -211,6 +211,8 @@
else
printf(" <unknown state %d>", state);
}
+ if (member->ifbr_pvid != 0)
+ printf(" pvid %u", (unsigned)member->ifbr_pvid);
printf("\n");
}
@@ -576,6 +578,24 @@
err(1, "BRDGSIFCOST %s", cost);
}
+static void
+setbridge_ifpvid(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 value: %s", vlanid);
+
+ strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
+ req.ifbr_pvid = val;
+
+ if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFPVID %s", vlanid);
+}
+
static void
setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg)
{
@@ -659,6 +679,7 @@
DEF_CMD_ARG2("ifpriority", setbridge_ifpriority),
DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost),
DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr),
+ DEF_CMD_ARG2("ifpvid", setbridge_ifpvid),
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
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -2695,6 +2695,11 @@
source addresses are dropped until an existing host cache entry expires or is
removed.
Set to 0 to disable.
+.It Cm ifpvid Ar interface Ar vlan-id
+Set the Port VLAN ID (PVID, sometimes called native VLAN ID) for this
+interface.
+Incoming frames which do not have an 802.1Q VLAN tag will inherit the
+PVID from the interface on which they were received.
.El
.Ss Link Aggregation and Link Failover Parameters
The following parameters are specific to lagg interfaces:
diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
--- 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 13, 2025
+.Dd May 17, 2025
.Dt IF_BRIDGE 4
.Os
.Sh NAME
@@ -269,6 +269,30 @@
.Va net.link.bridge.log_stp
node using
.Xr sysctl 8 .
+.Sh VLAN SUPPORT
+The
+.Nm
+driver has limited support for virtual LANs (VLANs).
+An incoming packet with an 802.1Q tag will be assigned to the
+appropriate VLAN.
+An interface's Port VLAN ID (PVID, sometimes called native VLAN ID) may
+be configured using the
+.Xr ifconfig 8
+.Cm ifpvid
+option; incoming packets with no 802.1Q tag (or where the VLAN ID in the
+tag is zero) will be assigned to the interface's PVID.
+.Pp
+An interface without a PVID configured will receive frames for all
+VLANs.
+An interface with a PVID configured will only receive frames for its
+configured PVID.
+In either case, if the incoming frame has an 802.1Q tag, the tag will be
+preserved in the output frame; if not, no tag will be added to the
+output frame.
+.Pp
+There is no support for adding or removing 802.1Q tags on outgoing
+frames, and for interfaces without a PVID configured, there is no way to
+restrict which VLANs the interface can send or receive frames for.
.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
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -254,6 +254,7 @@
uint32_t bif_addrcnt; /* cur. # of addresses */
uint32_t bif_addrexceeded;/* # of address violations */
struct epoch_context bif_epoch_ctx;
+ ether_vlanid_t bif_pvid; /* native vlan id */
};
/*
@@ -335,13 +336,12 @@
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 *,
@@ -400,6 +400,7 @@
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_sifpvid(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 +619,8 @@
{ bridge_ioctl_sifmaxaddr, sizeof(struct ifbreq),
BC_F_COPYIN|BC_F_SUSER },
+ { bridge_ioctl_sifpvid, sizeof(struct ifbreq),
+ BC_F_COPYIN|BC_F_SUSER },
};
static const int bridge_control_table_size = nitems(bridge_control_table);
@@ -1495,6 +1498,7 @@
req->ifbr_addrcnt = bif->bif_addrcnt;
req->ifbr_addrmax = bif->bif_addrmax;
req->ifbr_addrexceeded = bif->bif_addrexceeded;
+ req->ifbr_pvid = bif->bif_pvid;
/* Copy STP state options as flags */
if (bp->bp_operedge)
@@ -1872,6 +1876,23 @@
return (0);
}
+static int
+bridge_ioctl_sifpvid(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_pvid > DOT1Q_VID_MAX)
+ return (EINVAL);
+
+ bif->bif_pvid = req->ifbr_pvid;
+ return (0);
+}
+
static int
bridge_ioctl_addspan(struct bridge_softc *sc, void *arg)
{
@@ -2376,7 +2397,7 @@
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 +2451,11 @@
*/
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 +2466,6 @@
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 +2554,7 @@
}
if (dst_if == NULL) {
- bridge_broadcast(sc, src_if, m, 1);
+ bridge_broadcast(sc, src_if, m, 1, vlan);
return;
}
@@ -2555,6 +2574,12 @@
if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)
goto drop;
+ /*
+ * If the destination port is on a different vlan, drop the frame.
+ */
+ if ((dbif->bif_pvid != DOT1Q_VID_NULL) && (vlan != dbif->bif_pvid))
+ goto drop;
+
if ((dbif->bif_flags & IFBIF_STP) &&
dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING)
goto drop;
@@ -2636,6 +2661,18 @@
return (NULL);
}
+ /*
+ * If the frame has no vlan id, take the vlan from the interface.
+ * Otherwise, make sure it matches the port's pvid, if any.
+ */
+ if (vlan == DOT1Q_VID_NULL)
+ vlan = bif->bif_pvid;
+ else if ((bif->bif_pvid != DOT1Q_VID_NULL) && (vlan != bif->bif_pvid)) {
+ if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ return (NULL);
+ }
+
bridge_span(sc, m);
if (m->m_flags & (M_BCAST|M_MCAST)) {
@@ -2662,7 +2699,7 @@
}
/* Perform the bridge forwarding function with the copy. */
- bridge_forward(sc, bif, mc);
+ bridge_forward(sc, bif, mc, vlan);
#ifdef DEV_NETMAP
/*
@@ -2804,7 +2841,7 @@
#undef GRAB_OUR_PACKETS
/* Perform the bridge forwarding function. */
- bridge_forward(sc, bif, m);
+ bridge_forward(sc, bif, m, vlan);
return (NULL);
}
@@ -2842,7 +2879,7 @@
*/
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;
@@ -2870,6 +2907,11 @@
if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE))
continue;
+ /* VLAN filtering for interfaces with non-zero VLAN ID */
+ if ((dbif->bif_pvid != DOT1Q_VID_NULL) &&
+ (vlan != dbif->bif_pvid))
+ continue;
+
if ((dbif->bif_flags & IFBIF_STP) &&
dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING)
continue;
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
--- 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 BRDGSIFPVID 31 /* set if PVID */
/*
* Generic bridge control request.
@@ -139,7 +140,8 @@
uint32_t ifbr_addrcnt; /* member if addr number */
uint32_t ifbr_addrmax; /* member if addr max */
uint32_t ifbr_addrexceeded; /* member if addr violations */
- uint8_t pad[32];
+ ether_vlanid_t ifbr_pvid; /* member if PVID */
+ uint8_t pad[30];
};
/* BRDGGIFFLAGS, BRDGSIFFLAGS */
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -775,6 +775,165 @@
}
member_ifaddrs_disabled_cleanup()
+ epone=$(vnet_mkepair)
+{
+ 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 ifpvid ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a ifpvid ${eptwo}a 20
+
+ 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
+}
+
+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 ifpvid ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a ifpvid ${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 ifpvid ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a ifpvid ${eptwo}a 20
+
+ atf_check -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_tagged_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_tagged_noaccess" "cleanup"
+vlan_pvid_tagged_noaccess_head()
+{
+ atf_set descr 'bridge pvid with non-pvid tagged frames'
+ atf_set require.user root
+}
+
+vlan_pvid_tagged_noaccess_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 ifpvid ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a ifpvid ${eptwo}a 30
+
+ atf_check -o ignore -s exit:2 jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -o ignore -s exit:2 jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_tagged_noaccess_cleanup()
{
vnet_cleanup
}
@@ -796,4 +955,8 @@
atf_add_test_case "many_bridge_members"
atf_add_test_case "member_ifaddrs_enabled"
atf_add_test_case "member_ifaddrs_disabled"
+ atf_add_test_case "vlan_pvid"
+ atf_add_test_case "vlan_pvid_filtered"
+ atf_add_test_case "vlan_pvid_tagged"
+ atf_add_test_case "vlan_pvid_tagged_noaccess"
}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 5, 3:12 PM (10 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24845824
Default Alt Text
D49993.id155904.diff (13 KB)

Event Timeline