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 ret 258 local output 259 260 output="$($cmd)" 261 # it the command fails, return error right away 262 ret=$? 263 if [[ $ret -ne 0 ]]; then 264 return $ret 265 fi 266 output=$(echo $output | jq -r "$jq_exp") 267 echo $output 268 # return success only in case of non-empty output 269 [ ! -z "$output" ] 270} 271 272lldpad_app_wait_set() 273{ 274 local dev=$1; shift 275 276 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do 277 echo "$dev: waiting for lldpad to push pending APP updates" 278 sleep 5 279 done 280} 281 282lldpad_app_wait_del() 283{ 284 # Give lldpad a chance to push down the changes. If the device is downed 285 # too soon, the updates will be left pending. However, they will have 286 # been struck off the lldpad's DB already, so we won't be able to tell 287 # they are pending. Then on next test iteration this would cause 288 # weirdness as newly-added APP rules conflict with the old ones, 289 # sometimes getting stuck in an "unknown" state. 290 sleep 5 291} 292 293pre_cleanup() 294{ 295 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 296 echo "Pausing before cleanup, hit any key to continue" 297 read 298 fi 299} 300 301vrf_prepare() 302{ 303 ip -4 rule add pref 32765 table local 304 ip -4 rule del pref 0 305 ip -6 rule add pref 32765 table local 306 ip -6 rule del pref 0 307} 308 309vrf_cleanup() 310{ 311 ip -6 rule add pref 0 table local 312 ip -6 rule del pref 32765 313 ip -4 rule add pref 0 table local 314 ip -4 rule del pref 32765 315} 316 317__last_tb_id=0 318declare -A __TB_IDS 319 320__vrf_td_id_assign() 321{ 322 local vrf_name=$1 323 324 __last_tb_id=$((__last_tb_id + 1)) 325 __TB_IDS[$vrf_name]=$__last_tb_id 326 return $__last_tb_id 327} 328 329__vrf_td_id_lookup() 330{ 331 local vrf_name=$1 332 333 return ${__TB_IDS[$vrf_name]} 334} 335 336vrf_create() 337{ 338 local vrf_name=$1 339 local tb_id 340 341 __vrf_td_id_assign $vrf_name 342 tb_id=$? 343 344 ip link add dev $vrf_name type vrf table $tb_id 345 ip -4 route add table $tb_id unreachable default metric 4278198272 346 ip -6 route add table $tb_id unreachable default metric 4278198272 347} 348 349vrf_destroy() 350{ 351 local vrf_name=$1 352 local tb_id 353 354 __vrf_td_id_lookup $vrf_name 355 tb_id=$? 356 357 ip -6 route del table $tb_id unreachable default metric 4278198272 358 ip -4 route del table $tb_id unreachable default metric 4278198272 359 ip link del dev $vrf_name 360} 361 362__addr_add_del() 363{ 364 local if_name=$1 365 local add_del=$2 366 local array 367 368 shift 369 shift 370 array=("${@}") 371 372 for addrstr in "${array[@]}"; do 373 ip address $add_del $addrstr dev $if_name 374 done 375} 376 377__simple_if_init() 378{ 379 local if_name=$1; shift 380 local vrf_name=$1; shift 381 local addrs=("${@}") 382 383 ip link set dev $if_name master $vrf_name 384 ip link set dev $if_name up 385 386 __addr_add_del $if_name add "${addrs[@]}" 387} 388 389__simple_if_fini() 390{ 391 local if_name=$1; shift 392 local addrs=("${@}") 393 394 __addr_add_del $if_name del "${addrs[@]}" 395 396 ip link set dev $if_name down 397 ip link set dev $if_name nomaster 398} 399 400simple_if_init() 401{ 402 local if_name=$1 403 local vrf_name 404 local array 405 406 shift 407 vrf_name=v$if_name 408 array=("${@}") 409 410 vrf_create $vrf_name 411 ip link set dev $vrf_name up 412 __simple_if_init $if_name $vrf_name "${array[@]}" 413} 414 415simple_if_fini() 416{ 417 local if_name=$1 418 local vrf_name 419 local array 420 421 shift 422 vrf_name=v$if_name 423 array=("${@}") 424 425 __simple_if_fini $if_name "${array[@]}" 426 vrf_destroy $vrf_name 427} 428 429tunnel_create() 430{ 431 local name=$1; shift 432 local type=$1; shift 433 local local=$1; shift 434 local remote=$1; shift 435 436 ip link add name $name type $type \ 437 local $local remote $remote "$@" 438 ip link set dev $name up 439} 440 441tunnel_destroy() 442{ 443 local name=$1; shift 444 445 ip link del dev $name 446} 447 448vlan_create() 449{ 450 local if_name=$1; shift 451 local vid=$1; shift 452 local vrf=$1; shift 453 local ips=("${@}") 454 local name=$if_name.$vid 455 456 ip link add name $name link $if_name type vlan id $vid 457 if [ "$vrf" != "" ]; then 458 ip link set dev $name master $vrf 459 fi 460 ip link set dev $name up 461 __addr_add_del $name add "${ips[@]}" 462} 463 464vlan_destroy() 465{ 466 local if_name=$1; shift 467 local vid=$1; shift 468 local name=$if_name.$vid 469 470 ip link del dev $name 471} 472 473team_create() 474{ 475 local if_name=$1; shift 476 local mode=$1; shift 477 478 require_command $TEAMD 479 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 480 for slave in "$@"; do 481 ip link set dev $slave down 482 ip link set dev $slave master $if_name 483 ip link set dev $slave up 484 done 485 ip link set dev $if_name up 486} 487 488team_destroy() 489{ 490 local if_name=$1; shift 491 492 $TEAMD -t $if_name -k 493} 494 495master_name_get() 496{ 497 local if_name=$1 498 499 ip -j link show dev $if_name | jq -r '.[]["master"]' 500} 501 502link_stats_get() 503{ 504 local if_name=$1; shift 505 local dir=$1; shift 506 local stat=$1; shift 507 508 ip -j -s link show dev $if_name \ 509 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 510} 511 512link_stats_tx_packets_get() 513{ 514 link_stats_get $1 tx packets 515} 516 517link_stats_rx_errors_get() 518{ 519 link_stats_get $1 rx errors 520} 521 522tc_rule_stats_get() 523{ 524 local dev=$1; shift 525 local pref=$1; shift 526 local dir=$1; shift 527 528 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ 529 | jq '.[1].options.actions[].stats.packets' 530} 531 532ethtool_stats_get() 533{ 534 local dev=$1; shift 535 local stat=$1; shift 536 537 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 538} 539 540mac_get() 541{ 542 local if_name=$1 543 544 ip -j link show dev $if_name | jq -r '.[]["address"]' 545} 546 547bridge_ageing_time_get() 548{ 549 local bridge=$1 550 local ageing_time 551 552 # Need to divide by 100 to convert to seconds. 553 ageing_time=$(ip -j -d link show dev $bridge \ 554 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 555 echo $((ageing_time / 100)) 556} 557 558declare -A SYSCTL_ORIG 559sysctl_set() 560{ 561 local key=$1; shift 562 local value=$1; shift 563 564 SYSCTL_ORIG[$key]=$(sysctl -n $key) 565 sysctl -qw $key=$value 566} 567 568sysctl_restore() 569{ 570 local key=$1; shift 571 572 sysctl -qw $key=${SYSCTL_ORIG["$key"]} 573} 574 575forwarding_enable() 576{ 577 sysctl_set net.ipv4.conf.all.forwarding 1 578 sysctl_set net.ipv6.conf.all.forwarding 1 579} 580 581forwarding_restore() 582{ 583 sysctl_restore net.ipv6.conf.all.forwarding 584 sysctl_restore net.ipv4.conf.all.forwarding 585} 586 587declare -A MTU_ORIG 588mtu_set() 589{ 590 local dev=$1; shift 591 local mtu=$1; shift 592 593 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 594 ip link set dev $dev mtu $mtu 595} 596 597mtu_restore() 598{ 599 local dev=$1; shift 600 601 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 602} 603 604tc_offload_check() 605{ 606 local num_netifs=${1:-$NUM_NETIFS} 607 608 for ((i = 1; i <= num_netifs; ++i)); do 609 ethtool -k ${NETIFS[p$i]} \ 610 | grep "hw-tc-offload: on" &> /dev/null 611 if [[ $? -ne 0 ]]; then 612 return 1 613 fi 614 done 615 616 return 0 617} 618 619trap_install() 620{ 621 local dev=$1; shift 622 local direction=$1; shift 623 624 # Some devices may not support or need in-hardware trapping of traffic 625 # (e.g. the veth pairs that this library creates for non-existent 626 # loopbacks). Use continue instead, so that there is a filter in there 627 # (some tests check counters), and so that other filters are still 628 # processed. 629 tc filter add dev $dev $direction pref 1 \ 630 flower skip_sw action trap 2>/dev/null \ 631 || tc filter add dev $dev $direction pref 1 \ 632 flower action continue 633} 634 635trap_uninstall() 636{ 637 local dev=$1; shift 638 local direction=$1; shift 639 640 tc filter del dev $dev $direction pref 1 flower 641} 642 643slow_path_trap_install() 644{ 645 # For slow-path testing, we need to install a trap to get to 646 # slow path the packets that would otherwise be switched in HW. 647 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 648 trap_install "$@" 649 fi 650} 651 652slow_path_trap_uninstall() 653{ 654 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 655 trap_uninstall "$@" 656 fi 657} 658 659__icmp_capture_add_del() 660{ 661 local add_del=$1; shift 662 local pref=$1; shift 663 local vsuf=$1; shift 664 local tundev=$1; shift 665 local filter=$1; shift 666 667 tc filter $add_del dev "$tundev" ingress \ 668 proto ip$vsuf pref $pref \ 669 flower ip_proto icmp$vsuf $filter \ 670 action pass 671} 672 673icmp_capture_install() 674{ 675 __icmp_capture_add_del add 100 "" "$@" 676} 677 678icmp_capture_uninstall() 679{ 680 __icmp_capture_add_del del 100 "" "$@" 681} 682 683icmp6_capture_install() 684{ 685 __icmp_capture_add_del add 100 v6 "$@" 686} 687 688icmp6_capture_uninstall() 689{ 690 __icmp_capture_add_del del 100 v6 "$@" 691} 692 693__vlan_capture_add_del() 694{ 695 local add_del=$1; shift 696 local pref=$1; shift 697 local dev=$1; shift 698 local filter=$1; shift 699 700 tc filter $add_del dev "$dev" ingress \ 701 proto 802.1q pref $pref \ 702 flower $filter \ 703 action pass 704} 705 706vlan_capture_install() 707{ 708 __vlan_capture_add_del add 100 "$@" 709} 710 711vlan_capture_uninstall() 712{ 713 __vlan_capture_add_del del 100 "$@" 714} 715 716__dscp_capture_add_del() 717{ 718 local add_del=$1; shift 719 local dev=$1; shift 720 local base=$1; shift 721 local dscp; 722 723 for prio in {0..7}; do 724 dscp=$((base + prio)) 725 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 726 "skip_hw ip_tos $((dscp << 2))" 727 done 728} 729 730dscp_capture_install() 731{ 732 local dev=$1; shift 733 local base=$1; shift 734 735 __dscp_capture_add_del add $dev $base 736} 737 738dscp_capture_uninstall() 739{ 740 local dev=$1; shift 741 local base=$1; shift 742 743 __dscp_capture_add_del del $dev $base 744} 745 746dscp_fetch_stats() 747{ 748 local dev=$1; shift 749 local base=$1; shift 750 751 for prio in {0..7}; do 752 local dscp=$((base + prio)) 753 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 754 echo "[$dscp]=$t " 755 done 756} 757 758matchall_sink_create() 759{ 760 local dev=$1; shift 761 762 tc qdisc add dev $dev clsact 763 tc filter add dev $dev ingress \ 764 pref 10000 \ 765 matchall \ 766 action drop 767} 768 769tests_run() 770{ 771 local current_test 772 773 for current_test in ${TESTS:-$ALL_TESTS}; do 774 $current_test 775 done 776} 777 778multipath_eval() 779{ 780 local desc="$1" 781 local weight_rp12=$2 782 local weight_rp13=$3 783 local packets_rp12=$4 784 local packets_rp13=$5 785 local weights_ratio packets_ratio diff 786 787 RET=0 788 789 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 790 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 791 | bc -l) 792 else 793 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 794 | bc -l) 795 fi 796 797 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 798 check_err 1 "Packet difference is 0" 799 log_test "Multipath" 800 log_info "Expected ratio $weights_ratio" 801 return 802 fi 803 804 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 805 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 806 | bc -l) 807 else 808 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 809 | bc -l) 810 fi 811 812 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 813 diff=${diff#-} 814 815 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 816 check_err $? "Too large discrepancy between expected and measured ratios" 817 log_test "$desc" 818 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 819} 820 821in_ns() 822{ 823 local name=$1; shift 824 825 ip netns exec $name bash <<-EOF 826 NUM_NETIFS=0 827 source lib.sh 828 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 829 EOF 830} 831 832############################################################################## 833# Tests 834 835ping_do() 836{ 837 local if_name=$1 838 local dip=$2 839 local args=$3 840 local vrf_name 841 842 vrf_name=$(master_name_get $if_name) 843 ip vrf exec $vrf_name \ 844 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 845} 846 847ping_test() 848{ 849 RET=0 850 851 ping_do $1 $2 852 check_err $? 853 log_test "ping$3" 854} 855 856ping6_do() 857{ 858 local if_name=$1 859 local dip=$2 860 local args=$3 861 local vrf_name 862 863 vrf_name=$(master_name_get $if_name) 864 ip vrf exec $vrf_name \ 865 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 866} 867 868ping6_test() 869{ 870 RET=0 871 872 ping6_do $1 $2 873 check_err $? 874 log_test "ping6$3" 875} 876 877learning_test() 878{ 879 local bridge=$1 880 local br_port1=$2 # Connected to `host1_if`. 881 local host1_if=$3 882 local host2_if=$4 883 local mac=de:ad:be:ef:13:37 884 local ageing_time 885 886 RET=0 887 888 bridge -j fdb show br $bridge brport $br_port1 \ 889 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 890 check_fail $? "Found FDB record when should not" 891 892 # Disable unknown unicast flooding on `br_port1` to make sure 893 # packets are only forwarded through the port after a matching 894 # FDB entry was installed. 895 bridge link set dev $br_port1 flood off 896 897 tc qdisc add dev $host1_if ingress 898 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 899 flower dst_mac $mac action drop 900 901 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 902 sleep 1 903 904 tc -j -s filter show dev $host1_if ingress \ 905 | jq -e ".[] | select(.options.handle == 101) \ 906 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 907 check_fail $? "Packet reached second host when should not" 908 909 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 910 sleep 1 911 912 bridge -j fdb show br $bridge brport $br_port1 \ 913 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 914 check_err $? "Did not find FDB record when should" 915 916 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 917 sleep 1 918 919 tc -j -s filter show dev $host1_if ingress \ 920 | jq -e ".[] | select(.options.handle == 101) \ 921 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 922 check_err $? "Packet did not reach second host when should" 923 924 # Wait for 10 seconds after the ageing time to make sure FDB 925 # record was aged-out. 926 ageing_time=$(bridge_ageing_time_get $bridge) 927 sleep $((ageing_time + 10)) 928 929 bridge -j fdb show br $bridge brport $br_port1 \ 930 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 931 check_fail $? "Found FDB record when should not" 932 933 bridge link set dev $br_port1 learning off 934 935 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 936 sleep 1 937 938 bridge -j fdb show br $bridge brport $br_port1 \ 939 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 940 check_fail $? "Found FDB record when should not" 941 942 bridge link set dev $br_port1 learning on 943 944 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 945 tc qdisc del dev $host1_if ingress 946 947 bridge link set dev $br_port1 flood on 948 949 log_test "FDB learning" 950} 951 952flood_test_do() 953{ 954 local should_flood=$1 955 local mac=$2 956 local ip=$3 957 local host1_if=$4 958 local host2_if=$5 959 local err=0 960 961 # Add an ACL on `host2_if` which will tell us whether the packet 962 # was flooded to it or not. 963 tc qdisc add dev $host2_if ingress 964 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 965 flower dst_mac $mac action drop 966 967 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 968 sleep 1 969 970 tc -j -s filter show dev $host2_if ingress \ 971 | jq -e ".[] | select(.options.handle == 101) \ 972 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 973 if [[ $? -ne 0 && $should_flood == "true" || \ 974 $? -eq 0 && $should_flood == "false" ]]; then 975 err=1 976 fi 977 978 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 979 tc qdisc del dev $host2_if ingress 980 981 return $err 982} 983 984flood_unicast_test() 985{ 986 local br_port=$1 987 local host1_if=$2 988 local host2_if=$3 989 local mac=de:ad:be:ef:13:37 990 local ip=192.0.2.100 991 992 RET=0 993 994 bridge link set dev $br_port flood off 995 996 flood_test_do false $mac $ip $host1_if $host2_if 997 check_err $? "Packet flooded when should not" 998 999 bridge link set dev $br_port flood on 1000 1001 flood_test_do true $mac $ip $host1_if $host2_if 1002 check_err $? "Packet was not flooded when should" 1003 1004 log_test "Unknown unicast flood" 1005} 1006 1007flood_multicast_test() 1008{ 1009 local br_port=$1 1010 local host1_if=$2 1011 local host2_if=$3 1012 local mac=01:00:5e:00:00:01 1013 local ip=239.0.0.1 1014 1015 RET=0 1016 1017 bridge link set dev $br_port mcast_flood off 1018 1019 flood_test_do false $mac $ip $host1_if $host2_if 1020 check_err $? "Packet flooded when should not" 1021 1022 bridge link set dev $br_port mcast_flood on 1023 1024 flood_test_do true $mac $ip $host1_if $host2_if 1025 check_err $? "Packet was not flooded when should" 1026 1027 log_test "Unregistered multicast flood" 1028} 1029 1030flood_test() 1031{ 1032 # `br_port` is connected to `host2_if` 1033 local br_port=$1 1034 local host1_if=$2 1035 local host2_if=$3 1036 1037 flood_unicast_test $br_port $host1_if $host2_if 1038 flood_multicast_test $br_port $host1_if $host2_if 1039} 1040