GNU Linux-libre 4.19.207-gnu1
[releases.git] / tools / testing / selftests / net / forwarding / lib.sh
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3
4 ##############################################################################
5 # Defines
6
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
19 relative_path="${BASH_SOURCE%/*}"
20 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
21         relative_path="."
22 fi
23
24 if [[ -f $relative_path/forwarding.config ]]; then
25         source "$relative_path/forwarding.config"
26 fi
27
28 ##############################################################################
29 # Sanity checks
30
31 check_tc_version()
32 {
33         tc -j &> /dev/null
34         if [[ $? -ne 0 ]]; then
35                 echo "SKIP: iproute2 too old; tc is missing JSON support"
36                 exit 1
37         fi
38 }
39
40 check_tc_shblock_support()
41 {
42         tc filter help 2>&1 | grep block &> /dev/null
43         if [[ $? -ne 0 ]]; then
44                 echo "SKIP: iproute2 too old; tc is missing shared block support"
45                 exit 1
46         fi
47 }
48
49 check_tc_chain_support()
50 {
51         tc help 2>&1|grep chain &> /dev/null
52         if [[ $? -ne 0 ]]; then
53                 echo "SKIP: iproute2 too old; tc is missing chain support"
54                 exit 1
55         fi
56 }
57
58 if [[ "$(id -u)" -ne 0 ]]; then
59         echo "SKIP: need root privileges"
60         exit 0
61 fi
62
63 if [[ "$CHECK_TC" = "yes" ]]; then
64         check_tc_version
65 fi
66
67 require_command()
68 {
69         local cmd=$1; shift
70
71         if [[ ! -x "$(command -v "$cmd")" ]]; then
72                 echo "SKIP: $cmd not installed"
73                 exit 1
74         fi
75 }
76
77 require_command jq
78 require_command $MZ
79
80 if [[ ! -v NUM_NETIFS ]]; then
81         echo "SKIP: importer does not define \"NUM_NETIFS\""
82         exit 1
83 fi
84
85 ##############################################################################
86 # Command line options handling
87
88 count=0
89
90 while [[ $# -gt 0 ]]; do
91         if [[ "$count" -eq "0" ]]; then
92                 unset NETIFS
93                 declare -A NETIFS
94         fi
95         count=$((count + 1))
96         NETIFS[p$count]="$1"
97         shift
98 done
99
100 ##############################################################################
101 # Network interfaces configuration
102
103 create_netif_veth()
104 {
105         local i
106
107         for i in $(eval echo {1..$NUM_NETIFS}); do
108                 local j=$((i+1))
109
110                 ip link show dev ${NETIFS[p$i]} &> /dev/null
111                 if [[ $? -ne 0 ]]; then
112                         ip link add ${NETIFS[p$i]} type veth \
113                                 peer name ${NETIFS[p$j]}
114                         if [[ $? -ne 0 ]]; then
115                                 echo "Failed to create netif"
116                                 exit 1
117                         fi
118                 fi
119                 i=$j
120         done
121 }
122
123 create_netif()
124 {
125         case "$NETIF_TYPE" in
126         veth) create_netif_veth
127               ;;
128         *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
129            exit 1
130            ;;
131         esac
132 }
133
134 if [[ "$NETIF_CREATE" = "yes" ]]; then
135         create_netif
136 fi
137
138 for i in $(eval echo {1..$NUM_NETIFS}); do
139         ip link show dev ${NETIFS[p$i]} &> /dev/null
140         if [[ $? -ne 0 ]]; then
141                 echo "SKIP: could not find all required interfaces"
142                 exit 1
143         fi
144 done
145
146 ##############################################################################
147 # Helpers
148
149 # Exit status to return at the end. Set in case one of the tests fails.
150 EXIT_STATUS=0
151 # Per-test return value. Clear at the beginning of each test.
152 RET=0
153
154 check_err()
155 {
156         local err=$1
157         local msg=$2
158
159         if [[ $RET -eq 0 && $err -ne 0 ]]; then
160                 RET=$err
161                 retmsg=$msg
162         fi
163 }
164
165 check_fail()
166 {
167         local err=$1
168         local msg=$2
169
170         if [[ $RET -eq 0 && $err -eq 0 ]]; then
171                 RET=1
172                 retmsg=$msg
173         fi
174 }
175
176 check_err_fail()
177 {
178         local should_fail=$1; shift
179         local err=$1; shift
180         local what=$1; shift
181
182         if ((should_fail)); then
183                 check_fail $err "$what succeeded, but should have failed"
184         else
185                 check_err $err "$what failed"
186         fi
187 }
188
189 log_test()
190 {
191         local test_name=$1
192         local opt_str=$2
193
194         if [[ $# -eq 2 ]]; then
195                 opt_str="($opt_str)"
196         fi
197
198         if [[ $RET -ne 0 ]]; then
199                 EXIT_STATUS=1
200                 printf "TEST: %-60s  [FAIL]\n" "$test_name $opt_str"
201                 if [[ ! -z "$retmsg" ]]; then
202                         printf "\t%s\n" "$retmsg"
203                 fi
204                 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
205                         echo "Hit enter to continue, 'q' to quit"
206                         read a
207                         [ "$a" = "q" ] && exit 1
208                 fi
209                 return 1
210         fi
211
212         printf "TEST: %-60s  [PASS]\n" "$test_name $opt_str"
213         return 0
214 }
215
216 log_info()
217 {
218         local msg=$1
219
220         echo "INFO: $msg"
221 }
222
223 setup_wait_dev()
224 {
225         local dev=$1; shift
226
227         while true; do
228                 ip link show dev $dev up \
229                         | grep 'state UP' &> /dev/null
230                 if [[ $? -ne 0 ]]; then
231                         sleep 1
232                 else
233                         break
234                 fi
235         done
236 }
237
238 setup_wait()
239 {
240         local num_netifs=${1:-$NUM_NETIFS}
241
242         for ((i = 1; i <= num_netifs; ++i)); do
243                 setup_wait_dev ${NETIFS[p$i]}
244         done
245
246         # Make sure links are ready.
247         sleep $WAIT_TIME
248 }
249
250 lldpad_app_wait_set()
251 {
252         local dev=$1; shift
253
254         while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
255                 echo "$dev: waiting for lldpad to push pending APP updates"
256                 sleep 5
257         done
258 }
259
260 lldpad_app_wait_del()
261 {
262         # Give lldpad a chance to push down the changes. If the device is downed
263         # too soon, the updates will be left pending. However, they will have
264         # been struck off the lldpad's DB already, so we won't be able to tell
265         # they are pending. Then on next test iteration this would cause
266         # weirdness as newly-added APP rules conflict with the old ones,
267         # sometimes getting stuck in an "unknown" state.
268         sleep 5
269 }
270
271 pre_cleanup()
272 {
273         if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
274                 echo "Pausing before cleanup, hit any key to continue"
275                 read
276         fi
277 }
278
279 vrf_prepare()
280 {
281         ip -4 rule add pref 32765 table local
282         ip -4 rule del pref 0
283         ip -6 rule add pref 32765 table local
284         ip -6 rule del pref 0
285 }
286
287 vrf_cleanup()
288 {
289         ip -6 rule add pref 0 table local
290         ip -6 rule del pref 32765
291         ip -4 rule add pref 0 table local
292         ip -4 rule del pref 32765
293 }
294
295 __last_tb_id=0
296 declare -A __TB_IDS
297
298 __vrf_td_id_assign()
299 {
300         local vrf_name=$1
301
302         __last_tb_id=$((__last_tb_id + 1))
303         __TB_IDS[$vrf_name]=$__last_tb_id
304         return $__last_tb_id
305 }
306
307 __vrf_td_id_lookup()
308 {
309         local vrf_name=$1
310
311         return ${__TB_IDS[$vrf_name]}
312 }
313
314 vrf_create()
315 {
316         local vrf_name=$1
317         local tb_id
318
319         __vrf_td_id_assign $vrf_name
320         tb_id=$?
321
322         ip link add dev $vrf_name type vrf table $tb_id
323         ip -4 route add table $tb_id unreachable default metric 4278198272
324         ip -6 route add table $tb_id unreachable default metric 4278198272
325 }
326
327 vrf_destroy()
328 {
329         local vrf_name=$1
330         local tb_id
331
332         __vrf_td_id_lookup $vrf_name
333         tb_id=$?
334
335         ip -6 route del table $tb_id unreachable default metric 4278198272
336         ip -4 route del table $tb_id unreachable default metric 4278198272
337         ip link del dev $vrf_name
338 }
339
340 __addr_add_del()
341 {
342         local if_name=$1
343         local add_del=$2
344         local array
345
346         shift
347         shift
348         array=("${@}")
349
350         for addrstr in "${array[@]}"; do
351                 ip address $add_del $addrstr dev $if_name
352         done
353 }
354
355 __simple_if_init()
356 {
357         local if_name=$1; shift
358         local vrf_name=$1; shift
359         local addrs=("${@}")
360
361         ip link set dev $if_name master $vrf_name
362         ip link set dev $if_name up
363
364         __addr_add_del $if_name add "${addrs[@]}"
365 }
366
367 __simple_if_fini()
368 {
369         local if_name=$1; shift
370         local addrs=("${@}")
371
372         __addr_add_del $if_name del "${addrs[@]}"
373
374         ip link set dev $if_name down
375         ip link set dev $if_name nomaster
376 }
377
378 simple_if_init()
379 {
380         local if_name=$1
381         local vrf_name
382         local array
383
384         shift
385         vrf_name=v$if_name
386         array=("${@}")
387
388         vrf_create $vrf_name
389         ip link set dev $vrf_name up
390         __simple_if_init $if_name $vrf_name "${array[@]}"
391 }
392
393 simple_if_fini()
394 {
395         local if_name=$1
396         local vrf_name
397         local array
398
399         shift
400         vrf_name=v$if_name
401         array=("${@}")
402
403         __simple_if_fini $if_name "${array[@]}"
404         vrf_destroy $vrf_name
405 }
406
407 tunnel_create()
408 {
409         local name=$1; shift
410         local type=$1; shift
411         local local=$1; shift
412         local remote=$1; shift
413
414         ip link add name $name type $type \
415            local $local remote $remote "$@"
416         ip link set dev $name up
417 }
418
419 tunnel_destroy()
420 {
421         local name=$1; shift
422
423         ip link del dev $name
424 }
425
426 vlan_create()
427 {
428         local if_name=$1; shift
429         local vid=$1; shift
430         local vrf=$1; shift
431         local ips=("${@}")
432         local name=$if_name.$vid
433
434         ip link add name $name link $if_name type vlan id $vid
435         if [ "$vrf" != "" ]; then
436                 ip link set dev $name master $vrf
437         fi
438         ip link set dev $name up
439         __addr_add_del $name add "${ips[@]}"
440 }
441
442 vlan_destroy()
443 {
444         local if_name=$1; shift
445         local vid=$1; shift
446         local name=$if_name.$vid
447
448         ip link del dev $name
449 }
450
451 team_create()
452 {
453         local if_name=$1; shift
454         local mode=$1; shift
455
456         require_command $TEAMD
457         $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
458         for slave in "$@"; do
459                 ip link set dev $slave down
460                 ip link set dev $slave master $if_name
461                 ip link set dev $slave up
462         done
463         ip link set dev $if_name up
464 }
465
466 team_destroy()
467 {
468         local if_name=$1; shift
469
470         $TEAMD -t $if_name -k
471 }
472
473 master_name_get()
474 {
475         local if_name=$1
476
477         ip -j link show dev $if_name | jq -r '.[]["master"]'
478 }
479
480 link_stats_tx_packets_get()
481 {
482        local if_name=$1
483
484        ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
485 }
486
487 tc_rule_stats_get()
488 {
489         local dev=$1; shift
490         local pref=$1; shift
491         local dir=$1; shift
492
493         tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
494             | jq '.[1].options.actions[].stats.packets'
495 }
496
497 mac_get()
498 {
499         local if_name=$1
500
501         ip -j link show dev $if_name | jq -r '.[]["address"]'
502 }
503
504 bridge_ageing_time_get()
505 {
506         local bridge=$1
507         local ageing_time
508
509         # Need to divide by 100 to convert to seconds.
510         ageing_time=$(ip -j -d link show dev $bridge \
511                       | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
512         echo $((ageing_time / 100))
513 }
514
515 declare -A SYSCTL_ORIG
516 sysctl_set()
517 {
518         local key=$1; shift
519         local value=$1; shift
520
521         SYSCTL_ORIG[$key]=$(sysctl -n $key)
522         sysctl -qw $key=$value
523 }
524
525 sysctl_restore()
526 {
527         local key=$1; shift
528
529         sysctl -qw $key=${SYSCTL_ORIG["$key"]}
530 }
531
532 forwarding_enable()
533 {
534         sysctl_set net.ipv4.conf.all.forwarding 1
535         sysctl_set net.ipv6.conf.all.forwarding 1
536 }
537
538 forwarding_restore()
539 {
540         sysctl_restore net.ipv6.conf.all.forwarding
541         sysctl_restore net.ipv4.conf.all.forwarding
542 }
543
544 tc_offload_check()
545 {
546         local num_netifs=${1:-$NUM_NETIFS}
547
548         for ((i = 1; i <= num_netifs; ++i)); do
549                 ethtool -k ${NETIFS[p$i]} \
550                         | grep "hw-tc-offload: on" &> /dev/null
551                 if [[ $? -ne 0 ]]; then
552                         return 1
553                 fi
554         done
555
556         return 0
557 }
558
559 trap_install()
560 {
561         local dev=$1; shift
562         local direction=$1; shift
563
564         # Some devices may not support or need in-hardware trapping of traffic
565         # (e.g. the veth pairs that this library creates for non-existent
566         # loopbacks). Use continue instead, so that there is a filter in there
567         # (some tests check counters), and so that other filters are still
568         # processed.
569         tc filter add dev $dev $direction pref 1 \
570                 flower skip_sw action trap 2>/dev/null \
571             || tc filter add dev $dev $direction pref 1 \
572                        flower action continue
573 }
574
575 trap_uninstall()
576 {
577         local dev=$1; shift
578         local direction=$1; shift
579
580         tc filter del dev $dev $direction pref 1 flower
581 }
582
583 slow_path_trap_install()
584 {
585         # For slow-path testing, we need to install a trap to get to
586         # slow path the packets that would otherwise be switched in HW.
587         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
588                 trap_install "$@"
589         fi
590 }
591
592 slow_path_trap_uninstall()
593 {
594         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
595                 trap_uninstall "$@"
596         fi
597 }
598
599 __icmp_capture_add_del()
600 {
601         local add_del=$1; shift
602         local pref=$1; shift
603         local vsuf=$1; shift
604         local tundev=$1; shift
605         local filter=$1; shift
606
607         tc filter $add_del dev "$tundev" ingress \
608            proto ip$vsuf pref $pref \
609            flower ip_proto icmp$vsuf $filter \
610            action pass
611 }
612
613 icmp_capture_install()
614 {
615         __icmp_capture_add_del add 100 "" "$@"
616 }
617
618 icmp_capture_uninstall()
619 {
620         __icmp_capture_add_del del 100 "" "$@"
621 }
622
623 icmp6_capture_install()
624 {
625         __icmp_capture_add_del add 100 v6 "$@"
626 }
627
628 icmp6_capture_uninstall()
629 {
630         __icmp_capture_add_del del 100 v6 "$@"
631 }
632
633 __vlan_capture_add_del()
634 {
635         local add_del=$1; shift
636         local pref=$1; shift
637         local dev=$1; shift
638         local filter=$1; shift
639
640         tc filter $add_del dev "$dev" ingress \
641            proto 802.1q pref $pref \
642            flower $filter \
643            action pass
644 }
645
646 vlan_capture_install()
647 {
648         __vlan_capture_add_del add 100 "$@"
649 }
650
651 vlan_capture_uninstall()
652 {
653         __vlan_capture_add_del del 100 "$@"
654 }
655
656 __dscp_capture_add_del()
657 {
658         local add_del=$1; shift
659         local dev=$1; shift
660         local base=$1; shift
661         local dscp;
662
663         for prio in {0..7}; do
664                 dscp=$((base + prio))
665                 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
666                                        "skip_hw ip_tos $((dscp << 2))"
667         done
668 }
669
670 dscp_capture_install()
671 {
672         local dev=$1; shift
673         local base=$1; shift
674
675         __dscp_capture_add_del add $dev $base
676 }
677
678 dscp_capture_uninstall()
679 {
680         local dev=$1; shift
681         local base=$1; shift
682
683         __dscp_capture_add_del del $dev $base
684 }
685
686 dscp_fetch_stats()
687 {
688         local dev=$1; shift
689         local base=$1; shift
690
691         for prio in {0..7}; do
692                 local dscp=$((base + prio))
693                 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
694                 echo "[$dscp]=$t "
695         done
696 }
697
698 matchall_sink_create()
699 {
700         local dev=$1; shift
701
702         tc qdisc add dev $dev clsact
703         tc filter add dev $dev ingress \
704            pref 10000 \
705            matchall \
706            action drop
707 }
708
709 tests_run()
710 {
711         local current_test
712
713         for current_test in ${TESTS:-$ALL_TESTS}; do
714                 $current_test
715         done
716 }
717
718 multipath_eval()
719 {
720         local desc="$1"
721         local weight_rp12=$2
722         local weight_rp13=$3
723         local packets_rp12=$4
724         local packets_rp13=$5
725         local weights_ratio packets_ratio diff
726
727         RET=0
728
729         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
730                 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
731                                 | bc -l)
732         else
733                 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
734                                 | bc -l)
735         fi
736
737         if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
738                check_err 1 "Packet difference is 0"
739                log_test "Multipath"
740                log_info "Expected ratio $weights_ratio"
741                return
742         fi
743
744         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
745                 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
746                                 | bc -l)
747         else
748                 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
749                                 | bc -l)
750         fi
751
752         diff=$(echo $weights_ratio - $packets_ratio | bc -l)
753         diff=${diff#-}
754
755         test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
756         check_err $? "Too large discrepancy between expected and measured ratios"
757         log_test "$desc"
758         log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
759 }
760
761 ##############################################################################
762 # Tests
763
764 ping_do()
765 {
766         local if_name=$1
767         local dip=$2
768         local vrf_name
769
770         vrf_name=$(master_name_get $if_name)
771         ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
772 }
773
774 ping_test()
775 {
776         RET=0
777
778         ping_do $1 $2
779         check_err $?
780         log_test "ping"
781 }
782
783 ping6_do()
784 {
785         local if_name=$1
786         local dip=$2
787         local vrf_name
788
789         vrf_name=$(master_name_get $if_name)
790         ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
791 }
792
793 ping6_test()
794 {
795         RET=0
796
797         ping6_do $1 $2
798         check_err $?
799         log_test "ping6"
800 }
801
802 learning_test()
803 {
804         local bridge=$1
805         local br_port1=$2       # Connected to `host1_if`.
806         local host1_if=$3
807         local host2_if=$4
808         local mac=de:ad:be:ef:13:37
809         local ageing_time
810
811         RET=0
812
813         bridge -j fdb show br $bridge brport $br_port1 \
814                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
815         check_fail $? "Found FDB record when should not"
816
817         # Disable unknown unicast flooding on `br_port1` to make sure
818         # packets are only forwarded through the port after a matching
819         # FDB entry was installed.
820         bridge link set dev $br_port1 flood off
821
822         tc qdisc add dev $host1_if ingress
823         tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
824                 flower dst_mac $mac action drop
825
826         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
827         sleep 1
828
829         tc -j -s filter show dev $host1_if ingress \
830                 | jq -e ".[] | select(.options.handle == 101) \
831                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
832         check_fail $? "Packet reached second host when should not"
833
834         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
835         sleep 1
836
837         bridge -j fdb show br $bridge brport $br_port1 \
838                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
839         check_err $? "Did not find FDB record when should"
840
841         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
842         sleep 1
843
844         tc -j -s filter show dev $host1_if ingress \
845                 | jq -e ".[] | select(.options.handle == 101) \
846                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
847         check_err $? "Packet did not reach second host when should"
848
849         # Wait for 10 seconds after the ageing time to make sure FDB
850         # record was aged-out.
851         ageing_time=$(bridge_ageing_time_get $bridge)
852         sleep $((ageing_time + 10))
853
854         bridge -j fdb show br $bridge brport $br_port1 \
855                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
856         check_fail $? "Found FDB record when should not"
857
858         bridge link set dev $br_port1 learning off
859
860         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
861         sleep 1
862
863         bridge -j fdb show br $bridge brport $br_port1 \
864                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
865         check_fail $? "Found FDB record when should not"
866
867         bridge link set dev $br_port1 learning on
868
869         tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
870         tc qdisc del dev $host1_if ingress
871
872         bridge link set dev $br_port1 flood on
873
874         log_test "FDB learning"
875 }
876
877 flood_test_do()
878 {
879         local should_flood=$1
880         local mac=$2
881         local ip=$3
882         local host1_if=$4
883         local host2_if=$5
884         local err=0
885
886         # Add an ACL on `host2_if` which will tell us whether the packet
887         # was flooded to it or not.
888         tc qdisc add dev $host2_if ingress
889         tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
890                 flower dst_mac $mac action drop
891
892         $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
893         sleep 1
894
895         tc -j -s filter show dev $host2_if ingress \
896                 | jq -e ".[] | select(.options.handle == 101) \
897                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
898         if [[ $? -ne 0 && $should_flood == "true" || \
899               $? -eq 0 && $should_flood == "false" ]]; then
900                 err=1
901         fi
902
903         tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
904         tc qdisc del dev $host2_if ingress
905
906         return $err
907 }
908
909 flood_unicast_test()
910 {
911         local br_port=$1
912         local host1_if=$2
913         local host2_if=$3
914         local mac=de:ad:be:ef:13:37
915         local ip=192.0.2.100
916
917         RET=0
918
919         bridge link set dev $br_port flood off
920
921         flood_test_do false $mac $ip $host1_if $host2_if
922         check_err $? "Packet flooded when should not"
923
924         bridge link set dev $br_port flood on
925
926         flood_test_do true $mac $ip $host1_if $host2_if
927         check_err $? "Packet was not flooded when should"
928
929         log_test "Unknown unicast flood"
930 }
931
932 flood_multicast_test()
933 {
934         local br_port=$1
935         local host1_if=$2
936         local host2_if=$3
937         local mac=01:00:5e:00:00:01
938         local ip=239.0.0.1
939
940         RET=0
941
942         bridge link set dev $br_port mcast_flood off
943
944         flood_test_do false $mac $ip $host1_if $host2_if
945         check_err $? "Packet flooded when should not"
946
947         bridge link set dev $br_port mcast_flood on
948
949         flood_test_do true $mac $ip $host1_if $host2_if
950         check_err $? "Packet was not flooded when should"
951
952         log_test "Unregistered multicast flood"
953 }
954
955 flood_test()
956 {
957         # `br_port` is connected to `host2_if`
958         local br_port=$1
959         local host1_if=$2
960         local host2_if=$3
961
962         flood_unicast_test $br_port $host1_if $host2_if
963         flood_multicast_test $br_port $host1_if $host2_if
964 }