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