1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4############################################################################## 5# Topology description. p1 looped back to p2, p3 to p4 and so on. 6 7declare -A NETIFS=( 8 [p1]=veth0 9 [p2]=veth1 10 [p3]=veth2 11 [p4]=veth3 12 [p5]=veth4 13 [p6]=veth5 14 [p7]=veth6 15 [p8]=veth7 16 [p9]=veth8 17 [p10]=veth9 18) 19 20# Port that does not have a cable connected. 21: "${NETIF_NO_CABLE:=eth8}" 22 23############################################################################## 24# Defines 25 26# Networking utilities. 27: "${PING:=ping}" 28: "${PING6:=ping6}" # Some distros just use ping. 29: "${ARPING:=arping}" 30: "${TROUTE6:=traceroute6}" 31 32# Packet generator. 33: "${MZ:=mausezahn}" # Some distributions use 'mz'. 34: "${MZ_DELAY:=0}" 35 36# Host configuration tools. 37: "${TEAMD:=teamd}" 38: "${MCD:=smcrouted}" 39: "${MC_CLI:=smcroutectl}" 40 41# Constants for netdevice bring-up: 42# Default time in seconds to wait for an interface to come up before giving up 43# and bailing out. Used during initial setup. 44: "${INTERFACE_TIMEOUT:=600}" 45# Like INTERFACE_TIMEOUT, but default for ad-hoc waiting in testing scripts. 46: "${WAIT_TIMEOUT:=20}" 47# Time to wait after interfaces participating in the test are all UP. 48: "${WAIT_TIME:=5}" 49 50# Whether to pause on, respectively, after a failure and before cleanup. 51: "${PAUSE_ON_FAIL:=no}" 52: "${PAUSE_ON_CLEANUP:=no}" 53 54# Whether to create virtual interfaces, and what netdevice type they should be. 55: "${NETIF_CREATE:=yes}" 56: "${NETIF_TYPE:=veth}" 57 58# Constants for ping tests: 59# How many packets should be sent. 60: "${PING_COUNT:=10}" 61# Timeout (in seconds) before ping exits regardless of how many packets have 62# been sent or received 63: "${PING_TIMEOUT:=5}" 64 65# Minimum ageing_time (in centiseconds) supported by hardware 66: "${LOW_AGEING_TIME:=1000}" 67 68# Whether to check for availability of certain tools. 69: "${REQUIRE_JQ:=yes}" 70: "${REQUIRE_MZ:=yes}" 71: "${REQUIRE_MTOOLS:=no}" 72 73# Whether to override MAC addresses on interfaces participating in the test. 74: "${STABLE_MAC_ADDRS:=no}" 75 76# Flags for tcpdump 77: "${TCPDUMP_EXTRA_FLAGS:=}" 78 79# Flags for TC filters. 80: "${TC_FLAG:=skip_hw}" 81 82# Whether the machine is "slow" -- i.e. might be incapable of running tests 83# involving heavy traffic. This might be the case on a debug kernel, a VM, or 84# e.g. a low-power board. 85: "${KSFT_MACHINE_SLOW:=no}" 86 87############################################################################## 88# Find netifs by test-specified driver name 89 90driver_name_get() 91{ 92 local dev=$1; shift 93 local driver_path="/sys/class/net/$dev/device/driver" 94 95 if [[ -L $driver_path ]]; then 96 basename `realpath $driver_path` 97 fi 98} 99 100netif_find_driver() 101{ 102 local ifnames=`ip -j link show | jq -r ".[].ifname"` 103 local count=0 104 105 for ifname in $ifnames 106 do 107 local driver_name=`driver_name_get $ifname` 108 if [[ ! -z $driver_name && $driver_name == $NETIF_FIND_DRIVER ]]; then 109 count=$((count + 1)) 110 NETIFS[p$count]="$ifname" 111 fi 112 done 113} 114 115# Whether to find netdevice according to the driver speficied by the importer 116: "${NETIF_FIND_DRIVER:=}" 117 118if [[ $NETIF_FIND_DRIVER ]]; then 119 unset NETIFS 120 declare -A NETIFS 121 netif_find_driver 122fi 123 124net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") 125 126if [[ -f $net_forwarding_dir/forwarding.config ]]; then 127 source "$net_forwarding_dir/forwarding.config" 128fi 129 130source "$net_forwarding_dir/../lib.sh" 131 132# timeout in seconds 133slowwait() 134{ 135 local timeout_sec=$1; shift 136 137 loopy_wait "sleep 0.1" "$((timeout_sec * 1000))" "$@" 138} 139 140############################################################################## 141# Sanity checks 142 143check_tc_version() 144{ 145 tc -j &> /dev/null 146 if [[ $? -ne 0 ]]; then 147 echo "SKIP: iproute2 too old; tc is missing JSON support" 148 exit $ksft_skip 149 fi 150} 151 152# Old versions of tc don't understand "mpls_uc" 153check_tc_mpls_support() 154{ 155 local dev=$1; shift 156 157 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 158 matchall action pipe &> /dev/null 159 if [[ $? -ne 0 ]]; then 160 echo "SKIP: iproute2 too old; tc is missing MPLS support" 161 return $ksft_skip 162 fi 163 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 164 matchall 165} 166 167# Old versions of tc produce invalid json output for mpls lse statistics 168check_tc_mpls_lse_stats() 169{ 170 local dev=$1; shift 171 local ret; 172 173 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 174 flower mpls lse depth 2 \ 175 action continue &> /dev/null 176 177 if [[ $? -ne 0 ]]; then 178 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support" 179 return $ksft_skip 180 fi 181 182 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null 183 ret=$? 184 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \ 185 flower 186 187 if [[ $ret -ne 0 ]]; then 188 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters" 189 return $ksft_skip 190 fi 191} 192 193check_tc_shblock_support() 194{ 195 tc filter help 2>&1 | grep block &> /dev/null 196 if [[ $? -ne 0 ]]; then 197 echo "SKIP: iproute2 too old; tc is missing shared block support" 198 exit $ksft_skip 199 fi 200} 201 202check_tc_chain_support() 203{ 204 tc help 2>&1|grep chain &> /dev/null 205 if [[ $? -ne 0 ]]; then 206 echo "SKIP: iproute2 too old; tc is missing chain support" 207 exit $ksft_skip 208 fi 209} 210 211check_tc_action_hw_stats_support() 212{ 213 tc actions help 2>&1 | grep -q hw_stats 214 if [[ $? -ne 0 ]]; then 215 echo "SKIP: iproute2 too old; tc is missing action hw_stats support" 216 exit $ksft_skip 217 fi 218} 219 220check_tc_fp_support() 221{ 222 tc qdisc add dev lo mqprio help 2>&1 | grep -q "fp " 223 if [[ $? -ne 0 ]]; then 224 echo "SKIP: iproute2 too old; tc is missing frame preemption support" 225 exit $ksft_skip 226 fi 227} 228 229check_ethtool_lanes_support() 230{ 231 ethtool --help 2>&1| grep lanes &> /dev/null 232 if [[ $? -ne 0 ]]; then 233 echo "SKIP: ethtool too old; it is missing lanes support" 234 exit $ksft_skip 235 fi 236} 237 238check_ethtool_mm_support() 239{ 240 ethtool --help 2>&1| grep -- '--show-mm' &> /dev/null 241 if [[ $? -ne 0 ]]; then 242 echo "SKIP: ethtool too old; it is missing MAC Merge layer support" 243 exit $ksft_skip 244 fi 245} 246 247check_ethtool_counter_group_support() 248{ 249 ethtool --help 2>&1| grep -- '--all-groups' &> /dev/null 250 if [[ $? -ne 0 ]]; then 251 echo "SKIP: ethtool too old; it is missing standard counter group support" 252 exit $ksft_skip 253 fi 254} 255 256check_ethtool_pmac_std_stats_support() 257{ 258 local dev=$1; shift 259 local grp=$1; shift 260 261 [ 0 -ne $(ethtool --json -S $dev --all-groups --src pmac 2>/dev/null \ 262 | jq ".[].\"$grp\" | length") ] 263} 264 265check_locked_port_support() 266{ 267 if ! bridge -d link show | grep -q " locked"; then 268 echo "SKIP: iproute2 too old; Locked port feature not supported." 269 return $ksft_skip 270 fi 271} 272 273check_port_mab_support() 274{ 275 if ! bridge -d link show | grep -q "mab"; then 276 echo "SKIP: iproute2 too old; MacAuth feature not supported." 277 return $ksft_skip 278 fi 279} 280 281if [[ "$(id -u)" -ne 0 ]]; then 282 echo "SKIP: need root privileges" 283 exit $ksft_skip 284fi 285 286check_driver() 287{ 288 local dev=$1; shift 289 local expected=$1; shift 290 local driver_name=`driver_name_get $dev` 291 292 if [[ $driver_name != $expected ]]; then 293 echo "SKIP: expected driver $expected for $dev, got $driver_name instead" 294 exit $ksft_skip 295 fi 296} 297 298if [[ "$CHECK_TC" = "yes" ]]; then 299 check_tc_version 300fi 301 302require_command() 303{ 304 local cmd=$1; shift 305 306 if [[ ! -x "$(command -v "$cmd")" ]]; then 307 echo "SKIP: $cmd not installed" 308 exit $ksft_skip 309 fi 310} 311 312if [[ "$REQUIRE_JQ" = "yes" ]]; then 313 require_command jq 314fi 315if [[ "$REQUIRE_MZ" = "yes" ]]; then 316 require_command $MZ 317fi 318if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then 319 # https://github.com/vladimiroltean/mtools/ 320 # patched for IPv6 support 321 require_command msend 322 require_command mreceive 323fi 324 325############################################################################## 326# Command line options handling 327 328count=0 329 330while [[ $# -gt 0 ]]; do 331 if [[ "$count" -eq "0" ]]; then 332 unset NETIFS 333 declare -A NETIFS 334 fi 335 count=$((count + 1)) 336 NETIFS[p$count]="$1" 337 shift 338done 339 340############################################################################## 341# Network interfaces configuration 342 343if [[ ! -v NUM_NETIFS ]]; then 344 echo "SKIP: importer does not define \"NUM_NETIFS\"" 345 exit $ksft_skip 346fi 347 348if (( NUM_NETIFS > ${#NETIFS[@]} )); then 349 echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})" 350 exit $ksft_skip 351fi 352 353for i in $(seq ${#NETIFS[@]}); do 354 if [[ ! ${NETIFS[p$i]} ]]; then 355 echo "SKIP: NETIFS[p$i] not given" 356 exit $ksft_skip 357 fi 358done 359 360create_netif_veth() 361{ 362 local i 363 364 for ((i = 1; i <= NUM_NETIFS; ++i)); do 365 local j=$((i+1)) 366 367 if [ -z ${NETIFS[p$i]} ]; then 368 echo "SKIP: Cannot create interface. Name not specified" 369 exit $ksft_skip 370 fi 371 372 ip link show dev ${NETIFS[p$i]} &> /dev/null 373 if [[ $? -ne 0 ]]; then 374 ip link add ${NETIFS[p$i]} type veth \ 375 peer name ${NETIFS[p$j]} 376 if [[ $? -ne 0 ]]; then 377 echo "Failed to create netif" 378 exit 1 379 fi 380 fi 381 i=$j 382 done 383} 384 385create_netif() 386{ 387 case "$NETIF_TYPE" in 388 veth) create_netif_veth 389 ;; 390 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" 391 exit 1 392 ;; 393 esac 394} 395 396declare -A MAC_ADDR_ORIG 397mac_addr_prepare() 398{ 399 local new_addr= 400 local dev= 401 402 for ((i = 1; i <= NUM_NETIFS; ++i)); do 403 dev=${NETIFS[p$i]} 404 new_addr=$(printf "00:01:02:03:04:%02x" $i) 405 406 MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address') 407 # Strip quotes 408 MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/} 409 ip link set dev $dev address $new_addr 410 done 411} 412 413mac_addr_restore() 414{ 415 local dev= 416 417 for ((i = 1; i <= NUM_NETIFS; ++i)); do 418 dev=${NETIFS[p$i]} 419 ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]} 420 done 421} 422 423if [[ "$NETIF_CREATE" = "yes" ]]; then 424 create_netif 425fi 426 427if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then 428 mac_addr_prepare 429fi 430 431for ((i = 1; i <= NUM_NETIFS; ++i)); do 432 ip link show dev ${NETIFS[p$i]} &> /dev/null 433 if [[ $? -ne 0 ]]; then 434 echo "SKIP: could not find all required interfaces" 435 exit $ksft_skip 436 fi 437done 438 439############################################################################## 440# Helpers 441 442# Exit status to return at the end. Set in case one of the tests fails. 443EXIT_STATUS=0 444# Per-test return value. Clear at the beginning of each test. 445RET=0 446 447ret_set_ksft_status() 448{ 449 local ksft_status=$1; shift 450 local msg=$1; shift 451 452 RET=$(ksft_status_merge $RET $ksft_status) 453 if (( $? )); then 454 retmsg=$msg 455 fi 456} 457 458# Whether FAILs should be interpreted as XFAILs. Internal. 459FAIL_TO_XFAIL= 460 461check_err() 462{ 463 local err=$1 464 local msg=$2 465 466 if ((err)); then 467 if [[ $FAIL_TO_XFAIL = yes ]]; then 468 ret_set_ksft_status $ksft_xfail "$msg" 469 else 470 ret_set_ksft_status $ksft_fail "$msg" 471 fi 472 fi 473} 474 475check_fail() 476{ 477 local err=$1 478 local msg=$2 479 480 check_err $((!err)) "$msg" 481} 482 483check_err_fail() 484{ 485 local should_fail=$1; shift 486 local err=$1; shift 487 local what=$1; shift 488 489 if ((should_fail)); then 490 check_fail $err "$what succeeded, but should have failed" 491 else 492 check_err $err "$what failed" 493 fi 494} 495 496xfail_on_slow() 497{ 498 if [[ $KSFT_MACHINE_SLOW = yes ]]; then 499 FAIL_TO_XFAIL=yes "$@" 500 else 501 "$@" 502 fi 503} 504 505xfail_on_veth() 506{ 507 local dev=$1; shift 508 local kind 509 510 kind=$(ip -j -d link show dev $dev | 511 jq -r '.[].linkinfo.info_kind') 512 if [[ $kind = veth ]]; then 513 FAIL_TO_XFAIL=yes "$@" 514 else 515 "$@" 516 fi 517} 518 519log_test_result() 520{ 521 local test_name=$1; shift 522 local opt_str=$1; shift 523 local result=$1; shift 524 local retmsg=$1; shift 525 526 printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result" 527 if [[ $retmsg ]]; then 528 printf "\t%s\n" "$retmsg" 529 fi 530} 531 532pause_on_fail() 533{ 534 if [[ $PAUSE_ON_FAIL == yes ]]; then 535 echo "Hit enter to continue, 'q' to quit" 536 read a 537 [[ $a == q ]] && exit 1 538 fi 539} 540 541handle_test_result_pass() 542{ 543 local test_name=$1; shift 544 local opt_str=$1; shift 545 546 log_test_result "$test_name" "$opt_str" " OK " 547} 548 549handle_test_result_fail() 550{ 551 local test_name=$1; shift 552 local opt_str=$1; shift 553 554 log_test_result "$test_name" "$opt_str" FAIL "$retmsg" 555 pause_on_fail 556} 557 558handle_test_result_xfail() 559{ 560 local test_name=$1; shift 561 local opt_str=$1; shift 562 563 log_test_result "$test_name" "$opt_str" XFAIL "$retmsg" 564 pause_on_fail 565} 566 567handle_test_result_skip() 568{ 569 local test_name=$1; shift 570 local opt_str=$1; shift 571 572 log_test_result "$test_name" "$opt_str" SKIP "$retmsg" 573} 574 575log_test() 576{ 577 local test_name=$1 578 local opt_str=$2 579 580 if [[ $# -eq 2 ]]; then 581 opt_str="($opt_str)" 582 fi 583 584 if ((RET == ksft_pass)); then 585 handle_test_result_pass "$test_name" "$opt_str" 586 elif ((RET == ksft_xfail)); then 587 handle_test_result_xfail "$test_name" "$opt_str" 588 elif ((RET == ksft_skip)); then 589 handle_test_result_skip "$test_name" "$opt_str" 590 else 591 handle_test_result_fail "$test_name" "$opt_str" 592 fi 593 594 EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET) 595 return $RET 596} 597 598log_test_skip() 599{ 600 RET=$ksft_skip retmsg= log_test "$@" 601} 602 603log_test_xfail() 604{ 605 RET=$ksft_xfail retmsg= log_test "$@" 606} 607 608log_info() 609{ 610 local msg=$1 611 612 echo "INFO: $msg" 613} 614 615not() 616{ 617 "$@" 618 [[ $? != 0 ]] 619} 620 621get_max() 622{ 623 local arr=("$@") 624 625 max=${arr[0]} 626 for cur in ${arr[@]}; do 627 if [[ $cur -gt $max ]]; then 628 max=$cur 629 fi 630 done 631 632 echo $max 633} 634 635grep_bridge_fdb() 636{ 637 local addr=$1; shift 638 local word 639 local flag 640 641 if [ "$1" == "self" ] || [ "$1" == "master" ]; then 642 word=$1; shift 643 if [ "$1" == "-v" ]; then 644 flag=$1; shift 645 fi 646 fi 647 648 $@ | grep $addr | grep $flag "$word" 649} 650 651wait_for_port_up() 652{ 653 "$@" | grep -q "Link detected: yes" 654} 655 656wait_for_offload() 657{ 658 "$@" | grep -q offload 659} 660 661wait_for_trap() 662{ 663 "$@" | grep -q trap 664} 665 666until_counter_is() 667{ 668 local expr=$1; shift 669 local current=$("$@") 670 671 echo $((current)) 672 ((current $expr)) 673} 674 675busywait_for_counter() 676{ 677 local timeout=$1; shift 678 local delta=$1; shift 679 680 local base=$("$@") 681 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" 682} 683 684slowwait_for_counter() 685{ 686 local timeout=$1; shift 687 local delta=$1; shift 688 689 local base=$("$@") 690 slowwait "$timeout" until_counter_is ">= $((base + delta))" "$@" 691} 692 693setup_wait_dev() 694{ 695 local dev=$1; shift 696 local wait_time=${1:-$WAIT_TIME}; shift 697 698 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time 699 700 if (($?)); then 701 check_err 1 702 log_test setup_wait_dev ": Interface $dev does not come up." 703 exit 1 704 fi 705} 706 707setup_wait_dev_with_timeout() 708{ 709 local dev=$1; shift 710 local max_iterations=${1:-$WAIT_TIMEOUT}; shift 711 local wait_time=${1:-$WAIT_TIME}; shift 712 local i 713 714 for ((i = 1; i <= $max_iterations; ++i)); do 715 ip link show dev $dev up \ 716 | grep 'state UP' &> /dev/null 717 if [[ $? -ne 0 ]]; then 718 sleep 1 719 else 720 sleep $wait_time 721 return 0 722 fi 723 done 724 725 return 1 726} 727 728setup_wait() 729{ 730 local num_netifs=${1:-$NUM_NETIFS} 731 local i 732 733 for ((i = 1; i <= num_netifs; ++i)); do 734 setup_wait_dev ${NETIFS[p$i]} 0 735 done 736 737 # Make sure links are ready. 738 sleep $WAIT_TIME 739} 740 741wait_for_dev() 742{ 743 local dev=$1; shift 744 local timeout=${1:-$WAIT_TIMEOUT}; shift 745 746 slowwait $timeout ip link show dev $dev &> /dev/null 747 if (( $? )); then 748 check_err 1 749 log_test wait_for_dev "Interface $dev did not appear." 750 exit $EXIT_STATUS 751 fi 752} 753 754cmd_jq() 755{ 756 local cmd=$1 757 local jq_exp=$2 758 local jq_opts=$3 759 local ret 760 local output 761 762 output="$($cmd)" 763 # it the command fails, return error right away 764 ret=$? 765 if [[ $ret -ne 0 ]]; then 766 return $ret 767 fi 768 output=$(echo $output | jq -r $jq_opts "$jq_exp") 769 ret=$? 770 if [[ $ret -ne 0 ]]; then 771 return $ret 772 fi 773 echo $output 774 # return success only in case of non-empty output 775 [ ! -z "$output" ] 776} 777 778pre_cleanup() 779{ 780 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 781 echo "Pausing before cleanup, hit any key to continue" 782 read 783 fi 784 785 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then 786 mac_addr_restore 787 fi 788} 789 790vrf_prepare() 791{ 792 ip -4 rule add pref 32765 table local 793 ip -4 rule del pref 0 794 ip -6 rule add pref 32765 table local 795 ip -6 rule del pref 0 796} 797 798vrf_cleanup() 799{ 800 ip -6 rule add pref 0 table local 801 ip -6 rule del pref 32765 802 ip -4 rule add pref 0 table local 803 ip -4 rule del pref 32765 804} 805 806__last_tb_id=0 807declare -A __TB_IDS 808 809__vrf_td_id_assign() 810{ 811 local vrf_name=$1 812 813 __last_tb_id=$((__last_tb_id + 1)) 814 __TB_IDS[$vrf_name]=$__last_tb_id 815 return $__last_tb_id 816} 817 818__vrf_td_id_lookup() 819{ 820 local vrf_name=$1 821 822 return ${__TB_IDS[$vrf_name]} 823} 824 825vrf_create() 826{ 827 local vrf_name=$1 828 local tb_id 829 830 __vrf_td_id_assign $vrf_name 831 tb_id=$? 832 833 ip link add dev $vrf_name type vrf table $tb_id 834 ip -4 route add table $tb_id unreachable default metric 4278198272 835 ip -6 route add table $tb_id unreachable default metric 4278198272 836} 837 838vrf_destroy() 839{ 840 local vrf_name=$1 841 local tb_id 842 843 __vrf_td_id_lookup $vrf_name 844 tb_id=$? 845 846 ip -6 route del table $tb_id unreachable default metric 4278198272 847 ip -4 route del table $tb_id unreachable default metric 4278198272 848 ip link del dev $vrf_name 849} 850 851__addr_add_del() 852{ 853 local if_name=$1 854 local add_del=$2 855 local array 856 857 shift 858 shift 859 array=("${@}") 860 861 for addrstr in "${array[@]}"; do 862 ip address $add_del $addrstr dev $if_name 863 done 864} 865 866__simple_if_init() 867{ 868 local if_name=$1; shift 869 local vrf_name=$1; shift 870 local addrs=("${@}") 871 872 ip link set dev $if_name master $vrf_name 873 ip link set dev $if_name up 874 875 __addr_add_del $if_name add "${addrs[@]}" 876} 877 878__simple_if_fini() 879{ 880 local if_name=$1; shift 881 local addrs=("${@}") 882 883 __addr_add_del $if_name del "${addrs[@]}" 884 885 ip link set dev $if_name down 886 ip link set dev $if_name nomaster 887} 888 889simple_if_init() 890{ 891 local if_name=$1 892 local vrf_name 893 local array 894 895 shift 896 vrf_name=v$if_name 897 array=("${@}") 898 899 vrf_create $vrf_name 900 ip link set dev $vrf_name up 901 __simple_if_init $if_name $vrf_name "${array[@]}" 902} 903 904simple_if_fini() 905{ 906 local if_name=$1 907 local vrf_name 908 local array 909 910 shift 911 vrf_name=v$if_name 912 array=("${@}") 913 914 __simple_if_fini $if_name "${array[@]}" 915 vrf_destroy $vrf_name 916} 917 918tunnel_create() 919{ 920 local name=$1; shift 921 local type=$1; shift 922 local local=$1; shift 923 local remote=$1; shift 924 925 ip link add name $name type $type \ 926 local $local remote $remote "$@" 927 ip link set dev $name up 928} 929 930tunnel_destroy() 931{ 932 local name=$1; shift 933 934 ip link del dev $name 935} 936 937vlan_create() 938{ 939 local if_name=$1; shift 940 local vid=$1; shift 941 local vrf=$1; shift 942 local ips=("${@}") 943 local name=$if_name.$vid 944 945 ip link add name $name link $if_name type vlan id $vid 946 if [ "$vrf" != "" ]; then 947 ip link set dev $name master $vrf 948 fi 949 ip link set dev $name up 950 __addr_add_del $name add "${ips[@]}" 951} 952 953vlan_destroy() 954{ 955 local if_name=$1; shift 956 local vid=$1; shift 957 local name=$if_name.$vid 958 959 ip link del dev $name 960} 961 962team_create() 963{ 964 local if_name=$1; shift 965 local mode=$1; shift 966 967 require_command $TEAMD 968 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 969 for slave in "$@"; do 970 ip link set dev $slave down 971 ip link set dev $slave master $if_name 972 ip link set dev $slave up 973 done 974 ip link set dev $if_name up 975} 976 977team_destroy() 978{ 979 local if_name=$1; shift 980 981 $TEAMD -t $if_name -k 982} 983 984master_name_get() 985{ 986 local if_name=$1 987 988 ip -j link show dev $if_name | jq -r '.[]["master"]' 989} 990 991link_stats_get() 992{ 993 local if_name=$1; shift 994 local dir=$1; shift 995 local stat=$1; shift 996 997 ip -j -s link show dev $if_name \ 998 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 999} 1000 1001link_stats_tx_packets_get() 1002{ 1003 link_stats_get $1 tx packets 1004} 1005 1006link_stats_rx_errors_get() 1007{ 1008 link_stats_get $1 rx errors 1009} 1010 1011tc_rule_stats_get() 1012{ 1013 local dev=$1; shift 1014 local pref=$1; shift 1015 local dir=$1; shift 1016 local selector=${1:-.packets}; shift 1017 1018 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ 1019 | jq ".[1].options.actions[].stats$selector" 1020} 1021 1022tc_rule_handle_stats_get() 1023{ 1024 local id=$1; shift 1025 local handle=$1; shift 1026 local selector=${1:-.packets}; shift 1027 local netns=${1:-""}; shift 1028 1029 tc $netns -j -s filter show $id \ 1030 | jq ".[] | select(.options.handle == $handle) | \ 1031 .options.actions[0].stats$selector" 1032} 1033 1034ethtool_stats_get() 1035{ 1036 local dev=$1; shift 1037 local stat=$1; shift 1038 1039 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 1040} 1041 1042ethtool_std_stats_get() 1043{ 1044 local dev=$1; shift 1045 local grp=$1; shift 1046 local name=$1; shift 1047 local src=$1; shift 1048 1049 ethtool --json -S $dev --groups $grp -- --src $src | \ 1050 jq '.[]."'"$grp"'"."'$name'"' 1051} 1052 1053qdisc_stats_get() 1054{ 1055 local dev=$1; shift 1056 local handle=$1; shift 1057 local selector=$1; shift 1058 1059 tc -j -s qdisc show dev "$dev" \ 1060 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" 1061} 1062 1063qdisc_parent_stats_get() 1064{ 1065 local dev=$1; shift 1066 local parent=$1; shift 1067 local selector=$1; shift 1068 1069 tc -j -s qdisc show dev "$dev" invisible \ 1070 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" 1071} 1072 1073ipv6_stats_get() 1074{ 1075 local dev=$1; shift 1076 local stat=$1; shift 1077 1078 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 1079} 1080 1081hw_stats_get() 1082{ 1083 local suite=$1; shift 1084 local if_name=$1; shift 1085 local dir=$1; shift 1086 local stat=$1; shift 1087 1088 ip -j stats show dev $if_name group offload subgroup $suite | 1089 jq ".[0].stats64.$dir.$stat" 1090} 1091 1092__nh_stats_get() 1093{ 1094 local key=$1; shift 1095 local group_id=$1; shift 1096 local member_id=$1; shift 1097 1098 ip -j -s -s nexthop show id $group_id | 1099 jq --argjson member_id "$member_id" --arg key "$key" \ 1100 '.[].group_stats[] | select(.id == $member_id) | .[$key]' 1101} 1102 1103nh_stats_get() 1104{ 1105 local group_id=$1; shift 1106 local member_id=$1; shift 1107 1108 __nh_stats_get packets "$group_id" "$member_id" 1109} 1110 1111nh_stats_get_hw() 1112{ 1113 local group_id=$1; shift 1114 local member_id=$1; shift 1115 1116 __nh_stats_get packets_hw "$group_id" "$member_id" 1117} 1118 1119humanize() 1120{ 1121 local speed=$1; shift 1122 1123 for unit in bps Kbps Mbps Gbps; do 1124 if (($(echo "$speed < 1024" | bc))); then 1125 break 1126 fi 1127 1128 speed=$(echo "scale=1; $speed / 1024" | bc) 1129 done 1130 1131 echo "$speed${unit}" 1132} 1133 1134rate() 1135{ 1136 local t0=$1; shift 1137 local t1=$1; shift 1138 local interval=$1; shift 1139 1140 echo $((8 * (t1 - t0) / interval)) 1141} 1142 1143packets_rate() 1144{ 1145 local t0=$1; shift 1146 local t1=$1; shift 1147 local interval=$1; shift 1148 1149 echo $(((t1 - t0) / interval)) 1150} 1151 1152mac_get() 1153{ 1154 local if_name=$1 1155 1156 ip -j link show dev $if_name | jq -r '.[]["address"]' 1157} 1158 1159ipv6_lladdr_get() 1160{ 1161 local if_name=$1 1162 1163 ip -j addr show dev $if_name | \ 1164 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \ 1165 head -1 1166} 1167 1168bridge_ageing_time_get() 1169{ 1170 local bridge=$1 1171 local ageing_time 1172 1173 # Need to divide by 100 to convert to seconds. 1174 ageing_time=$(ip -j -d link show dev $bridge \ 1175 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 1176 echo $((ageing_time / 100)) 1177} 1178 1179declare -A SYSCTL_ORIG 1180sysctl_set() 1181{ 1182 local key=$1; shift 1183 local value=$1; shift 1184 1185 SYSCTL_ORIG[$key]=$(sysctl -n $key) 1186 sysctl -qw $key="$value" 1187} 1188 1189sysctl_restore() 1190{ 1191 local key=$1; shift 1192 1193 sysctl -qw $key="${SYSCTL_ORIG[$key]}" 1194} 1195 1196forwarding_enable() 1197{ 1198 sysctl_set net.ipv4.conf.all.forwarding 1 1199 sysctl_set net.ipv6.conf.all.forwarding 1 1200} 1201 1202forwarding_restore() 1203{ 1204 sysctl_restore net.ipv6.conf.all.forwarding 1205 sysctl_restore net.ipv4.conf.all.forwarding 1206} 1207 1208declare -A MTU_ORIG 1209mtu_set() 1210{ 1211 local dev=$1; shift 1212 local mtu=$1; shift 1213 1214 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 1215 ip link set dev $dev mtu $mtu 1216} 1217 1218mtu_restore() 1219{ 1220 local dev=$1; shift 1221 1222 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 1223} 1224 1225tc_offload_check() 1226{ 1227 local num_netifs=${1:-$NUM_NETIFS} 1228 1229 for ((i = 1; i <= num_netifs; ++i)); do 1230 ethtool -k ${NETIFS[p$i]} \ 1231 | grep "hw-tc-offload: on" &> /dev/null 1232 if [[ $? -ne 0 ]]; then 1233 return 1 1234 fi 1235 done 1236 1237 return 0 1238} 1239 1240trap_install() 1241{ 1242 local dev=$1; shift 1243 local direction=$1; shift 1244 1245 # Some devices may not support or need in-hardware trapping of traffic 1246 # (e.g. the veth pairs that this library creates for non-existent 1247 # loopbacks). Use continue instead, so that there is a filter in there 1248 # (some tests check counters), and so that other filters are still 1249 # processed. 1250 tc filter add dev $dev $direction pref 1 \ 1251 flower skip_sw action trap 2>/dev/null \ 1252 || tc filter add dev $dev $direction pref 1 \ 1253 flower action continue 1254} 1255 1256trap_uninstall() 1257{ 1258 local dev=$1; shift 1259 local direction=$1; shift 1260 1261 tc filter del dev $dev $direction pref 1 flower 1262} 1263 1264slow_path_trap_install() 1265{ 1266 # For slow-path testing, we need to install a trap to get to 1267 # slow path the packets that would otherwise be switched in HW. 1268 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1269 trap_install "$@" 1270 fi 1271} 1272 1273slow_path_trap_uninstall() 1274{ 1275 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1276 trap_uninstall "$@" 1277 fi 1278} 1279 1280__icmp_capture_add_del() 1281{ 1282 local add_del=$1; shift 1283 local pref=$1; shift 1284 local vsuf=$1; shift 1285 local tundev=$1; shift 1286 local filter=$1; shift 1287 1288 tc filter $add_del dev "$tundev" ingress \ 1289 proto ip$vsuf pref $pref \ 1290 flower ip_proto icmp$vsuf $filter \ 1291 action pass 1292} 1293 1294icmp_capture_install() 1295{ 1296 __icmp_capture_add_del add 100 "" "$@" 1297} 1298 1299icmp_capture_uninstall() 1300{ 1301 __icmp_capture_add_del del 100 "" "$@" 1302} 1303 1304icmp6_capture_install() 1305{ 1306 __icmp_capture_add_del add 100 v6 "$@" 1307} 1308 1309icmp6_capture_uninstall() 1310{ 1311 __icmp_capture_add_del del 100 v6 "$@" 1312} 1313 1314__vlan_capture_add_del() 1315{ 1316 local add_del=$1; shift 1317 local pref=$1; shift 1318 local dev=$1; shift 1319 local filter=$1; shift 1320 1321 tc filter $add_del dev "$dev" ingress \ 1322 proto 802.1q pref $pref \ 1323 flower $filter \ 1324 action pass 1325} 1326 1327vlan_capture_install() 1328{ 1329 __vlan_capture_add_del add 100 "$@" 1330} 1331 1332vlan_capture_uninstall() 1333{ 1334 __vlan_capture_add_del del 100 "$@" 1335} 1336 1337__dscp_capture_add_del() 1338{ 1339 local add_del=$1; shift 1340 local dev=$1; shift 1341 local base=$1; shift 1342 local dscp; 1343 1344 for prio in {0..7}; do 1345 dscp=$((base + prio)) 1346 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 1347 "skip_hw ip_tos $((dscp << 2))" 1348 done 1349} 1350 1351dscp_capture_install() 1352{ 1353 local dev=$1; shift 1354 local base=$1; shift 1355 1356 __dscp_capture_add_del add $dev $base 1357} 1358 1359dscp_capture_uninstall() 1360{ 1361 local dev=$1; shift 1362 local base=$1; shift 1363 1364 __dscp_capture_add_del del $dev $base 1365} 1366 1367dscp_fetch_stats() 1368{ 1369 local dev=$1; shift 1370 local base=$1; shift 1371 1372 for prio in {0..7}; do 1373 local dscp=$((base + prio)) 1374 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 1375 echo "[$dscp]=$t " 1376 done 1377} 1378 1379matchall_sink_create() 1380{ 1381 local dev=$1; shift 1382 1383 tc qdisc add dev $dev clsact 1384 tc filter add dev $dev ingress \ 1385 pref 10000 \ 1386 matchall \ 1387 action drop 1388} 1389 1390tests_run() 1391{ 1392 local current_test 1393 1394 for current_test in ${TESTS:-$ALL_TESTS}; do 1395 $current_test 1396 done 1397} 1398 1399multipath_eval() 1400{ 1401 local desc="$1" 1402 local weight_rp12=$2 1403 local weight_rp13=$3 1404 local packets_rp12=$4 1405 local packets_rp13=$5 1406 local weights_ratio packets_ratio diff 1407 1408 RET=0 1409 1410 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1411 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 1412 | bc -l) 1413 else 1414 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 1415 | bc -l) 1416 fi 1417 1418 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 1419 check_err 1 "Packet difference is 0" 1420 log_test "Multipath" 1421 log_info "Expected ratio $weights_ratio" 1422 return 1423 fi 1424 1425 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1426 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 1427 | bc -l) 1428 else 1429 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 1430 | bc -l) 1431 fi 1432 1433 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 1434 diff=${diff#-} 1435 1436 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 1437 check_err $? "Too large discrepancy between expected and measured ratios" 1438 log_test "$desc" 1439 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 1440} 1441 1442in_ns() 1443{ 1444 local name=$1; shift 1445 1446 ip netns exec $name bash <<-EOF 1447 NUM_NETIFS=0 1448 source lib.sh 1449 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 1450 EOF 1451} 1452 1453############################################################################## 1454# Tests 1455 1456ping_do() 1457{ 1458 local if_name=$1 1459 local dip=$2 1460 local args=$3 1461 local vrf_name 1462 1463 vrf_name=$(master_name_get $if_name) 1464 ip vrf exec $vrf_name \ 1465 $PING $args $dip -c $PING_COUNT -i 0.1 \ 1466 -w $PING_TIMEOUT &> /dev/null 1467} 1468 1469ping_test() 1470{ 1471 RET=0 1472 1473 ping_do $1 $2 1474 check_err $? 1475 log_test "ping$3" 1476} 1477 1478ping_test_fails() 1479{ 1480 RET=0 1481 1482 ping_do $1 $2 1483 check_fail $? 1484 log_test "ping fails$3" 1485} 1486 1487ping6_do() 1488{ 1489 local if_name=$1 1490 local dip=$2 1491 local args=$3 1492 local vrf_name 1493 1494 vrf_name=$(master_name_get $if_name) 1495 ip vrf exec $vrf_name \ 1496 $PING6 $args $dip -c $PING_COUNT -i 0.1 \ 1497 -w $PING_TIMEOUT &> /dev/null 1498} 1499 1500ping6_test() 1501{ 1502 RET=0 1503 1504 ping6_do $1 $2 1505 check_err $? 1506 log_test "ping6$3" 1507} 1508 1509ping6_test_fails() 1510{ 1511 RET=0 1512 1513 ping6_do $1 $2 1514 check_fail $? 1515 log_test "ping6 fails$3" 1516} 1517 1518learning_test() 1519{ 1520 local bridge=$1 1521 local br_port1=$2 # Connected to `host1_if`. 1522 local host1_if=$3 1523 local host2_if=$4 1524 local mac=de:ad:be:ef:13:37 1525 local ageing_time 1526 1527 RET=0 1528 1529 bridge -j fdb show br $bridge brport $br_port1 \ 1530 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1531 check_fail $? "Found FDB record when should not" 1532 1533 # Disable unknown unicast flooding on `br_port1` to make sure 1534 # packets are only forwarded through the port after a matching 1535 # FDB entry was installed. 1536 bridge link set dev $br_port1 flood off 1537 1538 ip link set $host1_if promisc on 1539 tc qdisc add dev $host1_if ingress 1540 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1541 flower dst_mac $mac action drop 1542 1543 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1544 sleep 1 1545 1546 tc -j -s filter show dev $host1_if ingress \ 1547 | jq -e ".[] | select(.options.handle == 101) \ 1548 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1549 check_fail $? "Packet reached first host when should not" 1550 1551 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1552 sleep 1 1553 1554 bridge -j fdb show br $bridge brport $br_port1 \ 1555 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1556 check_err $? "Did not find FDB record when should" 1557 1558 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1559 sleep 1 1560 1561 tc -j -s filter show dev $host1_if ingress \ 1562 | jq -e ".[] | select(.options.handle == 101) \ 1563 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1564 check_err $? "Packet did not reach second host when should" 1565 1566 # Wait for 10 seconds after the ageing time to make sure FDB 1567 # record was aged-out. 1568 ageing_time=$(bridge_ageing_time_get $bridge) 1569 sleep $((ageing_time + 10)) 1570 1571 bridge -j fdb show br $bridge brport $br_port1 \ 1572 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1573 check_fail $? "Found FDB record when should not" 1574 1575 bridge link set dev $br_port1 learning off 1576 1577 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1578 sleep 1 1579 1580 bridge -j fdb show br $bridge brport $br_port1 \ 1581 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1582 check_fail $? "Found FDB record when should not" 1583 1584 bridge link set dev $br_port1 learning on 1585 1586 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1587 tc qdisc del dev $host1_if ingress 1588 ip link set $host1_if promisc off 1589 1590 bridge link set dev $br_port1 flood on 1591 1592 log_test "FDB learning" 1593} 1594 1595flood_test_do() 1596{ 1597 local should_flood=$1 1598 local mac=$2 1599 local ip=$3 1600 local host1_if=$4 1601 local host2_if=$5 1602 local err=0 1603 1604 # Add an ACL on `host2_if` which will tell us whether the packet 1605 # was flooded to it or not. 1606 ip link set $host2_if promisc on 1607 tc qdisc add dev $host2_if ingress 1608 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1609 flower dst_mac $mac action drop 1610 1611 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1612 sleep 1 1613 1614 tc -j -s filter show dev $host2_if ingress \ 1615 | jq -e ".[] | select(.options.handle == 101) \ 1616 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1617 if [[ $? -ne 0 && $should_flood == "true" || \ 1618 $? -eq 0 && $should_flood == "false" ]]; then 1619 err=1 1620 fi 1621 1622 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1623 tc qdisc del dev $host2_if ingress 1624 ip link set $host2_if promisc off 1625 1626 return $err 1627} 1628 1629flood_unicast_test() 1630{ 1631 local br_port=$1 1632 local host1_if=$2 1633 local host2_if=$3 1634 local mac=de:ad:be:ef:13:37 1635 local ip=192.0.2.100 1636 1637 RET=0 1638 1639 bridge link set dev $br_port flood off 1640 1641 flood_test_do false $mac $ip $host1_if $host2_if 1642 check_err $? "Packet flooded when should not" 1643 1644 bridge link set dev $br_port flood on 1645 1646 flood_test_do true $mac $ip $host1_if $host2_if 1647 check_err $? "Packet was not flooded when should" 1648 1649 log_test "Unknown unicast flood" 1650} 1651 1652flood_multicast_test() 1653{ 1654 local br_port=$1 1655 local host1_if=$2 1656 local host2_if=$3 1657 local mac=01:00:5e:00:00:01 1658 local ip=239.0.0.1 1659 1660 RET=0 1661 1662 bridge link set dev $br_port mcast_flood off 1663 1664 flood_test_do false $mac $ip $host1_if $host2_if 1665 check_err $? "Packet flooded when should not" 1666 1667 bridge link set dev $br_port mcast_flood on 1668 1669 flood_test_do true $mac $ip $host1_if $host2_if 1670 check_err $? "Packet was not flooded when should" 1671 1672 log_test "Unregistered multicast flood" 1673} 1674 1675flood_test() 1676{ 1677 # `br_port` is connected to `host2_if` 1678 local br_port=$1 1679 local host1_if=$2 1680 local host2_if=$3 1681 1682 flood_unicast_test $br_port $host1_if $host2_if 1683 flood_multicast_test $br_port $host1_if $host2_if 1684} 1685 1686__start_traffic() 1687{ 1688 local pktsize=$1; shift 1689 local proto=$1; shift 1690 local h_in=$1; shift # Where the traffic egresses the host 1691 local sip=$1; shift 1692 local dip=$1; shift 1693 local dmac=$1; shift 1694 1695 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ 1696 -a own -b $dmac -t "$proto" -q "$@" & 1697 sleep 1 1698} 1699 1700start_traffic_pktsize() 1701{ 1702 local pktsize=$1; shift 1703 1704 __start_traffic $pktsize udp "$@" 1705} 1706 1707start_tcp_traffic_pktsize() 1708{ 1709 local pktsize=$1; shift 1710 1711 __start_traffic $pktsize tcp "$@" 1712} 1713 1714start_traffic() 1715{ 1716 start_traffic_pktsize 8000 "$@" 1717} 1718 1719start_tcp_traffic() 1720{ 1721 start_tcp_traffic_pktsize 8000 "$@" 1722} 1723 1724stop_traffic() 1725{ 1726 # Suppress noise from killing mausezahn. 1727 { kill %% && wait %%; } 2>/dev/null 1728} 1729 1730declare -A cappid 1731declare -A capfile 1732declare -A capout 1733 1734tcpdump_start() 1735{ 1736 local if_name=$1; shift 1737 local ns=$1; shift 1738 1739 capfile[$if_name]=$(mktemp) 1740 capout[$if_name]=$(mktemp) 1741 1742 if [ -z $ns ]; then 1743 ns_cmd="" 1744 else 1745 ns_cmd="ip netns exec ${ns}" 1746 fi 1747 1748 if [ -z $SUDO_USER ] ; then 1749 capuser="" 1750 else 1751 capuser="-Z $SUDO_USER" 1752 fi 1753 1754 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ 1755 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ 1756 > "${capout[$if_name]}" 2>&1 & 1757 cappid[$if_name]=$! 1758 1759 sleep 1 1760} 1761 1762tcpdump_stop() 1763{ 1764 local if_name=$1 1765 local pid=${cappid[$if_name]} 1766 1767 $ns_cmd kill "$pid" && wait "$pid" 1768 sleep 1 1769} 1770 1771tcpdump_cleanup() 1772{ 1773 local if_name=$1 1774 1775 rm ${capfile[$if_name]} ${capout[$if_name]} 1776} 1777 1778tcpdump_show() 1779{ 1780 local if_name=$1 1781 1782 tcpdump -e -n -r ${capfile[$if_name]} 2>&1 1783} 1784 1785# return 0 if the packet wasn't seen on host2_if or 1 if it was 1786mcast_packet_test() 1787{ 1788 local mac=$1 1789 local src_ip=$2 1790 local ip=$3 1791 local host1_if=$4 1792 local host2_if=$5 1793 local seen=0 1794 local tc_proto="ip" 1795 local mz_v6arg="" 1796 1797 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1798 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1799 tc_proto="ipv6" 1800 mz_v6arg="-6" 1801 fi 1802 1803 # Add an ACL on `host2_if` which will tell us whether the packet 1804 # was received by it or not. 1805 tc qdisc add dev $host2_if ingress 1806 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1807 flower ip_proto udp dst_mac $mac action drop 1808 1809 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1810 sleep 1 1811 1812 tc -j -s filter show dev $host2_if ingress \ 1813 | jq -e ".[] | select(.options.handle == 101) \ 1814 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1815 if [[ $? -eq 0 ]]; then 1816 seen=1 1817 fi 1818 1819 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1820 tc qdisc del dev $host2_if ingress 1821 1822 return $seen 1823} 1824 1825brmcast_check_sg_entries() 1826{ 1827 local report=$1; shift 1828 local slist=("$@") 1829 local sarg="" 1830 1831 for src in "${slist[@]}"; do 1832 sarg="${sarg} and .source_list[].address == \"$src\"" 1833 done 1834 bridge -j -d -s mdb show dev br0 \ 1835 | jq -e ".[].mdb[] | \ 1836 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1837 check_err $? "Wrong *,G entry source list after $report report" 1838 1839 for sgent in "${slist[@]}"; do 1840 bridge -j -d -s mdb show dev br0 \ 1841 | jq -e ".[].mdb[] | \ 1842 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1843 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1844 done 1845} 1846 1847brmcast_check_sg_fwding() 1848{ 1849 local should_fwd=$1; shift 1850 local sources=("$@") 1851 1852 for src in "${sources[@]}"; do 1853 local retval=0 1854 1855 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1856 retval=$? 1857 if [ $should_fwd -eq 1 ]; then 1858 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1859 else 1860 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1861 fi 1862 done 1863} 1864 1865brmcast_check_sg_state() 1866{ 1867 local is_blocked=$1; shift 1868 local sources=("$@") 1869 local should_fail=1 1870 1871 if [ $is_blocked -eq 1 ]; then 1872 should_fail=0 1873 fi 1874 1875 for src in "${sources[@]}"; do 1876 bridge -j -d -s mdb show dev br0 \ 1877 | jq -e ".[].mdb[] | \ 1878 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1879 .source_list[] | 1880 select(.address == \"$src\") | 1881 select(.timer == \"0.00\")" &>/dev/null 1882 check_err_fail $should_fail $? "Entry $src has zero timer" 1883 1884 bridge -j -d -s mdb show dev br0 \ 1885 | jq -e ".[].mdb[] | \ 1886 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1887 .flags[] == \"blocked\")" &>/dev/null 1888 check_err_fail $should_fail $? "Entry $src has blocked flag" 1889 done 1890} 1891 1892mc_join() 1893{ 1894 local if_name=$1 1895 local group=$2 1896 local vrf_name=$(master_name_get $if_name) 1897 1898 # We don't care about actual reception, just about joining the 1899 # IP multicast group and adding the L2 address to the device's 1900 # MAC filtering table 1901 ip vrf exec $vrf_name \ 1902 mreceive -g $group -I $if_name > /dev/null 2>&1 & 1903 mreceive_pid=$! 1904 1905 sleep 1 1906} 1907 1908mc_leave() 1909{ 1910 kill "$mreceive_pid" && wait "$mreceive_pid" 1911} 1912 1913mc_send() 1914{ 1915 local if_name=$1 1916 local groups=$2 1917 local vrf_name=$(master_name_get $if_name) 1918 1919 ip vrf exec $vrf_name \ 1920 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 1921} 1922 1923start_ip_monitor() 1924{ 1925 local mtype=$1; shift 1926 local ip=${1-ip}; shift 1927 1928 # start the monitor in the background 1929 tmpfile=`mktemp /var/run/nexthoptestXXX` 1930 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` 1931 sleep 0.2 1932 echo "$mpid $tmpfile" 1933} 1934 1935stop_ip_monitor() 1936{ 1937 local mpid=$1; shift 1938 local tmpfile=$1; shift 1939 local el=$1; shift 1940 local what=$1; shift 1941 1942 sleep 0.2 1943 kill $mpid 1944 local lines=`grep '^\w' $tmpfile | wc -l` 1945 test $lines -eq $el 1946 check_err $? "$what: $lines lines of events, expected $el" 1947 rm -rf $tmpfile 1948} 1949 1950hw_stats_monitor_test() 1951{ 1952 local dev=$1; shift 1953 local type=$1; shift 1954 local make_suitable=$1; shift 1955 local make_unsuitable=$1; shift 1956 local ip=${1-ip}; shift 1957 1958 RET=0 1959 1960 # Expect a notification about enablement. 1961 local ipmout=$(start_ip_monitor stats "$ip") 1962 $ip stats set dev $dev ${type}_stats on 1963 stop_ip_monitor $ipmout 1 "${type}_stats enablement" 1964 1965 # Expect a notification about offload. 1966 local ipmout=$(start_ip_monitor stats "$ip") 1967 $make_suitable 1968 stop_ip_monitor $ipmout 1 "${type}_stats installation" 1969 1970 # Expect a notification about loss of offload. 1971 local ipmout=$(start_ip_monitor stats "$ip") 1972 $make_unsuitable 1973 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" 1974 1975 # Expect a notification about disablement 1976 local ipmout=$(start_ip_monitor stats "$ip") 1977 $ip stats set dev $dev ${type}_stats off 1978 stop_ip_monitor $ipmout 1 "${type}_stats disablement" 1979 1980 log_test "${type}_stats notifications" 1981} 1982 1983ipv4_to_bytes() 1984{ 1985 local IP=$1; shift 1986 1987 printf '%02x:' ${IP//./ } | 1988 sed 's/:$//' 1989} 1990 1991# Convert a given IPv6 address, `IP' such that the :: token, if present, is 1992# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal 1993# digits. An optional `BYTESEP' parameter can be given to further separate 1994# individual bytes of each 16-bit group. 1995expand_ipv6() 1996{ 1997 local IP=$1; shift 1998 local bytesep=$1; shift 1999 2000 local cvt_ip=${IP/::/_} 2001 local colons=${cvt_ip//[^:]/} 2002 local allcol=::::::: 2003 # IP where :: -> the appropriate number of colons: 2004 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}} 2005 2006 echo $allcol_ip | tr : '\n' | 2007 sed s/^/0000/ | 2008 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' | 2009 tr '\n' : | 2010 sed 's/:$//' 2011} 2012 2013ipv6_to_bytes() 2014{ 2015 local IP=$1; shift 2016 2017 expand_ipv6 "$IP" : 2018} 2019 2020u16_to_bytes() 2021{ 2022 local u16=$1; shift 2023 2024 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' 2025} 2026 2027# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), 2028# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, 2029# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) 2030# stands for 00:00. 2031payload_template_calc_checksum() 2032{ 2033 local payload=$1; shift 2034 2035 ( 2036 # Set input radix. 2037 echo "16i" 2038 # Push zero for the initial checksum. 2039 echo 0 2040 2041 # Pad the payload with a terminating 00: in case we get an odd 2042 # number of bytes. 2043 echo "${payload%:}:00:" | 2044 sed 's/CHECKSUM/00:00/g' | 2045 tr '[:lower:]' '[:upper:]' | 2046 # Add the word to the checksum. 2047 sed 's/\(..\):\(..\):/\1\2+\n/g' | 2048 # Strip the extra odd byte we pushed if left unconverted. 2049 sed 's/\(..\):$//' 2050 2051 echo "10000 ~ +" # Calculate and add carry. 2052 echo "FFFF r - p" # Bit-flip and print. 2053 ) | 2054 dc | 2055 tr '[:upper:]' '[:lower:]' 2056} 2057 2058payload_template_expand_checksum() 2059{ 2060 local payload=$1; shift 2061 local checksum=$1; shift 2062 2063 local ckbytes=$(u16_to_bytes $checksum) 2064 2065 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" 2066} 2067 2068payload_template_nbytes() 2069{ 2070 local payload=$1; shift 2071 2072 payload_template_expand_checksum "${payload%:}" 0 | 2073 sed 's/:/\n/g' | wc -l 2074} 2075 2076igmpv3_is_in_get() 2077{ 2078 local GRP=$1; shift 2079 local sources=("$@") 2080 2081 local igmpv3 2082 local nsources=$(u16_to_bytes ${#sources[@]}) 2083 2084 # IS_IN ( $sources ) 2085 igmpv3=$(: 2086 )"22:"$( : Type - Membership Report 2087 )"00:"$( : Reserved 2088 )"CHECKSUM:"$( : Checksum 2089 )"00:00:"$( : Reserved 2090 )"00:01:"$( : Number of Group Records 2091 )"01:"$( : Record Type - IS_IN 2092 )"00:"$( : Aux Data Len 2093 )"${nsources}:"$( : Number of Sources 2094 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address 2095 )"$(for src in "${sources[@]}"; do 2096 ipv4_to_bytes $src 2097 echo -n : 2098 done)"$( : Source Addresses 2099 ) 2100 local checksum=$(payload_template_calc_checksum "$igmpv3") 2101 2102 payload_template_expand_checksum "$igmpv3" $checksum 2103} 2104 2105igmpv2_leave_get() 2106{ 2107 local GRP=$1; shift 2108 2109 local payload=$(: 2110 )"17:"$( : Type - Leave Group 2111 )"00:"$( : Max Resp Time - not meaningful 2112 )"CHECKSUM:"$( : Checksum 2113 )"$(ipv4_to_bytes $GRP)"$( : Group Address 2114 ) 2115 local checksum=$(payload_template_calc_checksum "$payload") 2116 2117 payload_template_expand_checksum "$payload" $checksum 2118} 2119 2120mldv2_is_in_get() 2121{ 2122 local SIP=$1; shift 2123 local GRP=$1; shift 2124 local sources=("$@") 2125 2126 local hbh 2127 local icmpv6 2128 local nsources=$(u16_to_bytes ${#sources[@]}) 2129 2130 hbh=$(: 2131 )"3a:"$( : Next Header - ICMPv6 2132 )"00:"$( : Hdr Ext Len 2133 )"00:00:00:00:00:00:"$( : Options and Padding 2134 ) 2135 2136 icmpv6=$(: 2137 )"8f:"$( : Type - MLDv2 Report 2138 )"00:"$( : Code 2139 )"CHECKSUM:"$( : Checksum 2140 )"00:00:"$( : Reserved 2141 )"00:01:"$( : Number of Group Records 2142 )"01:"$( : Record Type - IS_IN 2143 )"00:"$( : Aux Data Len 2144 )"${nsources}:"$( : Number of Sources 2145 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2146 )"$(for src in "${sources[@]}"; do 2147 ipv6_to_bytes $src 2148 echo -n : 2149 done)"$( : Source Addresses 2150 ) 2151 2152 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2153 local sudohdr=$(: 2154 )"$(ipv6_to_bytes $SIP):"$( : SIP 2155 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2156 )"${len}:"$( : Upper-layer length 2157 )"00:3a:"$( : Zero and next-header 2158 ) 2159 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2160 2161 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2162} 2163 2164mldv1_done_get() 2165{ 2166 local SIP=$1; shift 2167 local GRP=$1; shift 2168 2169 local hbh 2170 local icmpv6 2171 2172 hbh=$(: 2173 )"3a:"$( : Next Header - ICMPv6 2174 )"00:"$( : Hdr Ext Len 2175 )"00:00:00:00:00:00:"$( : Options and Padding 2176 ) 2177 2178 icmpv6=$(: 2179 )"84:"$( : Type - MLDv1 Done 2180 )"00:"$( : Code 2181 )"CHECKSUM:"$( : Checksum 2182 )"00:00:"$( : Max Resp Delay - not meaningful 2183 )"00:00:"$( : Reserved 2184 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2185 ) 2186 2187 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2188 local sudohdr=$(: 2189 )"$(ipv6_to_bytes $SIP):"$( : SIP 2190 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2191 )"${len}:"$( : Upper-layer length 2192 )"00:3a:"$( : Zero and next-header 2193 ) 2194 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2195 2196 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2197} 2198 2199bail_on_lldpad() 2200{ 2201 local reason1="$1"; shift 2202 local reason2="$1"; shift 2203 local caller=${FUNCNAME[1]} 2204 local src=${BASH_SOURCE[1]} 2205 2206 if systemctl is-active --quiet lldpad; then 2207 2208 cat >/dev/stderr <<-EOF 2209 WARNING: lldpad is running 2210 2211 lldpad will likely $reason1, and this test will 2212 $reason2. Both are not supported at the same time, 2213 one of them is arbitrarily going to overwrite the 2214 other. That will cause spurious failures (or, unlikely, 2215 passes) of this test. 2216 EOF 2217 2218 if [[ -z $ALLOW_LLDPAD ]]; then 2219 cat >/dev/stderr <<-EOF 2220 2221 If you want to run the test anyway, please set 2222 an environment variable ALLOW_LLDPAD to a 2223 non-empty string. 2224 EOF 2225 log_test_skip $src:$caller 2226 exit $EXIT_STATUS 2227 else 2228 return 2229 fi 2230 fi 2231} 2232 2233absval() 2234{ 2235 local v=$1; shift 2236 2237 echo $((v > 0 ? v : -v)) 2238} 2239