* [PATCH 0/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge
@ 2026-10-20 0:03 Laveesh Bansal
2026-10-20 0:54 ` [PATCH 1/2] " Laveesh Bansal
2026-10-20 0:56 ` [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch Laveesh Bansal
0 siblings, 2 replies; 6+ messages in thread
From: Laveesh Bansal @ 2026-10-20 0:03 UTC (permalink / raw)
To: Pablo Neira Ayuso, Florian Westphal
Cc: Phil Sutter, Nikolay Aleksandrov, Ido Schimmel, netfilter-devel,
coreteam, bridge, netdev
Hi,
This fixes the syzbot report "WARNING in nf_ct_bridge_post":
https://syzkaller.appspot.com/bug?extid=a8ba738fe2db6b4bb27f
It has been open since November 2025 and has a C reproducer.
With vlan_filtering on, a frame whose tag protocol differs from the
bridge's vlan_protocol (802.1Q on an 802.1ad bridge, or the other way
round) gets its tag moved back into the packet after bridge conntrack
has defragmented it. In postrouting nf_ct_bridge_refrag() no longer
recognises the protocol, warns and drops it.
The warning is only part of it. Fragmented IPv4 and IPv6 packets through
such a bridge are dropped, everything else works, so it looks like an MTU
problem: a 3000 byte ping in an 802.1Q vlan across an 802.1ad bridge with
bridge conntrack gets no reply, across an 802.1Q bridge it does.
Patch 1 skips over the tag in nf_ct_bridge_refrag() and puts it back on
each fragment. Patch 2 adds a selftest for it.
The Fixes: tags in patch 1 are the initial bridge conntrack support and
its IPv6 support, both from v5.3.
I tested this in QEMU guests on 7.3 and on v5.4, v6.1, v6.12 and v7.2, on
arm64 and on x86_64 with KVM. The selftest fails on all of them without
patch 1 (the control passes, the six other pings fail and the kernel
warns) and passes with it. The frame from the syzbot reproducer warns
without patch 1 and does not with it.
On a kernel with KASAN, lockdep, DEBUG_ATOMIC_SLEEP, kmemleak and
DEBUG_NET the selftest shows nothing, and neither do packets whose inner
ethertype an nft postrouting rule rewrites after defragmentation (to ARP,
and IPv4/IPv6 labelled as each other): they are dropped without a warning
or a leak. It builds without warnings with W=1 and CONFIG_IPV6=n or
CONFIG_BRIDGE_VLAN_FILTERING=n, and checkpatch.pl --strict is clean.
A tagged egress port (hardware tag together with the in-band tag), jumbo
frames, and br_netfilter enabled together with bridge conntrack were also
run: all fail on the unpatched kernel and pass with patch 1, except a
smaller egress mtu, where the 802.1Q bridge behaves the same.
Thanks,
Laveesh
Laveesh Bansal (2):
netfilter: bridge: refragment packets with a vlan tag moved by the
bridge
selftests: netfilter: add bridge conntrack test for vlan protocol
mismatch
include/linux/netfilter_bridge.h | 4 +
net/bridge/netfilter/nf_conntrack_bridge.c | 43 ++++-
.../testing/selftests/net/netfilter/Makefile | 1 +
.../net/netfilter/bridge_ct_vlan_proto.sh | 182 ++++++++++++++++++
tools/testing/selftests/net/netfilter/config | 1 +
5 files changed, 227 insertions(+), 4 deletions(-)
create mode 100755 tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh
--
2.54.0 (Apple Git-157)
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge 2026-10-20 0:03 [PATCH 0/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge Laveesh Bansal @ 2026-10-20 0:54 ` Laveesh Bansal 2026-10-20 8:23 ` Pablo Neira Ayuso 2026-10-23 9:04 ` netdev-bot+sashiko 2026-10-20 0:56 ` [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch Laveesh Bansal 1 sibling, 2 replies; 6+ messages in thread From: Laveesh Bansal @ 2026-10-20 0:54 UTC (permalink / raw) To: Pablo Neira Ayuso, Florian Westphal Cc: Nikolay Aleksandrov, Ido Schimmel, netfilter-devel, coreteam, bridge, netdev With vlan_filtering enabled, br_allowed_ingress() moves the vlan tag back into the packet when its protocol differs from the bridge's vlan_protocol, an 802.1Q tag on an 802.1ad bridge for instance, and sets skb->protocol to the vlan ethertype. That happens after the NF_BR_PRE_ROUTING hooks. Bridge conntrack has already defragmented the packet by then, based on the original protocol. In postrouting nf_ct_bridge_refrag() finds a vlan ethertype it cannot fragment, warns and drops the packet: WARNING: net/bridge/netfilter/nf_conntrack_bridge.c:361 at nf_ct_bridge_post Call trace: nf_ct_bridge_post nf_hook_slow br_forward_finish __br_forward deliver_clone br_flood br_handle_frame_finish br_handle_frame The warning is the smaller problem. Every fragmented IPv4 or IPv6 packet that crosses such a bridge is dropped while unfragmented traffic is fine, so it looks like an MTU issue. And anyone who can send fragments into a port of such a bridge can trigger the warning, which takes the machine down with panic_on_warn. Look past the vlan tag in nf_ct_bridge_refrag(): remember it, pull it, fragment as before and push it back onto each fragment in nf_ct_bridge_frag_restore(), together with the outer protocol, so that br_dev_queue_push_xmit() sees the fragments like any other tagged frame. The protocol behind the tag comes from the packet, so check it against the header before fragmenting. Removing the WARN_ON_ONCE() would only hide the problem, the packets would still be dropped. Not tracking these frames in nf_ct_bridge_pre() instead would change what is tracked today for unfragmented traffic, and would have to duplicate the decision that br_allowed_ingress() makes later. The syzbot reproducer is an IPv6 atomic fragment with a frag_max_size of 60, which br_ip6_fragment() drops silently because it is below IPV6_MIN_MTU. It no longer warns either. Reported-by: syzbot+a8ba738fe2db6b4bb27f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=a8ba738fe2db6b4bb27f Fixes: 3c171f496ef5 ("netfilter: bridge: add connection tracking system") Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Laveesh Bansal <laveeshb@laveeshbansal.com> --- include/linux/netfilter_bridge.h | 4 ++ net/bridge/netfilter/nf_conntrack_bridge.c | 43 ++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 743475ca7e9d..7091e01abe77 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -10,6 +10,10 @@ struct nf_bridge_frag_data { bool vlan_present; u16 vlan_tci; __be16 vlan_proto; + bool inband_vlan_present; + u16 inband_vlan_tci; + __be16 inband_vlan_proto; + __be16 inband_vlan_encap_proto; }; #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index b5444335b86f..e16071eb2df0 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -344,13 +344,41 @@ nf_ct_bridge_refrag(struct sk_buff *skb, const struct nf_hook_state *state, const struct nf_bridge_frag_data *data, struct sk_buff *)) { - struct nf_bridge_frag_data data; + struct nf_bridge_frag_data data = {}; + __be16 proto = skb->protocol; if (!BR_INPUT_SKB_CB(skb)->frag_max_size) return NF_ACCEPT; nf_ct_bridge_frag_save(skb, &data); - switch (skb->protocol) { + + /* A vlan filtering bridge moves a vlan tag into the packet if its + * protocol differs from the bridge's vlan_protocol, after prerouting + * defragmented the packet. Look past that tag. + */ + if (eth_type_vlan(proto)) { + const struct vlan_hdr *vhdr; + + if (skb_network_offset(skb) != VLAN_HLEN || + !pskb_may_pull(skb, VLAN_HLEN)) + return NF_DROP; + + vhdr = (const struct vlan_hdr *)skb->data; + data.inband_vlan_present = true; + data.inband_vlan_tci = ntohs(vhdr->h_vlan_TCI); + data.inband_vlan_proto = proto; + data.inband_vlan_encap_proto = vhdr->h_vlan_encapsulated_proto; + + proto = data.inband_vlan_encap_proto; + __skb_pull(skb, VLAN_HLEN); + skb->protocol = proto; + + if ((proto == htons(ETH_P_IP) && ip_hdr(skb)->version != 4) || + (proto == htons(ETH_P_IPV6) && ipv6_hdr(skb)->version != 6)) + return NF_DROP; + } + + switch (proto) { case htons(ETH_P_IP): nf_br_ip_fragment(state->net, state->sk, skb, &data, output); break; @@ -358,7 +386,7 @@ nf_ct_bridge_refrag(struct sk_buff *skb, const struct nf_hook_state *state, nf_br_ip6_fragment(state->net, state->sk, skb, &data, output); break; default: - WARN_ON_ONCE(1); + WARN_ON_ONCE(!data.inband_vlan_present); return NF_DROP; } @@ -371,11 +399,18 @@ static int nf_ct_bridge_frag_restore(struct sk_buff *skb, { int err; - err = skb_cow_head(skb, ETH_HLEN); + err = skb_cow_head(skb, ETH_HLEN + (data->inband_vlan_present ? VLAN_HLEN : 0)); if (err) { kfree_skb(skb); return -ENOMEM; } + if (data->inband_vlan_present) { + struct vlan_hdr *vhdr = skb_push(skb, VLAN_HLEN); + + vhdr->h_vlan_TCI = htons(data->inband_vlan_tci); + vhdr->h_vlan_encapsulated_proto = data->inband_vlan_encap_proto; + skb->protocol = data->inband_vlan_proto; + } if (data->vlan_present) __vlan_hwaccel_put_tag(skb, data->vlan_proto, data->vlan_tci); else if (skb_vlan_tag_present(skb)) -- 2.54.0 (Apple Git-157) ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge 2026-10-20 0:54 ` [PATCH 1/2] " Laveesh Bansal @ 2026-10-20 8:23 ` Pablo Neira Ayuso 2026-10-23 9:04 ` netdev-bot+sashiko 1 sibling, 0 replies; 6+ messages in thread From: Pablo Neira Ayuso @ 2026-10-20 8:23 UTC (permalink / raw) To: Laveesh Bansal Cc: Florian Westphal, Nikolay Aleksandrov, Ido Schimmel, netfilter-devel, coreteam, bridge, netdev On Mon, Oct 19, 2026 at 05:54:03PM -0700, Laveesh Bansal wrote: > With vlan_filtering enabled, br_allowed_ingress() moves the vlan tag back > into the packet when its protocol differs from the bridge's vlan_protocol, > an 802.1Q tag on an 802.1ad bridge for instance, and sets skb->protocol to > the vlan ethertype. That happens after the NF_BR_PRE_ROUTING hooks. > > Bridge conntrack has already defragmented the packet by then, based on the > original protocol. In postrouting nf_ct_bridge_refrag() finds a vlan > ethertype it cannot fragment, warns and drops the packet: > > WARNING: net/bridge/netfilter/nf_conntrack_bridge.c:361 at nf_ct_bridge_post > Call trace: > nf_ct_bridge_post > nf_hook_slow > br_forward_finish > __br_forward > deliver_clone > br_flood > br_handle_frame_finish > br_handle_frame > > The warning is the smaller problem. Every fragmented IPv4 or IPv6 packet > that crosses such a bridge is dropped while unfragmented traffic is fine, > so it looks like an MTU issue. And anyone who can send fragments into a > port of such a bridge can trigger the warning, which takes the machine > down with panic_on_warn. > > Look past the vlan tag in nf_ct_bridge_refrag(): remember it, pull it, > fragment as before and push it back onto each fragment in > nf_ct_bridge_frag_restore(), together with the outer protocol, so that > br_dev_queue_push_xmit() sees the fragments like any other tagged frame. > The protocol behind the tag comes from the packet, so check it against the > header before fragmenting. > > Removing the WARN_ON_ONCE() would only hide the problem, the packets would > still be dropped. Not tracking these frames in nf_ct_bridge_pre() instead > would change what is tracked today for unfragmented traffic, and would have > to duplicate the decision that br_allowed_ingress() makes later. Yes, but let's just fix this by turning this into a DEBUG_NET_WARN_ON_ONCE(1) and let the packet be dropped. Then, we can follow up to fill the gap for this feature in in nf-next. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge 2026-10-20 0:54 ` [PATCH 1/2] " Laveesh Bansal 2026-10-20 8:23 ` Pablo Neira Ayuso @ 2026-10-23 9:04 ` netdev-bot+sashiko 1 sibling, 0 replies; 6+ messages in thread From: netdev-bot+sashiko @ 2026-10-23 9:04 UTC (permalink / raw) To: laveeshb Cc: pablo, fw, razor, idosch, netfilter-devel, coreteam, bridge, netdev, kuba Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin. Low priority / nit picks for your consideration: - [Low] The comment above nf_ct_bridge_frag_restore() (net/bridge/netfilter/nf_conntrack_bridge.c:396), "Actually only slow… -- netfilter: bridge: refragment packets with a vlan tag moved by the bridge With this patch, nf_ct_bridge_refrag() looks past a vlan tag that br_allowed_ingress() moved into the packet after prerouting defragmentation. It saves the tag, pulls it off, fragments the inner IPv4 or IPv6 packet, and then pushes the tag back onto each fragment in nf_ct_bridge_frag_restore() together with the outer protocol. Closes: https://syzkaller.appspot.com/bug?extid=a8ba738fe2db6b4bb27f > diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c > index b5444335b86f..e16071eb2df0 100644 > --- a/net/bridge/netfilter/nf_conntrack_bridge.c > +++ b/net/bridge/netfilter/nf_conntrack_bridge.c [ ... ] > + proto = data.inband_vlan_encap_proto; > + __skb_pull(skb, VLAN_HLEN); > + skb->protocol = proto; [ ... ] > @@ -371,11 +399,18 @@ static int nf_ct_bridge_frag_restore(struct sk_buff *skb, > { > int err; > > - err = skb_cow_head(skb, ETH_HLEN); > + err = skb_cow_head(skb, ETH_HLEN + (data->inband_vlan_present ? VLAN_HLEN : 0)); > if (err) { > kfree_skb(skb); > return -ENOMEM; > } > + if (data->inband_vlan_present) { > + struct vlan_hdr *vhdr = skb_push(skb, VLAN_HLEN); > + > + vhdr->h_vlan_TCI = htons(data->inband_vlan_tci); > + vhdr->h_vlan_encapsulated_proto = data->inband_vlan_encap_proto; > + skb->protocol = data->inband_vlan_proto; > + } [Severity: Low] This isn't a bug, but the comment above nf_ct_bridge_frag_restore() hasn't been changed and still says: /* Actually only slow path refragmentation needs this. */ Now that nf_ct_bridge_refrag() pulls the vlan header and sets skb->protocol to the inner ethertype before it picks the fast or slow path, nf_ct_bridge_frag_restore() is the only code that pushes the header back. The frag_list fast paths also reach this function. nf_br_ip_fragment() and br_ip6_fragment() call output() for the head skb and for each fraglist member, and that leads to: output() nf_ct_bridge_refrag_post() nf_ct_bridge_frag_restore() Suppose a later change skipped the restore on the fast path, as the comment suggests is fine. The head fragment would then go out without its vlan header. data->mac would still hold the vlan TPID, and skb->protocol would be IP or IPv6. The comment was arguably wrong even before this patch, because fraglist members also need the MAC copy and the hwaccel tag. Since the fast path now depends on the vlan restore as well, could the comment be updated or removed? > if (data->vlan_present) > __vlan_hwaccel_put_tag(skb, data->vlan_proto, data->vlan_tci); > else if (skb_vlan_tag_present(skb)) -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020000308.37362-1-laveeshb%40laveeshbansal.com ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch 2026-10-20 0:03 [PATCH 0/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge Laveesh Bansal 2026-10-20 0:54 ` [PATCH 1/2] " Laveesh Bansal @ 2026-10-20 0:56 ` Laveesh Bansal 2026-10-23 9:04 ` netdev-bot+sashiko 1 sibling, 1 reply; 6+ messages in thread From: Laveesh Bansal @ 2026-10-20 0:56 UTC (permalink / raw) To: Pablo Neira Ayuso, Florian Westphal Cc: Nikolay Aleksandrov, Ido Schimmel, netfilter-devel, coreteam, bridge, netdev Add a test for fragmented packets in vlans that are forwarded by a vlan filtering bridge with bridge conntrack. If the protocol of the vlan tag differs from the vlan_protocol of the bridge, the bridge moves the tag into the packet after prerouting, which made bridge conntrack warn and drop the fragments. The test uses an 802.1Q vlan on an 802.1ad bridge and the reverse, and an 802.1Q vlan on an 802.1Q bridge as control. Send fragmented IPv4 and IPv6 pings through the bridge and check that they get through, that conntrack reassembled them and that the kernel did not warn. Repeat the first mismatching case with promisc set on the bridge, which makes it forward clones of the packets, so that fragmenting them takes the slow path. Assisted-by: LLM Signed-off-by: Laveesh Bansal <laveeshb@laveeshbansal.com> --- .../testing/selftests/net/netfilter/Makefile | 1 + .../net/netfilter/bridge_ct_vlan_proto.sh | 182 ++++++++++++++++++ tools/testing/selftests/net/netfilter/config | 1 + 3 files changed, 184 insertions(+) create mode 100755 tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile index f88dd4ef8d26..314f678f3dfc 100644 --- a/tools/testing/selftests/net/netfilter/Makefile +++ b/tools/testing/selftests/net/netfilter/Makefile @@ -10,6 +10,7 @@ TEST_PROGS := \ br_netfilter.sh \ br_netfilter_queue.sh \ bridge_brouter.sh \ + bridge_ct_vlan_proto.sh \ conntrack_clash.sh \ conntrack_dump_flush.sh \ conntrack_icmp_related.sh \ diff --git a/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh b/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh new file mode 100755 index 000000000000..19f842e17060 --- /dev/null +++ b/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh @@ -0,0 +1,182 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# A VLAN filtering bridge moves the vlan tag of a frame into the packet if the +# protocol of the tag differs from the vlan_protocol of the bridge, after the +# bridge prerouting hooks have run. skb->protocol is a vlan ethertype when the +# postrouting hooks see the frame. +# +# Bridge conntrack defragments IP packets in prerouting and fragments them +# again in postrouting. It used to warn and drop fragmented packets that are +# forwarded like this. +# +# Send fragmented ping packets in vlans through a bridge with bridge conntrack +# and check that they are forwarded, that they were reassembled by conntrack +# and that the kernel does not warn. +# +# ns0 (br0) +# ns1 eth0.100 -- veth0 ------- veth1 -- eth1.100 ns2 + +source lib.sh + +# TAINT_WARN +taint_warn=512 + +checktool "nft --version" "run test without nft tool" +checktool "bridge -V" "run test without bridge tool" +checktool "ping -V" "run test without ping" + +read -r t < /proc/sys/kernel/tainted +if [ $((t & taint_warn)) -ne 0 ]; then + echo "SKIP: kernel has already warned" + exit "$ksft_skip" +fi + +cleanup() { + cleanup_all_ns +} + +trap cleanup EXIT + +ret=0 + +# $1: vlan_protocol of the bridge, $2: protocol of the vlan devices +setup() +{ + local br_proto="$1" + local vlan_proto="$2" + + setup_ns ns0 ns1 ns2 + + ip -net "$ns0" link add br0 type bridge vlan_filtering 1 \ + vlan_protocol "$br_proto" || return "$ksft_skip" + ip -net "$ns0" link add veth0 type veth peer name eth0 netns "$ns1" || return "$ksft_skip" + ip -net "$ns0" link add veth1 type veth peer name eth1 netns "$ns2" || return "$ksft_skip" + + for i in 0 1; do + ip -net "$ns0" link set "veth$i" master br0 + ip -net "$ns0" link set "veth$i" up + # If the protocols match, vlan 100 has to be allowed. If not, + # the bridge treats the tag as part of the packet. + if [ "$br_proto" = "802.1q" ] && [ "$vlan_proto" = "802.1q" ]; then + ip netns exec "$ns0" bridge vlan add dev "veth$i" vid 100 || + return "$ksft_skip" + fi + done + ip -net "$ns0" link set br0 up + + ip -net "$ns1" link set eth0 up + ip -net "$ns2" link set eth1 up + + ip -net "$ns1" link add link eth0 name eth0.100 type vlan \ + protocol "$vlan_proto" id 100 || return "$ksft_skip" + ip -net "$ns2" link add link eth1 name eth1.100 type vlan \ + protocol "$vlan_proto" id 100 || return "$ksft_skip" + + ip -net "$ns1" link set eth0.100 mtu 1400 up + ip -net "$ns2" link set eth1.100 mtu 1400 up + + ip -net "$ns1" addr add 10.0.100.1/24 dev eth0.100 + ip -net "$ns2" addr add 10.0.100.2/24 dev eth1.100 + ip -net "$ns1" addr add dead:100::1/64 dev eth0.100 nodad + ip -net "$ns2" addr add dead:100::2/64 dev eth1.100 nodad + + # Loading a ct rule in the bridge family enables bridge conntrack. The + # postrouting counter sees the packets reassembled by conntrack. + ip netns exec "$ns0" nft -f - <<EOF || return "$ksft_skip" +table bridge t { + chain pre { + type filter hook prerouting priority 0; policy accept; + ct state new counter + } + chain post { + type filter hook postrouting priority 0; policy accept; + meta length > 1400 counter + } +} +EOF + + return 0 +} + +# $1: vlan_protocol of the bridge, $2: protocol of the vlan devices, +# $3: "clone" to make the bridge forward clones, which makes fragmenting take +# the slow path +run_case() +{ + local desc="$2 vlan on $1 bridge${3:+, $3}" + local rc=0 + + setup "$1" "$2" + rc=$? + if [ "$rc" -ne 0 ]; then + echo "SKIP: could not set up $desc" + return "$rc" + fi + + if [ "$3" = "clone" ]; then + # The bridge delivers a clone of the packets it forwards if it + # also receives them. + ip -net "$ns0" link set br0 promisc on + fi + + # Wait until the bridge forwards the plain vlan traffic. + if ! busywait 5000 ip netns exec "$ns1" ping -c 1 -W 1 -q 10.0.100.2 >/dev/null 2>&1; then + echo "FAIL: no connectivity through the bridge, $desc" + cleanup_all_ns + return 1 + fi + + # 3000 bytes of icmp payload do not fit the 1400 byte mtu, the sender + # fragments the packets and bridge conntrack reassembles and fragments + # them again. + for ip in 10.0.100.2 dead:100::2; do + if ip netns exec "$ns1" ping -c 3 -W 2 -s 3000 -q "$ip" >/dev/null; then + echo "PASS: fragmented ping to $ip, $desc" + else + echo "FAIL: fragmented ping to $ip, $desc" + rc=1 + fi + done + + if ! ip netns exec "$ns0" nft list chain bridge t post | grep -q "packets [1-9]"; then + echo "FAIL: conntrack did not reassemble the packets, $desc" + rc=1 + fi + + cleanup_all_ns + + return $rc +} + +run_all() +{ + local rc + + # bridge protocol, protocol of the vlan devices + for case in "802.1q 802.1q" "802.1ad 802.1q" "802.1q 802.1ad" "802.1ad 802.1q clone"; do + # shellcheck disable=SC2086 + run_case $case + rc=$? + if [ "$rc" -eq "$ksft_skip" ]; then + return "$ksft_skip" + fi + [ "$rc" -ne 0 ] && ret=1 + done + + return 0 +} + +run_all +rc=$? +if [ "$rc" -eq "$ksft_skip" ] && [ "$ret" -eq 0 ]; then + exit "$ksft_skip" +fi + +read -r t < /proc/sys/kernel/tainted +if [ $((t & taint_warn)) -ne 0 ]; then + echo "FAIL: kernel warned" + ret=1 +fi + +exit $ret diff --git a/tools/testing/selftests/net/netfilter/config b/tools/testing/selftests/net/netfilter/config index c3c121b6f300..e91bb10d1072 100644 --- a/tools/testing/selftests/net/netfilter/config +++ b/tools/testing/selftests/net/netfilter/config @@ -62,6 +62,7 @@ CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_NETEM=m CONFIG_NET_VRF=y CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_BRIDGE=m CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_MARK=y -- 2.54.0 (Apple Git-157) ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch 2026-10-20 0:56 ` [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch Laveesh Bansal @ 2026-10-23 9:04 ` netdev-bot+sashiko 0 siblings, 0 replies; 6+ messages in thread From: netdev-bot+sashiko @ 2026-10-23 9:04 UTC (permalink / raw) To: laveeshb Cc: pablo, fw, razor, idosch, netfilter-devel, coreteam, bridge, netdev, kuba Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider. Critical: 0 · High: 0 · Medium: 1 · Low: 1 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] In bridge_ct_vlan_proto.sh, the check "FAIL: conntrack did not reassemble the packets" cannot detect missing reassembly in the vlan… - [Low] In bridge_ct_vlan_proto.sh, run_all() returns ksft_skip as soon as any run_case() returns ksft_skip, even after earlier cases have run. -- selftests: netfilter: add bridge conntrack test for vlan protocol mismatch This adds a selftest that sends fragmented IPv4 and IPv6 pings through a vlan filtering bridge with bridge conntrack, using vlan tags whose protocol differs from (or matches) the bridge's vlan_protocol. It checks that the pings get through, that conntrack reassembled the packets, and that the kernel did not warn. > diff --git a/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh b/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh > new file mode 100755 > index 000000000000..19f842e17060 > --- /dev/null > +++ b/tools/testing/selftests/net/netfilter/bridge_ct_vlan_proto.sh > @@ -0,0 +1,182 @@ [ ... ] > + # Loading a ct rule in the bridge family enables bridge conntrack. The > + # postrouting counter sees the packets reassembled by conntrack. > + ip netns exec "$ns0" nft -f - <<EOF || return "$ksft_skip" > +table bridge t { > + chain pre { > + type filter hook prerouting priority 0; policy accept; > + ct state new counter > + } > + chain post { > + type filter hook postrouting priority 0; policy accept; > + meta length > 1400 counter > + } > +} > +EOF [Severity: Medium] Can this counter go up in the mismatch cases even when nothing was reassembled? In the 802.1ad/802.1q, 802.1q/802.1ad and clone cases, __allowed_ingress() moves the tag into the packet after prerouting: net/bridge/br_vlan.c:__allowed_ingress() { ... if (skb->vlan_proto != br->vlan_proto) { /* Protocol-mismatch, empty out vlan_tci for new tag */ skb_push(skb, ETH_HLEN); skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, skb_vlan_tag_get(skb)); ... } After that, skb->data sits at the 4 byte in-band vlan header. meta length is skb->len, so the post chain sees the IP length plus 4. With the 1400 byte mtu on eth0.100, each IPv6 fragment is 40 + 8 + 1352 = 1400 bytes. A fragment that was never reassembled shows up in the post chain as 1404 bytes and matches "meta length > 1400". IPv4 fragments come out at exactly 1400 and don't match. If bridge conntrack stopped defragmenting, nf_ct_bridge_refrag() would return early: net/bridge/netfilter/nf_conntrack_bridge.c:nf_ct_bridge_refrag() { ... if (!BR_INPUT_SKB_CB(skb)->frag_max_size) return NF_ACCEPT; ... } The fragments would still be forwarded, both pings would succeed, and the "packets [1-9]" check in run_case() would still pass. The refragmentation path this test targets would then go untested and nothing would flag it. Also, run_case() checks this one shared counter only once, after both the IPv4 and IPv6 pings. So the check can't tell which family was reassembled. For the mismatch cases, the comment "The postrouting counter sees the packets reassembled by conntrack" doesn't look accurate either. Would one of these work instead: put the counter in prerouting, where the tag is still hwaccel; use a threshold above any fragment size (for example > 1500); or keep separate "meta nfproto ipv4" and "meta nfproto ipv6" counters and check each one after its own ping? [ ... ] > +run_all() > +{ > + local rc > + > + # bridge protocol, protocol of the vlan devices > + for case in "802.1q 802.1q" "802.1ad 802.1q" "802.1q 802.1ad" "802.1ad 802.1q clone"; do > + # shellcheck disable=SC2086 > + run_case $case > + rc=$? > + if [ "$rc" -eq "$ksft_skip" ]; then > + return "$ksft_skip" > + fi > + [ "$rc" -ne 0 ] && ret=1 > + done > + > + return 0 > +} > + > +run_all > +rc=$? > +if [ "$rc" -eq "$ksft_skip" ] && [ "$ret" -eq 0 ]; then > + exit "$ksft_skip" > +fi > + > +read -r t < /proc/sys/kernel/tainted > +if [ $((t & taint_warn)) -ne 0 ]; then > + echo "FAIL: kernel warned" > + ret=1 > +fi [Severity: Low] Is it possible for a warning from an earlier case to get lost here? run_all() returns ksft_skip as soon as any run_case() returns ksft_skip, even when earlier cases have already run. The top level then exits with ksft_skip before it reads /proc/sys/kernel/tainted. Say an earlier case triggered a WARN but its pings still passed. ping -c 3 exits 0 if any reply arrives, so ret stays 0. If setup then fails in a later case, for example because the 802.1ad bridge or vlan device can't be created, the test reports SKIP and the WARN never gets reported. The commit message says the test checks "that the kernel did not warn". Could the taint check run before the skip decision, or could the test fail whenever TAINT_WARN appears during the run? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020000308.37362-1-laveeshb%40laveeshbansal.com ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-23 9:04 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-20 0:03 [PATCH 0/2] netfilter: bridge: refragment packets with a vlan tag moved by the bridge Laveesh Bansal 2026-10-20 0:54 ` [PATCH 1/2] " Laveesh Bansal 2026-10-20 8:23 ` Pablo Neira Ayuso 2026-10-23 9:04 ` netdev-bot+sashiko 2026-10-20 0:56 ` [PATCH 2/2] selftests: netfilter: add bridge conntrack test for vlan protocol mismatch Laveesh Bansal 2026-10-23 9:04 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox