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