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