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 677ipv6_stats_get() 678{ 679 local dev=$1; shift 680 local stat=$1; shift 681 682 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 683} 684 685humanize() 686{ 687 local speed=$1; shift 688 689 for unit in bps Kbps Mbps Gbps; do 690 if (($(echo "$speed < 1024" | bc))); then 691 break 692 fi 693 694 speed=$(echo "scale=1; $speed / 1024" | bc) 695 done 696 697 echo "$speed${unit}" 698} 699 700rate() 701{ 702 local t0=$1; shift 703 local t1=$1; shift 704 local interval=$1; shift 705 706 echo $((8 * (t1 - t0) / interval)) 707} 708 709mac_get() 710{ 711 local if_name=$1 712 713 ip -j link show dev $if_name | jq -r '.[]["address"]' 714} 715 716bridge_ageing_time_get() 717{ 718 local bridge=$1 719 local ageing_time 720 721 # Need to divide by 100 to convert to seconds. 722 ageing_time=$(ip -j -d link show dev $bridge \ 723 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 724 echo $((ageing_time / 100)) 725} 726 727declare -A SYSCTL_ORIG 728sysctl_set() 729{ 730 local key=$1; shift 731 local value=$1; shift 732 733 SYSCTL_ORIG[$key]=$(sysctl -n $key) 734 sysctl -qw $key=$value 735} 736 737sysctl_restore() 738{ 739 local key=$1; shift 740 741 sysctl -qw $key=${SYSCTL_ORIG["$key"]} 742} 743 744forwarding_enable() 745{ 746 sysctl_set net.ipv4.conf.all.forwarding 1 747 sysctl_set net.ipv6.conf.all.forwarding 1 748} 749 750forwarding_restore() 751{ 752 sysctl_restore net.ipv6.conf.all.forwarding 753 sysctl_restore net.ipv4.conf.all.forwarding 754} 755 756declare -A MTU_ORIG 757mtu_set() 758{ 759 local dev=$1; shift 760 local mtu=$1; shift 761 762 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 763 ip link set dev $dev mtu $mtu 764} 765 766mtu_restore() 767{ 768 local dev=$1; shift 769 770 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 771} 772 773tc_offload_check() 774{ 775 local num_netifs=${1:-$NUM_NETIFS} 776 777 for ((i = 1; i <= num_netifs; ++i)); do 778 ethtool -k ${NETIFS[p$i]} \ 779 | grep "hw-tc-offload: on" &> /dev/null 780 if [[ $? -ne 0 ]]; then 781 return 1 782 fi 783 done 784 785 return 0 786} 787 788trap_install() 789{ 790 local dev=$1; shift 791 local direction=$1; shift 792 793 # Some devices may not support or need in-hardware trapping of traffic 794 # (e.g. the veth pairs that this library creates for non-existent 795 # loopbacks). Use continue instead, so that there is a filter in there 796 # (some tests check counters), and so that other filters are still 797 # processed. 798 tc filter add dev $dev $direction pref 1 \ 799 flower skip_sw action trap 2>/dev/null \ 800 || tc filter add dev $dev $direction pref 1 \ 801 flower action continue 802} 803 804trap_uninstall() 805{ 806 local dev=$1; shift 807 local direction=$1; shift 808 809 tc filter del dev $dev $direction pref 1 flower 810} 811 812slow_path_trap_install() 813{ 814 # For slow-path testing, we need to install a trap to get to 815 # slow path the packets that would otherwise be switched in HW. 816 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 817 trap_install "$@" 818 fi 819} 820 821slow_path_trap_uninstall() 822{ 823 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 824 trap_uninstall "$@" 825 fi 826} 827 828__icmp_capture_add_del() 829{ 830 local add_del=$1; shift 831 local pref=$1; shift 832 local vsuf=$1; shift 833 local tundev=$1; shift 834 local filter=$1; shift 835 836 tc filter $add_del dev "$tundev" ingress \ 837 proto ip$vsuf pref $pref \ 838 flower ip_proto icmp$vsuf $filter \ 839 action pass 840} 841 842icmp_capture_install() 843{ 844 __icmp_capture_add_del add 100 "" "$@" 845} 846 847icmp_capture_uninstall() 848{ 849 __icmp_capture_add_del del 100 "" "$@" 850} 851 852icmp6_capture_install() 853{ 854 __icmp_capture_add_del add 100 v6 "$@" 855} 856 857icmp6_capture_uninstall() 858{ 859 __icmp_capture_add_del del 100 v6 "$@" 860} 861 862__vlan_capture_add_del() 863{ 864 local add_del=$1; shift 865 local pref=$1; shift 866 local dev=$1; shift 867 local filter=$1; shift 868 869 tc filter $add_del dev "$dev" ingress \ 870 proto 802.1q pref $pref \ 871 flower $filter \ 872 action pass 873} 874 875vlan_capture_install() 876{ 877 __vlan_capture_add_del add 100 "$@" 878} 879 880vlan_capture_uninstall() 881{ 882 __vlan_capture_add_del del 100 "$@" 883} 884 885__dscp_capture_add_del() 886{ 887 local add_del=$1; shift 888 local dev=$1; shift 889 local base=$1; shift 890 local dscp; 891 892 for prio in {0..7}; do 893 dscp=$((base + prio)) 894 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 895 "skip_hw ip_tos $((dscp << 2))" 896 done 897} 898 899dscp_capture_install() 900{ 901 local dev=$1; shift 902 local base=$1; shift 903 904 __dscp_capture_add_del add $dev $base 905} 906 907dscp_capture_uninstall() 908{ 909 local dev=$1; shift 910 local base=$1; shift 911 912 __dscp_capture_add_del del $dev $base 913} 914 915dscp_fetch_stats() 916{ 917 local dev=$1; shift 918 local base=$1; shift 919 920 for prio in {0..7}; do 921 local dscp=$((base + prio)) 922 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 923 echo "[$dscp]=$t " 924 done 925} 926 927matchall_sink_create() 928{ 929 local dev=$1; shift 930 931 tc qdisc add dev $dev clsact 932 tc filter add dev $dev ingress \ 933 pref 10000 \ 934 matchall \ 935 action drop 936} 937 938tests_run() 939{ 940 local current_test 941 942 for current_test in ${TESTS:-$ALL_TESTS}; do 943 $current_test 944 done 945} 946 947multipath_eval() 948{ 949 local desc="$1" 950 local weight_rp12=$2 951 local weight_rp13=$3 952 local packets_rp12=$4 953 local packets_rp13=$5 954 local weights_ratio packets_ratio diff 955 956 RET=0 957 958 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 959 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 960 | bc -l) 961 else 962 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 963 | bc -l) 964 fi 965 966 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 967 check_err 1 "Packet difference is 0" 968 log_test "Multipath" 969 log_info "Expected ratio $weights_ratio" 970 return 971 fi 972 973 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 974 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 975 | bc -l) 976 else 977 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 978 | bc -l) 979 fi 980 981 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 982 diff=${diff#-} 983 984 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 985 check_err $? "Too large discrepancy between expected and measured ratios" 986 log_test "$desc" 987 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 988} 989 990in_ns() 991{ 992 local name=$1; shift 993 994 ip netns exec $name bash <<-EOF 995 NUM_NETIFS=0 996 source lib.sh 997 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 998 EOF 999} 1000 1001############################################################################## 1002# Tests 1003 1004ping_do() 1005{ 1006 local if_name=$1 1007 local dip=$2 1008 local args=$3 1009 local vrf_name 1010 1011 vrf_name=$(master_name_get $if_name) 1012 ip vrf exec $vrf_name \ 1013 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 1014} 1015 1016ping_test() 1017{ 1018 RET=0 1019 1020 ping_do $1 $2 1021 check_err $? 1022 log_test "ping$3" 1023} 1024 1025ping6_do() 1026{ 1027 local if_name=$1 1028 local dip=$2 1029 local args=$3 1030 local vrf_name 1031 1032 vrf_name=$(master_name_get $if_name) 1033 ip vrf exec $vrf_name \ 1034 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 1035} 1036 1037ping6_test() 1038{ 1039 RET=0 1040 1041 ping6_do $1 $2 1042 check_err $? 1043 log_test "ping6$3" 1044} 1045 1046learning_test() 1047{ 1048 local bridge=$1 1049 local br_port1=$2 # Connected to `host1_if`. 1050 local host1_if=$3 1051 local host2_if=$4 1052 local mac=de:ad:be:ef:13:37 1053 local ageing_time 1054 1055 RET=0 1056 1057 bridge -j fdb show br $bridge brport $br_port1 \ 1058 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1059 check_fail $? "Found FDB record when should not" 1060 1061 # Disable unknown unicast flooding on `br_port1` to make sure 1062 # packets are only forwarded through the port after a matching 1063 # FDB entry was installed. 1064 bridge link set dev $br_port1 flood off 1065 1066 ip link set $host1_if promisc on 1067 tc qdisc add dev $host1_if ingress 1068 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1069 flower dst_mac $mac action drop 1070 1071 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1072 sleep 1 1073 1074 tc -j -s filter show dev $host1_if ingress \ 1075 | jq -e ".[] | select(.options.handle == 101) \ 1076 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1077 check_fail $? "Packet reached first host when should not" 1078 1079 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1080 sleep 1 1081 1082 bridge -j fdb show br $bridge brport $br_port1 \ 1083 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1084 check_err $? "Did not find FDB record when should" 1085 1086 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1087 sleep 1 1088 1089 tc -j -s filter show dev $host1_if ingress \ 1090 | jq -e ".[] | select(.options.handle == 101) \ 1091 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1092 check_err $? "Packet did not reach second host when should" 1093 1094 # Wait for 10 seconds after the ageing time to make sure FDB 1095 # record was aged-out. 1096 ageing_time=$(bridge_ageing_time_get $bridge) 1097 sleep $((ageing_time + 10)) 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 off 1104 1105 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1106 sleep 1 1107 1108 bridge -j fdb show br $bridge brport $br_port1 \ 1109 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1110 check_fail $? "Found FDB record when should not" 1111 1112 bridge link set dev $br_port1 learning on 1113 1114 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1115 tc qdisc del dev $host1_if ingress 1116 ip link set $host1_if promisc off 1117 1118 bridge link set dev $br_port1 flood on 1119 1120 log_test "FDB learning" 1121} 1122 1123flood_test_do() 1124{ 1125 local should_flood=$1 1126 local mac=$2 1127 local ip=$3 1128 local host1_if=$4 1129 local host2_if=$5 1130 local err=0 1131 1132 # Add an ACL on `host2_if` which will tell us whether the packet 1133 # was flooded to it or not. 1134 ip link set $host2_if promisc on 1135 tc qdisc add dev $host2_if ingress 1136 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1137 flower dst_mac $mac action drop 1138 1139 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1140 sleep 1 1141 1142 tc -j -s filter show dev $host2_if ingress \ 1143 | jq -e ".[] | select(.options.handle == 101) \ 1144 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1145 if [[ $? -ne 0 && $should_flood == "true" || \ 1146 $? -eq 0 && $should_flood == "false" ]]; then 1147 err=1 1148 fi 1149 1150 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1151 tc qdisc del dev $host2_if ingress 1152 ip link set $host2_if promisc off 1153 1154 return $err 1155} 1156 1157flood_unicast_test() 1158{ 1159 local br_port=$1 1160 local host1_if=$2 1161 local host2_if=$3 1162 local mac=de:ad:be:ef:13:37 1163 local ip=192.0.2.100 1164 1165 RET=0 1166 1167 bridge link set dev $br_port flood off 1168 1169 flood_test_do false $mac $ip $host1_if $host2_if 1170 check_err $? "Packet flooded when should not" 1171 1172 bridge link set dev $br_port flood on 1173 1174 flood_test_do true $mac $ip $host1_if $host2_if 1175 check_err $? "Packet was not flooded when should" 1176 1177 log_test "Unknown unicast flood" 1178} 1179 1180flood_multicast_test() 1181{ 1182 local br_port=$1 1183 local host1_if=$2 1184 local host2_if=$3 1185 local mac=01:00:5e:00:00:01 1186 local ip=239.0.0.1 1187 1188 RET=0 1189 1190 bridge link set dev $br_port mcast_flood off 1191 1192 flood_test_do false $mac $ip $host1_if $host2_if 1193 check_err $? "Packet flooded when should not" 1194 1195 bridge link set dev $br_port mcast_flood on 1196 1197 flood_test_do true $mac $ip $host1_if $host2_if 1198 check_err $? "Packet was not flooded when should" 1199 1200 log_test "Unregistered multicast flood" 1201} 1202 1203flood_test() 1204{ 1205 # `br_port` is connected to `host2_if` 1206 local br_port=$1 1207 local host1_if=$2 1208 local host2_if=$3 1209 1210 flood_unicast_test $br_port $host1_if $host2_if 1211 flood_multicast_test $br_port $host1_if $host2_if 1212} 1213 1214__start_traffic() 1215{ 1216 local proto=$1; shift 1217 local h_in=$1; shift # Where the traffic egresses the host 1218 local sip=$1; shift 1219 local dip=$1; shift 1220 local dmac=$1; shift 1221 1222 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ 1223 -a own -b $dmac -t "$proto" -q "$@" & 1224 sleep 1 1225} 1226 1227start_traffic() 1228{ 1229 __start_traffic udp "$@" 1230} 1231 1232start_tcp_traffic() 1233{ 1234 __start_traffic tcp "$@" 1235} 1236 1237stop_traffic() 1238{ 1239 # Suppress noise from killing mausezahn. 1240 { kill %% && wait %%; } 2>/dev/null 1241} 1242 1243tcpdump_start() 1244{ 1245 local if_name=$1; shift 1246 local ns=$1; shift 1247 1248 capfile=$(mktemp) 1249 capout=$(mktemp) 1250 1251 if [ -z $ns ]; then 1252 ns_cmd="" 1253 else 1254 ns_cmd="ip netns exec ${ns}" 1255 fi 1256 1257 if [ -z $SUDO_USER ] ; then 1258 capuser="" 1259 else 1260 capuser="-Z $SUDO_USER" 1261 fi 1262 1263 $ns_cmd tcpdump -e -n -Q in -i $if_name \ 1264 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 & 1265 cappid=$! 1266 1267 sleep 1 1268} 1269 1270tcpdump_stop() 1271{ 1272 $ns_cmd kill $cappid 1273 sleep 1 1274} 1275 1276tcpdump_cleanup() 1277{ 1278 rm $capfile $capout 1279} 1280 1281tcpdump_show() 1282{ 1283 tcpdump -e -n -r $capfile 2>&1 1284} 1285