Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F134786757
D49993.id155904.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D49993.id155904.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D49993: bridge(4): allow member interface vlan to be configured
Attached
Detach File
Event Timeline
Log In to Comment