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