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