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