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