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