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