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 795 tc -j -s filter show $id \ 796 | jq ".[] | select(.options.handle == $handle) | \ 797 .options.actions[0].stats$selector" 798} 799 800ethtool_stats_get() 801{ 802 local dev=$1; shift 803 local stat=$1; shift 804 805 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 806} 807 808ethtool_std_stats_get() 809{ 810 local dev=$1; shift 811 local grp=$1; shift 812 local name=$1; shift 813 local src=$1; shift 814 815 ethtool --json -S $dev --groups $grp -- --src $src | \ 816 jq '.[]."'"$grp"'"."'$name'"' 817} 818 819qdisc_stats_get() 820{ 821 local dev=$1; shift 822 local handle=$1; shift 823 local selector=$1; shift 824 825 tc -j -s qdisc show dev "$dev" \ 826 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" 827} 828 829qdisc_parent_stats_get() 830{ 831 local dev=$1; shift 832 local parent=$1; shift 833 local selector=$1; shift 834 835 tc -j -s qdisc show dev "$dev" invisible \ 836 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" 837} 838 839ipv6_stats_get() 840{ 841 local dev=$1; shift 842 local stat=$1; shift 843 844 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 845} 846 847hw_stats_get() 848{ 849 local suite=$1; shift 850 local if_name=$1; shift 851 local dir=$1; shift 852 local stat=$1; shift 853 854 ip -j stats show dev $if_name group offload subgroup $suite | 855 jq ".[0].stats64.$dir.$stat" 856} 857 858humanize() 859{ 860 local speed=$1; shift 861 862 for unit in bps Kbps Mbps Gbps; do 863 if (($(echo "$speed < 1024" | bc))); then 864 break 865 fi 866 867 speed=$(echo "scale=1; $speed / 1024" | bc) 868 done 869 870 echo "$speed${unit}" 871} 872 873rate() 874{ 875 local t0=$1; shift 876 local t1=$1; shift 877 local interval=$1; shift 878 879 echo $((8 * (t1 - t0) / interval)) 880} 881 882packets_rate() 883{ 884 local t0=$1; shift 885 local t1=$1; shift 886 local interval=$1; shift 887 888 echo $(((t1 - t0) / interval)) 889} 890 891mac_get() 892{ 893 local if_name=$1 894 895 ip -j link show dev $if_name | jq -r '.[]["address"]' 896} 897 898ipv6_lladdr_get() 899{ 900 local if_name=$1 901 902 ip -j addr show dev $if_name | \ 903 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \ 904 head -1 905} 906 907bridge_ageing_time_get() 908{ 909 local bridge=$1 910 local ageing_time 911 912 # Need to divide by 100 to convert to seconds. 913 ageing_time=$(ip -j -d link show dev $bridge \ 914 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 915 echo $((ageing_time / 100)) 916} 917 918declare -A SYSCTL_ORIG 919sysctl_set() 920{ 921 local key=$1; shift 922 local value=$1; shift 923 924 SYSCTL_ORIG[$key]=$(sysctl -n $key) 925 sysctl -qw $key="$value" 926} 927 928sysctl_restore() 929{ 930 local key=$1; shift 931 932 sysctl -qw $key="${SYSCTL_ORIG[$key]}" 933} 934 935forwarding_enable() 936{ 937 sysctl_set net.ipv4.conf.all.forwarding 1 938 sysctl_set net.ipv6.conf.all.forwarding 1 939} 940 941forwarding_restore() 942{ 943 sysctl_restore net.ipv6.conf.all.forwarding 944 sysctl_restore net.ipv4.conf.all.forwarding 945} 946 947declare -A MTU_ORIG 948mtu_set() 949{ 950 local dev=$1; shift 951 local mtu=$1; shift 952 953 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 954 ip link set dev $dev mtu $mtu 955} 956 957mtu_restore() 958{ 959 local dev=$1; shift 960 961 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 962} 963 964tc_offload_check() 965{ 966 local num_netifs=${1:-$NUM_NETIFS} 967 968 for ((i = 1; i <= num_netifs; ++i)); do 969 ethtool -k ${NETIFS[p$i]} \ 970 | grep "hw-tc-offload: on" &> /dev/null 971 if [[ $? -ne 0 ]]; then 972 return 1 973 fi 974 done 975 976 return 0 977} 978 979trap_install() 980{ 981 local dev=$1; shift 982 local direction=$1; shift 983 984 # Some devices may not support or need in-hardware trapping of traffic 985 # (e.g. the veth pairs that this library creates for non-existent 986 # loopbacks). Use continue instead, so that there is a filter in there 987 # (some tests check counters), and so that other filters are still 988 # processed. 989 tc filter add dev $dev $direction pref 1 \ 990 flower skip_sw action trap 2>/dev/null \ 991 || tc filter add dev $dev $direction pref 1 \ 992 flower action continue 993} 994 995trap_uninstall() 996{ 997 local dev=$1; shift 998 local direction=$1; shift 999 1000 tc filter del dev $dev $direction pref 1 flower 1001} 1002 1003slow_path_trap_install() 1004{ 1005 # For slow-path testing, we need to install a trap to get to 1006 # slow path the packets that would otherwise be switched in HW. 1007 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1008 trap_install "$@" 1009 fi 1010} 1011 1012slow_path_trap_uninstall() 1013{ 1014 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 1015 trap_uninstall "$@" 1016 fi 1017} 1018 1019__icmp_capture_add_del() 1020{ 1021 local add_del=$1; shift 1022 local pref=$1; shift 1023 local vsuf=$1; shift 1024 local tundev=$1; shift 1025 local filter=$1; shift 1026 1027 tc filter $add_del dev "$tundev" ingress \ 1028 proto ip$vsuf pref $pref \ 1029 flower ip_proto icmp$vsuf $filter \ 1030 action pass 1031} 1032 1033icmp_capture_install() 1034{ 1035 __icmp_capture_add_del add 100 "" "$@" 1036} 1037 1038icmp_capture_uninstall() 1039{ 1040 __icmp_capture_add_del del 100 "" "$@" 1041} 1042 1043icmp6_capture_install() 1044{ 1045 __icmp_capture_add_del add 100 v6 "$@" 1046} 1047 1048icmp6_capture_uninstall() 1049{ 1050 __icmp_capture_add_del del 100 v6 "$@" 1051} 1052 1053__vlan_capture_add_del() 1054{ 1055 local add_del=$1; shift 1056 local pref=$1; shift 1057 local dev=$1; shift 1058 local filter=$1; shift 1059 1060 tc filter $add_del dev "$dev" ingress \ 1061 proto 802.1q pref $pref \ 1062 flower $filter \ 1063 action pass 1064} 1065 1066vlan_capture_install() 1067{ 1068 __vlan_capture_add_del add 100 "$@" 1069} 1070 1071vlan_capture_uninstall() 1072{ 1073 __vlan_capture_add_del del 100 "$@" 1074} 1075 1076__dscp_capture_add_del() 1077{ 1078 local add_del=$1; shift 1079 local dev=$1; shift 1080 local base=$1; shift 1081 local dscp; 1082 1083 for prio in {0..7}; do 1084 dscp=$((base + prio)) 1085 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 1086 "skip_hw ip_tos $((dscp << 2))" 1087 done 1088} 1089 1090dscp_capture_install() 1091{ 1092 local dev=$1; shift 1093 local base=$1; shift 1094 1095 __dscp_capture_add_del add $dev $base 1096} 1097 1098dscp_capture_uninstall() 1099{ 1100 local dev=$1; shift 1101 local base=$1; shift 1102 1103 __dscp_capture_add_del del $dev $base 1104} 1105 1106dscp_fetch_stats() 1107{ 1108 local dev=$1; shift 1109 local base=$1; shift 1110 1111 for prio in {0..7}; do 1112 local dscp=$((base + prio)) 1113 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 1114 echo "[$dscp]=$t " 1115 done 1116} 1117 1118matchall_sink_create() 1119{ 1120 local dev=$1; shift 1121 1122 tc qdisc add dev $dev clsact 1123 tc filter add dev $dev ingress \ 1124 pref 10000 \ 1125 matchall \ 1126 action drop 1127} 1128 1129tests_run() 1130{ 1131 local current_test 1132 1133 for current_test in ${TESTS:-$ALL_TESTS}; do 1134 $current_test 1135 done 1136} 1137 1138multipath_eval() 1139{ 1140 local desc="$1" 1141 local weight_rp12=$2 1142 local weight_rp13=$3 1143 local packets_rp12=$4 1144 local packets_rp13=$5 1145 local weights_ratio packets_ratio diff 1146 1147 RET=0 1148 1149 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1150 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 1151 | bc -l) 1152 else 1153 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 1154 | bc -l) 1155 fi 1156 1157 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 1158 check_err 1 "Packet difference is 0" 1159 log_test "Multipath" 1160 log_info "Expected ratio $weights_ratio" 1161 return 1162 fi 1163 1164 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 1165 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 1166 | bc -l) 1167 else 1168 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 1169 | bc -l) 1170 fi 1171 1172 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 1173 diff=${diff#-} 1174 1175 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 1176 check_err $? "Too large discrepancy between expected and measured ratios" 1177 log_test "$desc" 1178 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 1179} 1180 1181in_ns() 1182{ 1183 local name=$1; shift 1184 1185 ip netns exec $name bash <<-EOF 1186 NUM_NETIFS=0 1187 source lib.sh 1188 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 1189 EOF 1190} 1191 1192############################################################################## 1193# Tests 1194 1195ping_do() 1196{ 1197 local if_name=$1 1198 local dip=$2 1199 local args=$3 1200 local vrf_name 1201 1202 vrf_name=$(master_name_get $if_name) 1203 ip vrf exec $vrf_name \ 1204 $PING $args $dip -c $PING_COUNT -i 0.1 \ 1205 -w $PING_TIMEOUT &> /dev/null 1206} 1207 1208ping_test() 1209{ 1210 RET=0 1211 1212 ping_do $1 $2 1213 check_err $? 1214 log_test "ping$3" 1215} 1216 1217ping6_do() 1218{ 1219 local if_name=$1 1220 local dip=$2 1221 local args=$3 1222 local vrf_name 1223 1224 vrf_name=$(master_name_get $if_name) 1225 ip vrf exec $vrf_name \ 1226 $PING6 $args $dip -c $PING_COUNT -i 0.1 \ 1227 -w $PING_TIMEOUT &> /dev/null 1228} 1229 1230ping6_test() 1231{ 1232 RET=0 1233 1234 ping6_do $1 $2 1235 check_err $? 1236 log_test "ping6$3" 1237} 1238 1239learning_test() 1240{ 1241 local bridge=$1 1242 local br_port1=$2 # Connected to `host1_if`. 1243 local host1_if=$3 1244 local host2_if=$4 1245 local mac=de:ad:be:ef:13:37 1246 local ageing_time 1247 1248 RET=0 1249 1250 bridge -j fdb show br $bridge brport $br_port1 \ 1251 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1252 check_fail $? "Found FDB record when should not" 1253 1254 # Disable unknown unicast flooding on `br_port1` to make sure 1255 # packets are only forwarded through the port after a matching 1256 # FDB entry was installed. 1257 bridge link set dev $br_port1 flood off 1258 1259 ip link set $host1_if promisc on 1260 tc qdisc add dev $host1_if ingress 1261 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1262 flower dst_mac $mac action drop 1263 1264 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1265 sleep 1 1266 1267 tc -j -s filter show dev $host1_if ingress \ 1268 | jq -e ".[] | select(.options.handle == 101) \ 1269 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1270 check_fail $? "Packet reached first host when should not" 1271 1272 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1273 sleep 1 1274 1275 bridge -j fdb show br $bridge brport $br_port1 \ 1276 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1277 check_err $? "Did not find FDB record when should" 1278 1279 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1280 sleep 1 1281 1282 tc -j -s filter show dev $host1_if ingress \ 1283 | jq -e ".[] | select(.options.handle == 101) \ 1284 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1285 check_err $? "Packet did not reach second host when should" 1286 1287 # Wait for 10 seconds after the ageing time to make sure FDB 1288 # record was aged-out. 1289 ageing_time=$(bridge_ageing_time_get $bridge) 1290 sleep $((ageing_time + 10)) 1291 1292 bridge -j fdb show br $bridge brport $br_port1 \ 1293 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1294 check_fail $? "Found FDB record when should not" 1295 1296 bridge link set dev $br_port1 learning off 1297 1298 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1299 sleep 1 1300 1301 bridge -j fdb show br $bridge brport $br_port1 \ 1302 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1303 check_fail $? "Found FDB record when should not" 1304 1305 bridge link set dev $br_port1 learning on 1306 1307 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1308 tc qdisc del dev $host1_if ingress 1309 ip link set $host1_if promisc off 1310 1311 bridge link set dev $br_port1 flood on 1312 1313 log_test "FDB learning" 1314} 1315 1316flood_test_do() 1317{ 1318 local should_flood=$1 1319 local mac=$2 1320 local ip=$3 1321 local host1_if=$4 1322 local host2_if=$5 1323 local err=0 1324 1325 # Add an ACL on `host2_if` which will tell us whether the packet 1326 # was flooded to it or not. 1327 ip link set $host2_if promisc on 1328 tc qdisc add dev $host2_if ingress 1329 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1330 flower dst_mac $mac action drop 1331 1332 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1333 sleep 1 1334 1335 tc -j -s filter show dev $host2_if ingress \ 1336 | jq -e ".[] | select(.options.handle == 101) \ 1337 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1338 if [[ $? -ne 0 && $should_flood == "true" || \ 1339 $? -eq 0 && $should_flood == "false" ]]; then 1340 err=1 1341 fi 1342 1343 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1344 tc qdisc del dev $host2_if ingress 1345 ip link set $host2_if promisc off 1346 1347 return $err 1348} 1349 1350flood_unicast_test() 1351{ 1352 local br_port=$1 1353 local host1_if=$2 1354 local host2_if=$3 1355 local mac=de:ad:be:ef:13:37 1356 local ip=192.0.2.100 1357 1358 RET=0 1359 1360 bridge link set dev $br_port flood off 1361 1362 flood_test_do false $mac $ip $host1_if $host2_if 1363 check_err $? "Packet flooded when should not" 1364 1365 bridge link set dev $br_port flood on 1366 1367 flood_test_do true $mac $ip $host1_if $host2_if 1368 check_err $? "Packet was not flooded when should" 1369 1370 log_test "Unknown unicast flood" 1371} 1372 1373flood_multicast_test() 1374{ 1375 local br_port=$1 1376 local host1_if=$2 1377 local host2_if=$3 1378 local mac=01:00:5e:00:00:01 1379 local ip=239.0.0.1 1380 1381 RET=0 1382 1383 bridge link set dev $br_port mcast_flood off 1384 1385 flood_test_do false $mac $ip $host1_if $host2_if 1386 check_err $? "Packet flooded when should not" 1387 1388 bridge link set dev $br_port mcast_flood on 1389 1390 flood_test_do true $mac $ip $host1_if $host2_if 1391 check_err $? "Packet was not flooded when should" 1392 1393 log_test "Unregistered multicast flood" 1394} 1395 1396flood_test() 1397{ 1398 # `br_port` is connected to `host2_if` 1399 local br_port=$1 1400 local host1_if=$2 1401 local host2_if=$3 1402 1403 flood_unicast_test $br_port $host1_if $host2_if 1404 flood_multicast_test $br_port $host1_if $host2_if 1405} 1406 1407__start_traffic() 1408{ 1409 local pktsize=$1; shift 1410 local proto=$1; shift 1411 local h_in=$1; shift # Where the traffic egresses the host 1412 local sip=$1; shift 1413 local dip=$1; shift 1414 local dmac=$1; shift 1415 1416 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ 1417 -a own -b $dmac -t "$proto" -q "$@" & 1418 sleep 1 1419} 1420 1421start_traffic_pktsize() 1422{ 1423 local pktsize=$1; shift 1424 1425 __start_traffic $pktsize udp "$@" 1426} 1427 1428start_tcp_traffic_pktsize() 1429{ 1430 local pktsize=$1; shift 1431 1432 __start_traffic $pktsize tcp "$@" 1433} 1434 1435start_traffic() 1436{ 1437 start_traffic_pktsize 8000 "$@" 1438} 1439 1440start_tcp_traffic() 1441{ 1442 start_tcp_traffic_pktsize 8000 "$@" 1443} 1444 1445stop_traffic() 1446{ 1447 # Suppress noise from killing mausezahn. 1448 { kill %% && wait %%; } 2>/dev/null 1449} 1450 1451declare -A cappid 1452declare -A capfile 1453declare -A capout 1454 1455tcpdump_start() 1456{ 1457 local if_name=$1; shift 1458 local ns=$1; shift 1459 1460 capfile[$if_name]=$(mktemp) 1461 capout[$if_name]=$(mktemp) 1462 1463 if [ -z $ns ]; then 1464 ns_cmd="" 1465 else 1466 ns_cmd="ip netns exec ${ns}" 1467 fi 1468 1469 if [ -z $SUDO_USER ] ; then 1470 capuser="" 1471 else 1472 capuser="-Z $SUDO_USER" 1473 fi 1474 1475 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ 1476 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ 1477 > "${capout[$if_name]}" 2>&1 & 1478 cappid[$if_name]=$! 1479 1480 sleep 1 1481} 1482 1483tcpdump_stop() 1484{ 1485 local if_name=$1 1486 local pid=${cappid[$if_name]} 1487 1488 $ns_cmd kill "$pid" && wait "$pid" 1489 sleep 1 1490} 1491 1492tcpdump_cleanup() 1493{ 1494 local if_name=$1 1495 1496 rm ${capfile[$if_name]} ${capout[$if_name]} 1497} 1498 1499tcpdump_show() 1500{ 1501 local if_name=$1 1502 1503 tcpdump -e -n -r ${capfile[$if_name]} 2>&1 1504} 1505 1506# return 0 if the packet wasn't seen on host2_if or 1 if it was 1507mcast_packet_test() 1508{ 1509 local mac=$1 1510 local src_ip=$2 1511 local ip=$3 1512 local host1_if=$4 1513 local host2_if=$5 1514 local seen=0 1515 local tc_proto="ip" 1516 local mz_v6arg="" 1517 1518 # basic check to see if we were passed an IPv4 address, if not assume IPv6 1519 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 1520 tc_proto="ipv6" 1521 mz_v6arg="-6" 1522 fi 1523 1524 # Add an ACL on `host2_if` which will tell us whether the packet 1525 # was received by it or not. 1526 tc qdisc add dev $host2_if ingress 1527 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ 1528 flower ip_proto udp dst_mac $mac action drop 1529 1530 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q 1531 sleep 1 1532 1533 tc -j -s filter show dev $host2_if ingress \ 1534 | jq -e ".[] | select(.options.handle == 101) \ 1535 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1536 if [[ $? -eq 0 ]]; then 1537 seen=1 1538 fi 1539 1540 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower 1541 tc qdisc del dev $host2_if ingress 1542 1543 return $seen 1544} 1545 1546brmcast_check_sg_entries() 1547{ 1548 local report=$1; shift 1549 local slist=("$@") 1550 local sarg="" 1551 1552 for src in "${slist[@]}"; do 1553 sarg="${sarg} and .source_list[].address == \"$src\"" 1554 done 1555 bridge -j -d -s mdb show dev br0 \ 1556 | jq -e ".[].mdb[] | \ 1557 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null 1558 check_err $? "Wrong *,G entry source list after $report report" 1559 1560 for sgent in "${slist[@]}"; do 1561 bridge -j -d -s mdb show dev br0 \ 1562 | jq -e ".[].mdb[] | \ 1563 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null 1564 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" 1565 done 1566} 1567 1568brmcast_check_sg_fwding() 1569{ 1570 local should_fwd=$1; shift 1571 local sources=("$@") 1572 1573 for src in "${sources[@]}"; do 1574 local retval=0 1575 1576 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 1577 retval=$? 1578 if [ $should_fwd -eq 1 ]; then 1579 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" 1580 else 1581 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" 1582 fi 1583 done 1584} 1585 1586brmcast_check_sg_state() 1587{ 1588 local is_blocked=$1; shift 1589 local sources=("$@") 1590 local should_fail=1 1591 1592 if [ $is_blocked -eq 1 ]; then 1593 should_fail=0 1594 fi 1595 1596 for src in "${sources[@]}"; do 1597 bridge -j -d -s mdb show dev br0 \ 1598 | jq -e ".[].mdb[] | \ 1599 select(.grp == \"$TEST_GROUP\" and .source_list != null) | 1600 .source_list[] | 1601 select(.address == \"$src\") | 1602 select(.timer == \"0.00\")" &>/dev/null 1603 check_err_fail $should_fail $? "Entry $src has zero timer" 1604 1605 bridge -j -d -s mdb show dev br0 \ 1606 | jq -e ".[].mdb[] | \ 1607 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ 1608 .flags[] == \"blocked\")" &>/dev/null 1609 check_err_fail $should_fail $? "Entry $src has blocked flag" 1610 done 1611} 1612 1613mc_join() 1614{ 1615 local if_name=$1 1616 local group=$2 1617 local vrf_name=$(master_name_get $if_name) 1618 1619 # We don't care about actual reception, just about joining the 1620 # IP multicast group and adding the L2 address to the device's 1621 # MAC filtering table 1622 ip vrf exec $vrf_name \ 1623 mreceive -g $group -I $if_name > /dev/null 2>&1 & 1624 mreceive_pid=$! 1625 1626 sleep 1 1627} 1628 1629mc_leave() 1630{ 1631 kill "$mreceive_pid" && wait "$mreceive_pid" 1632} 1633 1634mc_send() 1635{ 1636 local if_name=$1 1637 local groups=$2 1638 local vrf_name=$(master_name_get $if_name) 1639 1640 ip vrf exec $vrf_name \ 1641 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 1642} 1643 1644start_ip_monitor() 1645{ 1646 local mtype=$1; shift 1647 local ip=${1-ip}; shift 1648 1649 # start the monitor in the background 1650 tmpfile=`mktemp /var/run/nexthoptestXXX` 1651 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` 1652 sleep 0.2 1653 echo "$mpid $tmpfile" 1654} 1655 1656stop_ip_monitor() 1657{ 1658 local mpid=$1; shift 1659 local tmpfile=$1; shift 1660 local el=$1; shift 1661 local what=$1; shift 1662 1663 sleep 0.2 1664 kill $mpid 1665 local lines=`grep '^\w' $tmpfile | wc -l` 1666 test $lines -eq $el 1667 check_err $? "$what: $lines lines of events, expected $el" 1668 rm -rf $tmpfile 1669} 1670 1671hw_stats_monitor_test() 1672{ 1673 local dev=$1; shift 1674 local type=$1; shift 1675 local make_suitable=$1; shift 1676 local make_unsuitable=$1; shift 1677 local ip=${1-ip}; shift 1678 1679 RET=0 1680 1681 # Expect a notification about enablement. 1682 local ipmout=$(start_ip_monitor stats "$ip") 1683 $ip stats set dev $dev ${type}_stats on 1684 stop_ip_monitor $ipmout 1 "${type}_stats enablement" 1685 1686 # Expect a notification about offload. 1687 local ipmout=$(start_ip_monitor stats "$ip") 1688 $make_suitable 1689 stop_ip_monitor $ipmout 1 "${type}_stats installation" 1690 1691 # Expect a notification about loss of offload. 1692 local ipmout=$(start_ip_monitor stats "$ip") 1693 $make_unsuitable 1694 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" 1695 1696 # Expect a notification about disablement 1697 local ipmout=$(start_ip_monitor stats "$ip") 1698 $ip stats set dev $dev ${type}_stats off 1699 stop_ip_monitor $ipmout 1 "${type}_stats disablement" 1700 1701 log_test "${type}_stats notifications" 1702} 1703 1704ipv4_to_bytes() 1705{ 1706 local IP=$1; shift 1707 1708 printf '%02x:' ${IP//./ } | 1709 sed 's/:$//' 1710} 1711 1712# Convert a given IPv6 address, `IP' such that the :: token, if present, is 1713# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal 1714# digits. An optional `BYTESEP' parameter can be given to further separate 1715# individual bytes of each 16-bit group. 1716expand_ipv6() 1717{ 1718 local IP=$1; shift 1719 local bytesep=$1; shift 1720 1721 local cvt_ip=${IP/::/_} 1722 local colons=${cvt_ip//[^:]/} 1723 local allcol=::::::: 1724 # IP where :: -> the appropriate number of colons: 1725 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}} 1726 1727 echo $allcol_ip | tr : '\n' | 1728 sed s/^/0000/ | 1729 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' | 1730 tr '\n' : | 1731 sed 's/:$//' 1732} 1733 1734ipv6_to_bytes() 1735{ 1736 local IP=$1; shift 1737 1738 expand_ipv6 "$IP" : 1739} 1740 1741u16_to_bytes() 1742{ 1743 local u16=$1; shift 1744 1745 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' 1746} 1747 1748# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), 1749# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, 1750# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) 1751# stands for 00:00. 1752payload_template_calc_checksum() 1753{ 1754 local payload=$1; shift 1755 1756 ( 1757 # Set input radix. 1758 echo "16i" 1759 # Push zero for the initial checksum. 1760 echo 0 1761 1762 # Pad the payload with a terminating 00: in case we get an odd 1763 # number of bytes. 1764 echo "${payload%:}:00:" | 1765 sed 's/CHECKSUM/00:00/g' | 1766 tr '[:lower:]' '[:upper:]' | 1767 # Add the word to the checksum. 1768 sed 's/\(..\):\(..\):/\1\2+\n/g' | 1769 # Strip the extra odd byte we pushed if left unconverted. 1770 sed 's/\(..\):$//' 1771 1772 echo "10000 ~ +" # Calculate and add carry. 1773 echo "FFFF r - p" # Bit-flip and print. 1774 ) | 1775 dc | 1776 tr '[:upper:]' '[:lower:]' 1777} 1778 1779payload_template_expand_checksum() 1780{ 1781 local payload=$1; shift 1782 local checksum=$1; shift 1783 1784 local ckbytes=$(u16_to_bytes $checksum) 1785 1786 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" 1787} 1788 1789payload_template_nbytes() 1790{ 1791 local payload=$1; shift 1792 1793 payload_template_expand_checksum "${payload%:}" 0 | 1794 sed 's/:/\n/g' | wc -l 1795} 1796 1797igmpv3_is_in_get() 1798{ 1799 local GRP=$1; shift 1800 local sources=("$@") 1801 1802 local igmpv3 1803 local nsources=$(u16_to_bytes ${#sources[@]}) 1804 1805 # IS_IN ( $sources ) 1806 igmpv3=$(: 1807 )"22:"$( : Type - Membership Report 1808 )"00:"$( : Reserved 1809 )"CHECKSUM:"$( : Checksum 1810 )"00:00:"$( : Reserved 1811 )"00:01:"$( : Number of Group Records 1812 )"01:"$( : Record Type - IS_IN 1813 )"00:"$( : Aux Data Len 1814 )"${nsources}:"$( : Number of Sources 1815 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address 1816 )"$(for src in "${sources[@]}"; do 1817 ipv4_to_bytes $src 1818 echo -n : 1819 done)"$( : Source Addresses 1820 ) 1821 local checksum=$(payload_template_calc_checksum "$igmpv3") 1822 1823 payload_template_expand_checksum "$igmpv3" $checksum 1824} 1825 1826igmpv2_leave_get() 1827{ 1828 local GRP=$1; shift 1829 1830 local payload=$(: 1831 )"17:"$( : Type - Leave Group 1832 )"00:"$( : Max Resp Time - not meaningful 1833 )"CHECKSUM:"$( : Checksum 1834 )"$(ipv4_to_bytes $GRP)"$( : Group Address 1835 ) 1836 local checksum=$(payload_template_calc_checksum "$payload") 1837 1838 payload_template_expand_checksum "$payload" $checksum 1839} 1840 1841mldv2_is_in_get() 1842{ 1843 local SIP=$1; shift 1844 local GRP=$1; shift 1845 local sources=("$@") 1846 1847 local hbh 1848 local icmpv6 1849 local nsources=$(u16_to_bytes ${#sources[@]}) 1850 1851 hbh=$(: 1852 )"3a:"$( : Next Header - ICMPv6 1853 )"00:"$( : Hdr Ext Len 1854 )"00:00:00:00:00:00:"$( : Options and Padding 1855 ) 1856 1857 icmpv6=$(: 1858 )"8f:"$( : Type - MLDv2 Report 1859 )"00:"$( : Code 1860 )"CHECKSUM:"$( : Checksum 1861 )"00:00:"$( : Reserved 1862 )"00:01:"$( : Number of Group Records 1863 )"01:"$( : Record Type - IS_IN 1864 )"00:"$( : Aux Data Len 1865 )"${nsources}:"$( : Number of Sources 1866 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 1867 )"$(for src in "${sources[@]}"; do 1868 ipv6_to_bytes $src 1869 echo -n : 1870 done)"$( : Source Addresses 1871 ) 1872 1873 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 1874 local sudohdr=$(: 1875 )"$(ipv6_to_bytes $SIP):"$( : SIP 1876 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 1877 )"${len}:"$( : Upper-layer length 1878 )"00:3a:"$( : Zero and next-header 1879 ) 1880 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 1881 1882 payload_template_expand_checksum "$hbh$icmpv6" $checksum 1883} 1884 1885mldv1_done_get() 1886{ 1887 local SIP=$1; shift 1888 local GRP=$1; shift 1889 1890 local hbh 1891 local icmpv6 1892 1893 hbh=$(: 1894 )"3a:"$( : Next Header - ICMPv6 1895 )"00:"$( : Hdr Ext Len 1896 )"00:00:00:00:00:00:"$( : Options and Padding 1897 ) 1898 1899 icmpv6=$(: 1900 )"84:"$( : Type - MLDv1 Done 1901 )"00:"$( : Code 1902 )"CHECKSUM:"$( : Checksum 1903 )"00:00:"$( : Max Resp Delay - not meaningful 1904 )"00:00:"$( : Reserved 1905 )"$(ipv6_to_bytes $GRP):"$( : Multicast address 1906 ) 1907 1908 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6)) 1909 local sudohdr=$(: 1910 )"$(ipv6_to_bytes $SIP):"$( : SIP 1911 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address 1912 )"${len}:"$( : Upper-layer length 1913 )"00:3a:"$( : Zero and next-header 1914 ) 1915 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6}) 1916 1917 payload_template_expand_checksum "$hbh$icmpv6" $checksum 1918} 1919 1920bail_on_lldpad() 1921{ 1922 local reason1="$1"; shift 1923 local reason2="$1"; shift 1924 1925 if systemctl is-active --quiet lldpad; then 1926 1927 cat >/dev/stderr <<-EOF 1928 WARNING: lldpad is running 1929 1930 lldpad will likely $reason1, and this test will 1931 $reason2. Both are not supported at the same time, 1932 one of them is arbitrarily going to overwrite the 1933 other. That will cause spurious failures (or, unlikely, 1934 passes) of this test. 1935 EOF 1936 1937 if [[ -z $ALLOW_LLDPAD ]]; then 1938 cat >/dev/stderr <<-EOF 1939 1940 If you want to run the test anyway, please set 1941 an environment variable ALLOW_LLDPAD to a 1942 non-empty string. 1943 EOF 1944 exit 1 1945 else 1946 return 1947 fi 1948 fi 1949} 1950