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