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