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_n() 530{ 531 local num_netifs=$1; shift 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 542setup_wait() 543{ 544 setup_wait_n "$NUM_NETIFS" 545} 546 547wait_for_dev() 548{ 549 local dev=$1; shift 550 local timeout=${1:-$WAIT_TIMEOUT}; shift 551 552 slowwait $timeout ip link show dev $dev &> /dev/null 553 if (( $? )); then 554 check_err 1 555 log_test wait_for_dev "Interface $dev did not appear." 556 exit $EXIT_STATUS 557 fi 558} 559 560cmd_jq() 561{ 562 local cmd=$1 563 local jq_exp=$2 564 local jq_opts=$3 565 local ret 566 local output 567 568 output="$($cmd)" 569 # it the command fails, return error right away 570 ret=$? 571 if [[ $ret -ne 0 ]]; then 572 return $ret 573 fi 574 output=$(echo $output | jq -r $jq_opts "$jq_exp") 575 ret=$? 576 if [[ $ret -ne 0 ]]; then 577 return $ret 578 fi 579 echo $output 580 # return success only in case of non-empty output 581 [ ! -z "$output" ] 582} 583 584pre_cleanup() 585{ 586 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 587 echo "Pausing before cleanup, hit any key to continue" 588 read 589 fi 590 591 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then 592 mac_addr_restore 593 fi 594} 595 596vrf_prepare() 597{ 598 ip -4 rule add pref 32765 table local 599 ip -4 rule del pref 0 600 ip -6 rule add pref 32765 table local 601 ip -6 rule del pref 0 602} 603 604vrf_cleanup() 605{ 606 ip -6 rule add pref 0 table local 607 ip -6 rule del pref 32765 608 ip -4 rule add pref 0 table local 609 ip -4 rule del pref 32765 610} 611 612__last_tb_id=0 613declare -A __TB_IDS 614 615__vrf_td_id_assign() 616{ 617 local vrf_name=$1 618 619 __last_tb_id=$((__last_tb_id + 1)) 620 __TB_IDS[$vrf_name]=$__last_tb_id 621 return $__last_tb_id 622} 623 624__vrf_td_id_lookup() 625{ 626 local vrf_name=$1 627 628 return ${__TB_IDS[$vrf_name]} 629} 630 631vrf_create() 632{ 633 local vrf_name=$1 634 local tb_id 635 636 __vrf_td_id_assign $vrf_name 637 tb_id=$? 638 639 ip link add dev $vrf_name type vrf table $tb_id 640 ip -4 route add table $tb_id unreachable default metric 4278198272 641 ip -6 route add table $tb_id unreachable default metric 4278198272 642} 643 644vrf_destroy() 645{ 646 local vrf_name=$1 647 local tb_id 648 649 __vrf_td_id_lookup $vrf_name 650 tb_id=$? 651 652 ip -6 route del table $tb_id unreachable default metric 4278198272 653 ip -4 route del table $tb_id unreachable default metric 4278198272 654 ip link del dev $vrf_name 655} 656 657__addr_add_del() 658{ 659 local if_name=$1 660 local add_del=$2 661 local array 662 663 shift 664 shift 665 array=("${@}") 666 667 for addrstr in "${array[@]}"; do 668 ip address $add_del $addrstr dev $if_name 669 done 670} 671 672__simple_if_init() 673{ 674 local if_name=$1; shift 675 local vrf_name=$1; shift 676 local addrs=("${@}") 677 678 ip link set dev $if_name master $vrf_name 679 ip link set dev $if_name up 680 681 __addr_add_del $if_name add "${addrs[@]}" 682} 683 684__simple_if_fini() 685{ 686 local if_name=$1; shift 687 local addrs=("${@}") 688 689 __addr_add_del $if_name del "${addrs[@]}" 690 691 ip link set dev $if_name down 692 ip link set dev $if_name nomaster 693} 694 695simple_if_init() 696{ 697 local if_name=$1 698 local vrf_name 699 local array 700 701 shift 702 vrf_name=v$if_name 703 array=("${@}") 704 705 vrf_create $vrf_name 706 ip link set dev $vrf_name up 707 __simple_if_init $if_name $vrf_name "${array[@]}" 708} 709 710simple_if_fini() 711{ 712 local if_name=$1 713 local vrf_name 714 local array 715 716 shift 717 vrf_name=v$if_name 718 array=("${@}") 719 720 __simple_if_fini $if_name "${array[@]}" 721 vrf_destroy $vrf_name 722} 723 724tunnel_create() 725{ 726 local name=$1; shift 727 local type=$1; shift 728 local local=$1; shift 729 local remote=$1; shift 730 731 ip link add name $name type $type \ 732 local $local remote $remote "$@" 733 ip link set dev $name up 734} 735 736tunnel_destroy() 737{ 738 local name=$1; shift 739 740 ip link del dev $name 741} 742 743vlan_create() 744{ 745 local if_name=$1; shift 746 local vid=$1; shift 747 local vrf=$1; shift 748 local ips=("${@}") 749 local name=$if_name.$vid 750 751 ip link add name $name link $if_name type vlan id $vid 752 if [ "$vrf" != "" ]; then 753 ip link set dev $name master $vrf 754 fi 755 ip link set dev $name up 756 __addr_add_del $name add "${ips[@]}" 757} 758 759vlan_destroy() 760{ 761 local if_name=$1; shift 762 local vid=$1; shift 763 local name=$if_name.$vid 764 765 ip link del dev $name 766} 767 768team_create() 769{ 770 local if_name=$1; shift 771 local mode=$1; shift 772 773 require_command $TEAMD 774 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 775 for slave in "$@"; do 776 ip link set dev $slave down 777 ip link set dev $slave master $if_name 778 ip link set dev $slave up 779 done 780 ip link set dev $if_name up 781} 782 783team_destroy() 784{ 785 local if_name=$1; shift 786 787 $TEAMD -t $if_name -k 788} 789 790master_name_get() 791{ 792 local if_name=$1 793 794 ip -j link show dev $if_name | jq -r '.[]["master"]' 795} 796 797link_stats_get() 798{ 799 local if_name=$1; shift 800 local dir=$1; shift 801 local stat=$1; shift 802 803 ip -j -s link show dev $if_name \ 804 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 805} 806 807link_stats_tx_packets_get() 808{ 809 link_stats_get $1 tx packets 810} 811 812link_stats_rx_errors_get() 813{ 814 link_stats_get $1 rx errors 815} 816 817ethtool_stats_get() 818{ 819 local dev=$1; shift 820 local stat=$1; shift 821 822 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 823} 824 825ethtool_std_stats_get() 826{ 827 local dev=$1; shift 828 local grp=$1; shift 829 local name=$1; shift 830 local src=$1; shift 831 832 ethtool --json -S $dev --groups $grp -- --src $src | \ 833 jq '.[]."'"$grp"'"."'$name'"' 834} 835 836qdisc_stats_get() 837{ 838 local dev=$1; shift 839 local handle=$1; shift 840 local selector=$1; shift 841 842 tc -j -s qdisc show dev "$dev" \ 843 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" 844} 845 846qdisc_parent_stats_get() 847{ 848 local dev=$1; shift 849 local parent=$1; shift 850 local selector=$1; shift 851 852 tc -j -s qdisc show dev "$dev" invisible \ 853 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" 854} 855 856ipv6_stats_get() 857{ 858 local dev=$1; shift 859 local stat=$1; shift 860 861 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 862} 863 864hw_stats_get() 865{ 866 local suite=$1; shift 867 local if_name=$1; shift 868 local dir=$1; shift 869 local stat=$1; shift 870 871 ip -j stats show dev $if_name group offload subgroup $suite | 872 jq ".[0].stats64.$dir.$stat" 873} 874 875__nh_stats_get() 876{ 877 local key=$1; shift 878 local group_id=$1; shift 879 local member_id=$1; shift 880 881 ip -j -s -s nexthop show id $group_id | 882 jq --argjson member_id "$member_id" --arg key "$key" \ 883 '.[].group_stats[] | select(.id == $member_id) | .[$key]' 884} 885 886nh_stats_get() 887{ 888 local group_id=$1; shift 889 local member_id=$1; shift 890 891 __nh_stats_get packets "$group_id" "$member_id" 892} 893 894nh_stats_get_hw() 895{ 896 local group_id=$1; shift 897 local member_id=$1; shift 898 899 __nh_stats_get packets_hw "$group_id" "$member_id" 900} 901 902humanize() 903{ 904 local speed=$1; shift 905 906 for unit in bps Kbps Mbps Gbps; do 907 if (($(echo "$speed < 1024" | bc))); then 908 break 909 fi 910 911 speed=$(echo "scale=1; $speed / 1024" | bc) 912 done 913 914 echo "$speed${unit}" 915} 916 917rate() 918{ 919 local t0=$1; shift 920 local t1=$1; shift 921 local interval=$1; shift 922 923 echo $((8 * (t1 - t0) / interval)) 924} 925 926packets_rate() 927{ 928 local t0=$1; shift 929 local t1=$1; shift 930 local interval=$1; shift 931 932 echo $(((t1 - t0) / interval)) 933} 934 935ether_addr_to_u64() 936{ 937 local addr="$1" 938 local order="$((1 << 40))" 939 local val=0 940 local byte 941 942 addr="${addr//:/ }" 943 944 for byte in $addr; do 945 byte="0x$byte" 946 val=$((val + order * byte)) 947 order=$((order >> 8)) 948 done 949 950 printf "0x%x" $val 951} 952 953u64_to_ether_addr() 954{ 955 local val=$1 956 local byte 957 local i 958 959 for ((i = 40; i >= 0; i -= 8)); do 960 byte=$(((val & (0xff << i)) >> i)) 961 printf "%02x" $byte 962 if [ $i -ne 0 ]; then 963 printf ":" 964 fi 965 done 966} 967 968ipv6_lladdr_get() 969{ 970 local if_name=$1 971 972 ip -j addr show dev $if_name | \ 973 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \ 974 head -1 975} 976 977bridge_ageing_time_get() 978{ 979 local bridge=$1 980 local ageing_time 981 982 # Need to divide by 100 to convert to seconds. 983 ageing_time=$(ip -j -d link show dev $bridge \ 984 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 985 echo $((ageing_time / 100)) 986} 987 988declare -A SYSCTL_ORIG 989sysctl_save() 990{ 991 local key=$1; shift 992 993 SYSCTL_ORIG[$key]=$(sysctl -n $key) 994} 995 996sysctl_set() 997{ 998 local key=$1; shift 999 local value=$1; shift 1000 1001 sysctl_save "$key" 1002 sysctl -qw $key="$value" 1003} 1004 1005sysctl_restore() 1006{ 1007 local key=$1; shift 1008 1009 sysctl -qw $key="${SYSCTL_ORIG[$key]}" 1010} 1011 1012forwarding_enable() 1013{ 1014 sysctl_set net.ipv4.conf.all.forwarding 1 1015 sysctl_set net.ipv6.conf.all.forwarding 1 1016} 1017 1018forwarding_restore() 1019{ 1020 sysctl_restore net.ipv6.conf.all.forwarding 1021 sysctl_restore net.ipv4.conf.all.forwarding 1022} 1023 1024declare -A MTU_ORIG 1025mtu_set() 1026{ 1027 local dev=$1; shift 1028 local mtu=$1; shift 1029 1030 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 1031 ip link set dev $dev mtu $mtu 1032} 1033 1034mtu_restore() 1035{ 1036 local dev=$1; shift 1037 1038 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 1039} 1040 1041tc_offload_check() 1042{ 1043 local num_netifs=${1:-$NUM_NETIFS} 1044 1045 for ((i = 1; i <= num_netifs; ++i)); do 1046 ethtool -k ${NETIFS[p$i]} \ 1047 | grep "hw-tc-offload: on" &> /dev/null 1048 if [[ $? -ne 0 ]]; then 1049 return 1 1050 fi 1051 done 1052 1053 return 0 1054} 1055 1056trap_install() 1057{ 1058 local dev=$1; shift 1059 local direction=$1; shift 1060 1061 # Some devices may not support or need in-hardware trapping of traffic 1062 # (e.g. the veth pairs that this library creates for non-existent 1063 # loopbacks). Use continue instead, so that there is a filter in there 1064 # (some tests check counters), and so that other filters are still 1065 # processed. 1066 tc filter add dev $dev $direction pref 1 \ 1067 flower skip_sw action trap 2>/dev/null \ 1068 || tc filter add dev $dev $direction pref 1 \ 1069 flower action continue 1070} 1071 1072trap_uninstall() 1073{ 1074 local dev=$1; shift 1075 local direction=$1; shift 1076 1077 tc filter del dev $dev $direction pref 1 flower 1078} 1079 1080__icmp_capture_add_del() 1081{ 1082 local add_del=$1; shift 1083 local pref=$1; shift 1084 local vsuf=$1; shift 1085 local tundev=$1; shift 1086 local filter=$1; shift 1087 1088 tc filter $add_del dev "$tundev" ingress \ 1089 proto ip$vsuf pref $pref \ 1090 flower ip_proto icmp$vsuf $filter \ 1091 action pass 1092} 1093 1094icmp_capture_install() 1095{ 1096 local tundev=$1; shift 1097 local filter=$1; shift 1098 1099 __icmp_capture_add_del add 100 "" "$tundev" "$filter" 1100} 1101 1102icmp_capture_uninstall() 1103{ 1104 local tundev=$1; shift 1105 local filter=$1; shift 1106 1107 __icmp_capture_add_del del 100 "" "$tundev" "$filter" 1108} 1109 1110icmp6_capture_install() 1111{ 1112 local tundev=$1; shift 1113 local filter=$1; shift 1114 1115 __icmp_capture_add_del add 100 v6 "$tundev" "$filter" 1116} 1117 1118icmp6_capture_uninstall() 1119{ 1120 local tundev=$1; shift 1121 local filter=$1; shift 1122 1123 __icmp_capture_add_del del 100 v6 "$tundev" "$filter" 1124} 1125 1126__vlan_capture_add_del() 1127{ 1128 local add_del=$1; shift 1129 local pref=$1; shift 1130 local dev=$1; shift 1131 local filter=$1; shift 1132 1133 tc filter $add_del dev "$dev" ingress \ 1134 proto 802.1q pref $pref \ 1135 flower $filter \ 1136 action pass 1137} 1138 1139vlan_capture_install() 1140{ 1141 local dev=$1; shift 1142 local filter=$1; shift 1143 1144 __vlan_capture_add_del add 100 "$dev" "$filter" 1145} 1146 1147vlan_capture_uninstall() 1148{ 1149 local dev=$1; shift 1150 local filter=$1; shift 1151 1152 __vlan_capture_add_del del 100 "$dev" "$filter" 1153} 1154 1155__dscp_capture_add_del() 1156{ 1157 local add_del=$1; shift 1158 local dev=$1; shift 1159 local base=$1; shift 1160 local dscp; 1161 1162 for prio in {0..7}; do 1163 dscp=$((base + prio)) 1164 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 1165 "skip_hw ip_tos $((dscp << 2))" 1166 done 1167} 1168 1169dscp_capture_install() 1170{ 1171 local dev=$1; shift 1172 local base=$1; shift 1173 1174 __dscp_capture_add_del add $dev $base 1175} 1176 1177dscp_capture_uninstall() 1178{ 1179 local dev=$1; shift 1180 local base=$1; shift 1181 1182 __dscp_capture_add_del del $dev $base 1183} 1184 1185dscp_fetch_stats() 1186{ 1187 local dev=$1; shift 1188 local base=$1; shift 1189 1190 for prio in {0..7}; do 1191 local dscp=$((base + prio)) 1192 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 1193 echo "[$dscp]=$t " 1194 done 1195} 1196 1197matchall_sink_create() 1198{ 1199 local dev=$1; shift 1200 1201 tc qdisc add dev $dev clsact 1202 tc filter add dev $dev ingress \ 1203 pref 10000 \ 1204 matchall \ 1205 action drop 1206} 1207 1208cleanup() 1209{ 1210 pre_cleanup 1211 defer_scopes_cleanup 1212} 1213 1214multipath_eval() 1215{ 1216 local desc="$1" 1217 local weight_rp12=$2 1218 local weight_rp13=$3 1219 local packets_rp12=$4 1220 local packets_rp13=$5 1221 local weights_ratio packets_ratio diff 1222 1223 RET=0 1224 1225 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1226 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 1227 | bc -l) 1228 else 1229 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 1230 | bc -l) 1231 fi 1232 1233 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 1234 check_err 1 "Packet difference is 0" 1235 log_test "Multipath" 1236 log_info "Expected ratio $weights_ratio" 1237 return 1238 fi 1239 1240 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1241 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 1242 | bc -l) 1243 else 1244 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 1245 | bc -l) 1246 fi 1247 1248 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 1249 diff=${diff#-} 1250 1251 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 1252 check_err $? "Too large discrepancy between expected and measured ratios" 1253 log_test "$desc" 1254 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 1255} 1256 1257in_ns() 1258{ 1259 local name=$1; shift 1260 1261 ip netns exec $name bash <<-EOF 1262 NUM_NETIFS=0 1263 source lib.sh 1264 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 1265 EOF 1266} 1267 1268############################################################################## 1269# Tests 1270 1271ping_do() 1272{ 1273 local if_name=$1 1274 local dip=$2 1275 local args=$3 1276 local vrf_name 1277 1278 vrf_name=$(master_name_get $if_name) 1279 ip vrf exec $vrf_name \ 1280 $PING $args $dip -c $PING_COUNT -i 0.1 \ 1281 -w $PING_TIMEOUT &> /dev/null 1282} 1283 1284ping_test() 1285{ 1286 RET=0 1287 1288 ping_do $1 $2 1289 check_err $? 1290 log_test "ping$3" 1291} 1292 1293ping_test_fails() 1294{ 1295 RET=0 1296 1297 ping_do $1 $2 1298 check_fail $? 1299 log_test "ping fails$3" 1300} 1301 1302ping6_do() 1303{ 1304 local if_name=$1 1305 local dip=$2 1306 local args=$3 1307 local vrf_name 1308 1309 vrf_name=$(master_name_get $if_name) 1310 ip vrf exec $vrf_name \ 1311 $PING6 $args $dip -c $PING_COUNT -i 0.1 \ 1312 -w $PING_TIMEOUT &> /dev/null 1313} 1314 1315ping6_test() 1316{ 1317 RET=0 1318 1319 ping6_do $1 $2 1320 check_err $? 1321 log_test "ping6$3" 1322} 1323 1324ping6_test_fails() 1325{ 1326 RET=0 1327 1328 ping6_do $1 $2 1329 check_fail $? 1330 log_test "ping6 fails$3" 1331} 1332 1333learning_test() 1334{ 1335 local bridge=$1 1336 local br_port1=$2 # Connected to `host1_if`. 1337 local host1_if=$3 1338 local host2_if=$4 1339 local mac=de:ad:be:ef:13:37 1340 local ageing_time 1341 1342 RET=0 1343 1344 bridge -j fdb show br $bridge brport $br_port1 \ 1345 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1346 check_fail $? "Found FDB record when should not" 1347 1348 # Disable unknown unicast flooding on `br_port1` to make sure 1349 # packets are only forwarded through the port after a matching 1350 # FDB entry was installed. 1351 bridge link set dev $br_port1 flood off 1352 1353 ip link set $host1_if promisc on 1354 tc qdisc add dev $host1_if ingress 1355 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1356 flower dst_mac $mac action drop 1357 1358 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1359 sleep 1 1360 1361 tc -j -s filter show dev $host1_if ingress \ 1362 | jq -e ".[] | select(.options.handle == 101) \ 1363 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1364 check_fail $? "Packet reached first host when should not" 1365 1366 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1367 sleep 1 1368 1369 bridge -j fdb show br $bridge brport $br_port1 \ 1370 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1371 check_err $? "Did not find FDB record when should" 1372 1373 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1374 sleep 1 1375 1376 tc -j -s filter show dev $host1_if ingress \ 1377 | jq -e ".[] | select(.options.handle == 101) \ 1378 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1379 check_err $? "Packet did not reach second host when should" 1380 1381 # Wait for 10 seconds after the ageing time to make sure FDB 1382 # record was aged-out. 1383 ageing_time=$(bridge_ageing_time_get $bridge) 1384 sleep $((ageing_time + 10)) 1385 1386 bridge -j fdb show br $bridge brport $br_port1 \ 1387 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1388 check_fail $? "Found FDB record when should not" 1389 1390 bridge link set dev $br_port1 learning off 1391 1392 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1393 sleep 1 1394 1395 bridge -j fdb show br $bridge brport $br_port1 \ 1396 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1397 check_fail $? "Found FDB record when should not" 1398 1399 bridge link set dev $br_port1 learning on 1400 1401 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1402 tc qdisc del dev $host1_if ingress 1403 ip link set $host1_if promisc off 1404 1405 bridge link set dev $br_port1 flood on 1406 1407 log_test "FDB learning" 1408} 1409 1410flood_test_do() 1411{ 1412 local should_flood=$1 1413 local mac=$2 1414 local ip=$3 1415 local host1_if=$4 1416 local host2_if=$5 1417 local err=0 1418 1419 # Add an ACL on `host2_if` which will tell us whether the packet 1420 # was flooded to it or not. 1421 ip link set $host2_if promisc on 1422 tc qdisc add dev $host2_if ingress 1423 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1424 flower dst_mac $mac action drop 1425 1426 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1427 sleep 1 1428 1429 tc -j -s filter show dev $host2_if ingress \ 1430 | jq -e ".[] | select(.options.handle == 101) \ 1431 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1432 if [[ $? -ne 0 && $should_flood == "true" || \ 1433 $? -eq 0 && $should_flood == "false" ]]; then 1434 err=1 1435 fi 1436 1437 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1438 tc qdisc del dev $host2_if ingress 1439 ip link set $host2_if promisc off 1440 1441 return $err 1442} 1443 1444flood_unicast_test() 1445{ 1446 local br_port=$1 1447 local host1_if=$2 1448 local host2_if=$3 1449 local mac=de:ad:be:ef:13:37 1450 local ip=192.0.2.100 1451 1452 RET=0 1453 1454 bridge link set dev $br_port flood off 1455 1456 flood_test_do false $mac $ip $host1_if $host2_if 1457 check_err $? "Packet flooded when should not" 1458 1459 bridge link set dev $br_port flood on 1460 1461 flood_test_do true $mac $ip $host1_if $host2_if 1462 check_err $? "Packet was not flooded when should" 1463 1464 log_test "Unknown unicast flood" 1465} 1466 1467flood_multicast_test() 1468{ 1469 local br_port=$1 1470 local host1_if=$2 1471 local host2_if=$3 1472 local mac=01:00:5e:00:00:01 1473 local ip=239.0.0.1 1474 1475 RET=0 1476 1477 bridge link set dev $br_port mcast_flood off 1478 1479 flood_test_do false $mac $ip $host1_if $host2_if 1480 check_err $? "Packet flooded when should not" 1481 1482 bridge link set dev $br_port mcast_flood on 1483 1484 flood_test_do true $mac $ip $host1_if $host2_if 1485 check_err $? "Packet was not flooded when should" 1486 1487 log_test "Unregistered multicast flood" 1488} 1489 1490flood_test() 1491{ 1492 # `br_port` is connected to `host2_if` 1493 local br_port=$1 1494 local host1_if=$2 1495 local host2_if=$3 1496 1497 flood_unicast_test $br_port $host1_if $host2_if 1498 flood_multicast_test $br_port $host1_if $host2_if 1499} 1500 1501__start_traffic() 1502{ 1503 local pktsize=$1; shift 1504 local proto=$1; shift 1505 local h_in=$1; shift # Where the traffic egresses the host 1506 local sip=$1; shift 1507 local dip=$1; shift 1508 local dmac=$1; shift 1509 local -a mz_args=("$@") 1510 1511 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ 1512 -a own -b $dmac -t "$proto" -q "${mz_args[@]}" & 1513 sleep 1 1514} 1515 1516start_traffic_pktsize() 1517{ 1518 local pktsize=$1; shift 1519 local h_in=$1; shift 1520 local sip=$1; shift 1521 local dip=$1; shift 1522 local dmac=$1; shift 1523 local -a mz_args=("$@") 1524 1525 __start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \ 1526 "${mz_args[@]}" 1527} 1528 1529start_tcp_traffic_pktsize() 1530{ 1531 local pktsize=$1; shift 1532 local h_in=$1; shift 1533 local sip=$1; shift 1534 local dip=$1; shift 1535 local dmac=$1; shift 1536 local -a mz_args=("$@") 1537 1538 __start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \ 1539 "${mz_args[@]}" 1540} 1541 1542start_traffic() 1543{ 1544 local h_in=$1; shift 1545 local sip=$1; shift 1546 local dip=$1; shift 1547 local dmac=$1; shift 1548 local -a mz_args=("$@") 1549 1550 start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \ 1551 "${mz_args[@]}" 1552} 1553 1554start_tcp_traffic() 1555{ 1556 local h_in=$1; shift 1557 local sip=$1; shift 1558 local dip=$1; shift 1559 local dmac=$1; shift 1560 local -a mz_args=("$@") 1561 1562 start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \ 1563 "${mz_args[@]}" 1564} 1565 1566stop_traffic() 1567{ 1568 local pid=${1-%%}; shift 1569 1570 kill_process "$pid" 1571} 1572 1573declare -A cappid 1574declare -A capfile 1575declare -A capout 1576 1577tcpdump_start() 1578{ 1579 local if_name=$1; shift 1580 local ns=$1; shift 1581 1582 capfile[$if_name]=$(mktemp) 1583 capout[$if_name]=$(mktemp) 1584 1585 if [ -z $ns ]; then 1586 ns_cmd="" 1587 else 1588 ns_cmd="ip netns exec ${ns}" 1589 fi 1590 1591 if [ -z $SUDO_USER ] ; then 1592 capuser="" 1593 else 1594 capuser="-Z $SUDO_USER" 1595 fi 1596 1597 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ 1598 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ 1599 > "${capout[$if_name]}" 2>&1 & 1600 cappid[$if_name]=$! 1601 1602 sleep 1 1603} 1604 1605tcpdump_stop() 1606{ 1607 local if_name=$1 1608 local pid=${cappid[$if_name]} 1609 1610 $ns_cmd kill "$pid" && wait "$pid" 1611 sleep 1 1612} 1613 1614tcpdump_cleanup() 1615{ 1616 local if_name=$1 1617 1618 rm ${capfile[$if_name]} ${capout[$if_name]} 1619} 1620 1621tcpdump_show() 1622{ 1623 local if_name=$1 1624 1625 tcpdump -e -n -r ${capfile[$if_name]} 2>&1 1626} 1627 1628# return 0 if the packet wasn't seen on host2_if or 1 if it was 1629mcast_packet_test() 1630{ 1631 local mac=$1 1632 local src_ip=$2 1633 local ip=$3 1634 local host1_if=$4 1635 local host2_if=$5 1636 local seen=0 1637 local tc_proto="ip" 1638 local mz_v6arg="" 1639 1640 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1641 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1642 tc_proto="ipv6" 1643 mz_v6arg="-6" 1644 fi 1645 1646 # Add an ACL on `host2_if` which will tell us whether the packet 1647 # was received by it or not. 1648 tc qdisc add dev $host2_if ingress 1649 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1650 flower ip_proto udp dst_mac $mac action drop 1651 1652 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1653 sleep 1 1654 1655 tc -j -s filter show dev $host2_if ingress \ 1656 | jq -e ".[] | select(.options.handle == 101) \ 1657 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1658 if [[ $? -eq 0 ]]; then 1659 seen=1 1660 fi 1661 1662 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1663 tc qdisc del dev $host2_if ingress 1664 1665 return $seen 1666} 1667 1668brmcast_check_sg_entries() 1669{ 1670 local report=$1; shift 1671 local slist=("$@") 1672 local sarg="" 1673 1674 for src in "${slist[@]}"; do 1675 sarg="${sarg} and .source_list[].address == \"$src\"" 1676 done 1677 bridge -j -d -s mdb show dev br0 \ 1678 | jq -e ".[].mdb[] | \ 1679 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1680 check_err $? "Wrong *,G entry source list after $report report" 1681 1682 for sgent in "${slist[@]}"; do 1683 bridge -j -d -s mdb show dev br0 \ 1684 | jq -e ".[].mdb[] | \ 1685 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1686 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1687 done 1688} 1689 1690brmcast_check_sg_fwding() 1691{ 1692 local should_fwd=$1; shift 1693 local sources=("$@") 1694 1695 for src in "${sources[@]}"; do 1696 local retval=0 1697 1698 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1699 retval=$? 1700 if [ $should_fwd -eq 1 ]; then 1701 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1702 else 1703 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1704 fi 1705 done 1706} 1707 1708brmcast_check_sg_state() 1709{ 1710 local is_blocked=$1; shift 1711 local sources=("$@") 1712 local should_fail=1 1713 1714 if [ $is_blocked -eq 1 ]; then 1715 should_fail=0 1716 fi 1717 1718 for src in "${sources[@]}"; do 1719 bridge -j -d -s mdb show dev br0 \ 1720 | jq -e ".[].mdb[] | \ 1721 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1722 .source_list[] | 1723 select(.address == \"$src\") | 1724 select(.timer == \"0.00\")" &>/dev/null 1725 check_err_fail $should_fail $? "Entry $src has zero timer" 1726 1727 bridge -j -d -s mdb show dev br0 \ 1728 | jq -e ".[].mdb[] | \ 1729 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1730 .flags[] == \"blocked\")" &>/dev/null 1731 check_err_fail $should_fail $? "Entry $src has blocked flag" 1732 done 1733} 1734 1735mc_join() 1736{ 1737 local if_name=$1 1738 local group=$2 1739 local vrf_name=$(master_name_get $if_name) 1740 1741 # We don't care about actual reception, just about joining the 1742 # IP multicast group and adding the L2 address to the device's 1743 # MAC filtering table 1744 ip vrf exec $vrf_name \ 1745 mreceive -g $group -I $if_name > /dev/null 2>&1 & 1746 mreceive_pid=$! 1747 1748 sleep 1 1749} 1750 1751mc_leave() 1752{ 1753 kill "$mreceive_pid" && wait "$mreceive_pid" 1754} 1755 1756mc_send() 1757{ 1758 local if_name=$1 1759 local groups=$2 1760 local vrf_name=$(master_name_get $if_name) 1761 1762 ip vrf exec $vrf_name \ 1763 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 1764} 1765 1766adf_mcd_start() 1767{ 1768 local ifs=("$@") 1769 1770 local table_name="$MCD_TABLE_NAME" 1771 local smcroutedir 1772 local pid 1773 local if 1774 local i 1775 1776 check_command "$MCD" || return 1 1777 check_command "$MC_CLI" || return 1 1778 1779 smcroutedir=$(mktemp -d) 1780 defer rm -rf "$smcroutedir" 1781 1782 for ((i = 1; i <= NUM_NETIFS; ++i)); do 1783 echo "phyint ${NETIFS[p$i]} enable" >> \ 1784 "$smcroutedir/$table_name.conf" 1785 done 1786 1787 for if in "${ifs[@]}"; do 1788 if ! ip_link_has_flag "$if" MULTICAST; then 1789 ip link set dev "$if" multicast on 1790 defer ip link set dev "$if" multicast off 1791 fi 1792 1793 echo "phyint $if enable" >> \ 1794 "$smcroutedir/$table_name.conf" 1795 done 1796 1797 "$MCD" -N -I "$table_name" -f "$smcroutedir/$table_name.conf" \ 1798 -P "$smcroutedir/$table_name.pid" 1799 busywait "$BUSYWAIT_TIMEOUT" test -e "$smcroutedir/$table_name.pid" 1800 pid=$(cat "$smcroutedir/$table_name.pid") 1801 defer kill_process "$pid" 1802} 1803 1804mc_cli() 1805{ 1806 local table_name="$MCD_TABLE_NAME" 1807 1808 "$MC_CLI" -I "$table_name" "$@" 1809} 1810 1811start_ip_monitor() 1812{ 1813 local mtype=$1; shift 1814 local ip=${1-ip}; shift 1815 1816 # start the monitor in the background 1817 tmpfile=`mktemp /var/run/nexthoptestXXX` 1818 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` 1819 sleep 0.2 1820 echo "$mpid $tmpfile" 1821} 1822 1823stop_ip_monitor() 1824{ 1825 local mpid=$1; shift 1826 local tmpfile=$1; shift 1827 local el=$1; shift 1828 local what=$1; shift 1829 1830 sleep 0.2 1831 kill $mpid 1832 local lines=`grep '^\w' $tmpfile | wc -l` 1833 test $lines -eq $el 1834 check_err $? "$what: $lines lines of events, expected $el" 1835 rm -rf $tmpfile 1836} 1837 1838hw_stats_monitor_test() 1839{ 1840 local dev=$1; shift 1841 local type=$1; shift 1842 local make_suitable=$1; shift 1843 local make_unsuitable=$1; shift 1844 local ip=${1-ip}; shift 1845 1846 RET=0 1847 1848 # Expect a notification about enablement. 1849 local ipmout=$(start_ip_monitor stats "$ip") 1850 $ip stats set dev $dev ${type}_stats on 1851 stop_ip_monitor $ipmout 1 "${type}_stats enablement" 1852 1853 # Expect a notification about offload. 1854 local ipmout=$(start_ip_monitor stats "$ip") 1855 $make_suitable 1856 stop_ip_monitor $ipmout 1 "${type}_stats installation" 1857 1858 # Expect a notification about loss of offload. 1859 local ipmout=$(start_ip_monitor stats "$ip") 1860 $make_unsuitable 1861 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" 1862 1863 # Expect a notification about disablement 1864 local ipmout=$(start_ip_monitor stats "$ip") 1865 $ip stats set dev $dev ${type}_stats off 1866 stop_ip_monitor $ipmout 1 "${type}_stats disablement" 1867 1868 log_test "${type}_stats notifications" 1869} 1870 1871ipv4_to_bytes() 1872{ 1873 local IP=$1; shift 1874 1875 printf '%02x:' ${IP//./ } | 1876 sed 's/:$//' 1877} 1878 1879# Convert a given IPv6 address, `IP' such that the :: token, if present, is 1880# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal 1881# digits. An optional `BYTESEP' parameter can be given to further separate 1882# individual bytes of each 16-bit group. 1883expand_ipv6() 1884{ 1885 local IP=$1; shift 1886 local bytesep=$1; shift 1887 1888 local cvt_ip=${IP/::/_} 1889 local colons=${cvt_ip//[^:]/} 1890 local allcol=::::::: 1891 # IP where :: -> the appropriate number of colons: 1892 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}} 1893 1894 echo $allcol_ip | tr : '\n' | 1895 sed s/^/0000/ | 1896 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' | 1897 tr '\n' : | 1898 sed 's/:$//' 1899} 1900 1901ipv6_to_bytes() 1902{ 1903 local IP=$1; shift 1904 1905 expand_ipv6 "$IP" : 1906} 1907 1908u16_to_bytes() 1909{ 1910 local u16=$1; shift 1911 1912 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' 1913} 1914 1915# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), 1916# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, 1917# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) 1918# stands for 00:00. 1919payload_template_calc_checksum() 1920{ 1921 local payload=$1; shift 1922 1923 ( 1924 # Set input radix. 1925 echo "16i" 1926 # Push zero for the initial checksum. 1927 echo 0 1928 1929 # Pad the payload with a terminating 00: in case we get an odd 1930 # number of bytes. 1931 echo "${payload%:}:00:" | 1932 sed 's/CHECKSUM/00:00/g' | 1933 tr '[:lower:]' '[:upper:]' | 1934 # Add the word to the checksum. 1935 sed 's/\(..\):\(..\):/\1\2+\n/g' | 1936 # Strip the extra odd byte we pushed if left unconverted. 1937 sed 's/\(..\):$//' 1938 1939 echo "10000 ~ +" # Calculate and add carry. 1940 echo "FFFF r - p" # Bit-flip and print. 1941 ) | 1942 dc | 1943 tr '[:upper:]' '[:lower:]' 1944} 1945 1946payload_template_expand_checksum() 1947{ 1948 local payload=$1; shift 1949 local checksum=$1; shift 1950 1951 local ckbytes=$(u16_to_bytes $checksum) 1952 1953 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" 1954} 1955 1956payload_template_nbytes() 1957{ 1958 local payload=$1; shift 1959 1960 payload_template_expand_checksum "${payload%:}" 0 | 1961 sed 's/:/\n/g' | wc -l 1962} 1963 1964igmpv3_is_in_get() 1965{ 1966 local GRP=$1; shift 1967 local sources=("$@") 1968 1969 local igmpv3 1970 local nsources=$(u16_to_bytes ${#sources[@]}) 1971 1972 # IS_IN ( $sources ) 1973 igmpv3=$(: 1974 )"22:"$( : Type - Membership Report 1975 )"00:"$( : Reserved 1976 )"CHECKSUM:"$( : Checksum 1977 )"00:00:"$( : Reserved 1978 )"00:01:"$( : Number of Group Records 1979 )"01:"$( : Record Type - IS_IN 1980 )"00:"$( : Aux Data Len 1981 )"${nsources}:"$( : Number of Sources 1982 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address 1983 )"$(for src in "${sources[@]}"; do 1984 ipv4_to_bytes $src 1985 echo -n : 1986 done)"$( : Source Addresses 1987 ) 1988 local checksum=$(payload_template_calc_checksum "$igmpv3") 1989 1990 payload_template_expand_checksum "$igmpv3" $checksum 1991} 1992 1993igmpv2_leave_get() 1994{ 1995 local GRP=$1; shift 1996 1997 local payload=$(: 1998 )"17:"$( : Type - Leave Group 1999 )"00:"$( : Max Resp Time - not meaningful 2000 )"CHECKSUM:"$( : Checksum 2001 )"$(ipv4_to_bytes $GRP)"$( : Group Address 2002 ) 2003 local checksum=$(payload_template_calc_checksum "$payload") 2004 2005 payload_template_expand_checksum "$payload" $checksum 2006} 2007 2008mldv2_is_in_get() 2009{ 2010 local SIP=$1; shift 2011 local GRP=$1; shift 2012 local sources=("$@") 2013 2014 local hbh 2015 local icmpv6 2016 local nsources=$(u16_to_bytes ${#sources[@]}) 2017 2018 hbh=$(: 2019 )"3a:"$( : Next Header - ICMPv6 2020 )"00:"$( : Hdr Ext Len 2021 )"00:00:00:00:00:00:"$( : Options and Padding 2022 ) 2023 2024 icmpv6=$(: 2025 )"8f:"$( : Type - MLDv2 Report 2026 )"00:"$( : Code 2027 )"CHECKSUM:"$( : Checksum 2028 )"00:00:"$( : Reserved 2029 )"00:01:"$( : Number of Group Records 2030 )"01:"$( : Record Type - IS_IN 2031 )"00:"$( : Aux Data Len 2032 )"${nsources}:"$( : Number of Sources 2033 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2034 )"$(for src in "${sources[@]}"; do 2035 ipv6_to_bytes $src 2036 echo -n : 2037 done)"$( : Source Addresses 2038 ) 2039 2040 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2041 local sudohdr=$(: 2042 )"$(ipv6_to_bytes $SIP):"$( : SIP 2043 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2044 )"${len}:"$( : Upper-layer length 2045 )"00:3a:"$( : Zero and next-header 2046 ) 2047 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2048 2049 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2050} 2051 2052mldv1_done_get() 2053{ 2054 local SIP=$1; shift 2055 local GRP=$1; shift 2056 2057 local hbh 2058 local icmpv6 2059 2060 hbh=$(: 2061 )"3a:"$( : Next Header - ICMPv6 2062 )"00:"$( : Hdr Ext Len 2063 )"00:00:00:00:00:00:"$( : Options and Padding 2064 ) 2065 2066 icmpv6=$(: 2067 )"84:"$( : Type - MLDv1 Done 2068 )"00:"$( : Code 2069 )"CHECKSUM:"$( : Checksum 2070 )"00:00:"$( : Max Resp Delay - not meaningful 2071 )"00:00:"$( : Reserved 2072 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 2073 ) 2074 2075 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 2076 local sudohdr=$(: 2077 )"$(ipv6_to_bytes $SIP):"$( : SIP 2078 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 2079 )"${len}:"$( : Upper-layer length 2080 )"00:3a:"$( : Zero and next-header 2081 ) 2082 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 2083 2084 payload_template_expand_checksum "$hbh$icmpv6" $checksum 2085} 2086 2087bail_on_lldpad() 2088{ 2089 local reason1="$1"; shift 2090 local reason2="$1"; shift 2091 local caller=${FUNCNAME[1]} 2092 local src=${BASH_SOURCE[1]} 2093 2094 if systemctl is-active --quiet lldpad; then 2095 2096 cat >/dev/stderr <<-EOF 2097 WARNING: lldpad is running 2098 2099 lldpad will likely $reason1, and this test will 2100 $reason2. Both are not supported at the same time, 2101 one of them is arbitrarily going to overwrite the 2102 other. That will cause spurious failures (or, unlikely, 2103 passes) of this test. 2104 EOF 2105 2106 if [[ -z $ALLOW_LLDPAD ]]; then 2107 cat >/dev/stderr <<-EOF 2108 2109 If you want to run the test anyway, please set 2110 an environment variable ALLOW_LLDPAD to a 2111 non-empty string. 2112 EOF 2113 log_test_skip $src:$caller 2114 exit $EXIT_STATUS 2115 else 2116 return 2117 fi 2118 fi 2119} 2120 2121absval() 2122{ 2123 local v=$1; shift 2124 2125 echo $((v > 0 ? v : -v)) 2126} 2127 2128has_unicast_flt() 2129{ 2130 local dev=$1; shift 2131 local mac_addr=$(mac_get $dev) 2132 local tmp=$(ether_addr_to_u64 $mac_addr) 2133 local promisc 2134 2135 ip link set $dev up 2136 ip link add link $dev name macvlan-tmp type macvlan mode private 2137 ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1))) 2138 ip link set macvlan-tmp up 2139 2140 promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity') 2141 2142 ip link del macvlan-tmp 2143 2144 [[ $promisc == 1 ]] && echo "no" || echo "yes" 2145} 2146