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