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 <[email protected]> --- .../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)
