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 1218ping6_do() 1219{ 1220 local if_name=$1 1221 local dip=$2 1222 local args=$3 1223 local vrf_name 1224 1225 vrf_name=$(master_name_get $if_name) 1226 ip vrf exec $vrf_name \ 1227 $PING6 $args $dip -c $PING_COUNT -i 0.1 \ 1228 -w $PING_TIMEOUT &> /dev/null 1229} 1230 1231ping6_test() 1232{ 1233 RET=0 1234 1235 ping6_do $1 $2 1236 check_err $? 1237 log_test "ping6$3" 1238} 1239 1240learning_test() 1241{ 1242 local bridge=$1 1243 local br_port1=$2 # Connected to `host1_if`. 1244 local host1_if=$3 1245 local host2_if=$4 1246 local mac=de:ad:be:ef:13:37 1247 local ageing_time 1248 1249 RET=0 1250 1251 bridge -j fdb show br $bridge brport $br_port1 \ 1252 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1253 check_fail $? "Found FDB record when should not" 1254 1255 # Disable unknown unicast flooding on `br_port1` to make sure 1256 # packets are only forwarded through the port after a matching 1257 # FDB entry was installed. 1258 bridge link set dev $br_port1 flood off 1259 1260 ip link set $host1_if promisc on 1261 tc qdisc add dev $host1_if ingress 1262 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1263 flower dst_mac $mac action drop 1264 1265 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1266 sleep 1 1267 1268 tc -j -s filter show dev $host1_if ingress \ 1269 | jq -e ".[] | select(.options.handle == 101) \ 1270 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1271 check_fail $? "Packet reached first host when should not" 1272 1273 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1274 sleep 1 1275 1276 bridge -j fdb show br $bridge brport $br_port1 \ 1277 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1278 check_err $? "Did not find FDB record when should" 1279 1280 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1281 sleep 1 1282 1283 tc -j -s filter show dev $host1_if ingress \ 1284 | jq -e ".[] | select(.options.handle == 101) \ 1285 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1286 check_err $? "Packet did not reach second host when should" 1287 1288 # Wait for 10 seconds after the ageing time to make sure FDB 1289 # record was aged-out. 1290 ageing_time=$(bridge_ageing_time_get $bridge) 1291 sleep $((ageing_time + 10)) 1292 1293 bridge -j fdb show br $bridge brport $br_port1 \ 1294 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1295 check_fail $? "Found FDB record when should not" 1296 1297 bridge link set dev $br_port1 learning off 1298 1299 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1300 sleep 1 1301 1302 bridge -j fdb show br $bridge brport $br_port1 \ 1303 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1304 check_fail $? "Found FDB record when should not" 1305 1306 bridge link set dev $br_port1 learning on 1307 1308 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1309 tc qdisc del dev $host1_if ingress 1310 ip link set $host1_if promisc off 1311 1312 bridge link set dev $br_port1 flood on 1313 1314 log_test "FDB learning" 1315} 1316 1317flood_test_do() 1318{ 1319 local should_flood=$1 1320 local mac=$2 1321 local ip=$3 1322 local host1_if=$4 1323 local host2_if=$5 1324 local err=0 1325 1326 # Add an ACL on `host2_if` which will tell us whether the packet 1327 # was flooded to it or not. 1328 ip link set $host2_if promisc on 1329 tc qdisc add dev $host2_if ingress 1330 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1331 flower dst_mac $mac action drop 1332 1333 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1334 sleep 1 1335 1336 tc -j -s filter show dev $host2_if ingress \ 1337 | jq -e ".[] | select(.options.handle == 101) \ 1338 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1339 if [[ $? -ne 0 && $should_flood == "true" || \ 1340 $? -eq 0 && $should_flood == "false" ]]; then 1341 err=1 1342 fi 1343 1344 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1345 tc qdisc del dev $host2_if ingress 1346 ip link set $host2_if promisc off 1347 1348 return $err 1349} 1350 1351flood_unicast_test() 1352{ 1353 local br_port=$1 1354 local host1_if=$2 1355 local host2_if=$3 1356 local mac=de:ad:be:ef:13:37 1357 local ip=192.0.2.100 1358 1359 RET=0 1360 1361 bridge link set dev $br_port flood off 1362 1363 flood_test_do false $mac $ip $host1_if $host2_if 1364 check_err $? "Packet flooded when should not" 1365 1366 bridge link set dev $br_port flood on 1367 1368 flood_test_do true $mac $ip $host1_if $host2_if 1369 check_err $? "Packet was not flooded when should" 1370 1371 log_test "Unknown unicast flood" 1372} 1373 1374flood_multicast_test() 1375{ 1376 local br_port=$1 1377 local host1_if=$2 1378 local host2_if=$3 1379 local mac=01:00:5e:00:00:01 1380 local ip=239.0.0.1 1381 1382 RET=0 1383 1384 bridge link set dev $br_port mcast_flood off 1385 1386 flood_test_do false $mac $ip $host1_if $host2_if 1387 check_err $? "Packet flooded when should not" 1388 1389 bridge link set dev $br_port mcast_flood on 1390 1391 flood_test_do true $mac $ip $host1_if $host2_if 1392 check_err $? "Packet was not flooded when should" 1393 1394 log_test "Unregistered multicast flood" 1395} 1396 1397flood_test() 1398{ 1399 # `br_port` is connected to `host2_if` 1400 local br_port=$1 1401 local host1_if=$2 1402 local host2_if=$3 1403 1404 flood_unicast_test $br_port $host1_if $host2_if 1405 flood_multicast_test $br_port $host1_if $host2_if 1406} 1407 1408__start_traffic() 1409{ 1410 local pktsize=$1; shift 1411 local proto=$1; shift 1412 local h_in=$1; shift # Where the traffic egresses the host 1413 local sip=$1; shift 1414 local dip=$1; shift 1415 local dmac=$1; shift 1416 1417 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ 1418 -a own -b $dmac -t "$proto" -q "$@" & 1419 sleep 1 1420} 1421 1422start_traffic_pktsize() 1423{ 1424 local pktsize=$1; shift 1425 1426 __start_traffic $pktsize udp "$@" 1427} 1428 1429start_tcp_traffic_pktsize() 1430{ 1431 local pktsize=$1; shift 1432 1433 __start_traffic $pktsize tcp "$@" 1434} 1435 1436start_traffic() 1437{ 1438 start_traffic_pktsize 8000 "$@" 1439} 1440 1441start_tcp_traffic() 1442{ 1443 start_tcp_traffic_pktsize 8000 "$@" 1444} 1445 1446stop_traffic() 1447{ 1448 # Suppress noise from killing mausezahn. 1449 { kill %% && wait %%; } 2>/dev/null 1450} 1451 1452declare -A cappid 1453declare -A capfile 1454declare -A capout 1455 1456tcpdump_start() 1457{ 1458 local if_name=$1; shift 1459 local ns=$1; shift 1460 1461 capfile[$if_name]=$(mktemp) 1462 capout[$if_name]=$(mktemp) 1463 1464 if [ -z $ns ]; then 1465 ns_cmd="" 1466 else 1467 ns_cmd="ip netns exec ${ns}" 1468 fi 1469 1470 if [ -z $SUDO_USER ] ; then 1471 capuser="" 1472 else 1473 capuser="-Z $SUDO_USER" 1474 fi 1475 1476 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ 1477 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ 1478 > "${capout[$if_name]}" 2>&1 & 1479 cappid[$if_name]=$! 1480 1481 sleep 1 1482} 1483 1484tcpdump_stop() 1485{ 1486 local if_name=$1 1487 local pid=${cappid[$if_name]} 1488 1489 $ns_cmd kill "$pid" && wait "$pid" 1490 sleep 1 1491} 1492 1493tcpdump_cleanup() 1494{ 1495 local if_name=$1 1496 1497 rm ${capfile[$if_name]} ${capout[$if_name]} 1498} 1499 1500tcpdump_show() 1501{ 1502 local if_name=$1 1503 1504 tcpdump -e -n -r ${capfile[$if_name]} 2>&1 1505} 1506 1507# return 0 if the packet wasn't seen on host2_if or 1 if it was 1508mcast_packet_test() 1509{ 1510 local mac=$1 1511 local src_ip=$2 1512 local ip=$3 1513 local host1_if=$4 1514 local host2_if=$5 1515 local seen=0 1516 local tc_proto="ip" 1517 local mz_v6arg="" 1518 1519 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1520 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1521 tc_proto="ipv6" 1522 mz_v6arg="-6" 1523 fi 1524 1525 # Add an ACL on `host2_if` which will tell us whether the packet 1526 # was received by it or not. 1527 tc qdisc add dev $host2_if ingress 1528 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1529 flower ip_proto udp dst_mac $mac action drop 1530 1531 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1532 sleep 1 1533 1534 tc -j -s filter show dev $host2_if ingress \ 1535 | jq -e ".[] | select(.options.handle == 101) \ 1536 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1537 if [[ $? -eq 0 ]]; then 1538 seen=1 1539 fi 1540 1541 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1542 tc qdisc del dev $host2_if ingress 1543 1544 return $seen 1545} 1546 1547brmcast_check_sg_entries() 1548{ 1549 local report=$1; shift 1550 local slist=("$@") 1551 local sarg="" 1552 1553 for src in "${slist[@]}"; do 1554 sarg="${sarg} and .source_list[].address == \"$src\"" 1555 done 1556 bridge -j -d -s mdb show dev br0 \ 1557 | jq -e ".[].mdb[] | \ 1558 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1559 check_err $? "Wrong *,G entry source list after $report report" 1560 1561 for sgent in "${slist[@]}"; do 1562 bridge -j -d -s mdb show dev br0 \ 1563 | jq -e ".[].mdb[] | \ 1564 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1565 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1566 done 1567} 1568 1569brmcast_check_sg_fwding() 1570{ 1571 local should_fwd=$1; shift 1572 local sources=("$@") 1573 1574 for src in "${sources[@]}"; do 1575 local retval=0 1576 1577 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1578 retval=$? 1579 if [ $should_fwd -eq 1 ]; then 1580 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1581 else 1582 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1583 fi 1584 done 1585} 1586 1587brmcast_check_sg_state() 1588{ 1589 local is_blocked=$1; shift 1590 local sources=("$@") 1591 local should_fail=1 1592 1593 if [ $is_blocked -eq 1 ]; then 1594 should_fail=0 1595 fi 1596 1597 for src in "${sources[@]}"; do 1598 bridge -j -d -s mdb show dev br0 \ 1599 | jq -e ".[].mdb[] | \ 1600 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1601 .source_list[] | 1602 select(.address == \"$src\") | 1603 select(.timer == \"0.00\")" &>/dev/null 1604 check_err_fail $should_fail $? "Entry $src has zero timer" 1605 1606 bridge -j -d -s mdb show dev br0 \ 1607 | jq -e ".[].mdb[] | \ 1608 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1609 .flags[] == \"blocked\")" &>/dev/null 1610 check_err_fail $should_fail $? "Entry $src has blocked flag" 1611 done 1612} 1613 1614mc_join() 1615{ 1616 local if_name=$1 1617 local group=$2 1618 local vrf_name=$(master_name_get $if_name) 1619 1620 # We don't care about actual reception, just about joining the 1621 # IP multicast group and adding the L2 address to the device's 1622 # MAC filtering table 1623 ip vrf exec $vrf_name \ 1624 mreceive -g $group -I $if_name > /dev/null 2>&1 & 1625 mreceive_pid=$! 1626 1627 sleep 1 1628} 1629 1630mc_leave() 1631{ 1632 kill "$mreceive_pid" && wait "$mreceive_pid" 1633} 1634 1635mc_send() 1636{ 1637 local if_name=$1 1638 local groups=$2 1639 local vrf_name=$(master_name_get $if_name) 1640 1641 ip vrf exec $vrf_name \ 1642 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 1643} 1644 1645start_ip_monitor() 1646{ 1647 local mtype=$1; shift 1648 local ip=${1-ip}; shift 1649 1650 # start the monitor in the background 1651 tmpfile=`mktemp /var/run/nexthoptestXXX` 1652 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` 1653 sleep 0.2 1654 echo "$mpid $tmpfile" 1655} 1656 1657stop_ip_monitor() 1658{ 1659 local mpid=$1; shift 1660 local tmpfile=$1; shift 1661 local el=$1; shift 1662 local what=$1; shift 1663 1664 sleep 0.2 1665 kill $mpid 1666 local lines=`grep '^\w' $tmpfile | wc -l` 1667 test $lines -eq $el 1668 check_err $? "$what: $lines lines of events, expected $el" 1669 rm -rf $tmpfile 1670} 1671 1672hw_stats_monitor_test() 1673{ 1674 local dev=$1; shift 1675 local type=$1; shift 1676 local make_suitable=$1; shift 1677 local make_unsuitable=$1; shift 1678 local ip=${1-ip}; shift 1679 1680 RET=0 1681 1682 # Expect a notification about enablement. 1683 local ipmout=$(start_ip_monitor stats "$ip") 1684 $ip stats set dev $dev ${type}_stats on 1685 stop_ip_monitor $ipmout 1 "${type}_stats enablement" 1686 1687 # Expect a notification about offload. 1688 local ipmout=$(start_ip_monitor stats "$ip") 1689 $make_suitable 1690 stop_ip_monitor $ipmout 1 "${type}_stats installation" 1691 1692 # Expect a notification about loss of offload. 1693 local ipmout=$(start_ip_monitor stats "$ip") 1694 $make_unsuitable 1695 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" 1696 1697 # Expect a notification about disablement 1698 local ipmout=$(start_ip_monitor stats "$ip") 1699 $ip stats set dev $dev ${type}_stats off 1700 stop_ip_monitor $ipmout 1 "${type}_stats disablement" 1701 1702 log_test "${type}_stats notifications" 1703} 1704 1705ipv4_to_bytes() 1706{ 1707 local IP=$1; shift 1708 1709 printf '%02x:' ${IP//./ } | 1710 sed 's/:$//' 1711} 1712 1713# Convert a given IPv6 address, `IP' such that the :: token, if present, is 1714# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal 1715# digits. An optional `BYTESEP' parameter can be given to further separate 1716# individual bytes of each 16-bit group. 1717expand_ipv6() 1718{ 1719 local IP=$1; shift 1720 local bytesep=$1; shift 1721 1722 local cvt_ip=${IP/::/_} 1723 local colons=${cvt_ip//[^:]/} 1724 local allcol=::::::: 1725 # IP where :: -> the appropriate number of colons: 1726 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}} 1727 1728 echo $allcol_ip | tr : '\n' | 1729 sed s/^/0000/ | 1730 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' | 1731 tr '\n' : | 1732 sed 's/:$//' 1733} 1734 1735ipv6_to_bytes() 1736{ 1737 local IP=$1; shift 1738 1739 expand_ipv6 "$IP" : 1740} 1741 1742u16_to_bytes() 1743{ 1744 local u16=$1; shift 1745 1746 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' 1747} 1748 1749# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), 1750# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, 1751# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) 1752# stands for 00:00. 1753payload_template_calc_checksum() 1754{ 1755 local payload=$1; shift 1756 1757 ( 1758 # Set input radix. 1759 echo "16i" 1760 # Push zero for the initial checksum. 1761 echo 0 1762 1763 # Pad the payload with a terminating 00: in case we get an odd 1764 # number of bytes. 1765 echo "${payload%:}:00:" | 1766 sed 's/CHECKSUM/00:00/g' | 1767 tr '[:lower:]' '[:upper:]' | 1768 # Add the word to the checksum. 1769 sed 's/\(..\):\(..\):/\1\2+\n/g' | 1770 # Strip the extra odd byte we pushed if left unconverted. 1771 sed 's/\(..\):$//' 1772 1773 echo "10000 ~ +" # Calculate and add carry. 1774 echo "FFFF r - p" # Bit-flip and print. 1775 ) | 1776 dc | 1777 tr '[:upper:]' '[:lower:]' 1778} 1779 1780payload_template_expand_checksum() 1781{ 1782 local payload=$1; shift 1783 local checksum=$1; shift 1784 1785 local ckbytes=$(u16_to_bytes $checksum) 1786 1787 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" 1788} 1789 1790payload_template_nbytes() 1791{ 1792 local payload=$1; shift 1793 1794 payload_template_expand_checksum "${payload%:}" 0 | 1795 sed 's/:/\n/g' | wc -l 1796} 1797 1798igmpv3_is_in_get() 1799{ 1800 local GRP=$1; shift 1801 local sources=("$@") 1802 1803 local igmpv3 1804 local nsources=$(u16_to_bytes ${#sources[@]}) 1805 1806 # IS_IN ( $sources ) 1807 igmpv3=$(: 1808 )"22:"$( : Type - Membership Report 1809 )"00:"$( : Reserved 1810 )"CHECKSUM:"$( : Checksum 1811 )"00:00:"$( : Reserved 1812 )"00:01:"$( : Number of Group Records 1813 )"01:"$( : Record Type - IS_IN 1814 )"00:"$( : Aux Data Len 1815 )"${nsources}:"$( : Number of Sources 1816 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address 1817 )"$(for src in "${sources[@]}"; do 1818 ipv4_to_bytes $src 1819 echo -n : 1820 done)"$( : Source Addresses 1821 ) 1822 local checksum=$(payload_template_calc_checksum "$igmpv3") 1823 1824 payload_template_expand_checksum "$igmpv3" $checksum 1825} 1826 1827igmpv2_leave_get() 1828{ 1829 local GRP=$1; shift 1830 1831 local payload=$(: 1832 )"17:"$( : Type - Leave Group 1833 )"00:"$( : Max Resp Time - not meaningful 1834 )"CHECKSUM:"$( : Checksum 1835 )"$(ipv4_to_bytes $GRP)"$( : Group Address 1836 ) 1837 local checksum=$(payload_template_calc_checksum "$payload") 1838 1839 payload_template_expand_checksum "$payload" $checksum 1840} 1841 1842mldv2_is_in_get() 1843{ 1844 local SIP=$1; shift 1845 local GRP=$1; shift 1846 local sources=("$@") 1847 1848 local hbh 1849 local icmpv6 1850 local nsources=$(u16_to_bytes ${#sources[@]}) 1851 1852 hbh=$(: 1853 )"3a:"$( : Next Header - ICMPv6 1854 )"00:"$( : Hdr Ext Len 1855 )"00:00:00:00:00:00:"$( : Options and Padding 1856 ) 1857 1858 icmpv6=$(: 1859 )"8f:"$( : Type - MLDv2 Report 1860 )"00:"$( : Code 1861 )"CHECKSUM:"$( : Checksum 1862 )"00:00:"$( : Reserved 1863 )"00:01:"$( : Number of Group Records 1864 )"01:"$( : Record Type - IS_IN 1865 )"00:"$( : Aux Data Len 1866 )"${nsources}:"$( : Number of Sources 1867 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 1868 )"$(for src in "${sources[@]}"; do 1869 ipv6_to_bytes $src 1870 echo -n : 1871 done)"$( : Source Addresses 1872 ) 1873 1874 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 1875 local sudohdr=$(: 1876 )"$(ipv6_to_bytes $SIP):"$( : SIP 1877 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 1878 )"${len}:"$( : Upper-layer length 1879 )"00:3a:"$( : Zero and next-header 1880 ) 1881 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 1882 1883 payload_template_expand_checksum "$hbh$icmpv6" $checksum 1884} 1885 1886mldv1_done_get() 1887{ 1888 local SIP=$1; shift 1889 local GRP=$1; shift 1890 1891 local hbh 1892 local icmpv6 1893 1894 hbh=$(: 1895 )"3a:"$( : Next Header - ICMPv6 1896 )"00:"$( : Hdr Ext Len 1897 )"00:00:00:00:00:00:"$( : Options and Padding 1898 ) 1899 1900 icmpv6=$(: 1901 )"84:"$( : Type - MLDv1 Done 1902 )"00:"$( : Code 1903 )"CHECKSUM:"$( : Checksum 1904 )"00:00:"$( : Max Resp Delay - not meaningful 1905 )"00:00:"$( : Reserved 1906 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 1907 ) 1908 1909 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 1910 local sudohdr=$(: 1911 )"$(ipv6_to_bytes $SIP):"$( : SIP 1912 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 1913 )"${len}:"$( : Upper-layer length 1914 )"00:3a:"$( : Zero and next-header 1915 ) 1916 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 1917 1918 payload_template_expand_checksum "$hbh$icmpv6" $checksum 1919} 1920 1921bail_on_lldpad() 1922{ 1923 local reason1="$1"; shift 1924 local reason2="$1"; shift 1925 1926 if systemctl is-active --quiet lldpad; then 1927 1928 cat >/dev/stderr <<-EOF 1929 WARNING: lldpad is running 1930 1931 lldpad will likely $reason1, and this test will 1932 $reason2. Both are not supported at the same time, 1933 one of them is arbitrarily going to overwrite the 1934 other. That will cause spurious failures (or, unlikely, 1935 passes) of this test. 1936 EOF 1937 1938 if [[ -z $ALLOW_LLDPAD ]]; then 1939 cat >/dev/stderr <<-EOF 1940 1941 If you want to run the test anyway, please set 1942 an environment variable ALLOW_LLDPAD to a 1943 non-empty string. 1944 EOF 1945 exit 1 1946 else 1947 return 1948 fi 1949 fi 1950} 1951