]> www.infradead.org Git - users/hch/misc.git/commitdiff
selftests: forwarding: Add test for BR_BOOLOPT_FDB_LOCAL_VLAN_0
authorPetr Machata <petrm@nvidia.com>
Thu, 4 Sep 2025 17:07:27 +0000 (19:07 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 12 Sep 2025 02:02:51 +0000 (19:02 -0700)
Add a selftest to check the operation of this newly-introduced bridge
option.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://patch.msgid.link/62294f96884ab5d341648eef21243fa099a2dee5.1757004393.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/selftests/net/forwarding/Makefile
tools/testing/selftests/net/forwarding/bridge_fdb_local_vlan_0.sh [new file with mode: 0755]

index 0a0d4c2a85f74198dc6dd5f668cf5bc7626f057f..e6f482a600da44f84f660827e37213dafc1eb723 100644 (file)
@@ -5,6 +5,7 @@ TEST_PROGS = \
        bridge_fdb_learning_limit.sh \
        bridge_igmp.sh \
        bridge_locked_port.sh \
+       bridge_fdb_local_vlan_0.sh \
        bridge_mdb.sh \
        bridge_mdb_host.sh \
        bridge_mdb_max.sh \
diff --git a/tools/testing/selftests/net/forwarding/bridge_fdb_local_vlan_0.sh b/tools/testing/selftests/net/forwarding/bridge_fdb_local_vlan_0.sh
new file mode 100755 (executable)
index 0000000..5a0b43a
--- /dev/null
@@ -0,0 +1,374 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +-----------------------+ +-----------------------+ +-----------------------+
+# | H1 (vrf)              | | H2 (vrf)              | | H3 (vrf)              |
+# |    + $h1              | |    + $h2              | |    + $h3              |
+# |    | 192.0.2.1/28     | |    | 192.0.2.2/28     | |    | 192.0.2.18/28    |
+# |    | 2001:db8:1::1/64 | |    | 2001:db8:1::2/64 | |    | 2001:db8:2::2/64 |
+# |    |                  | |    |                  | |    |                  |
+# +----|------------------+ +----|------------------+ +----|------------------+
+#      |                         |                         |
+# +----|-------------------------|-------------------------|------------------+
+# | +--|-------------------------|------------------+      |                  |
+# | |  + $swp1                   + $swp2            |      + $swp3            |
+# | |                                               |        192.0.2.17/28    |
+# | |  BR1 (802.1q)                                 |        2001:db8:2::1/64 |
+# | |  192.0.2.3/28                                 |                         |
+# | |  2001:db8:1::3/64                             |                         |
+# | +-----------------------------------------------+                      SW |
+# +---------------------------------------------------------------------------+
+#
+#shellcheck disable=SC2317 # SC doesn't see our uses of functions.
+#shellcheck disable=SC2034 # ... and global variables
+
+ALL_TESTS="
+       test_d_no_sharing
+       test_d_sharing
+       test_q_no_sharing
+       test_q_sharing
+"
+
+NUM_NETIFS=6
+source lib.sh
+
+pMAC=00:11:22:33:44:55
+bMAC=00:11:22:33:44:66
+mMAC=00:11:22:33:44:77
+xMAC=00:11:22:33:44:88
+
+host_create()
+{
+       local h=$1; shift
+       local ipv4=$1; shift
+       local ipv6=$1; shift
+
+       simple_if_init "$h" "$ipv4" "$ipv6"
+       defer simple_if_fini "$h" "$ipv4" "$ipv6"
+
+       ip_route_add vrf "v$h" 192.0.2.16/28 nexthop via 192.0.2.3
+       ip_route_add vrf "v$h" 2001:db8:2::/64 nexthop via 2001:db8:1::3
+}
+
+h3_create()
+{
+       simple_if_init "$h3" 192.0.2.18/28 2001:db8:2::2/64
+       defer simple_if_fini "$h3" 192.0.2.18/28 2001:db8:2::2/64
+
+       ip_route_add vrf "v$h3" 192.0.2.0/28 nexthop via 192.0.2.17
+       ip_route_add vrf "v$h3" 2001:db8:1::/64 nexthop via 2001:db8:2::1
+
+       tc qdisc add dev "$h3" clsact
+       defer tc qdisc del dev "$h3" clsact
+
+       tc filter add dev "$h3" ingress proto ip pref 104 \
+          flower skip_hw ip_proto udp dst_port 4096 \
+          action pass
+       defer tc filter del dev "$h3" ingress proto ip pref 104
+
+       tc qdisc add dev "$h2" clsact
+       defer tc qdisc del dev "$h2" clsact
+
+       tc filter add dev "$h2" ingress proto ip pref 104 \
+          flower skip_hw ip_proto udp dst_port 4096 \
+          action pass
+       defer tc filter del dev "$h2" ingress proto ip pref 104
+}
+
+switch_create()
+{
+       ip_link_set_up "$swp1"
+
+       ip_link_set_up "$swp2"
+
+       ip_addr_add "$swp3" 192.0.2.17/28
+       ip_addr_add "$swp3" 2001:db8:2::1/64
+       ip_link_set_up "$swp3"
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       swp3=${NETIFS[p5]}
+       h3=${NETIFS[p6]}
+
+       vrf_prepare
+       defer vrf_cleanup
+
+       forwarding_enable
+       defer forwarding_restore
+
+       host_create "$h1" 192.0.2.1/28 2001:db8:1::1/64
+       host_create "$h2" 192.0.2.2/28 2001:db8:1::2/64
+       h3_create
+
+       switch_create
+}
+
+adf_bridge_create()
+{
+       local dev
+       local mac
+
+       ip_link_add br up type bridge vlan_default_pvid 0 "$@"
+       mac=$(mac_get br)
+       ip_addr_add br 192.0.2.3/28
+       ip_addr_add br 2001:db8:1::3/64
+
+       bridge_vlan_add dev br vid 1 pvid untagged self
+       bridge_vlan_add dev br vid 2 self
+       bridge_vlan_add dev br vid 3 self
+
+       for dev in "$swp1" "$swp2"; do
+               ip_link_set_master "$dev" br
+               bridge_vlan_add dev "$dev" vid 1 pvid untagged
+               bridge_vlan_add dev "$dev" vid 2
+               bridge_vlan_add dev "$dev" vid 3
+       done
+
+       ip_link_set_addr br "$mac"
+}
+
+check_fdb_local_vlan_0_support()
+{
+       if ip_link_add XXbr up type bridge vlan_filtering 1 fdb_local_vlan_0 1 \
+                   &>/dev/null; then
+               return 0
+       fi
+
+       log_test_skip "FDB sharing" \
+                     "iproute 2 or the kernel do not support fdb_local_vlan_0"
+}
+
+check_mac_presence()
+{
+       local should_fail=$1; shift
+       local dev=$1; shift
+       local vlan=$1; shift
+       local mac
+
+       mac=$(mac_get "$dev")
+
+       if ((vlan == 0)); then
+               vlan=null
+       fi
+
+       bridge -j fdb show dev "$dev" |
+           jq -e --arg mac "$mac" --argjson vlan "$vlan" \
+              '.[] | select(.mac == $mac) | select(.vlan == $vlan)' > /dev/null
+       check_err_fail "$should_fail" $? "FDB dev $dev vid $vlan addr $mac exists"
+}
+
+do_sharing_test()
+{
+       local should_fail=$1; shift
+       local what=$1; shift
+       local dev
+
+       RET=0
+
+       for dev in "$swp1" "$swp2" br; do
+               check_mac_presence 0 "$dev" 0
+               check_mac_presence "$should_fail" "$dev" 1
+               check_mac_presence "$should_fail" "$dev" 2
+               check_mac_presence "$should_fail" "$dev" 3
+       done
+
+       log_test "$what"
+}
+
+do_end_to_end_test()
+{
+       local mac=$1; shift
+       local what=$1; shift
+       local probe_dev=${1-$h3}; shift
+       local expect=${1-10}; shift
+
+       local t0
+       local t1
+       local dd
+
+       RET=0
+
+       # In mausezahn, use $dev MAC as the destination MAC. In the MAC sharing
+       # context, that will cause an FDB miss on VLAN 1 and prompt a second
+       # lookup in VLAN 0.
+
+       t0=$(tc_rule_stats_get "$probe_dev" 104 ingress)
+
+       $MZ "$h1" -c 10 -p 64 -a own -b "$mac" \
+                 -A 192.0.2.1 -B 192.0.2.18 -t udp "dp=4096,sp=2048" -q
+       sleep 1
+
+       t1=$(tc_rule_stats_get "$probe_dev" 104 ingress)
+       dd=$((t1 - t0))
+
+       ((dd == expect))
+       check_err $? "Expected $expect packets on $probe_dev got $dd"
+
+       log_test "$what"
+}
+
+do_tests()
+{
+       local should_fail=$1; shift
+       local what=$1; shift
+       local swp1_mac
+       local br_mac
+
+       swp1_mac=$(mac_get "$swp1")
+       br_mac=$(mac_get br)
+
+       do_sharing_test "$should_fail" "$what"
+       do_end_to_end_test "$swp1_mac" "$what: end to end, $swp1 MAC"
+       do_end_to_end_test "$br_mac" "$what: end to end, br MAC"
+}
+
+bridge_standard()
+{
+       local vlan_filtering=$1; shift
+
+       if ((vlan_filtering)); then
+               echo 802.1q
+       else
+               echo 802.1d
+       fi
+}
+
+nonexistent_fdb_test()
+{
+       local vlan_filtering=$1; shift
+       local standard
+
+       standard=$(bridge_standard "$vlan_filtering")
+
+       # We expect flooding, so $h2 should get the traffic.
+       do_end_to_end_test "$xMAC" "$standard: Nonexistent FDB" "$h2"
+}
+
+misleading_fdb_test()
+{
+       local vlan_filtering=$1; shift
+       local standard
+
+       standard=$(bridge_standard "$vlan_filtering")
+
+       defer_scope_push
+               # Add an FDB entry on VLAN 0. The lookup on VLAN-aware bridge
+               # shouldn't pick this up even with fdb_local_vlan_0 enabled, so
+               # the traffic should be flooded. This all holds on
+               # vlan_filtering bridge, on non-vlan_filtering one the FDB entry
+               # is expected to be found as usual, no flooding takes place.
+               #
+               # Adding only on VLAN 0 is a bit tricky, because bridge is
+               # trying to be nice and interprets the request as if the FDB
+               # should be added on each VLAN.
+
+               bridge fdb add "$mMAC" dev "$swp1" master
+               bridge fdb del "$mMAC" dev "$swp1" vlan 1 master
+               bridge fdb del "$mMAC" dev "$swp1" vlan 2 master
+               bridge fdb del "$mMAC" dev "$swp1" vlan 3 master
+
+               local expect=$((vlan_filtering ? 10 : 0))
+               do_end_to_end_test "$mMAC" \
+                                  "$standard: Lookup of non-local MAC on VLAN 0" \
+                                  "$h2" "$expect"
+       defer_scope_pop
+}
+
+change_mac()
+{
+       local dev=$1; shift
+       local mac=$1; shift
+       local cur_mac
+
+       cur_mac=$(mac_get "$dev")
+
+       log_info "Change $dev MAC $cur_mac -> $mac"
+       ip_link_set_addr "$dev" "$mac"
+       defer log_info "Change $dev MAC back"
+}
+
+do_test_no_sharing()
+{
+       local vlan_filtering=$1; shift
+       local standard
+
+       standard=$(bridge_standard "$vlan_filtering")
+
+       adf_bridge_create vlan_filtering "$vlan_filtering"
+       setup_wait
+
+       do_tests 0 "$standard, no FDB sharing"
+
+       change_mac "$swp1" "$pMAC"
+       change_mac br "$bMAC"
+
+       do_tests 0 "$standard, no FDB sharing after MAC change"
+
+       in_defer_scope check_fdb_local_vlan_0_support || return
+
+       log_info "Set fdb_local_vlan_0=1"
+       ip link set dev br type bridge fdb_local_vlan_0 1
+
+       do_tests 1 "$standard, fdb sharing after toggle"
+}
+
+do_test_sharing()
+{
+       local vlan_filtering=$1; shift
+       local standard
+
+       standard=$(bridge_standard "$vlan_filtering")
+
+       in_defer_scope check_fdb_local_vlan_0_support || return
+
+       adf_bridge_create vlan_filtering "$vlan_filtering" fdb_local_vlan_0 1
+       setup_wait
+
+       do_tests 1 "$standard, FDB sharing"
+
+       nonexistent_fdb_test "$vlan_filtering"
+       misleading_fdb_test "$vlan_filtering"
+
+       change_mac "$swp1" "$pMAC"
+       change_mac br "$bMAC"
+
+       do_tests 1 "$standard, FDB sharing after MAC change"
+
+       log_info "Set fdb_local_vlan_0=0"
+       ip link set dev br type bridge fdb_local_vlan_0 0
+
+       do_tests 0 "$standard, No FDB sharing after toggle"
+}
+
+test_d_no_sharing()
+{
+       do_test_no_sharing 0
+}
+
+test_d_sharing()
+{
+       do_test_sharing 0
+}
+
+test_q_no_sharing()
+{
+       do_test_no_sharing 1
+}
+
+test_q_sharing()
+{
+       do_test_sharing 1
+}
+
+
+trap cleanup EXIT
+
+setup_prepare
+tests_run