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