The branch main has been updated by ivy:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=877a7a325b9824995e920d070a0bfb0c6a1cc7e2

commit 877a7a325b9824995e920d070a0bfb0c6a1cc7e2
Author:     Lexi Winter <i...@freebsd.org>
AuthorDate: 2025-07-05 04:54:25 +0000
Commit:     Lexi Winter <i...@freebsd.org>
CommitDate: 2025-07-05 07:04:31 +0000

    bridge: transparently add and remove VLAN tags
    
    When vlan filtering is enabled, add or remove tags as required to allow
    ports with different configurations to communicate:
    
    - When receiving an untagged frame, insert a new tag based on the
      interface's configured untagged vlan.
    
    - When sending a tagged frame, and the frame's vlan id matches the
      outgoing interface's configured untagged vlan, strip the tag.
    
    Since we now set the vlan id in the mbuf, remove the vlan argument to
    bridge_forward() and bridge_broadcast() and take it from VLANTAGOF
    instead.
    
    Add tests for the new functionality.
    
    Reviewed by:    kp, des
    Approved by:    des (mentor)
    Differential Revision:  https://reviews.freebsd.org/D50500
---
 share/man/man4/bridge.4         |  7 ++--
 sys/net/if_bridge.c             | 76 ++++++++++++++++++++++++++---------------
 tests/sys/net/if_bridge_test.sh | 46 +++++++++++++++++++++++++
 3 files changed, 99 insertions(+), 30 deletions(-)

diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
index 73e7fd56af78..6fae37004efe 100644
--- a/share/man/man4/bridge.4
+++ b/share/man/man4/bridge.4
@@ -298,8 +298,11 @@ 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.
+The bridge will automatically insert or remove 802.1q tags as needed,
+based on the interface configuration, when forwarding frames between
+interfaces.
+This tag processing is only done for interfaces with VLAN filtering
+enabled.
 .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 d8eff929e47b..5b54c119eabf 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -332,16 +332,16 @@ static void       bridge_inject(struct ifnet *, struct 
mbuf *);
 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 *,
@@ -2175,12 +2175,25 @@ bridge_stop(struct ifnet *ifp, int disable)
  *
  */
 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;
@@ -2188,6 +2201,18 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet 
*dst_ifp, struct mbuf *m)
                len = m->m_pkthdr.len;
                mflags = m->m_flags;
 
+               /*
+                * If VLAN filtering is enabled, and the native VLAN ID of the
+                * outgoing interface matches the VLAN ID of the frame, remove
+                * the VLAN header.
+                */
+               if ((bif->bif_flags & IFBIF_VLANFILTER) &&
+                   bif->bif_untagged != DOT1Q_VID_NULL &&
+                   VLANTAGOF(m) == bif->bif_untagged) {
+                       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.
@@ -2259,7 +2284,7 @@ bridge_dummynet(struct mbuf *m, struct ifnet *ifp)
                        return;
        }
 
-       bridge_enqueue(sc, ifp, m);
+       bridge_enqueue(sc, ifp, m, NULL);
 }
 
 /*
@@ -2354,7 +2379,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct 
sockaddr *sa,
                                }
                        }
 
-                       bridge_enqueue(sc, dst_if, mc);
+                       bridge_enqueue(sc, dst_if, mc, bif);
                }
                if (used == 0)
                        m_freem(m);
@@ -2372,7 +2397,7 @@ sendunicast:
                return (0);
        }
 
-       bridge_enqueue(sc, dst_if, m);
+       bridge_enqueue(sc, dst_if, m, NULL);
        return (0);
 }
 
@@ -2399,9 +2424,9 @@ bridge_transmit(struct ifnet *ifp, struct mbuf *m)
        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);
 }
@@ -2455,18 +2480,20 @@ bridge_qflush(struct ifnet *ifp __unused)
  */
 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);
@@ -2558,7 +2585,7 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
        }
 
        if (dst_if == NULL) {
-               bridge_broadcast(sc, src_if, m, 1, vlan);
+               bridge_broadcast(sc, src_if, m, 1);
                return;
        }
 
@@ -2593,7 +2620,7 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
                        return;
        }
 
-       bridge_enqueue(sc, dst_if, m);
+       bridge_enqueue(sc, dst_if, m, dbif);
        return;
 
 drop:
@@ -2682,6 +2709,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 
                /* Otherwise, assign the untagged frame to the correct vlan. */
                vlan = bif->bif_untagged;
+               m->m_pkthdr.ether_vtag = bif->bif_untagged;
+               m->m_flags |= M_VLANTAG;
        }
 
        bridge_span(sc, m);
@@ -2710,7 +2739,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                }
 
                /* Perform the bridge forwarding function with the copy. */
-               bridge_forward(sc, bif, mc, vlan);
+               bridge_forward(sc, bif, mc);
 
 #ifdef DEV_NETMAP
                /*
@@ -2849,7 +2878,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 #undef GRAB_OUR_PACKETS
 
        /* Perform the bridge forwarding function. */
-       bridge_forward(sc, bif, m, vlan);
+       bridge_forward(sc, bif, m);
 
        return (NULL);
 }
@@ -2887,16 +2916,18 @@ 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, 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) {
@@ -2962,7 +2993,7 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet 
*src_if,
                                continue;
                }
 
-               bridge_enqueue(sc, dst_if, mc);
+               bridge_enqueue(sc, dst_if, mc, dbif);
        }
        if (used == 0)
                m_freem(m);
@@ -2998,7 +3029,7 @@ bridge_span(struct bridge_softc *sc, struct mbuf *m)
                        continue;
                }
 
-               bridge_enqueue(sc, dst_if, mc);
+               bridge_enqueue(sc, dst_if, mc, bif);
        }
 }
 
@@ -3040,17 +3071,6 @@ bridge_vfilter_out(const struct bridge_iflist *dbif, 
const struct mbuf *m,
        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.
         */
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
index c6fa0a69ea7c..90cc91ac594f 100755
--- a/tests/sys/net/if_bridge_test.sh
+++ b/tests/sys/net/if_bridge_test.sh
@@ -952,6 +952,51 @@ vlan_pvid_tagged_cleanup()
 {
        vnet_cleanup
 }
+
+atf_test_case "vlan_pvid_1q" "cleanup"
+vlan_pvid_1q_head()
+{
+       atf_set descr '802.1q tag addition and removal'
+       atf_set require.user root
+}
+
+vlan_pvid_1q_body()
+{
+       vnet_init
+       vnet_init_bridge
+
+       epone=$(vnet_mkepair)
+       eptwo=$(vnet_mkepair)
+
+       vnet_mkjail one ${epone}b
+       vnet_mkjail two ${eptwo}b
+
+       # 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} addm ${epone}a untagged ${epone}a 20
+       ifconfig ${bridge} addm ${eptwo}a
+
+       ifconfig ${bridge} up
+       ifconfig ${epone}a up
+       ifconfig ${eptwo}a up
+
+       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_1q_cleanup()
+{
+       vnet_cleanup
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case "bridge_transmit_ipv4_unicast"
@@ -971,6 +1016,7 @@ atf_init_test_cases()
        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_1q"
        atf_add_test_case "vlan_pvid_filtered"
        atf_add_test_case "vlan_pvid_tagged"
 }

Reply via email to