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