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