1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4############################################################################## 5# Defines 6 7# Can be overridden by the configuration file. 8PING=${PING:=ping} 9PING6=${PING6:=ping6} 10MZ=${MZ:=mausezahn} 11ARPING=${ARPING:=arping} 12TEAMD=${TEAMD:=teamd} 13WAIT_TIME=${WAIT_TIME:=5} 14PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 15PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} 16NETIF_TYPE=${NETIF_TYPE:=veth} 17NETIF_CREATE=${NETIF_CREATE:=yes} 18MCD=${MCD:=smcrouted} 19MC_CLI=${MC_CLI:=smcroutectl} 20PING_TIMEOUT=${PING_TIMEOUT:=5} 21 22relative_path="${BASH_SOURCE%/*}" 23if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then 24 relative_path="." 25fi 26 27if [[ -f $relative_path/forwarding.config ]]; then 28 source "$relative_path/forwarding.config" 29fi 30 31############################################################################## 32# Sanity checks 33 34check_tc_version() 35{ 36 tc -j &> /dev/null 37 if [[ $? -ne 0 ]]; then 38 echo "SKIP: iproute2 too old; tc is missing JSON support" 39 exit 1 40 fi 41} 42 43check_tc_shblock_support() 44{ 45 tc filter help 2>&1 | grep block &> /dev/null 46 if [[ $? -ne 0 ]]; then 47 echo "SKIP: iproute2 too old; tc is missing shared block support" 48 exit 1 49 fi 50} 51 52check_tc_chain_support() 53{ 54 tc help 2>&1|grep chain &> /dev/null 55 if [[ $? -ne 0 ]]; then 56 echo "SKIP: iproute2 too old; tc is missing chain support" 57 exit 1 58 fi 59} 60 61if [[ "$(id -u)" -ne 0 ]]; then 62 echo "SKIP: need root privileges" 63 exit 0 64fi 65 66if [[ "$CHECK_TC" = "yes" ]]; then 67 check_tc_version 68fi 69 70require_command() 71{ 72 local cmd=$1; shift 73 74 if [[ ! -x "$(command -v "$cmd")" ]]; then 75 echo "SKIP: $cmd not installed" 76 exit 1 77 fi 78} 79 80require_command jq 81require_command $MZ 82 83if [[ ! -v NUM_NETIFS ]]; then 84 echo "SKIP: importer does not define \"NUM_NETIFS\"" 85 exit 1 86fi 87 88############################################################################## 89# Command line options handling 90 91count=0 92 93while [[ $# -gt 0 ]]; do 94 if [[ "$count" -eq "0" ]]; then 95 unset NETIFS 96 declare -A NETIFS 97 fi 98 count=$((count + 1)) 99 NETIFS[p$count]="$1" 100 shift 101done 102 103############################################################################## 104# Network interfaces configuration 105 106create_netif_veth() 107{ 108 local i 109 110 for ((i = 1; i <= NUM_NETIFS; ++i)); do 111 local j=$((i+1)) 112 113 ip link show dev ${NETIFS[p$i]} &> /dev/null 114 if [[ $? -ne 0 ]]; then 115 ip link add ${NETIFS[p$i]} type veth \ 116 peer name ${NETIFS[p$j]} 117 if [[ $? -ne 0 ]]; then 118 echo "Failed to create netif" 119 exit 1 120 fi 121 fi 122 i=$j 123 done 124} 125 126create_netif() 127{ 128 case "$NETIF_TYPE" in 129 veth) create_netif_veth 130 ;; 131 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" 132 exit 1 133 ;; 134 esac 135} 136 137if [[ "$NETIF_CREATE" = "yes" ]]; then 138 create_netif 139fi 140 141for ((i = 1; i <= NUM_NETIFS; ++i)); do 142 ip link show dev ${NETIFS[p$i]} &> /dev/null 143 if [[ $? -ne 0 ]]; then 144 echo "SKIP: could not find all required interfaces" 145 exit 1 146 fi 147done 148 149############################################################################## 150# Helpers 151 152# Exit status to return at the end. Set in case one of the tests fails. 153EXIT_STATUS=0 154# Per-test return value. Clear at the beginning of each test. 155RET=0 156 157check_err() 158{ 159 local err=$1 160 local msg=$2 161 162 if [[ $RET -eq 0 && $err -ne 0 ]]; then 163 RET=$err 164 retmsg=$msg 165 fi 166} 167 168check_fail() 169{ 170 local err=$1 171 local msg=$2 172 173 if [[ $RET -eq 0 && $err -eq 0 ]]; then 174 RET=1 175 retmsg=$msg 176 fi 177} 178 179check_err_fail() 180{ 181 local should_fail=$1; shift 182 local err=$1; shift 183 local what=$1; shift 184 185 if ((should_fail)); then 186 check_fail $err "$what succeeded, but should have failed" 187 else 188 check_err $err "$what failed" 189 fi 190} 191 192log_test() 193{ 194 local test_name=$1 195 local opt_str=$2 196 197 if [[ $# -eq 2 ]]; then 198 opt_str="($opt_str)" 199 fi 200 201 if [[ $RET -ne 0 ]]; then 202 EXIT_STATUS=1 203 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" 204 if [[ ! -z "$retmsg" ]]; then 205 printf "\t%s\n" "$retmsg" 206 fi 207 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 208 echo "Hit enter to continue, 'q' to quit" 209 read a 210 [ "$a" = "q" ] && exit 1 211 fi 212 return 1 213 fi 214 215 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str" 216 return 0 217} 218 219log_info() 220{ 221 local msg=$1 222 223 echo "INFO: $msg" 224} 225 226setup_wait_dev() 227{ 228 local dev=$1; shift 229 230 while true; do 231 ip link show dev $dev up \ 232 | grep 'state UP' &> /dev/null 233 if [[ $? -ne 0 ]]; then 234 sleep 1 235 else 236 break 237 fi 238 done 239} 240 241setup_wait() 242{ 243 local num_netifs=${1:-$NUM_NETIFS} 244 245 for ((i = 1; i <= num_netifs; ++i)); do 246 setup_wait_dev ${NETIFS[p$i]} 247 done 248 249 # Make sure links are ready. 250 sleep $WAIT_TIME 251} 252 253cmd_jq() 254{ 255 local cmd=$1 256 local jq_exp=$2 257 local jq_opts=$3 258 local ret 259 local output 260 261 output="$($cmd)" 262 # it the command fails, return error right away 263 ret=$? 264 if [[ $ret -ne 0 ]]; then 265 return $ret 266 fi 267 output=$(echo $output | jq -r $jq_opts "$jq_exp") 268 ret=$? 269 if [[ $ret -ne 0 ]]; then 270 return $ret 271 fi 272 echo $output 273 # return success only in case of non-empty output 274 [ ! -z "$output" ] 275} 276 277lldpad_app_wait_set() 278{ 279 local dev=$1; shift 280 281 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do 282 echo "$dev: waiting for lldpad to push pending APP updates" 283 sleep 5 284 done 285} 286 287lldpad_app_wait_del() 288{ 289 # Give lldpad a chance to push down the changes. If the device is downed 290 # too soon, the updates will be left pending. However, they will have 291 # been struck off the lldpad's DB already, so we won't be able to tell 292 # they are pending. Then on next test iteration this would cause 293 # weirdness as newly-added APP rules conflict with the old ones, 294 # sometimes getting stuck in an "unknown" state. 295 sleep 5 296} 297 298pre_cleanup() 299{ 300 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 301 echo "Pausing before cleanup, hit any key to continue" 302 read 303 fi 304} 305 306vrf_prepare() 307{ 308 ip -4 rule add pref 32765 table local 309 ip -4 rule del pref 0 310 ip -6 rule add pref 32765 table local 311 ip -6 rule del pref 0 312} 313 314vrf_cleanup() 315{ 316 ip -6 rule add pref 0 table local 317 ip -6 rule del pref 32765 318 ip -4 rule add pref 0 table local 319 ip -4 rule del pref 32765 320} 321 322__last_tb_id=0 323declare -A __TB_IDS 324 325__vrf_td_id_assign() 326{ 327 local vrf_name=$1 328 329 __last_tb_id=$((__last_tb_id + 1)) 330 __TB_IDS[$vrf_name]=$__last_tb_id 331 return $__last_tb_id 332} 333 334__vrf_td_id_lookup() 335{ 336 local vrf_name=$1 337 338 return ${__TB_IDS[$vrf_name]} 339} 340 341vrf_create() 342{ 343 local vrf_name=$1 344 local tb_id 345 346 __vrf_td_id_assign $vrf_name 347 tb_id=$? 348 349 ip link add dev $vrf_name type vrf table $tb_id 350 ip -4 route add table $tb_id unreachable default metric 4278198272 351 ip -6 route add table $tb_id unreachable default metric 4278198272 352} 353 354vrf_destroy() 355{ 356 local vrf_name=$1 357 local tb_id 358 359 __vrf_td_id_lookup $vrf_name 360 tb_id=$? 361 362 ip -6 route del table $tb_id unreachable default metric 4278198272 363 ip -4 route del table $tb_id unreachable default metric 4278198272 364 ip link del dev $vrf_name 365} 366 367__addr_add_del() 368{ 369 local if_name=$1 370 local add_del=$2 371 local array 372 373 shift 374 shift 375 array=("${@}") 376 377 for addrstr in "${array[@]}"; do 378 ip address $add_del $addrstr dev $if_name 379 done 380} 381 382__simple_if_init() 383{ 384 local if_name=$1; shift 385 local vrf_name=$1; shift 386 local addrs=("${@}") 387 388 ip link set dev $if_name master $vrf_name 389 ip link set dev $if_name up 390 391 __addr_add_del $if_name add "${addrs[@]}" 392} 393 394__simple_if_fini() 395{ 396 local if_name=$1; shift 397 local addrs=("${@}") 398 399 __addr_add_del $if_name del "${addrs[@]}" 400 401 ip link set dev $if_name down 402 ip link set dev $if_name nomaster 403} 404 405simple_if_init() 406{ 407 local if_name=$1 408 local vrf_name 409 local array 410 411 shift 412 vrf_name=v$if_name 413 array=("${@}") 414 415 vrf_create $vrf_name 416 ip link set dev $vrf_name up 417 __simple_if_init $if_name $vrf_name "${array[@]}" 418} 419 420simple_if_fini() 421{ 422 local if_name=$1 423 local vrf_name 424 local array 425 426 shift 427 vrf_name=v$if_name 428 array=("${@}") 429 430 __simple_if_fini $if_name "${array[@]}" 431 vrf_destroy $vrf_name 432} 433 434tunnel_create() 435{ 436 local name=$1; shift 437 local type=$1; shift 438 local local=$1; shift 439 local remote=$1; shift 440 441 ip link add name $name type $type \ 442 local $local remote $remote "$@" 443 ip link set dev $name up 444} 445 446tunnel_destroy() 447{ 448 local name=$1; shift 449 450 ip link del dev $name 451} 452 453vlan_create() 454{ 455 local if_name=$1; shift 456 local vid=$1; shift 457 local vrf=$1; shift 458 local ips=("${@}") 459 local name=$if_name.$vid 460 461 ip link add name $name link $if_name type vlan id $vid 462 if [ "$vrf" != "" ]; then 463 ip link set dev $name master $vrf 464 fi 465 ip link set dev $name up 466 __addr_add_del $name add "${ips[@]}" 467} 468 469vlan_destroy() 470{ 471 local if_name=$1; shift 472 local vid=$1; shift 473 local name=$if_name.$vid 474 475 ip link del dev $name 476} 477 478team_create() 479{ 480 local if_name=$1; shift 481 local mode=$1; shift 482 483 require_command $TEAMD 484 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 485 for slave in "$@"; do 486 ip link set dev $slave down 487 ip link set dev $slave master $if_name 488 ip link set dev $slave up 489 done 490 ip link set dev $if_name up 491} 492 493team_destroy() 494{ 495 local if_name=$1; shift 496 497 $TEAMD -t $if_name -k 498} 499 500master_name_get() 501{ 502 local if_name=$1 503 504 ip -j link show dev $if_name | jq -r '.[]["master"]' 505} 506 507link_stats_get() 508{ 509 local if_name=$1; shift 510 local dir=$1; shift 511 local stat=$1; shift 512 513 ip -j -s link show dev $if_name \ 514 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 515} 516 517link_stats_tx_packets_get() 518{ 519 link_stats_get $1 tx packets 520} 521 522link_stats_rx_errors_get() 523{ 524 link_stats_get $1 rx errors 525} 526 527tc_rule_stats_get() 528{ 529 local dev=$1; shift 530 local pref=$1; shift 531 local dir=$1; shift 532 533 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ 534 | jq '.[1].options.actions[].stats.packets' 535} 536 537ethtool_stats_get() 538{ 539 local dev=$1; shift 540 local stat=$1; shift 541 542 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 543} 544 545mac_get() 546{ 547 local if_name=$1 548 549 ip -j link show dev $if_name | jq -r '.[]["address"]' 550} 551 552bridge_ageing_time_get() 553{ 554 local bridge=$1 555 local ageing_time 556 557 # Need to divide by 100 to convert to seconds. 558 ageing_time=$(ip -j -d link show dev $bridge \ 559 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 560 echo $((ageing_time / 100)) 561} 562 563declare -A SYSCTL_ORIG 564sysctl_set() 565{ 566 local key=$1; shift 567 local value=$1; shift 568 569 SYSCTL_ORIG[$key]=$(sysctl -n $key) 570 sysctl -qw $key=$value 571} 572 573sysctl_restore() 574{ 575 local key=$1; shift 576 577 sysctl -qw $key=${SYSCTL_ORIG["$key"]} 578} 579 580forwarding_enable() 581{ 582 sysctl_set net.ipv4.conf.all.forwarding 1 583 sysctl_set net.ipv6.conf.all.forwarding 1 584} 585 586forwarding_restore() 587{ 588 sysctl_restore net.ipv6.conf.all.forwarding 589 sysctl_restore net.ipv4.conf.all.forwarding 590} 591 592declare -A MTU_ORIG 593mtu_set() 594{ 595 local dev=$1; shift 596 local mtu=$1; shift 597 598 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 599 ip link set dev $dev mtu $mtu 600} 601 602mtu_restore() 603{ 604 local dev=$1; shift 605 606 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 607} 608 609tc_offload_check() 610{ 611 local num_netifs=${1:-$NUM_NETIFS} 612 613 for ((i = 1; i <= num_netifs; ++i)); do 614 ethtool -k ${NETIFS[p$i]} \ 615 | grep "hw-tc-offload: on" &> /dev/null 616 if [[ $? -ne 0 ]]; then 617 return 1 618 fi 619 done 620 621 return 0 622} 623 624trap_install() 625{ 626 local dev=$1; shift 627 local direction=$1; shift 628 629 # Some devices may not support or need in-hardware trapping of traffic 630 # (e.g. the veth pairs that this library creates for non-existent 631 # loopbacks). Use continue instead, so that there is a filter in there 632 # (some tests check counters), and so that other filters are still 633 # processed. 634 tc filter add dev $dev $direction pref 1 \ 635 flower skip_sw action trap 2>/dev/null \ 636 || tc filter add dev $dev $direction pref 1 \ 637 flower action continue 638} 639 640trap_uninstall() 641{ 642 local dev=$1; shift 643 local direction=$1; shift 644 645 tc filter del dev $dev $direction pref 1 flower 646} 647 648slow_path_trap_install() 649{ 650 # For slow-path testing, we need to install a trap to get to 651 # slow path the packets that would otherwise be switched in HW. 652 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 653 trap_install "$@" 654 fi 655} 656 657slow_path_trap_uninstall() 658{ 659 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 660 trap_uninstall "$@" 661 fi 662} 663 664__icmp_capture_add_del() 665{ 666 local add_del=$1; shift 667 local pref=$1; shift 668 local vsuf=$1; shift 669 local tundev=$1; shift 670 local filter=$1; shift 671 672 tc filter $add_del dev "$tundev" ingress \ 673 proto ip$vsuf pref $pref \ 674 flower ip_proto icmp$vsuf $filter \ 675 action pass 676} 677 678icmp_capture_install() 679{ 680 __icmp_capture_add_del add 100 "" "$@" 681} 682 683icmp_capture_uninstall() 684{ 685 __icmp_capture_add_del del 100 "" "$@" 686} 687 688icmp6_capture_install() 689{ 690 __icmp_capture_add_del add 100 v6 "$@" 691} 692 693icmp6_capture_uninstall() 694{ 695 __icmp_capture_add_del del 100 v6 "$@" 696} 697 698__vlan_capture_add_del() 699{ 700 local add_del=$1; shift 701 local pref=$1; shift 702 local dev=$1; shift 703 local filter=$1; shift 704 705 tc filter $add_del dev "$dev" ingress \ 706 proto 802.1q pref $pref \ 707 flower $filter \ 708 action pass 709} 710 711vlan_capture_install() 712{ 713 __vlan_capture_add_del add 100 "$@" 714} 715 716vlan_capture_uninstall() 717{ 718 __vlan_capture_add_del del 100 "$@" 719} 720 721__dscp_capture_add_del() 722{ 723 local add_del=$1; shift 724 local dev=$1; shift 725 local base=$1; shift 726 local dscp; 727 728 for prio in {0..7}; do 729 dscp=$((base + prio)) 730 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 731 "skip_hw ip_tos $((dscp << 2))" 732 done 733} 734 735dscp_capture_install() 736{ 737 local dev=$1; shift 738 local base=$1; shift 739 740 __dscp_capture_add_del add $dev $base 741} 742 743dscp_capture_uninstall() 744{ 745 local dev=$1; shift 746 local base=$1; shift 747 748 __dscp_capture_add_del del $dev $base 749} 750 751dscp_fetch_stats() 752{ 753 local dev=$1; shift 754 local base=$1; shift 755 756 for prio in {0..7}; do 757 local dscp=$((base + prio)) 758 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 759 echo "[$dscp]=$t " 760 done 761} 762 763matchall_sink_create() 764{ 765 local dev=$1; shift 766 767 tc qdisc add dev $dev clsact 768 tc filter add dev $dev ingress \ 769 pref 10000 \ 770 matchall \ 771 action drop 772} 773 774tests_run() 775{ 776 local current_test 777 778 for current_test in ${TESTS:-$ALL_TESTS}; do 779 $current_test 780 done 781} 782 783multipath_eval() 784{ 785 local desc="$1" 786 local weight_rp12=$2 787 local weight_rp13=$3 788 local packets_rp12=$4 789 local packets_rp13=$5 790 local weights_ratio packets_ratio diff 791 792 RET=0 793 794 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 795 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 796 | bc -l) 797 else 798 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 799 | bc -l) 800 fi 801 802 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 803 check_err 1 "Packet difference is 0" 804 log_test "Multipath" 805 log_info "Expected ratio $weights_ratio" 806 return 807 fi 808 809 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 810 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 811 | bc -l) 812 else 813 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 814 | bc -l) 815 fi 816 817 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 818 diff=${diff#-} 819 820 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 821 check_err $? "Too large discrepancy between expected and measured ratios" 822 log_test "$desc" 823 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 824} 825 826in_ns() 827{ 828 local name=$1; shift 829 830 ip netns exec $name bash <<-EOF 831 NUM_NETIFS=0 832 source lib.sh 833 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 834 EOF 835} 836 837############################################################################## 838# Tests 839 840ping_do() 841{ 842 local if_name=$1 843 local dip=$2 844 local args=$3 845 local vrf_name 846 847 vrf_name=$(master_name_get $if_name) 848 ip vrf exec $vrf_name \ 849 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 850} 851 852ping_test() 853{ 854 RET=0 855 856 ping_do $1 $2 857 check_err $? 858 log_test "ping$3" 859} 860 861ping6_do() 862{ 863 local if_name=$1 864 local dip=$2 865 local args=$3 866 local vrf_name 867 868 vrf_name=$(master_name_get $if_name) 869 ip vrf exec $vrf_name \ 870 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 871} 872 873ping6_test() 874{ 875 RET=0 876 877 ping6_do $1 $2 878 check_err $? 879 log_test "ping6$3" 880} 881 882learning_test() 883{ 884 local bridge=$1 885 local br_port1=$2 # Connected to `host1_if`. 886 local host1_if=$3 887 local host2_if=$4 888 local mac=de:ad:be:ef:13:37 889 local ageing_time 890 891 RET=0 892 893 bridge -j fdb show br $bridge brport $br_port1 \ 894 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 895 check_fail $? "Found FDB record when should not" 896 897 # Disable unknown unicast flooding on `br_port1` to make sure 898 # packets are only forwarded through the port after a matching 899 # FDB entry was installed. 900 bridge link set dev $br_port1 flood off 901 902 tc qdisc add dev $host1_if ingress 903 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 904 flower dst_mac $mac action drop 905 906 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 907 sleep 1 908 909 tc -j -s filter show dev $host1_if ingress \ 910 | jq -e ".[] | select(.options.handle == 101) \ 911 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 912 check_fail $? "Packet reached second host when should not" 913 914 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 915 sleep 1 916 917 bridge -j fdb show br $bridge brport $br_port1 \ 918 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 919 check_err $? "Did not find FDB record when should" 920 921 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 922 sleep 1 923 924 tc -j -s filter show dev $host1_if ingress \ 925 | jq -e ".[] | select(.options.handle == 101) \ 926 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 927 check_err $? "Packet did not reach second host when should" 928 929 # Wait for 10 seconds after the ageing time to make sure FDB 930 # record was aged-out. 931 ageing_time=$(bridge_ageing_time_get $bridge) 932 sleep $((ageing_time + 10)) 933 934 bridge -j fdb show br $bridge brport $br_port1 \ 935 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 936 check_fail $? "Found FDB record when should not" 937 938 bridge link set dev $br_port1 learning off 939 940 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 941 sleep 1 942 943 bridge -j fdb show br $bridge brport $br_port1 \ 944 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 945 check_fail $? "Found FDB record when should not" 946 947 bridge link set dev $br_port1 learning on 948 949 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 950 tc qdisc del dev $host1_if ingress 951 952 bridge link set dev $br_port1 flood on 953 954 log_test "FDB learning" 955} 956 957flood_test_do() 958{ 959 local should_flood=$1 960 local mac=$2 961 local ip=$3 962 local host1_if=$4 963 local host2_if=$5 964 local err=0 965 966 # Add an ACL on `host2_if` which will tell us whether the packet 967 # was flooded to it or not. 968 tc qdisc add dev $host2_if ingress 969 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 970 flower dst_mac $mac action drop 971 972 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 973 sleep 1 974 975 tc -j -s filter show dev $host2_if ingress \ 976 | jq -e ".[] | select(.options.handle == 101) \ 977 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 978 if [[ $? -ne 0 && $should_flood == "true" || \ 979 $? -eq 0 && $should_flood == "false" ]]; then 980 err=1 981 fi 982 983 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 984 tc qdisc del dev $host2_if ingress 985 986 return $err 987} 988 989flood_unicast_test() 990{ 991 local br_port=$1 992 local host1_if=$2 993 local host2_if=$3 994 local mac=de:ad:be:ef:13:37 995 local ip=192.0.2.100 996 997 RET=0 998 999 bridge link set dev $br_port flood off 1000 1001 flood_test_do false $mac $ip $host1_if $host2_if 1002 check_err $? "Packet flooded when should not" 1003 1004 bridge link set dev $br_port flood on 1005 1006 flood_test_do true $mac $ip $host1_if $host2_if 1007 check_err $? "Packet was not flooded when should" 1008 1009 log_test "Unknown unicast flood" 1010} 1011 1012flood_multicast_test() 1013{ 1014 local br_port=$1 1015 local host1_if=$2 1016 local host2_if=$3 1017 local mac=01:00:5e:00:00:01 1018 local ip=239.0.0.1 1019 1020 RET=0 1021 1022 bridge link set dev $br_port mcast_flood off 1023 1024 flood_test_do false $mac $ip $host1_if $host2_if 1025 check_err $? "Packet flooded when should not" 1026 1027 bridge link set dev $br_port mcast_flood on 1028 1029 flood_test_do true $mac $ip $host1_if $host2_if 1030 check_err $? "Packet was not flooded when should" 1031 1032 log_test "Unregistered multicast flood" 1033} 1034 1035flood_test() 1036{ 1037 # `br_port` is connected to `host2_if` 1038 local br_port=$1 1039 local host1_if=$2 1040 local host2_if=$3 1041 1042 flood_unicast_test $br_port $host1_if $host2_if 1043 flood_multicast_test $br_port $host1_if $host2_if 1044} 1045