WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / net / forwarding / lib.sh
blob98ea37d26c44a2a4de5cce53ee9503c693e36b58
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
4 ##############################################################################
5 # Defines
7 # Can be overridden by the configuration file.
8 PING=${PING:=ping}
9 PING6=${PING6:=ping6}
10 MZ=${MZ:=mausezahn}
11 ARPING=${ARPING:=arping}
12 TEAMD=${TEAMD:=teamd}
13 WAIT_TIME=${WAIT_TIME:=5}
14 PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
15 PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
16 NETIF_TYPE=${NETIF_TYPE:=veth}
17 NETIF_CREATE=${NETIF_CREATE:=yes}
18 MCD=${MCD:=smcrouted}
19 MC_CLI=${MC_CLI:=smcroutectl}
20 PING_TIMEOUT=${PING_TIMEOUT:=5}
21 WAIT_TIMEOUT=${WAIT_TIMEOUT:=20}
22 INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600}
24 relative_path="${BASH_SOURCE%/*}"
25 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
26 relative_path="."
29 if [[ -f $relative_path/forwarding.config ]]; then
30 source "$relative_path/forwarding.config"
33 ##############################################################################
34 # Sanity checks
36 check_tc_version()
38 tc -j &> /dev/null
39 if [[ $? -ne 0 ]]; then
40 echo "SKIP: iproute2 too old; tc is missing JSON support"
41 exit 1
45 check_tc_shblock_support()
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
54 check_tc_chain_support()
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
63 check_tc_action_hw_stats_support()
65 tc actions help 2>&1 | grep -q hw_stats
66 if [[ $? -ne 0 ]]; then
67 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
68 exit 1
72 if [[ "$(id -u)" -ne 0 ]]; then
73 echo "SKIP: need root privileges"
74 exit 0
77 if [[ "$CHECK_TC" = "yes" ]]; then
78 check_tc_version
81 require_command()
83 local cmd=$1; shift
85 if [[ ! -x "$(command -v "$cmd")" ]]; then
86 echo "SKIP: $cmd not installed"
87 exit 1
91 require_command jq
92 require_command $MZ
94 if [[ ! -v NUM_NETIFS ]]; then
95 echo "SKIP: importer does not define \"NUM_NETIFS\""
96 exit 1
99 ##############################################################################
100 # Command line options handling
102 count=0
104 while [[ $# -gt 0 ]]; do
105 if [[ "$count" -eq "0" ]]; then
106 unset NETIFS
107 declare -A NETIFS
109 count=$((count + 1))
110 NETIFS[p$count]="$1"
111 shift
112 done
114 ##############################################################################
115 # Network interfaces configuration
117 create_netif_veth()
119 local i
121 for ((i = 1; i <= NUM_NETIFS; ++i)); do
122 local j=$((i+1))
124 ip link show dev ${NETIFS[p$i]} &> /dev/null
125 if [[ $? -ne 0 ]]; then
126 ip link add ${NETIFS[p$i]} type veth \
127 peer name ${NETIFS[p$j]}
128 if [[ $? -ne 0 ]]; then
129 echo "Failed to create netif"
130 exit 1
133 i=$j
134 done
137 create_netif()
139 case "$NETIF_TYPE" in
140 veth) create_netif_veth
142 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
143 exit 1
145 esac
148 if [[ "$NETIF_CREATE" = "yes" ]]; then
149 create_netif
152 for ((i = 1; i <= NUM_NETIFS; ++i)); do
153 ip link show dev ${NETIFS[p$i]} &> /dev/null
154 if [[ $? -ne 0 ]]; then
155 echo "SKIP: could not find all required interfaces"
156 exit 1
158 done
160 ##############################################################################
161 # Helpers
163 # Exit status to return at the end. Set in case one of the tests fails.
164 EXIT_STATUS=0
165 # Per-test return value. Clear at the beginning of each test.
166 RET=0
168 check_err()
170 local err=$1
171 local msg=$2
173 if [[ $RET -eq 0 && $err -ne 0 ]]; then
174 RET=$err
175 retmsg=$msg
179 check_fail()
181 local err=$1
182 local msg=$2
184 if [[ $RET -eq 0 && $err -eq 0 ]]; then
185 RET=1
186 retmsg=$msg
190 check_err_fail()
192 local should_fail=$1; shift
193 local err=$1; shift
194 local what=$1; shift
196 if ((should_fail)); then
197 check_fail $err "$what succeeded, but should have failed"
198 else
199 check_err $err "$what failed"
203 log_test()
205 local test_name=$1
206 local opt_str=$2
208 if [[ $# -eq 2 ]]; then
209 opt_str="($opt_str)"
212 if [[ $RET -ne 0 ]]; then
213 EXIT_STATUS=1
214 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
215 if [[ ! -z "$retmsg" ]]; then
216 printf "\t%s\n" "$retmsg"
218 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
219 echo "Hit enter to continue, 'q' to quit"
220 read a
221 [ "$a" = "q" ] && exit 1
223 return 1
226 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str"
227 return 0
230 log_info()
232 local msg=$1
234 echo "INFO: $msg"
237 busywait()
239 local timeout=$1; shift
241 local start_time="$(date -u +%s%3N)"
242 while true
244 local out
245 out=$("$@")
246 local ret=$?
247 if ((!ret)); then
248 echo -n "$out"
249 return 0
252 local current_time="$(date -u +%s%3N)"
253 if ((current_time - start_time > timeout)); then
254 echo -n "$out"
255 return 1
257 done
260 not()
262 "$@"
263 [[ $? != 0 ]]
266 grep_bridge_fdb()
268 local addr=$1; shift
269 local word
270 local flag
272 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
273 word=$1; shift
274 if [ "$1" == "-v" ]; then
275 flag=$1; shift
279 $@ | grep $addr | grep $flag "$word"
282 wait_for_offload()
284 "$@" | grep -q offload
287 until_counter_is()
289 local expr=$1; shift
290 local current=$("$@")
292 echo $((current))
293 ((current $expr))
296 busywait_for_counter()
298 local timeout=$1; shift
299 local delta=$1; shift
301 local base=$("$@")
302 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
305 setup_wait_dev()
307 local dev=$1; shift
308 local wait_time=${1:-$WAIT_TIME}; shift
310 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
312 if (($?)); then
313 check_err 1
314 log_test setup_wait_dev ": Interface $dev does not come up."
315 exit 1
319 setup_wait_dev_with_timeout()
321 local dev=$1; shift
322 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
323 local wait_time=${1:-$WAIT_TIME}; shift
324 local i
326 for ((i = 1; i <= $max_iterations; ++i)); do
327 ip link show dev $dev up \
328 | grep 'state UP' &> /dev/null
329 if [[ $? -ne 0 ]]; then
330 sleep 1
331 else
332 sleep $wait_time
333 return 0
335 done
337 return 1
340 setup_wait()
342 local num_netifs=${1:-$NUM_NETIFS}
343 local i
345 for ((i = 1; i <= num_netifs; ++i)); do
346 setup_wait_dev ${NETIFS[p$i]} 0
347 done
349 # Make sure links are ready.
350 sleep $WAIT_TIME
353 cmd_jq()
355 local cmd=$1
356 local jq_exp=$2
357 local jq_opts=$3
358 local ret
359 local output
361 output="$($cmd)"
362 # it the command fails, return error right away
363 ret=$?
364 if [[ $ret -ne 0 ]]; then
365 return $ret
367 output=$(echo $output | jq -r $jq_opts "$jq_exp")
368 ret=$?
369 if [[ $ret -ne 0 ]]; then
370 return $ret
372 echo $output
373 # return success only in case of non-empty output
374 [ ! -z "$output" ]
377 lldpad_app_wait_set()
379 local dev=$1; shift
381 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
382 echo "$dev: waiting for lldpad to push pending APP updates"
383 sleep 5
384 done
387 lldpad_app_wait_del()
389 # Give lldpad a chance to push down the changes. If the device is downed
390 # too soon, the updates will be left pending. However, they will have
391 # been struck off the lldpad's DB already, so we won't be able to tell
392 # they are pending. Then on next test iteration this would cause
393 # weirdness as newly-added APP rules conflict with the old ones,
394 # sometimes getting stuck in an "unknown" state.
395 sleep 5
398 pre_cleanup()
400 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
401 echo "Pausing before cleanup, hit any key to continue"
402 read
406 vrf_prepare()
408 ip -4 rule add pref 32765 table local
409 ip -4 rule del pref 0
410 ip -6 rule add pref 32765 table local
411 ip -6 rule del pref 0
414 vrf_cleanup()
416 ip -6 rule add pref 0 table local
417 ip -6 rule del pref 32765
418 ip -4 rule add pref 0 table local
419 ip -4 rule del pref 32765
422 __last_tb_id=0
423 declare -A __TB_IDS
425 __vrf_td_id_assign()
427 local vrf_name=$1
429 __last_tb_id=$((__last_tb_id + 1))
430 __TB_IDS[$vrf_name]=$__last_tb_id
431 return $__last_tb_id
434 __vrf_td_id_lookup()
436 local vrf_name=$1
438 return ${__TB_IDS[$vrf_name]}
441 vrf_create()
443 local vrf_name=$1
444 local tb_id
446 __vrf_td_id_assign $vrf_name
447 tb_id=$?
449 ip link add dev $vrf_name type vrf table $tb_id
450 ip -4 route add table $tb_id unreachable default metric 4278198272
451 ip -6 route add table $tb_id unreachable default metric 4278198272
454 vrf_destroy()
456 local vrf_name=$1
457 local tb_id
459 __vrf_td_id_lookup $vrf_name
460 tb_id=$?
462 ip -6 route del table $tb_id unreachable default metric 4278198272
463 ip -4 route del table $tb_id unreachable default metric 4278198272
464 ip link del dev $vrf_name
467 __addr_add_del()
469 local if_name=$1
470 local add_del=$2
471 local array
473 shift
474 shift
475 array=("${@}")
477 for addrstr in "${array[@]}"; do
478 ip address $add_del $addrstr dev $if_name
479 done
482 __simple_if_init()
484 local if_name=$1; shift
485 local vrf_name=$1; shift
486 local addrs=("${@}")
488 ip link set dev $if_name master $vrf_name
489 ip link set dev $if_name up
491 __addr_add_del $if_name add "${addrs[@]}"
494 __simple_if_fini()
496 local if_name=$1; shift
497 local addrs=("${@}")
499 __addr_add_del $if_name del "${addrs[@]}"
501 ip link set dev $if_name down
502 ip link set dev $if_name nomaster
505 simple_if_init()
507 local if_name=$1
508 local vrf_name
509 local array
511 shift
512 vrf_name=v$if_name
513 array=("${@}")
515 vrf_create $vrf_name
516 ip link set dev $vrf_name up
517 __simple_if_init $if_name $vrf_name "${array[@]}"
520 simple_if_fini()
522 local if_name=$1
523 local vrf_name
524 local array
526 shift
527 vrf_name=v$if_name
528 array=("${@}")
530 __simple_if_fini $if_name "${array[@]}"
531 vrf_destroy $vrf_name
534 tunnel_create()
536 local name=$1; shift
537 local type=$1; shift
538 local local=$1; shift
539 local remote=$1; shift
541 ip link add name $name type $type \
542 local $local remote $remote "$@"
543 ip link set dev $name up
546 tunnel_destroy()
548 local name=$1; shift
550 ip link del dev $name
553 vlan_create()
555 local if_name=$1; shift
556 local vid=$1; shift
557 local vrf=$1; shift
558 local ips=("${@}")
559 local name=$if_name.$vid
561 ip link add name $name link $if_name type vlan id $vid
562 if [ "$vrf" != "" ]; then
563 ip link set dev $name master $vrf
565 ip link set dev $name up
566 __addr_add_del $name add "${ips[@]}"
569 vlan_destroy()
571 local if_name=$1; shift
572 local vid=$1; shift
573 local name=$if_name.$vid
575 ip link del dev $name
578 team_create()
580 local if_name=$1; shift
581 local mode=$1; shift
583 require_command $TEAMD
584 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
585 for slave in "$@"; do
586 ip link set dev $slave down
587 ip link set dev $slave master $if_name
588 ip link set dev $slave up
589 done
590 ip link set dev $if_name up
593 team_destroy()
595 local if_name=$1; shift
597 $TEAMD -t $if_name -k
600 master_name_get()
602 local if_name=$1
604 ip -j link show dev $if_name | jq -r '.[]["master"]'
607 link_stats_get()
609 local if_name=$1; shift
610 local dir=$1; shift
611 local stat=$1; shift
613 ip -j -s link show dev $if_name \
614 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
617 link_stats_tx_packets_get()
619 link_stats_get $1 tx packets
622 link_stats_rx_errors_get()
624 link_stats_get $1 rx errors
627 tc_rule_stats_get()
629 local dev=$1; shift
630 local pref=$1; shift
631 local dir=$1; shift
632 local selector=${1:-.packets}; shift
634 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
635 | jq ".[1].options.actions[].stats$selector"
638 tc_rule_handle_stats_get()
640 local id=$1; shift
641 local handle=$1; shift
642 local selector=${1:-.packets}; shift
644 tc -j -s filter show $id \
645 | jq ".[] | select(.options.handle == $handle) | \
646 .options.actions[0].stats$selector"
649 ethtool_stats_get()
651 local dev=$1; shift
652 local stat=$1; shift
654 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
657 qdisc_stats_get()
659 local dev=$1; shift
660 local handle=$1; shift
661 local selector=$1; shift
663 tc -j -s qdisc show dev "$dev" \
664 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
667 qdisc_parent_stats_get()
669 local dev=$1; shift
670 local parent=$1; shift
671 local selector=$1; shift
673 tc -j -s qdisc show dev "$dev" invisible \
674 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
677 humanize()
679 local speed=$1; shift
681 for unit in bps Kbps Mbps Gbps; do
682 if (($(echo "$speed < 1024" | bc))); then
683 break
686 speed=$(echo "scale=1; $speed / 1024" | bc)
687 done
689 echo "$speed${unit}"
692 rate()
694 local t0=$1; shift
695 local t1=$1; shift
696 local interval=$1; shift
698 echo $((8 * (t1 - t0) / interval))
701 mac_get()
703 local if_name=$1
705 ip -j link show dev $if_name | jq -r '.[]["address"]'
708 bridge_ageing_time_get()
710 local bridge=$1
711 local ageing_time
713 # Need to divide by 100 to convert to seconds.
714 ageing_time=$(ip -j -d link show dev $bridge \
715 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
716 echo $((ageing_time / 100))
719 declare -A SYSCTL_ORIG
720 sysctl_set()
722 local key=$1; shift
723 local value=$1; shift
725 SYSCTL_ORIG[$key]=$(sysctl -n $key)
726 sysctl -qw $key=$value
729 sysctl_restore()
731 local key=$1; shift
733 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
736 forwarding_enable()
738 sysctl_set net.ipv4.conf.all.forwarding 1
739 sysctl_set net.ipv6.conf.all.forwarding 1
742 forwarding_restore()
744 sysctl_restore net.ipv6.conf.all.forwarding
745 sysctl_restore net.ipv4.conf.all.forwarding
748 declare -A MTU_ORIG
749 mtu_set()
751 local dev=$1; shift
752 local mtu=$1; shift
754 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
755 ip link set dev $dev mtu $mtu
758 mtu_restore()
760 local dev=$1; shift
762 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
765 tc_offload_check()
767 local num_netifs=${1:-$NUM_NETIFS}
769 for ((i = 1; i <= num_netifs; ++i)); do
770 ethtool -k ${NETIFS[p$i]} \
771 | grep "hw-tc-offload: on" &> /dev/null
772 if [[ $? -ne 0 ]]; then
773 return 1
775 done
777 return 0
780 trap_install()
782 local dev=$1; shift
783 local direction=$1; shift
785 # Some devices may not support or need in-hardware trapping of traffic
786 # (e.g. the veth pairs that this library creates for non-existent
787 # loopbacks). Use continue instead, so that there is a filter in there
788 # (some tests check counters), and so that other filters are still
789 # processed.
790 tc filter add dev $dev $direction pref 1 \
791 flower skip_sw action trap 2>/dev/null \
792 || tc filter add dev $dev $direction pref 1 \
793 flower action continue
796 trap_uninstall()
798 local dev=$1; shift
799 local direction=$1; shift
801 tc filter del dev $dev $direction pref 1 flower
804 slow_path_trap_install()
806 # For slow-path testing, we need to install a trap to get to
807 # slow path the packets that would otherwise be switched in HW.
808 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
809 trap_install "$@"
813 slow_path_trap_uninstall()
815 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
816 trap_uninstall "$@"
820 __icmp_capture_add_del()
822 local add_del=$1; shift
823 local pref=$1; shift
824 local vsuf=$1; shift
825 local tundev=$1; shift
826 local filter=$1; shift
828 tc filter $add_del dev "$tundev" ingress \
829 proto ip$vsuf pref $pref \
830 flower ip_proto icmp$vsuf $filter \
831 action pass
834 icmp_capture_install()
836 __icmp_capture_add_del add 100 "" "$@"
839 icmp_capture_uninstall()
841 __icmp_capture_add_del del 100 "" "$@"
844 icmp6_capture_install()
846 __icmp_capture_add_del add 100 v6 "$@"
849 icmp6_capture_uninstall()
851 __icmp_capture_add_del del 100 v6 "$@"
854 __vlan_capture_add_del()
856 local add_del=$1; shift
857 local pref=$1; shift
858 local dev=$1; shift
859 local filter=$1; shift
861 tc filter $add_del dev "$dev" ingress \
862 proto 802.1q pref $pref \
863 flower $filter \
864 action pass
867 vlan_capture_install()
869 __vlan_capture_add_del add 100 "$@"
872 vlan_capture_uninstall()
874 __vlan_capture_add_del del 100 "$@"
877 __dscp_capture_add_del()
879 local add_del=$1; shift
880 local dev=$1; shift
881 local base=$1; shift
882 local dscp;
884 for prio in {0..7}; do
885 dscp=$((base + prio))
886 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
887 "skip_hw ip_tos $((dscp << 2))"
888 done
891 dscp_capture_install()
893 local dev=$1; shift
894 local base=$1; shift
896 __dscp_capture_add_del add $dev $base
899 dscp_capture_uninstall()
901 local dev=$1; shift
902 local base=$1; shift
904 __dscp_capture_add_del del $dev $base
907 dscp_fetch_stats()
909 local dev=$1; shift
910 local base=$1; shift
912 for prio in {0..7}; do
913 local dscp=$((base + prio))
914 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
915 echo "[$dscp]=$t "
916 done
919 matchall_sink_create()
921 local dev=$1; shift
923 tc qdisc add dev $dev clsact
924 tc filter add dev $dev ingress \
925 pref 10000 \
926 matchall \
927 action drop
930 tests_run()
932 local current_test
934 for current_test in ${TESTS:-$ALL_TESTS}; do
935 $current_test
936 done
939 multipath_eval()
941 local desc="$1"
942 local weight_rp12=$2
943 local weight_rp13=$3
944 local packets_rp12=$4
945 local packets_rp13=$5
946 local weights_ratio packets_ratio diff
948 RET=0
950 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
951 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
952 | bc -l)
953 else
954 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
955 | bc -l)
958 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
959 check_err 1 "Packet difference is 0"
960 log_test "Multipath"
961 log_info "Expected ratio $weights_ratio"
962 return
965 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
966 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
967 | bc -l)
968 else
969 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
970 | bc -l)
973 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
974 diff=${diff#-}
976 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
977 check_err $? "Too large discrepancy between expected and measured ratios"
978 log_test "$desc"
979 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
982 in_ns()
984 local name=$1; shift
986 ip netns exec $name bash <<-EOF
987 NUM_NETIFS=0
988 source lib.sh
989 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
993 ##############################################################################
994 # Tests
996 ping_do()
998 local if_name=$1
999 local dip=$2
1000 local args=$3
1001 local vrf_name
1003 vrf_name=$(master_name_get $if_name)
1004 ip vrf exec $vrf_name \
1005 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1008 ping_test()
1010 RET=0
1012 ping_do $1 $2
1013 check_err $?
1014 log_test "ping$3"
1017 ping6_do()
1019 local if_name=$1
1020 local dip=$2
1021 local args=$3
1022 local vrf_name
1024 vrf_name=$(master_name_get $if_name)
1025 ip vrf exec $vrf_name \
1026 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1029 ping6_test()
1031 RET=0
1033 ping6_do $1 $2
1034 check_err $?
1035 log_test "ping6$3"
1038 learning_test()
1040 local bridge=$1
1041 local br_port1=$2 # Connected to `host1_if`.
1042 local host1_if=$3
1043 local host2_if=$4
1044 local mac=de:ad:be:ef:13:37
1045 local ageing_time
1047 RET=0
1049 bridge -j fdb show br $bridge brport $br_port1 \
1050 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1051 check_fail $? "Found FDB record when should not"
1053 # Disable unknown unicast flooding on `br_port1` to make sure
1054 # packets are only forwarded through the port after a matching
1055 # FDB entry was installed.
1056 bridge link set dev $br_port1 flood off
1058 tc qdisc add dev $host1_if ingress
1059 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1060 flower dst_mac $mac action drop
1062 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1063 sleep 1
1065 tc -j -s filter show dev $host1_if ingress \
1066 | jq -e ".[] | select(.options.handle == 101) \
1067 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1068 check_fail $? "Packet reached second host when should not"
1070 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1071 sleep 1
1073 bridge -j fdb show br $bridge brport $br_port1 \
1074 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1075 check_err $? "Did not find FDB record when should"
1077 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1078 sleep 1
1080 tc -j -s filter show dev $host1_if ingress \
1081 | jq -e ".[] | select(.options.handle == 101) \
1082 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1083 check_err $? "Packet did not reach second host when should"
1085 # Wait for 10 seconds after the ageing time to make sure FDB
1086 # record was aged-out.
1087 ageing_time=$(bridge_ageing_time_get $bridge)
1088 sleep $((ageing_time + 10))
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"
1094 bridge link set dev $br_port1 learning off
1096 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1097 sleep 1
1099 bridge -j fdb show br $bridge brport $br_port1 \
1100 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1101 check_fail $? "Found FDB record when should not"
1103 bridge link set dev $br_port1 learning on
1105 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1106 tc qdisc del dev $host1_if ingress
1108 bridge link set dev $br_port1 flood on
1110 log_test "FDB learning"
1113 flood_test_do()
1115 local should_flood=$1
1116 local mac=$2
1117 local ip=$3
1118 local host1_if=$4
1119 local host2_if=$5
1120 local err=0
1122 # Add an ACL on `host2_if` which will tell us whether the packet
1123 # was flooded to it or not.
1124 tc qdisc add dev $host2_if ingress
1125 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1126 flower dst_mac $mac action drop
1128 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1129 sleep 1
1131 tc -j -s filter show dev $host2_if ingress \
1132 | jq -e ".[] | select(.options.handle == 101) \
1133 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1134 if [[ $? -ne 0 && $should_flood == "true" || \
1135 $? -eq 0 && $should_flood == "false" ]]; then
1136 err=1
1139 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1140 tc qdisc del dev $host2_if ingress
1142 return $err
1145 flood_unicast_test()
1147 local br_port=$1
1148 local host1_if=$2
1149 local host2_if=$3
1150 local mac=de:ad:be:ef:13:37
1151 local ip=192.0.2.100
1153 RET=0
1155 bridge link set dev $br_port flood off
1157 flood_test_do false $mac $ip $host1_if $host2_if
1158 check_err $? "Packet flooded when should not"
1160 bridge link set dev $br_port flood on
1162 flood_test_do true $mac $ip $host1_if $host2_if
1163 check_err $? "Packet was not flooded when should"
1165 log_test "Unknown unicast flood"
1168 flood_multicast_test()
1170 local br_port=$1
1171 local host1_if=$2
1172 local host2_if=$3
1173 local mac=01:00:5e:00:00:01
1174 local ip=239.0.0.1
1176 RET=0
1178 bridge link set dev $br_port mcast_flood off
1180 flood_test_do false $mac $ip $host1_if $host2_if
1181 check_err $? "Packet flooded when should not"
1183 bridge link set dev $br_port mcast_flood on
1185 flood_test_do true $mac $ip $host1_if $host2_if
1186 check_err $? "Packet was not flooded when should"
1188 log_test "Unregistered multicast flood"
1191 flood_test()
1193 # `br_port` is connected to `host2_if`
1194 local br_port=$1
1195 local host1_if=$2
1196 local host2_if=$3
1198 flood_unicast_test $br_port $host1_if $host2_if
1199 flood_multicast_test $br_port $host1_if $host2_if
1202 __start_traffic()
1204 local proto=$1; shift
1205 local h_in=$1; shift # Where the traffic egresses the host
1206 local sip=$1; shift
1207 local dip=$1; shift
1208 local dmac=$1; shift
1210 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
1211 -a own -b $dmac -t "$proto" -q "$@" &
1212 sleep 1
1215 start_traffic()
1217 __start_traffic udp "$@"
1220 start_tcp_traffic()
1222 __start_traffic tcp "$@"
1225 stop_traffic()
1227 # Suppress noise from killing mausezahn.
1228 { kill %% && wait %%; } 2>/dev/null
1231 tcpdump_start()
1233 local if_name=$1; shift
1234 local ns=$1; shift
1236 capfile=$(mktemp)
1237 capout=$(mktemp)
1239 if [ -z $ns ]; then
1240 ns_cmd=""
1241 else
1242 ns_cmd="ip netns exec ${ns}"
1245 if [ -z $SUDO_USER ] ; then
1246 capuser=""
1247 else
1248 capuser="-Z $SUDO_USER"
1251 $ns_cmd tcpdump -e -n -Q in -i $if_name \
1252 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
1253 cappid=$!
1255 sleep 1
1258 tcpdump_stop()
1260 $ns_cmd kill $cappid
1261 sleep 1
1264 tcpdump_cleanup()
1266 rm $capfile $capout
1269 tcpdump_show()
1271 tcpdump -e -n -r $capfile 2>&1
1274 # return 0 if the packet wasn't seen on host2_if or 1 if it was
1275 mcast_packet_test()
1277 local mac=$1
1278 local src_ip=$2
1279 local ip=$3
1280 local host1_if=$4
1281 local host2_if=$5
1282 local seen=0
1283 local tc_proto="ip"
1284 local mz_v6arg=""
1286 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1287 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1288 tc_proto="ipv6"
1289 mz_v6arg="-6"
1292 # Add an ACL on `host2_if` which will tell us whether the packet
1293 # was received by it or not.
1294 tc qdisc add dev $host2_if ingress
1295 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1296 flower ip_proto udp dst_mac $mac action drop
1298 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1299 sleep 1
1301 tc -j -s filter show dev $host2_if ingress \
1302 | jq -e ".[] | select(.options.handle == 101) \
1303 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1304 if [[ $? -eq 0 ]]; then
1305 seen=1
1308 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1309 tc qdisc del dev $host2_if ingress
1311 return $seen
1314 brmcast_check_sg_entries()
1316 local report=$1; shift
1317 local slist=("$@")
1318 local sarg=""
1320 for src in "${slist[@]}"; do
1321 sarg="${sarg} and .source_list[].address == \"$src\""
1322 done
1323 bridge -j -d -s mdb show dev br0 \
1324 | jq -e ".[].mdb[] | \
1325 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1326 check_err $? "Wrong *,G entry source list after $report report"
1328 for sgent in "${slist[@]}"; do
1329 bridge -j -d -s mdb show dev br0 \
1330 | jq -e ".[].mdb[] | \
1331 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1332 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1333 done
1336 brmcast_check_sg_fwding()
1338 local should_fwd=$1; shift
1339 local sources=("$@")
1341 for src in "${sources[@]}"; do
1342 local retval=0
1344 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1345 retval=$?
1346 if [ $should_fwd -eq 1 ]; then
1347 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1348 else
1349 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1351 done
1354 brmcast_check_sg_state()
1356 local is_blocked=$1; shift
1357 local sources=("$@")
1358 local should_fail=1
1360 if [ $is_blocked -eq 1 ]; then
1361 should_fail=0
1364 for src in "${sources[@]}"; do
1365 bridge -j -d -s mdb show dev br0 \
1366 | jq -e ".[].mdb[] | \
1367 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1368 .source_list[] |
1369 select(.address == \"$src\") |
1370 select(.timer == \"0.00\")" &>/dev/null
1371 check_err_fail $should_fail $? "Entry $src has zero timer"
1373 bridge -j -d -s mdb show dev br0 \
1374 | jq -e ".[].mdb[] | \
1375 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1376 .flags[] == \"blocked\")" &>/dev/null
1377 check_err_fail $should_fail $? "Entry $src has blocked flag"
1378 done