Add a suite covering the fdb_n_learned and fdb_max_learned bridge
features, touching all special cases in accounting at least once.

Acked-by: Nikolay Aleksandrov <ra...@blackwall.org>
Signed-off-by: Johannes Nixdorf <jnixdorf-...@avm.de>
---
 tools/testing/selftests/net/forwarding/Makefile    |   3 +-
 .../net/forwarding/bridge_fdb_learning_limit.sh    | 283 +++++++++++++++++++++
 2 files changed, 285 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/forwarding/Makefile 
b/tools/testing/selftests/net/forwarding/Makefile
index 74e754e266c3..df593b7b3e6b 100644
--- a/tools/testing/selftests/net/forwarding/Makefile
+++ b/tools/testing/selftests/net/forwarding/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0+ OR MIT
 
-TEST_PROGS = bridge_igmp.sh \
+TEST_PROGS = bridge_fdb_learning_limit.sh \
+       bridge_igmp.sh \
        bridge_locked_port.sh \
        bridge_mdb.sh \
        bridge_mdb_host.sh \
diff --git 
a/tools/testing/selftests/net/forwarding/bridge_fdb_learning_limit.sh 
b/tools/testing/selftests/net/forwarding/bridge_fdb_learning_limit.sh
new file mode 100755
index 000000000000..0760a34b7114
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/bridge_fdb_learning_limit.sh
@@ -0,0 +1,283 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# ShellCheck incorrectly believes that most of the code here is unreachable
+# because it's invoked by variable name following ALL_TESTS.
+#
+# shellcheck disable=SC2317
+
+ALL_TESTS="check_accounting check_limit"
+NUM_NETIFS=6
+source lib.sh
+
+TEST_MAC_BASE=de:ad:be:ef:42:
+
+NUM_PKTS=16
+FDB_LIMIT=8
+
+FDB_TYPES=(
+       # name          is counted?     overrides learned?
+       'learned        1               0'
+       'static         0               1'
+       'user           0               1'
+       'extern_learn   0               1'
+       'local          0               1'
+)
+
+mac()
+{
+       printf "${TEST_MAC_BASE}%02x" "$1"
+}
+
+H1_DEFAULT_MAC=$(mac 42)
+
+switch_create()
+{
+       ip link add dev br0 type bridge
+
+       ip link set dev "$swp1" master br0
+       ip link set dev "$swp2" master br0
+       # swp3 is used to add local MACs, so do not add it to the bridge yet.
+
+       # swp2 is only used for replying when learning on swp1, its MAC should 
not be learned.
+       ip link set dev "$swp2" type bridge_slave learning off
+
+       ip link set dev br0 up
+
+       ip link set dev "$swp1" up
+       ip link set dev "$swp2" up
+       ip link set dev "$swp3" up
+}
+
+switch_destroy()
+{
+       ip link set dev "$swp3" down
+       ip link set dev "$swp2" down
+       ip link set dev "$swp1" down
+
+       ip link del dev br0
+}
+
+h_create()
+{
+       ip link set "$h1" addr "$H1_DEFAULT_MAC"
+
+       simple_if_init "$h1" 192.0.2.1/24
+       simple_if_init "$h2" 192.0.2.2/24
+}
+
+h_destroy()
+{
+       simple_if_fini "$h1" 192.0.2.1/24
+       simple_if_fini "$h2" 192.0.2.2/24
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       h2=${NETIFS[p3]}
+       swp2=${NETIFS[p4]}
+
+       swp3=${NETIFS[p6]}
+
+       vrf_prepare
+
+       h_create
+
+       switch_create
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       switch_destroy
+
+       h_destroy
+
+       vrf_cleanup
+}
+
+fdb_get_n_learned()
+{
+       ip -d -j link show dev br0 type bridge | \
+               jq '.[]["linkinfo"]["info_data"]["fdb_n_learned"]'
+}
+
+fdb_get_n_mac()
+{
+       local mac=${1}
+
+       bridge -j fdb show br br0 | \
+               jq "map(select(.mac == \"${mac}\" and (has(\"vlan\") | not))) | 
length"
+}
+
+fdb_fill_learned()
+{
+       local i
+
+       for i in $(seq 1 "$NUM_PKTS"); do
+               fdb_add learned "$(mac "$i")"
+       done
+}
+
+fdb_reset()
+{
+       bridge fdb flush dev br0
+
+       # Keep the default MAC address of h1 in the table. We set it to a 
different one when
+       # testing dynamic learning.
+       bridge fdb add "$H1_DEFAULT_MAC" dev "$swp1" master static use
+}
+
+fdb_add()
+{
+       local type=$1 mac=$2
+
+       case "$type" in
+               learned)
+                       ip link set "$h1" addr "$mac"
+                       # Wait for a reply so we implicitly wait until after 
the forwarding
+                       # code finished and the FDB entry was created.
+                       PING_COUNT=1 ping_do "$h1" 192.0.2.2
+                       check_err $? "Failed to ping another bridge port"
+                       ip link set "$h1" addr "$H1_DEFAULT_MAC"
+                       ;;
+               local)
+                       ip link set dev "$swp3" addr "$mac" && ip link set 
"$swp3" master br0
+                       ;;
+               static)
+                       bridge fdb replace "$mac" dev "$swp1" master static
+                       ;;
+               user)
+                       bridge fdb replace "$mac" dev "$swp1" master static use
+                       ;;
+               extern_learn)
+                       bridge fdb replace "$mac" dev "$swp1" master 
extern_learn
+                       ;;
+       esac
+
+       check_err $? "Failed to add a FDB entry of type ${type}"
+}
+
+fdb_del()
+{
+       local type=$1 mac=$2
+
+       case "$type" in
+               local)
+                       ip link set "$swp3" nomaster
+                       ;;
+               *)
+                       bridge fdb del "$mac" dev "$swp1" master
+                       ;;
+       esac
+
+       check_err $? "Failed to remove a FDB entry of type ${type}"
+}
+
+check_accounting_one_type()
+{
+       local type=$1 is_counted=$2 overrides_learned=$3
+       shift 3
+       RET=0
+
+       fdb_reset
+       fdb_add "$type" "$(mac 0)"
+       learned=$(fdb_get_n_learned)
+       [ "$learned" -ne "$is_counted" ]
+       check_fail $? "Inserted FDB type ${type}: Expected the count 
${is_counted}, but got ${learned}"
+
+       fdb_del "$type" "$(mac 0)"
+       learned=$(fdb_get_n_learned)
+       [ "$learned" -ne 0 ]
+       check_fail $? "Removed FDB type ${type}: Expected the count 0, but got 
${learned}"
+
+       if [ "$overrides_learned" -eq 1 ]; then
+               fdb_reset
+               fdb_add learned "$(mac 0)"
+               fdb_add "$type" "$(mac 0)"
+               learned=$(fdb_get_n_learned)
+               [ "$learned" -ne "$is_counted" ]
+               check_fail $? "Set a learned entry to FDB type ${type}: 
Expected the count ${is_counted}, but got ${learned}"
+               fdb_del "$type" "$(mac 0)"
+       fi
+
+       log_test "FDB accounting interacting with FDB type ${type}"
+}
+
+check_accounting()
+{
+       local type_args learned
+       RET=0
+
+       fdb_reset
+       learned=$(fdb_get_n_learned)
+       [ "$learned" -ne 0 ]
+       check_fail $? "Flushed the FDB table: Expected the count 0, but got 
${learned}"
+
+       fdb_fill_learned
+       sleep 1
+
+       learned=$(fdb_get_n_learned)
+       [ "$learned" -ne "$NUM_PKTS" ]
+       check_fail $? "Filled the FDB table: Expected the count ${NUM_PKTS}, 
but got ${learned}"
+
+       log_test "FDB accounting"
+
+       for type_args in "${FDB_TYPES[@]}"; do
+               # This is intentional use of word splitting.
+               # shellcheck disable=SC2086
+               check_accounting_one_type $type_args
+       done
+}
+
+check_limit_one_type()
+{
+       local type=$1 is_counted=$2
+       local n_mac expected=$((1 - is_counted))
+       RET=0
+
+       fdb_reset
+       fdb_fill_learned
+
+       fdb_add "$type" "$(mac 0)"
+       n_mac=$(fdb_get_n_mac "$(mac 0)")
+       [ "$n_mac" -ne "$expected" ]
+       check_fail $? "Inserted FDB type ${type} at limit: Expected the count 
${expected}, but got ${n_mac}"
+
+       log_test "FDB limits interacting with FDB type ${type}"
+}
+
+check_limit()
+{
+       local learned
+       RET=0
+
+       ip link set br0 type bridge fdb_max_learned "$FDB_LIMIT"
+
+       fdb_reset
+       fdb_fill_learned
+
+       learned=$(fdb_get_n_learned)
+       [ "$learned" -ne "$FDB_LIMIT" ]
+       check_fail $? "Filled the limited FDB table: Expected the count 
${FDB_LIMIT}, but got ${learned}"
+
+       log_test "FDB limits"
+
+       for type_args in "${FDB_TYPES[@]}"; do
+               # This is intentional use of word splitting.
+               # shellcheck disable=SC2086
+               check_limit_one_type $type_args
+       done
+}
+
+trap cleanup EXIT
+
+setup_prepare
+
+tests_run
+
+exit $EXIT_STATUS

-- 
2.42.0

Reply via email to