Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167323382
D50500.id155947.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D50500.id155947.diff
View Options
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
--- 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 May 23, 2025
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -2698,8 +2698,10 @@
.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.
+The effect of this option is described in the
+.Dq VLAN SUPPORT
+section of
+.Xr bridge 4 .
.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 17, 2025
+.Dd May 23, 2025
.Dt IF_BRIDGE 4
.Os
.Sh NAME
@@ -275,24 +275,25 @@
driver has limited support for virtual LANs (VLANs).
An incoming packet with an 802.1Q tag will be assigned to the
appropriate VLAN.
+The bridge always implements independent VLAN learning, i.e. MAC
+addresses are learnt on a per-VLAN basis, and the same MAC address may
+be learnt on multiple ports on different VLANs.
+.Pp
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.
+option.
+This affects handling of both incoming and outgoing frames.
+For incoming frames, if the frame has no 802.1Q tag (or the VLAN ID in
+the tag is zero), a tag will be inserted based on the port's PVID.
+For outgoing frames, if the frame has an 802.1Q tag which matches the
+port's PVID, the tag will be stripped from the frame prior to sending.
.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.
+An interface with a PVID configured may only send and receive frames for
+its configured PVID.
.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
@@ -332,16 +332,16 @@
static int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
static int bridge_enqueue(struct bridge_softc *, struct ifnet *,
- struct mbuf *);
+ struct mbuf *, struct bridge_iflist *);
static void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int);
static void bridge_forward(struct bridge_softc *, struct bridge_iflist *,
- struct mbuf *m, ether_vlanid_t vlan);
+ struct mbuf *m);
static bool bridge_member_ifaddrs(void);
static void bridge_timer(void *);
static void bridge_broadcast(struct bridge_softc *, struct ifnet *,
- struct mbuf *, int, ether_vlanid_t);
+ struct mbuf *, int);
static void bridge_span(struct bridge_softc *, struct mbuf *);
static int bridge_rtupdate(struct bridge_softc *, const uint8_t *,
@@ -2172,12 +2172,25 @@
*
*/
static int
-bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m)
+bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m,
+ struct bridge_iflist *bif)
{
int len, err = 0;
short mflags;
struct mbuf *m0;
+ /*
+ * Find the bridge member port this packet is being sent on, if the
+ * caller didn't already provide it.
+ */
+ if (bif == NULL)
+ bif = bridge_lookup_member_if(sc, dst_ifp);
+ if (bif == NULL) {
+ /* Perhaps the interface was removed from the bridge */
+ m_freem(m);
+ return (EINVAL);
+ }
+
/* We may be sending a fragment so traverse the mbuf */
for (; m; m = m0) {
m0 = m->m_nextpkt;
@@ -2185,6 +2198,15 @@
len = m->m_pkthdr.len;
mflags = m->m_flags;
+ /*
+ * If the native VLAN ID of the outgoing interfaces matches the
+ * VLAN ID of this frame, remove the VLAN header.
+ */
+ if (bif->bif_pvid != 0 && VLANTAGOF(m) == bif->bif_pvid) {
+ m->m_flags &= ~M_VLANTAG;
+ m->m_pkthdr.ether_vtag = 0;
+ }
+
/*
* If underlying interface can not do VLAN tag insertion itself
* then attach a packet tag that holds it.
@@ -2256,7 +2278,7 @@
return;
}
- bridge_enqueue(sc, ifp, m);
+ bridge_enqueue(sc, ifp, m, NULL);
}
/*
@@ -2351,7 +2373,7 @@
}
}
- bridge_enqueue(sc, dst_if, mc);
+ bridge_enqueue(sc, dst_if, mc, bif);
}
if (used == 0)
m_freem(m);
@@ -2369,7 +2391,7 @@
return (0);
}
- bridge_enqueue(sc, dst_if, m);
+ bridge_enqueue(sc, dst_if, m, NULL);
return (0);
}
@@ -2396,9 +2418,9 @@
if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) &&
(dst_if = bridge_rtlookup(sc, eh->ether_dhost, DOT1Q_VID_NULL)) !=
NULL) {
- error = bridge_enqueue(sc, dst_if, m);
+ error = bridge_enqueue(sc, dst_if, m, NULL);
} else
- bridge_broadcast(sc, ifp, m, 0, DOT1Q_VID_NULL);
+ bridge_broadcast(sc, ifp, m, 0);
return (error);
}
@@ -2452,18 +2474,20 @@
*/
static void
bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif,
- struct mbuf *m, ether_vlanid_t vlan)
+ struct mbuf *m)
{
struct bridge_iflist *dbif;
struct ifnet *src_if, *dst_if, *ifp;
struct ether_header *eh;
uint8_t *dst;
int error;
+ ether_vlanid_t vlan;
NET_EPOCH_ASSERT();
src_if = m->m_pkthdr.rcvif;
ifp = sc->sc_ifp;
+ vlan = VLANTAGOF(m);
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
@@ -2555,7 +2579,7 @@
}
if (dst_if == NULL) {
- bridge_broadcast(sc, src_if, m, 1, vlan);
+ bridge_broadcast(sc, src_if, m, 1);
return;
}
@@ -2592,7 +2616,7 @@
return;
}
- bridge_enqueue(sc, dst_if, m);
+ bridge_enqueue(sc, dst_if, m, dbif);
return;
drop:
@@ -2663,15 +2687,30 @@
}
/*
- * 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 the port has a pvid assigned, do VLAN access checks.
*/
- 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);
+ if (bif->bif_pvid != DOT1Q_VID_NULL) {
+ if (vlan != DOT1Q_VID_NULL && vlan != bif->bif_pvid) {
+ /*
+ * If the frame has a tag, and the tag's vlan id doesn't
+ * match the pvid, drop the frame.
+ */
+ if_inc_counter(bifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ return (NULL);
+ } else if (vlan == DOT1Q_VID_NULL) {
+ /*
+ * If the frame is untagged, set its vlan to the port's
+ * pvid.
+ */
+ vlan = bif->bif_pvid;
+ m->m_pkthdr.ether_vtag = bif->bif_pvid;
+ m->m_flags |= M_VLANTAG;
+ }
+ /*
+ * Otherwise, vlan == bif_pvid, i.e. this is a tagged frame
+ * where the tag matches the pvid, so accept it as-is.
+ */
}
bridge_span(sc, m);
@@ -2700,7 +2739,7 @@
}
/* Perform the bridge forwarding function with the copy. */
- bridge_forward(sc, bif, mc, vlan);
+ bridge_forward(sc, bif, mc);
#ifdef DEV_NETMAP
/*
@@ -2842,7 +2881,7 @@
#undef GRAB_OUR_PACKETS
/* Perform the bridge forwarding function. */
- bridge_forward(sc, bif, m, vlan);
+ bridge_forward(sc, bif, m);
return (NULL);
}
@@ -2880,16 +2919,18 @@
*/
static void
bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
- struct mbuf *m, int runfilt, ether_vlanid_t vlan)
+ struct mbuf *m, int runfilt)
{
struct bridge_iflist *dbif, *sbif;
struct mbuf *mc;
struct ifnet *dst_if;
int used = 0, i;
+ ether_vlanid_t vlan;
NET_EPOCH_ASSERT();
sbif = bridge_lookup_member_if(sc, src_if);
+ vlan = VLANTAGOF(m);
/* Filter on the bridge interface before broadcasting */
if (runfilt && PFIL_HOOKED_OUT_46) {
@@ -2956,7 +2997,7 @@
continue;
}
- bridge_enqueue(sc, dst_if, mc);
+ bridge_enqueue(sc, dst_if, mc, dbif);
}
if (used == 0)
m_freem(m);
@@ -2992,7 +3033,7 @@
continue;
}
- bridge_enqueue(sc, dst_if, mc);
+ bridge_enqueue(sc, dst_if, mc, bif);
}
}
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
@@ -856,14 +856,14 @@
vnet_cleanup
}
-atf_test_case "vlan_pvid_tagged" "cleanup"
-vlan_pvid_tagged_head()
+atf_test_case "vlan_pvid_1q" "cleanup"
+vlan_pvid_1q_head()
{
- atf_set descr 'bridge pvid with tagged frames for pvid'
+ atf_set descr '802.1q tag addition and removal'
atf_set require.user root
}
-vlan_pvid_tagged_body()
+vlan_pvid_1q_body()
{
vnet_init
vnet_init_bridge
@@ -874,68 +874,32 @@
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
+ # Set up one jail with an access port, and the other with a trunk port.
+ # This forces the bridge to add and remove .1q tags to bridge the
+ # traffic.
+
+ jexec one ifconfig ${epone}b 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
+ # ifpvid forces frames on vlan 20 to be untagged.
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)
+ # No PVID here, so we send/receive tagged frames.
+ ifconfig ${bridge} addm ${eptwo}a
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
+ 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_tagged_noaccess_cleanup()
+vlan_pvid_1q_cleanup()
{
- vnet_cleanup
+ vnet_cleanup
}
atf_init_test_cases()
@@ -956,7 +920,6 @@
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_1q"
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
Fri, Aug 21, 9:51 PM (18 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37057829
Default Alt Text
D50500.id155947.diff (11 KB)
Attached To
Mode
D50500: bridge: transparently add and remove VLAN tags
Attached
Detach File
Event Timeline
Log In to Comment