1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4############################################################################## 5# Defines 6 7# Can be overridden by the configuration file. 8PING=${PING:=ping} 9PING6=${PING6:=ping6} 10MZ=${MZ:=mausezahn} 11ARPING=${ARPING:=arping} 12TEAMD=${TEAMD:=teamd} 13WAIT_TIME=${WAIT_TIME:=5} 14PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} 15PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} 16NETIF_TYPE=${NETIF_TYPE:=veth} 17NETIF_CREATE=${NETIF_CREATE:=yes} 18MCD=${MCD:=smcrouted} 19MC_CLI=${MC_CLI:=smcroutectl} 20PING_TIMEOUT=${PING_TIMEOUT:=5} 21WAIT_TIMEOUT=${WAIT_TIMEOUT:=20} 22INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600} 23 24relative_path="${BASH_SOURCE%/*}" 25if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then 26 relative_path="." 27fi 28 29if [[ -f $relative_path/forwarding.config ]]; then 30 source "$relative_path/forwarding.config" 31fi 32 33############################################################################## 34# Sanity checks 35 36check_tc_version() 37{ 38 tc -j &> /dev/null 39 if [[ $? -ne 0 ]]; then 40 echo "SKIP: iproute2 too old; tc is missing JSON support" 41 exit 1 42 fi 43} 44 45check_tc_shblock_support() 46{ 47 tc filter help 2>&1 | grep block &> /dev/null 48 if [[ $? -ne 0 ]]; then 49 echo "SKIP: iproute2 too old; tc is missing shared block support" 50 exit 1 51 fi 52} 53 54check_tc_chain_support() 55{ 56 tc help 2>&1|grep chain &> /dev/null 57 if [[ $? -ne 0 ]]; then 58 echo "SKIP: iproute2 too old; tc is missing chain support" 59 exit 1 60 fi 61} 62 63if [[ "$(id -u)" -ne 0 ]]; then 64 echo "SKIP: need root privileges" 65 exit 0 66fi 67 68if [[ "$CHECK_TC" = "yes" ]]; then 69 check_tc_version 70fi 71 72require_command() 73{ 74 local cmd=$1; shift 75 76 if [[ ! -x "$(command -v "$cmd")" ]]; then 77 echo "SKIP: $cmd not installed" 78 exit 1 79 fi 80} 81 82require_command jq 83require_command $MZ 84 85if [[ ! -v NUM_NETIFS ]]; then 86 echo "SKIP: importer does not define \"NUM_NETIFS\"" 87 exit 1 88fi 89 90############################################################################## 91# Command line options handling 92 93count=0 94 95while [[ $# -gt 0 ]]; do 96 if [[ "$count" -eq "0" ]]; then 97 unset NETIFS 98 declare -A NETIFS 99 fi 100 count=$((count + 1)) 101 NETIFS[p$count]="$1" 102 shift 103done 104 105############################################################################## 106# Network interfaces configuration 107 108create_netif_veth() 109{ 110 local i 111 112 for ((i = 1; i <= NUM_NETIFS; ++i)); do 113 local j=$((i+1)) 114 115 ip link show dev ${NETIFS[p$i]} &> /dev/null 116 if [[ $? -ne 0 ]]; then 117 ip link add ${NETIFS[p$i]} type veth \ 118 peer name ${NETIFS[p$j]} 119 if [[ $? -ne 0 ]]; then 120 echo "Failed to create netif" 121 exit 1 122 fi 123 fi 124 i=$j 125 done 126} 127 128create_netif() 129{ 130 case "$NETIF_TYPE" in 131 veth) create_netif_veth 132 ;; 133 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" 134 exit 1 135 ;; 136 esac 137} 138 139if [[ "$NETIF_CREATE" = "yes" ]]; then 140 create_netif 141fi 142 143for ((i = 1; i <= NUM_NETIFS; ++i)); do 144 ip link show dev ${NETIFS[p$i]} &> /dev/null 145 if [[ $? -ne 0 ]]; then 146 echo "SKIP: could not find all required interfaces" 147 exit 1 148 fi 149done 150 151############################################################################## 152# Helpers 153 154# Exit status to return at the end. Set in case one of the tests fails. 155EXIT_STATUS=0 156# Per-test return value. Clear at the beginning of each test. 157RET=0 158 159check_err() 160{ 161 local err=$1 162 local msg=$2 163 164 if [[ $RET -eq 0 && $err -ne 0 ]]; then 165 RET=$err 166 retmsg=$msg 167 fi 168} 169 170check_fail() 171{ 172 local err=$1 173 local msg=$2 174 175 if [[ $RET -eq 0 && $err -eq 0 ]]; then 176 RET=1 177 retmsg=$msg 178 fi 179} 180 181check_err_fail() 182{ 183 local should_fail=$1; shift 184 local err=$1; shift 185 local what=$1; shift 186 187 if ((should_fail)); then 188 check_fail $err "$what succeeded, but should have failed" 189 else 190 check_err $err "$what failed" 191 fi 192} 193 194log_test() 195{ 196 local test_name=$1 197 local opt_str=$2 198 199 if [[ $# -eq 2 ]]; then 200 opt_str="($opt_str)" 201 fi 202 203 if [[ $RET -ne 0 ]]; then 204 EXIT_STATUS=1 205 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" 206 if [[ ! -z "$retmsg" ]]; then 207 printf "\t%s\n" "$retmsg" 208 fi 209 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then 210 echo "Hit enter to continue, 'q' to quit" 211 read a 212 [ "$a" = "q" ] && exit 1 213 fi 214 return 1 215 fi 216 217 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str" 218 return 0 219} 220 221log_info() 222{ 223 local msg=$1 224 225 echo "INFO: $msg" 226} 227 228busywait() 229{ 230 local timeout=$1; shift 231 232 local start_time="$(date -u +%s%3N)" 233 while true 234 do 235 local out 236 out=$("$@") 237 local ret=$? 238 if ((!ret)); then 239 echo -n "$out" 240 return 0 241 fi 242 243 local current_time="$(date -u +%s%3N)" 244 if ((current_time - start_time > timeout)); then 245 echo -n "$out" 246 return 1 247 fi 248 done 249} 250 251not() 252{ 253 "$@" 254 [[ $? != 0 ]] 255} 256 257grep_bridge_fdb() 258{ 259 local addr=$1; shift 260 local word 261 local flag 262 263 if [ "$1" == "self" ] || [ "$1" == "master" ]; then 264 word=$1; shift 265 if [ "$1" == "-v" ]; then 266 flag=$1; shift 267 fi 268 fi 269 270 $@ | grep $addr | grep $flag "$word" 271} 272 273wait_for_offload() 274{ 275 "$@" | grep -q offload 276} 277 278until_counter_is() 279{ 280 local expr=$1; shift 281 local current=$("$@") 282 283 echo $((current)) 284 ((current $expr)) 285} 286 287busywait_for_counter() 288{ 289 local timeout=$1; shift 290 local delta=$1; shift 291 292 local base=$("$@") 293 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" 294} 295 296setup_wait_dev() 297{ 298 local dev=$1; shift 299 local wait_time=${1:-$WAIT_TIME}; shift 300 301 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time 302 303 if (($?)); then 304 check_err 1 305 log_test setup_wait_dev ": Interface $dev does not come up." 306 exit 1 307 fi 308} 309 310setup_wait_dev_with_timeout() 311{ 312 local dev=$1; shift 313 local max_iterations=${1:-$WAIT_TIMEOUT}; shift 314 local wait_time=${1:-$WAIT_TIME}; shift 315 local i 316 317 for ((i = 1; i <= $max_iterations; ++i)); do 318 ip link show dev $dev up \ 319 | grep 'state UP' &> /dev/null 320 if [[ $? -ne 0 ]]; then 321 sleep 1 322 else 323 sleep $wait_time 324 return 0 325 fi 326 done 327 328 return 1 329} 330 331setup_wait() 332{ 333 local num_netifs=${1:-$NUM_NETIFS} 334 local i 335 336 for ((i = 1; i <= num_netifs; ++i)); do 337 setup_wait_dev ${NETIFS[p$i]} 0 338 done 339 340 # Make sure links are ready. 341 sleep $WAIT_TIME 342} 343 344cmd_jq() 345{ 346 local cmd=$1 347 local jq_exp=$2 348 local jq_opts=$3 349 local ret 350 local output 351 352 output="$($cmd)" 353 # it the command fails, return error right away 354 ret=$? 355 if [[ $ret -ne 0 ]]; then 356 return $ret 357 fi 358 output=$(echo $output | jq -r $jq_opts "$jq_exp") 359 ret=$? 360 if [[ $ret -ne 0 ]]; then 361 return $ret 362 fi 363 echo $output 364 # return success only in case of non-empty output 365 [ ! -z "$output" ] 366} 367 368lldpad_app_wait_set() 369{ 370 local dev=$1; shift 371 372 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do 373 echo "$dev: waiting for lldpad to push pending APP updates" 374 sleep 5 375 done 376} 377 378lldpad_app_wait_del() 379{ 380 # Give lldpad a chance to push down the changes. If the device is downed 381 # too soon, the updates will be left pending. However, they will have 382 # been struck off the lldpad's DB already, so we won't be able to tell 383 # they are pending. Then on next test iteration this would cause 384 # weirdness as newly-added APP rules conflict with the old ones, 385 # sometimes getting stuck in an "unknown" state. 386 sleep 5 387} 388 389pre_cleanup() 390{ 391 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then 392 echo "Pausing before cleanup, hit any key to continue" 393 read 394 fi 395} 396 397vrf_prepare() 398{ 399 ip -4 rule add pref 32765 table local 400 ip -4 rule del pref 0 401 ip -6 rule add pref 32765 table local 402 ip -6 rule del pref 0 403} 404 405vrf_cleanup() 406{ 407 ip -6 rule add pref 0 table local 408 ip -6 rule del pref 32765 409 ip -4 rule add pref 0 table local 410 ip -4 rule del pref 32765 411} 412 413__last_tb_id=0 414declare -A __TB_IDS 415 416__vrf_td_id_assign() 417{ 418 local vrf_name=$1 419 420 __last_tb_id=$((__last_tb_id + 1)) 421 __TB_IDS[$vrf_name]=$__last_tb_id 422 return $__last_tb_id 423} 424 425__vrf_td_id_lookup() 426{ 427 local vrf_name=$1 428 429 return ${__TB_IDS[$vrf_name]} 430} 431 432vrf_create() 433{ 434 local vrf_name=$1 435 local tb_id 436 437 __vrf_td_id_assign $vrf_name 438 tb_id=$? 439 440 ip link add dev $vrf_name type vrf table $tb_id 441 ip -4 route add table $tb_id unreachable default metric 4278198272 442 ip -6 route add table $tb_id unreachable default metric 4278198272 443} 444 445vrf_destroy() 446{ 447 local vrf_name=$1 448 local tb_id 449 450 __vrf_td_id_lookup $vrf_name 451 tb_id=$? 452 453 ip -6 route del table $tb_id unreachable default metric 4278198272 454 ip -4 route del table $tb_id unreachable default metric 4278198272 455 ip link del dev $vrf_name 456} 457 458__addr_add_del() 459{ 460 local if_name=$1 461 local add_del=$2 462 local array 463 464 shift 465 shift 466 array=("${@}") 467 468 for addrstr in "${array[@]}"; do 469 ip address $add_del $addrstr dev $if_name 470 done 471} 472 473__simple_if_init() 474{ 475 local if_name=$1; shift 476 local vrf_name=$1; shift 477 local addrs=("${@}") 478 479 ip link set dev $if_name master $vrf_name 480 ip link set dev $if_name up 481 482 __addr_add_del $if_name add "${addrs[@]}" 483} 484 485__simple_if_fini() 486{ 487 local if_name=$1; shift 488 local addrs=("${@}") 489 490 __addr_add_del $if_name del "${addrs[@]}" 491 492 ip link set dev $if_name down 493 ip link set dev $if_name nomaster 494} 495 496simple_if_init() 497{ 498 local if_name=$1 499 local vrf_name 500 local array 501 502 shift 503 vrf_name=v$if_name 504 array=("${@}") 505 506 vrf_create $vrf_name 507 ip link set dev $vrf_name up 508 __simple_if_init $if_name $vrf_name "${array[@]}" 509} 510 511simple_if_fini() 512{ 513 local if_name=$1 514 local vrf_name 515 local array 516 517 shift 518 vrf_name=v$if_name 519 array=("${@}") 520 521 __simple_if_fini $if_name "${array[@]}" 522 vrf_destroy $vrf_name 523} 524 525tunnel_create() 526{ 527 local name=$1; shift 528 local type=$1; shift 529 local local=$1; shift 530 local remote=$1; shift 531 532 ip link add name $name type $type \ 533 local $local remote $remote "$@" 534 ip link set dev $name up 535} 536 537tunnel_destroy() 538{ 539 local name=$1; shift 540 541 ip link del dev $name 542} 543 544vlan_create() 545{ 546 local if_name=$1; shift 547 local vid=$1; shift 548 local vrf=$1; shift 549 local ips=("${@}") 550 local name=$if_name.$vid 551 552 ip link add name $name link $if_name type vlan id $vid 553 if [ "$vrf" != "" ]; then 554 ip link set dev $name master $vrf 555 fi 556 ip link set dev $name up 557 __addr_add_del $name add "${ips[@]}" 558} 559 560vlan_destroy() 561{ 562 local if_name=$1; shift 563 local vid=$1; shift 564 local name=$if_name.$vid 565 566 ip link del dev $name 567} 568 569team_create() 570{ 571 local if_name=$1; shift 572 local mode=$1; shift 573 574 require_command $TEAMD 575 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' 576 for slave in "$@"; do 577 ip link set dev $slave down 578 ip link set dev $slave master $if_name 579 ip link set dev $slave up 580 done 581 ip link set dev $if_name up 582} 583 584team_destroy() 585{ 586 local if_name=$1; shift 587 588 $TEAMD -t $if_name -k 589} 590 591master_name_get() 592{ 593 local if_name=$1 594 595 ip -j link show dev $if_name | jq -r '.[]["master"]' 596} 597 598link_stats_get() 599{ 600 local if_name=$1; shift 601 local dir=$1; shift 602 local stat=$1; shift 603 604 ip -j -s link show dev $if_name \ 605 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' 606} 607 608link_stats_tx_packets_get() 609{ 610 link_stats_get $1 tx packets 611} 612 613link_stats_rx_errors_get() 614{ 615 link_stats_get $1 rx errors 616} 617 618tc_rule_stats_get() 619{ 620 local dev=$1; shift 621 local pref=$1; shift 622 local dir=$1; shift 623 local selector=${1:-.packets}; shift 624 625 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ 626 | jq ".[1].options.actions[].stats$selector" 627} 628 629tc_rule_handle_stats_get() 630{ 631 local id=$1; shift 632 local handle=$1; shift 633 local selector=${1:-.packets}; shift 634 635 tc -j -s filter show $id \ 636 | jq ".[] | select(.options.handle == $handle) | \ 637 .options.actions[0].stats$selector" 638} 639 640ethtool_stats_get() 641{ 642 local dev=$1; shift 643 local stat=$1; shift 644 645 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 646} 647 648qdisc_stats_get() 649{ 650 local dev=$1; shift 651 local handle=$1; shift 652 local selector=$1; shift 653 654 tc -j -s qdisc show dev "$dev" \ 655 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" 656} 657 658qdisc_parent_stats_get() 659{ 660 local dev=$1; shift 661 local parent=$1; shift 662 local selector=$1; shift 663 664 tc -j -s qdisc show dev "$dev" invisible \ 665 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" 666} 667 668humanize() 669{ 670 local speed=$1; shift 671 672 for unit in bps Kbps Mbps Gbps; do 673 if (($(echo "$speed < 1024" | bc))); then 674 break 675 fi 676 677 speed=$(echo "scale=1; $speed / 1024" | bc) 678 done 679 680 echo "$speed${unit}" 681} 682 683rate() 684{ 685 local t0=$1; shift 686 local t1=$1; shift 687 local interval=$1; shift 688 689 echo $((8 * (t1 - t0) / interval)) 690} 691 692mac_get() 693{ 694 local if_name=$1 695 696 ip -j link show dev $if_name | jq -r '.[]["address"]' 697} 698 699bridge_ageing_time_get() 700{ 701 local bridge=$1 702 local ageing_time 703 704 # Need to divide by 100 to convert to seconds. 705 ageing_time=$(ip -j -d link show dev $bridge \ 706 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') 707 echo $((ageing_time / 100)) 708} 709 710declare -A SYSCTL_ORIG 711sysctl_set() 712{ 713 local key=$1; shift 714 local value=$1; shift 715 716 SYSCTL_ORIG[$key]=$(sysctl -n $key) 717 sysctl -qw $key=$value 718} 719 720sysctl_restore() 721{ 722 local key=$1; shift 723 724 sysctl -qw $key=${SYSCTL_ORIG["$key"]} 725} 726 727forwarding_enable() 728{ 729 sysctl_set net.ipv4.conf.all.forwarding 1 730 sysctl_set net.ipv6.conf.all.forwarding 1 731} 732 733forwarding_restore() 734{ 735 sysctl_restore net.ipv6.conf.all.forwarding 736 sysctl_restore net.ipv4.conf.all.forwarding 737} 738 739declare -A MTU_ORIG 740mtu_set() 741{ 742 local dev=$1; shift 743 local mtu=$1; shift 744 745 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') 746 ip link set dev $dev mtu $mtu 747} 748 749mtu_restore() 750{ 751 local dev=$1; shift 752 753 ip link set dev $dev mtu ${MTU_ORIG["$dev"]} 754} 755 756tc_offload_check() 757{ 758 local num_netifs=${1:-$NUM_NETIFS} 759 760 for ((i = 1; i <= num_netifs; ++i)); do 761 ethtool -k ${NETIFS[p$i]} \ 762 | grep "hw-tc-offload: on" &> /dev/null 763 if [[ $? -ne 0 ]]; then 764 return 1 765 fi 766 done 767 768 return 0 769} 770 771trap_install() 772{ 773 local dev=$1; shift 774 local direction=$1; shift 775 776 # Some devices may not support or need in-hardware trapping of traffic 777 # (e.g. the veth pairs that this library creates for non-existent 778 # loopbacks). Use continue instead, so that there is a filter in there 779 # (some tests check counters), and so that other filters are still 780 # processed. 781 tc filter add dev $dev $direction pref 1 \ 782 flower skip_sw action trap 2>/dev/null \ 783 || tc filter add dev $dev $direction pref 1 \ 784 flower action continue 785} 786 787trap_uninstall() 788{ 789 local dev=$1; shift 790 local direction=$1; shift 791 792 tc filter del dev $dev $direction pref 1 flower 793} 794 795slow_path_trap_install() 796{ 797 # For slow-path testing, we need to install a trap to get to 798 # slow path the packets that would otherwise be switched in HW. 799 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 800 trap_install "$@" 801 fi 802} 803 804slow_path_trap_uninstall() 805{ 806 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then 807 trap_uninstall "$@" 808 fi 809} 810 811__icmp_capture_add_del() 812{ 813 local add_del=$1; shift 814 local pref=$1; shift 815 local vsuf=$1; shift 816 local tundev=$1; shift 817 local filter=$1; shift 818 819 tc filter $add_del dev "$tundev" ingress \ 820 proto ip$vsuf pref $pref \ 821 flower ip_proto icmp$vsuf $filter \ 822 action pass 823} 824 825icmp_capture_install() 826{ 827 __icmp_capture_add_del add 100 "" "$@" 828} 829 830icmp_capture_uninstall() 831{ 832 __icmp_capture_add_del del 100 "" "$@" 833} 834 835icmp6_capture_install() 836{ 837 __icmp_capture_add_del add 100 v6 "$@" 838} 839 840icmp6_capture_uninstall() 841{ 842 __icmp_capture_add_del del 100 v6 "$@" 843} 844 845__vlan_capture_add_del() 846{ 847 local add_del=$1; shift 848 local pref=$1; shift 849 local dev=$1; shift 850 local filter=$1; shift 851 852 tc filter $add_del dev "$dev" ingress \ 853 proto 802.1q pref $pref \ 854 flower $filter \ 855 action pass 856} 857 858vlan_capture_install() 859{ 860 __vlan_capture_add_del add 100 "$@" 861} 862 863vlan_capture_uninstall() 864{ 865 __vlan_capture_add_del del 100 "$@" 866} 867 868__dscp_capture_add_del() 869{ 870 local add_del=$1; shift 871 local dev=$1; shift 872 local base=$1; shift 873 local dscp; 874 875 for prio in {0..7}; do 876 dscp=$((base + prio)) 877 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ 878 "skip_hw ip_tos $((dscp << 2))" 879 done 880} 881 882dscp_capture_install() 883{ 884 local dev=$1; shift 885 local base=$1; shift 886 887 __dscp_capture_add_del add $dev $base 888} 889 890dscp_capture_uninstall() 891{ 892 local dev=$1; shift 893 local base=$1; shift 894 895 __dscp_capture_add_del del $dev $base 896} 897 898dscp_fetch_stats() 899{ 900 local dev=$1; shift 901 local base=$1; shift 902 903 for prio in {0..7}; do 904 local dscp=$((base + prio)) 905 local t=$(tc_rule_stats_get $dev $((dscp + 100))) 906 echo "[$dscp]=$t " 907 done 908} 909 910matchall_sink_create() 911{ 912 local dev=$1; shift 913 914 tc qdisc add dev $dev clsact 915 tc filter add dev $dev ingress \ 916 pref 10000 \ 917 matchall \ 918 action drop 919} 920 921tests_run() 922{ 923 local current_test 924 925 for current_test in ${TESTS:-$ALL_TESTS}; do 926 $current_test 927 done 928} 929 930multipath_eval() 931{ 932 local desc="$1" 933 local weight_rp12=$2 934 local weight_rp13=$3 935 local packets_rp12=$4 936 local packets_rp13=$5 937 local weights_ratio packets_ratio diff 938 939 RET=0 940 941 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 942 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ 943 | bc -l) 944 else 945 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ 946 | bc -l) 947 fi 948 949 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then 950 check_err 1 "Packet difference is 0" 951 log_test "Multipath" 952 log_info "Expected ratio $weights_ratio" 953 return 954 fi 955 956 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then 957 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ 958 | bc -l) 959 else 960 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ 961 | bc -l) 962 fi 963 964 diff=$(echo $weights_ratio - $packets_ratio | bc -l) 965 diff=${diff#-} 966 967 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 968 check_err $? "Too large discrepancy between expected and measured ratios" 969 log_test "$desc" 970 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" 971} 972 973in_ns() 974{ 975 local name=$1; shift 976 977 ip netns exec $name bash <<-EOF 978 NUM_NETIFS=0 979 source lib.sh 980 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) 981 EOF 982} 983 984############################################################################## 985# Tests 986 987ping_do() 988{ 989 local if_name=$1 990 local dip=$2 991 local args=$3 992 local vrf_name 993 994 vrf_name=$(master_name_get $if_name) 995 ip vrf exec $vrf_name \ 996 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 997} 998 999ping_test() 1000{ 1001 RET=0 1002 1003 ping_do $1 $2 1004 check_err $? 1005 log_test "ping$3" 1006} 1007 1008ping6_do() 1009{ 1010 local if_name=$1 1011 local dip=$2 1012 local args=$3 1013 local vrf_name 1014 1015 vrf_name=$(master_name_get $if_name) 1016 ip vrf exec $vrf_name \ 1017 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null 1018} 1019 1020ping6_test() 1021{ 1022 RET=0 1023 1024 ping6_do $1 $2 1025 check_err $? 1026 log_test "ping6$3" 1027} 1028 1029learning_test() 1030{ 1031 local bridge=$1 1032 local br_port1=$2 # Connected to `host1_if`. 1033 local host1_if=$3 1034 local host2_if=$4 1035 local mac=de:ad:be:ef:13:37 1036 local ageing_time 1037 1038 RET=0 1039 1040 bridge -j fdb show br $bridge brport $br_port1 \ 1041 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1042 check_fail $? "Found FDB record when should not" 1043 1044 # Disable unknown unicast flooding on `br_port1` to make sure 1045 # packets are only forwarded through the port after a matching 1046 # FDB entry was installed. 1047 bridge link set dev $br_port1 flood off 1048 1049 tc qdisc add dev $host1_if ingress 1050 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ 1051 flower dst_mac $mac action drop 1052 1053 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1054 sleep 1 1055 1056 tc -j -s filter show dev $host1_if ingress \ 1057 | jq -e ".[] | select(.options.handle == 101) \ 1058 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1059 check_fail $? "Packet reached second host when should not" 1060 1061 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1062 sleep 1 1063 1064 bridge -j fdb show br $bridge brport $br_port1 \ 1065 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1066 check_err $? "Did not find FDB record when should" 1067 1068 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q 1069 sleep 1 1070 1071 tc -j -s filter show dev $host1_if ingress \ 1072 | jq -e ".[] | select(.options.handle == 101) \ 1073 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1074 check_err $? "Packet did not reach second host when should" 1075 1076 # Wait for 10 seconds after the ageing time to make sure FDB 1077 # record was aged-out. 1078 ageing_time=$(bridge_ageing_time_get $bridge) 1079 sleep $((ageing_time + 10)) 1080 1081 bridge -j fdb show br $bridge brport $br_port1 \ 1082 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1083 check_fail $? "Found FDB record when should not" 1084 1085 bridge link set dev $br_port1 learning off 1086 1087 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q 1088 sleep 1 1089 1090 bridge -j fdb show br $bridge brport $br_port1 \ 1091 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null 1092 check_fail $? "Found FDB record when should not" 1093 1094 bridge link set dev $br_port1 learning on 1095 1096 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower 1097 tc qdisc del dev $host1_if ingress 1098 1099 bridge link set dev $br_port1 flood on 1100 1101 log_test "FDB learning" 1102} 1103 1104flood_test_do() 1105{ 1106 local should_flood=$1 1107 local mac=$2 1108 local ip=$3 1109 local host1_if=$4 1110 local host2_if=$5 1111 local err=0 1112 1113 # Add an ACL on `host2_if` which will tell us whether the packet 1114 # was flooded to it or not. 1115 tc qdisc add dev $host2_if ingress 1116 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ 1117 flower dst_mac $mac action drop 1118 1119 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q 1120 sleep 1 1121 1122 tc -j -s filter show dev $host2_if ingress \ 1123 | jq -e ".[] | select(.options.handle == 101) \ 1124 | select(.options.actions[0].stats.packets == 1)" &> /dev/null 1125 if [[ $? -ne 0 && $should_flood == "true" || \ 1126 $? -eq 0 && $should_flood == "false" ]]; then 1127 err=1 1128 fi 1129 1130 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower 1131 tc qdisc del dev $host2_if ingress 1132 1133 return $err 1134} 1135 1136flood_unicast_test() 1137{ 1138 local br_port=$1 1139 local host1_if=$2 1140 local host2_if=$3 1141 local mac=de:ad:be:ef:13:37 1142 local ip=192.0.2.100 1143 1144 RET=0 1145 1146 bridge link set dev $br_port flood off 1147 1148 flood_test_do false $mac $ip $host1_if $host2_if 1149 check_err $? "Packet flooded when should not" 1150 1151 bridge link set dev $br_port flood on 1152 1153 flood_test_do true $mac $ip $host1_if $host2_if 1154 check_err $? "Packet was not flooded when should" 1155 1156 log_test "Unknown unicast flood" 1157} 1158 1159flood_multicast_test() 1160{ 1161 local br_port=$1 1162 local host1_if=$2 1163 local host2_if=$3 1164 local mac=01:00:5e:00:00:01 1165 local ip=239.0.0.1 1166 1167 RET=0 1168 1169 bridge link set dev $br_port mcast_flood off 1170 1171 flood_test_do false $mac $ip $host1_if $host2_if 1172 check_err $? "Packet flooded when should not" 1173 1174 bridge link set dev $br_port mcast_flood on 1175 1176 flood_test_do true $mac $ip $host1_if $host2_if 1177 check_err $? "Packet was not flooded when should" 1178 1179 log_test "Unregistered multicast flood" 1180} 1181 1182flood_test() 1183{ 1184 # `br_port` is connected to `host2_if` 1185 local br_port=$1 1186 local host1_if=$2 1187 local host2_if=$3 1188 1189 flood_unicast_test $br_port $host1_if $host2_if 1190 flood_multicast_test $br_port $host1_if $host2_if 1191} 1192 1193__start_traffic() 1194{ 1195 local proto=$1; shift 1196 local h_in=$1; shift # Where the traffic egresses the host 1197 local sip=$1; shift 1198 local dip=$1; shift 1199 local dmac=$1; shift 1200 1201 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ 1202 -a own -b $dmac -t "$proto" -q "$@" & 1203 sleep 1 1204} 1205 1206start_traffic() 1207{ 1208 __start_traffic udp "$@" 1209} 1210 1211start_tcp_traffic() 1212{ 1213 __start_traffic tcp "$@" 1214} 1215 1216stop_traffic() 1217{ 1218 # Suppress noise from killing mausezahn. 1219 { kill %% && wait %%; } 2>/dev/null 1220} 1221