From nobody Sat Jul 5 07:13:17 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4bZ1tj4tDgz61Bjf; Sat, 05 Jul 2025 07:13:17 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bZ1tj1QqNz444l; Sat, 05 Jul 2025 07:13:17 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1751699597; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zeBrJswbzaheTLlm36bySi09Ezv6v+uHR6njnDCiKgo=; b=yKgXHrT1IcooQUDqCprHTrPYgYBqQ8PPaTYzcpUObXF+kqdckHt3ZD4pQ0V+FIcVZGLBZR HEJUUSjIPtWQWevYAenAEF+g1PBJignhlD0PJBx4KI2XAavFBEMnH1ZXrkgp3yS9JtdXIj tOdAikwXxd6DcWF7f89DNTMyt7cwiT/feW8DFDlYNwfj0GrnOMtCZx4jEk7NzVy6QeW/H8 zfwDYIe0IMm7msZWlg7+ASxjK3Irk76coT6kxVQTjcLQUn6pstUB7yOib9qEZdSLb4DN3i RHoL9TYs9i7nXsHDP8zybT+1OSHvGD/AGjqj96smJu9AfslAXLPLIwcGl+9xmg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1751699597; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zeBrJswbzaheTLlm36bySi09Ezv6v+uHR6njnDCiKgo=; b=XLxKVA4B2sKs+fdceCKw7ISBYLuU1nkmp0P/nAcDwo2Io2iOVb2XaiHLFpFO6RDvBZNBxV MUUQ2YuUoTyh2YZ5ZkggcOf1qXg62Sjhzm2Pyh672+hDI/ZDzHp0XFUcJeAVpfWWDJssDf 0alw8AnFevLJqioKy6yXPMRn2Qo3zOr2tNNA3JOSQ2B0aIUIlpLk1XPuABbCarKKzCxvKO WvqyKCDaC+rG1J6uN9AWPXqqRNMpeTpkk2RFV2lQ1cZ7Ckiq7iIqzFqdgxquzO3cUwwNNW Vot3kU+WASvobfz6KsjFUMq0wxBjCQFHimdhDsPNYiMIOkMaep1vb4YByi2H8A== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1751699597; a=rsa-sha256; cv=none; b=uGjHhJbJwhH5teDni7d6yM0pWnIHdSDKgGKld29N4uStiKnCdtVeTzmzrTXtZm2A+VE9Bw RIyktx14yKITgA6Wt3nAibv/2yTtPmobiZ90OwIW4ykHCNbmNYjVcAW1qhvq4KIJSZDJ7R Or3OCVV9wQlS01infGgD62lWChemfYwcvLKsgeXIzqV7bZ/eSDnjdwYr74rOBEAytZpjzE AF4EiLCVBJiBCadgtrb0YANudcgxZ5/z0SeSX/4ot8aXmD91v/PhxXQlMZH/jpEK51vP+T zjZVpFn4iJyeEsHhJRPgxyA/WUhHoYIsSCmHgO+G70gkp8SN2Lz31fYQvOm9Lg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4bZ1tj11Lvz9H1; Sat, 05 Jul 2025 07:13:17 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 5657DHIG017512; Sat, 5 Jul 2025 07:13:17 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 5657DH2K017509; Sat, 5 Jul 2025 07:13:17 GMT (envelope-from git) Date: Sat, 5 Jul 2025 07:13:17 GMT Message-Id: <202507050713.5657DH2K017509@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Lexi Winter Subject: git: 877a7a325b98 - main - bridge: transparently add and remove VLAN tags List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: ivy X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 877a7a325b9824995e920d070a0bfb0c6a1cc7e2 Auto-Submitted: auto-generated The branch main has been updated by ivy: URL: https://cgit.FreeBSD.org/src/commit/?id=877a7a325b9824995e920d070a0bfb0c6a1cc7e2 commit 877a7a325b9824995e920d070a0bfb0c6a1cc7e2 Author: Lexi Winter AuthorDate: 2025-07-05 04:54:25 +0000 Commit: Lexi Winter 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" }