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