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