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