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