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