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