From nobody Tue Aug 5 19:26:01 2025 X-Original-To: dev-commits-src-all@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 4bxNgs74V7z63cWf; Tue, 05 Aug 2025 19:26:01 +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 4bxNgs6Gk1z3qXP; Tue, 05 Aug 2025 19:26:01 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754421961; 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=2RAw1jzZE7TvI1zII+FjmQQxcLOWGvYkP5BQ9B41bfI=; b=e3D9q8PACAUCLGD0EMiU7KVJw6qox7kkKUW97xCifuNJpZhdzTOkMp+7F8C1wsc9pDhcxM UUPMqiN9rVfA2UR7Uiz60GcrZh2dDH1mwumi6PR1ZUurBTNDHjwCsOgIJpMt3QCgt38EXw sPV8RQWKF0iRtkvWDzRqZ137b7UbcBj42v0kY83JcmnWa4VWifc+ZPbLPr++u9N/iXZOar 6ldIDEpd4+ehOTNcMAux9apfsNDza7eA2Mb9XrSR/cnqIXVwcA9vUOUXJfUVKZsvS0RTSJ VEpVx86Pi1+IVOYYQMJIHZ93t6di+0znclazOebU02uxVssFPSaojbkTvRvs9g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754421961; 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=2RAw1jzZE7TvI1zII+FjmQQxcLOWGvYkP5BQ9B41bfI=; b=BtYxUblb1eRDZ0E8ZuV282wqfImxZkCS/F/8EBEMLhM9l1P3+3EIqI50H26qXlg3tz+UOV OVkWFWxwsNO1EPOUEKxNu/ym1SA63GFvk0JVwecmRZA6zl5CvF5UffnRCu8T9GWNy3q74B mjoz3JN/r+DPRv5wLJG+Cq81n2v+AqSkKNA+TmI2/p7h3GqzDMnMH7QrzasGv2/W9mny0F gJIUOK8rDfZhfdrSdFplzSbNGocCR4mxTQUPIibCR6c2N5hWYbUYdhq+7+r7qgHf6pAIRT d7rXGlFVWBezfxbi7GrVL+Yil+cZGM/CWCwB8qAjQEhHT28MYtH1s3ssxZxVsQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1754421961; a=rsa-sha256; cv=none; b=OckBXyQKopVZ1G/mkumH8CkD7cdM7YUvGKY/GjIHZKmu0TdSXFwPVX9hUF7RtjU78w+3qN XadLGH0eRePdabwvpDp1mrd4fp3SFQ5JO1MnQAhC09P41AeYAmE3exPo1rc6E/PfzKAL7T YWd0x0IG8sE0jRuk5GwFj6HWDowk4g603zhHUihE68LWwjwt8/L/L2asNE/ft4LafGjtaj qZ/bnjZHzFX4blUGIjFgQDDD3fax+J5mpEE/O8bbsGrAoVgiuk0+TECBQqdOidRmvgIOvJ /wXdeCPVPc/WEI/Tmr3c8DHtod4Phg0QTo6QwD7VdM/dE1hMLKcE7AnxRcx6kw== 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 4bxNgs57mrzpPK; Tue, 05 Aug 2025 19:26:01 +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 575JQ1Vi074566; Tue, 5 Aug 2025 19:26:01 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 575JQ1Zf074563; Tue, 5 Aug 2025 19:26:01 GMT (envelope-from git) Date: Tue, 5 Aug 2025 19:26:01 GMT Message-Id: <202508051926.575JQ1Zf074563@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: d43ac3b76ef7 - main - bridge: Make 802.1ad (Q-in-Q) configurable List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@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: d43ac3b76ef7897c72dcc6153cb1c33363b7b024 Auto-Submitted: auto-generated The branch main has been updated by ivy: URL: https://cgit.FreeBSD.org/src/commit/?id=d43ac3b76ef7897c72dcc6153cb1c33363b7b024 commit d43ac3b76ef7897c72dcc6153cb1c33363b7b024 Author: Lexi Winter AuthorDate: 2025-08-05 17:43:31 +0000 Commit: Lexi Winter CommitDate: 2025-08-05 18:34:47 +0000 bridge: Make 802.1ad (Q-in-Q) configurable Allowing tag stacking by default can permit VLAN-hopping attacks in certain configurations. To mitigate this, disallow sending Q-in-Q frames by default unless the new "qinq" option is enabled on the interface. The bridge flag "defqinq" can be used to restore the previous behaviour of allowing Q-in-Q on all interfaces. The bridge.4 changes from the differential are omitted here and will be landed via D51185. Reviewed by: kevans, pauamma_gundo.com (manpages) Differential Revision: https://reviews.freebsd.org/D51227 --- sbin/ifconfig/ifbridge.c | 18 ++++++++++++++++++ sbin/ifconfig/ifconfig.8 | 20 +++++++++++++++++++- sys/net/if_bridge.c | 24 ++++++++++++++++++++++++ sys/net/if_bridgevar.h | 8 ++++---- tests/sys/net/if_bridge_test.sh | 36 +++++++++++++++++++++++------------- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 335ed9bf513f..3566acdcf54c 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -883,6 +883,18 @@ unsetbridge_defuntagged(if_ctx *ctx, const char *val __unused, int dummy __unuse err(1, "BRDGSDEFPVID"); } +static void +setbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused) +{ + do_bridgeflag(ctx, val, IFBIF_QINQ, 1); +} + +static void +unsetbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused) +{ + do_bridgeflag(ctx, val, IFBIF_QINQ, 0); +} + static struct cmd bridge_cmds[] = { DEF_CMD_ARG("addm", setbridge_add), DEF_CMD_ARG("deletem", setbridge_delete), @@ -933,6 +945,12 @@ static struct cmd bridge_cmds[] = { unsetbridge_flags), DEF_CMD_ARG("defuntagged", setbridge_defuntagged), DEF_CMD("-defuntagged", 0, unsetbridge_defuntagged), + DEF_CMD("defqinq", (int32_t)IFBRF_DEFQINQ, + setbridge_flags), + DEF_CMD("-defqinq", (int32_t)IFBRF_DEFQINQ, + unsetbridge_flags), + DEF_CMD_ARG("qinq", setbridge_qinq), + DEF_CMD_ARG("-qinq", unsetbridge_qinq), }; static struct afswtch af_bridge = { diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 06ec62197fba..69a81f72421f 100644 --- 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 July 30, 2025 +.Dd August 5, 2025 .Dt IFCONFIG 8 .Os .Sh NAME @@ -2747,6 +2747,24 @@ Remove the provided list of VLAN IDs from the interface's VLAN access list. The list should be formatted as described for .Cm tagged . +.It Cm qinq Ar interface +Allow this interface to send 802.1ad +.Dq Q-in-Q +frames. +.It Cm -qinq Ar interface +Do not allow this interface to send 802.1ad +.Dq Q-in-Q +frames. +This is the default behavior. +.It Cm defqinq +Enable the +.Cm qinq +option by default on newly added members. +.It Cm -defqinq +Do not enable the +.Cm qinq +option by default on newly added members. +This is the default behavior. .El .Ss Link Aggregation and Link Failover Parameters The following parameters are specific to lagg interfaces: diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 46e54339a171..26ea4400e67d 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1496,6 +1496,8 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) bif->bif_savedcaps = ifs->if_capenable; if (sc->sc_flags & IFBRF_VLANFILTER) bif->bif_pvid = sc->sc_defpvid; + if (sc->sc_flags & IFBRF_DEFQINQ) + bif->bif_flags |= IFBIF_QINQ; /* * Assign the interface's MAC address to the bridge if it's the first @@ -2828,6 +2830,16 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) NET_EPOCH_ASSERT(); + /* We need the Ethernet header later, so make sure we have it now. */ + if (m->m_len < ETHER_HDR_LEN) { + m = m_pullup(m, ETHER_HDR_LEN); + if (m == NULL) { + if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return (NULL); + } + } + eh = mtod(m, struct ether_header *); vlan = VLANTAGOF(m); @@ -3250,6 +3262,18 @@ bridge_vfilter_in(const struct bridge_iflist *sbif, struct mbuf *m) if ((sbif->bif_sc->sc_flags & IFBRF_VLANFILTER) == 0) return (true); + /* If Q-in-Q is disabled, check for stacked tags. */ + if ((sbif->bif_flags & IFBIF_QINQ) == 0) { + struct ether_header *eh; + uint16_t proto; + + eh = mtod(m, struct ether_header *); + proto = ntohs(eh->ether_type); + + if (proto == ETHERTYPE_VLAN || proto == ETHERTYPE_QINQ) + return (false); + } + if (vlan == DOT1Q_VID_NULL) { /* * The frame doesn't have a tag. If the interface does not diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index 6718c5ebcc34..15194fecff7c 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -136,8 +136,9 @@ typedef uint32_t ifbr_flags_t; #define IFBRF_VLANFILTER (1U<<0) /* VLAN filtering enabled */ +#define IFBRF_DEFQINQ (1U<<1) /* 802.1ad Q-in-Q allowed by default */ -#define IFBRFBITS "\020\01VLANFILTER" +#define IFBRFBITS "\020\01VLANFILTER\02DEFQINQ" /* * Generic bridge control request. @@ -172,12 +173,11 @@ struct ifbreq { #define IFBIF_BSTP_ADMEDGE 0x0200 /* member stp admin edge enabled */ #define IFBIF_BSTP_ADMCOST 0x0400 /* member stp admin path cost */ #define IFBIF_PRIVATE 0x0800 /* if is a private segment */ -/* was IFBIF_VLANFILTER 0x1000 */ -#define IFBIF_QINQ 0x2000 /* if allows 802.1ad Q-in-Q */ +#define IFBIF_QINQ 0x1000 /* if allows 802.1ad Q-in-Q */ #define IFBIFBITS "\020\001LEARNING\002DISCOVER\003STP\004SPAN" \ "\005STICKY\014PRIVATE\006EDGE\007AUTOEDGE\010PTP" \ - "\011AUTOPTP" + "\011AUTOPTP\015QINQ" #define IFBIFMASK ~(IFBIF_BSTP_EDGE|IFBIF_BSTP_AUTOEDGE|IFBIF_BSTP_PTP| \ IFBIF_BSTP_AUTOPTP|IFBIF_BSTP_ADMEDGE| \ IFBIF_BSTP_ADMCOST) /* not saved */ diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh index 534144f46632..8a6c730014ce 100755 --- a/tests/sys/net/if_bridge_test.sh +++ b/tests/sys/net/if_bridge_test.sh @@ -1268,21 +1268,25 @@ vlan_qinq_body() # Create a QinQ trunk between the two jails. The outer (provider) tag # is 5, and the inner tag is 10. - jexec one ifconfig ${epone}b up - jexec one ifconfig ${epone}b.5 create vlanproto 802.1ad up - jexec one ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up - jexec two ifconfig ${eptwo}b up - jexec two ifconfig ${eptwo}b.5 create vlanproto 802.1ad up - jexec two ifconfig ${eptwo}b.5.10 create inet 192.0.2.2/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5.10 create inet 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 vlanfilter ${epone}a - ifconfig ${bridge} addm ${eptwo}a vlanfilter ${eptwo}a + atf_check -s exit:0 ifconfig ${bridge} vlanfilter defqinq up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a # Right now there are no VLANs on the access list, so everything # should be blocked. @@ -1290,10 +1294,16 @@ vlan_qinq_body() atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 # Add the provider tag to the access list; now traffic should be passed. - ifconfig ${bridge} +tagged ${epone}a 5 - ifconfig ${bridge} +tagged ${eptwo}a 5 + atf_check -s exit:0 ifconfig ${bridge} +tagged ${epone}a 5 + atf_check -s exit:0 ifconfig ${bridge} +tagged ${eptwo}a 5 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 + + # Remove the qinq flag from one of the interfaces; traffic should + # be blocked again. + atf_check -s exit:0 ifconfig ${bridge} -qinq ${epone}a + 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_qinq_cleanup()