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