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