#!/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