1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# ShellCheck incorrectly believes that most of the code here is unreachable 5# because it's invoked by variable name following ALL_TESTS. 6# 7# shellcheck disable=SC2317 8 9ALL_TESTS="check_accounting check_limit" 10NUM_NETIFS=6 11source lib.sh 12 13TEST_MAC_BASE=de:ad:be:ef:42: 14 15NUM_PKTS=16 16FDB_LIMIT=8 17 18FDB_TYPES=( 19 # name is counted? overrides learned? 20 'learned 1 0' 21 'static 0 1' 22 'user 0 1' 23 'extern_learn 0 1' 24 'local 0 1' 25) 26 27mac() 28{ 29 printf "${TEST_MAC_BASE}%02x" "$1" 30} 31 32H1_DEFAULT_MAC=$(mac 42) 33 34switch_create() 35{ 36 ip link add dev br0 type bridge 37 38 ip link set dev "$swp1" master br0 39 ip link set dev "$swp2" master br0 40 # swp3 is used to add local MACs, so do not add it to the bridge yet. 41 42 # swp2 is only used for replying when learning on swp1, its MAC should not be learned. 43 ip link set dev "$swp2" type bridge_slave learning off 44 45 ip link set dev br0 up 46 47 ip link set dev "$swp1" up 48 ip link set dev "$swp2" up 49 ip link set dev "$swp3" up 50} 51 52switch_destroy() 53{ 54 ip link set dev "$swp3" down 55 ip link set dev "$swp2" down 56 ip link set dev "$swp1" down 57 58 ip link del dev br0 59} 60 61h_create() 62{ 63 ip link set "$h1" addr "$H1_DEFAULT_MAC" 64 65 simple_if_init "$h1" 192.0.2.1/24 66 simple_if_init "$h2" 192.0.2.2/24 67} 68 69h_destroy() 70{ 71 simple_if_fini "$h1" 192.0.2.1/24 72 simple_if_fini "$h2" 192.0.2.2/24 73} 74 75setup_prepare() 76{ 77 h1=${NETIFS[p1]} 78 swp1=${NETIFS[p2]} 79 80 h2=${NETIFS[p3]} 81 swp2=${NETIFS[p4]} 82 83 swp3=${NETIFS[p6]} 84 85 vrf_prepare 86 87 h_create 88 89 switch_create 90} 91 92cleanup() 93{ 94 pre_cleanup 95 96 switch_destroy 97 98 h_destroy 99 100 vrf_cleanup 101} 102 103fdb_get_n_learned() 104{ 105 ip -d -j link show dev br0 type bridge | \ 106 jq '.[]["linkinfo"]["info_data"]["fdb_n_learned"]' 107} 108 109fdb_get_n_mac() 110{ 111 local mac=${1} 112 113 bridge -j fdb show br br0 | \ 114 jq "map(select(.mac == \"${mac}\" and (has(\"vlan\") | not))) | length" 115} 116 117fdb_fill_learned() 118{ 119 local i 120 121 for i in $(seq 1 "$NUM_PKTS"); do 122 fdb_add learned "$(mac "$i")" 123 done 124} 125 126fdb_reset() 127{ 128 bridge fdb flush dev br0 129 130 # Keep the default MAC address of h1 in the table. We set it to a different one when 131 # testing dynamic learning. 132 bridge fdb add "$H1_DEFAULT_MAC" dev "$swp1" master static use 133} 134 135fdb_add() 136{ 137 local type=$1 mac=$2 138 139 case "$type" in 140 learned) 141 ip link set "$h1" addr "$mac" 142 # Wait for a reply so we implicitly wait until after the forwarding 143 # code finished and the FDB entry was created. 144 PING_COUNT=1 ping_do "$h1" 192.0.2.2 145 check_err $? "Failed to ping another bridge port" 146 ip link set "$h1" addr "$H1_DEFAULT_MAC" 147 ;; 148 local) 149 ip link set dev "$swp3" addr "$mac" && ip link set "$swp3" master br0 150 ;; 151 static) 152 bridge fdb replace "$mac" dev "$swp1" master static 153 ;; 154 user) 155 bridge fdb replace "$mac" dev "$swp1" master static use 156 ;; 157 extern_learn) 158 bridge fdb replace "$mac" dev "$swp1" master extern_learn 159 ;; 160 esac 161 162 check_err $? "Failed to add a FDB entry of type ${type}" 163} 164 165fdb_del() 166{ 167 local type=$1 mac=$2 168 169 case "$type" in 170 local) 171 ip link set "$swp3" nomaster 172 ;; 173 *) 174 bridge fdb del "$mac" dev "$swp1" master 175 ;; 176 esac 177 178 check_err $? "Failed to remove a FDB entry of type ${type}" 179} 180 181check_fdb_n_learned_support() 182{ 183 if ! ip link help bridge 2>&1 | grep -q "fdb_max_learned"; then 184 echo "SKIP: iproute2 too old, missing bridge max learned support" 185 exit $ksft_skip 186 fi 187 188 ip link add dev br0 type bridge 189 local learned=$(fdb_get_n_learned) 190 ip link del dev br0 191 if [ "$learned" == "null" ]; then 192 echo "SKIP: kernel too old; bridge fdb_n_learned feature not supported." 193 exit $ksft_skip 194 fi 195} 196 197check_accounting_one_type() 198{ 199 local type=$1 is_counted=$2 overrides_learned=$3 200 shift 3 201 RET=0 202 203 fdb_reset 204 fdb_add "$type" "$(mac 0)" 205 learned=$(fdb_get_n_learned) 206 [ "$learned" -ne "$is_counted" ] 207 check_fail $? "Inserted FDB type ${type}: Expected the count ${is_counted}, but got ${learned}" 208 209 fdb_del "$type" "$(mac 0)" 210 learned=$(fdb_get_n_learned) 211 [ "$learned" -ne 0 ] 212 check_fail $? "Removed FDB type ${type}: Expected the count 0, but got ${learned}" 213 214 if [ "$overrides_learned" -eq 1 ]; then 215 fdb_reset 216 fdb_add learned "$(mac 0)" 217 fdb_add "$type" "$(mac 0)" 218 learned=$(fdb_get_n_learned) 219 [ "$learned" -ne "$is_counted" ] 220 check_fail $? "Set a learned entry to FDB type ${type}: Expected the count ${is_counted}, but got ${learned}" 221 fdb_del "$type" "$(mac 0)" 222 fi 223 224 log_test "FDB accounting interacting with FDB type ${type}" 225} 226 227check_accounting() 228{ 229 local type_args learned 230 RET=0 231 232 fdb_reset 233 learned=$(fdb_get_n_learned) 234 [ "$learned" -ne 0 ] 235 check_fail $? "Flushed the FDB table: Expected the count 0, but got ${learned}" 236 237 fdb_fill_learned 238 sleep 1 239 240 learned=$(fdb_get_n_learned) 241 [ "$learned" -ne "$NUM_PKTS" ] 242 check_fail $? "Filled the FDB table: Expected the count ${NUM_PKTS}, but got ${learned}" 243 244 log_test "FDB accounting" 245 246 for type_args in "${FDB_TYPES[@]}"; do 247 # This is intentional use of word splitting. 248 # shellcheck disable=SC2086 249 check_accounting_one_type $type_args 250 done 251} 252 253check_limit_one_type() 254{ 255 local type=$1 is_counted=$2 256 local n_mac expected=$((1 - is_counted)) 257 RET=0 258 259 fdb_reset 260 fdb_fill_learned 261 262 fdb_add "$type" "$(mac 0)" 263 n_mac=$(fdb_get_n_mac "$(mac 0)") 264 [ "$n_mac" -ne "$expected" ] 265 check_fail $? "Inserted FDB type ${type} at limit: Expected the count ${expected}, but got ${n_mac}" 266 267 log_test "FDB limits interacting with FDB type ${type}" 268} 269 270check_limit() 271{ 272 local learned 273 RET=0 274 275 ip link set br0 type bridge fdb_max_learned "$FDB_LIMIT" 276 277 fdb_reset 278 fdb_fill_learned 279 280 learned=$(fdb_get_n_learned) 281 [ "$learned" -ne "$FDB_LIMIT" ] 282 check_fail $? "Filled the limited FDB table: Expected the count ${FDB_LIMIT}, but got ${learned}" 283 284 log_test "FDB limits" 285 286 for type_args in "${FDB_TYPES[@]}"; do 287 # This is intentional use of word splitting. 288 # shellcheck disable=SC2086 289 check_limit_one_type $type_args 290 done 291} 292 293check_fdb_n_learned_support 294 295trap cleanup EXIT 296 297setup_prepare 298 299tests_run 300 301exit $EXIT_STATUS 302