]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/testing/selftests/net/forwarding/lib.sh
selftests: forwarding: Have lldpad_app_wait_set() wait for unknown, too
[linux.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 ethtool_stats_get()
498 {
499         local dev=$1; shift
500         local stat=$1; shift
501
502         ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
503 }
504
505 mac_get()
506 {
507         local if_name=$1
508
509         ip -j link show dev $if_name | jq -r '.[]["address"]'
510 }
511
512 bridge_ageing_time_get()
513 {
514         local bridge=$1
515         local ageing_time
516
517         # Need to divide by 100 to convert to seconds.
518         ageing_time=$(ip -j -d link show dev $bridge \
519                       | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
520         echo $((ageing_time / 100))
521 }
522
523 declare -A SYSCTL_ORIG
524 sysctl_set()
525 {
526         local key=$1; shift
527         local value=$1; shift
528
529         SYSCTL_ORIG[$key]=$(sysctl -n $key)
530         sysctl -qw $key=$value
531 }
532
533 sysctl_restore()
534 {
535         local key=$1; shift
536
537         sysctl -qw $key=${SYSCTL_ORIG["$key"]}
538 }
539
540 forwarding_enable()
541 {
542         sysctl_set net.ipv4.conf.all.forwarding 1
543         sysctl_set net.ipv6.conf.all.forwarding 1
544 }
545
546 forwarding_restore()
547 {
548         sysctl_restore net.ipv6.conf.all.forwarding
549         sysctl_restore net.ipv4.conf.all.forwarding
550 }
551
552 declare -A MTU_ORIG
553 mtu_set()
554 {
555         local dev=$1; shift
556         local mtu=$1; shift
557
558         MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
559         ip link set dev $dev mtu $mtu
560 }
561
562 mtu_restore()
563 {
564         local dev=$1; shift
565
566         ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
567 }
568
569 tc_offload_check()
570 {
571         local num_netifs=${1:-$NUM_NETIFS}
572
573         for ((i = 1; i <= num_netifs; ++i)); do
574                 ethtool -k ${NETIFS[p$i]} \
575                         | grep "hw-tc-offload: on" &> /dev/null
576                 if [[ $? -ne 0 ]]; then
577                         return 1
578                 fi
579         done
580
581         return 0
582 }
583
584 trap_install()
585 {
586         local dev=$1; shift
587         local direction=$1; shift
588
589         # Some devices may not support or need in-hardware trapping of traffic
590         # (e.g. the veth pairs that this library creates for non-existent
591         # loopbacks). Use continue instead, so that there is a filter in there
592         # (some tests check counters), and so that other filters are still
593         # processed.
594         tc filter add dev $dev $direction pref 1 \
595                 flower skip_sw action trap 2>/dev/null \
596             || tc filter add dev $dev $direction pref 1 \
597                        flower action continue
598 }
599
600 trap_uninstall()
601 {
602         local dev=$1; shift
603         local direction=$1; shift
604
605         tc filter del dev $dev $direction pref 1 flower
606 }
607
608 slow_path_trap_install()
609 {
610         # For slow-path testing, we need to install a trap to get to
611         # slow path the packets that would otherwise be switched in HW.
612         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
613                 trap_install "$@"
614         fi
615 }
616
617 slow_path_trap_uninstall()
618 {
619         if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
620                 trap_uninstall "$@"
621         fi
622 }
623
624 __icmp_capture_add_del()
625 {
626         local add_del=$1; shift
627         local pref=$1; shift
628         local vsuf=$1; shift
629         local tundev=$1; shift
630         local filter=$1; shift
631
632         tc filter $add_del dev "$tundev" ingress \
633            proto ip$vsuf pref $pref \
634            flower ip_proto icmp$vsuf $filter \
635            action pass
636 }
637
638 icmp_capture_install()
639 {
640         __icmp_capture_add_del add 100 "" "$@"
641 }
642
643 icmp_capture_uninstall()
644 {
645         __icmp_capture_add_del del 100 "" "$@"
646 }
647
648 icmp6_capture_install()
649 {
650         __icmp_capture_add_del add 100 v6 "$@"
651 }
652
653 icmp6_capture_uninstall()
654 {
655         __icmp_capture_add_del del 100 v6 "$@"
656 }
657
658 __vlan_capture_add_del()
659 {
660         local add_del=$1; shift
661         local pref=$1; shift
662         local dev=$1; shift
663         local filter=$1; shift
664
665         tc filter $add_del dev "$dev" ingress \
666            proto 802.1q pref $pref \
667            flower $filter \
668            action pass
669 }
670
671 vlan_capture_install()
672 {
673         __vlan_capture_add_del add 100 "$@"
674 }
675
676 vlan_capture_uninstall()
677 {
678         __vlan_capture_add_del del 100 "$@"
679 }
680
681 __dscp_capture_add_del()
682 {
683         local add_del=$1; shift
684         local dev=$1; shift
685         local base=$1; shift
686         local dscp;
687
688         for prio in {0..7}; do
689                 dscp=$((base + prio))
690                 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
691                                        "skip_hw ip_tos $((dscp << 2))"
692         done
693 }
694
695 dscp_capture_install()
696 {
697         local dev=$1; shift
698         local base=$1; shift
699
700         __dscp_capture_add_del add $dev $base
701 }
702
703 dscp_capture_uninstall()
704 {
705         local dev=$1; shift
706         local base=$1; shift
707
708         __dscp_capture_add_del del $dev $base
709 }
710
711 dscp_fetch_stats()
712 {
713         local dev=$1; shift
714         local base=$1; shift
715
716         for prio in {0..7}; do
717                 local dscp=$((base + prio))
718                 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
719                 echo "[$dscp]=$t "
720         done
721 }
722
723 matchall_sink_create()
724 {
725         local dev=$1; shift
726
727         tc qdisc add dev $dev clsact
728         tc filter add dev $dev ingress \
729            pref 10000 \
730            matchall \
731            action drop
732 }
733
734 tests_run()
735 {
736         local current_test
737
738         for current_test in ${TESTS:-$ALL_TESTS}; do
739                 $current_test
740         done
741 }
742
743 multipath_eval()
744 {
745         local desc="$1"
746         local weight_rp12=$2
747         local weight_rp13=$3
748         local packets_rp12=$4
749         local packets_rp13=$5
750         local weights_ratio packets_ratio diff
751
752         RET=0
753
754         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
755                 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
756                                 | bc -l)
757         else
758                 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
759                                 | bc -l)
760         fi
761
762         if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
763                check_err 1 "Packet difference is 0"
764                log_test "Multipath"
765                log_info "Expected ratio $weights_ratio"
766                return
767         fi
768
769         if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
770                 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
771                                 | bc -l)
772         else
773                 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
774                                 | bc -l)
775         fi
776
777         diff=$(echo $weights_ratio - $packets_ratio | bc -l)
778         diff=${diff#-}
779
780         test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
781         check_err $? "Too large discrepancy between expected and measured ratios"
782         log_test "$desc"
783         log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
784 }
785
786 ##############################################################################
787 # Tests
788
789 ping_do()
790 {
791         local if_name=$1
792         local dip=$2
793         local vrf_name
794
795         vrf_name=$(master_name_get $if_name)
796         ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
797 }
798
799 ping_test()
800 {
801         RET=0
802
803         ping_do $1 $2
804         check_err $?
805         log_test "ping"
806 }
807
808 ping6_do()
809 {
810         local if_name=$1
811         local dip=$2
812         local vrf_name
813
814         vrf_name=$(master_name_get $if_name)
815         ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
816 }
817
818 ping6_test()
819 {
820         RET=0
821
822         ping6_do $1 $2
823         check_err $?
824         log_test "ping6"
825 }
826
827 learning_test()
828 {
829         local bridge=$1
830         local br_port1=$2       # Connected to `host1_if`.
831         local host1_if=$3
832         local host2_if=$4
833         local mac=de:ad:be:ef:13:37
834         local ageing_time
835
836         RET=0
837
838         bridge -j fdb show br $bridge brport $br_port1 \
839                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
840         check_fail $? "Found FDB record when should not"
841
842         # Disable unknown unicast flooding on `br_port1` to make sure
843         # packets are only forwarded through the port after a matching
844         # FDB entry was installed.
845         bridge link set dev $br_port1 flood off
846
847         tc qdisc add dev $host1_if ingress
848         tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
849                 flower dst_mac $mac action drop
850
851         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
852         sleep 1
853
854         tc -j -s filter show dev $host1_if ingress \
855                 | jq -e ".[] | select(.options.handle == 101) \
856                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
857         check_fail $? "Packet reached second host when should not"
858
859         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
860         sleep 1
861
862         bridge -j fdb show br $bridge brport $br_port1 \
863                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
864         check_err $? "Did not find FDB record when should"
865
866         $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
867         sleep 1
868
869         tc -j -s filter show dev $host1_if ingress \
870                 | jq -e ".[] | select(.options.handle == 101) \
871                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
872         check_err $? "Packet did not reach second host when should"
873
874         # Wait for 10 seconds after the ageing time to make sure FDB
875         # record was aged-out.
876         ageing_time=$(bridge_ageing_time_get $bridge)
877         sleep $((ageing_time + 10))
878
879         bridge -j fdb show br $bridge brport $br_port1 \
880                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
881         check_fail $? "Found FDB record when should not"
882
883         bridge link set dev $br_port1 learning off
884
885         $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
886         sleep 1
887
888         bridge -j fdb show br $bridge brport $br_port1 \
889                 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
890         check_fail $? "Found FDB record when should not"
891
892         bridge link set dev $br_port1 learning on
893
894         tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
895         tc qdisc del dev $host1_if ingress
896
897         bridge link set dev $br_port1 flood on
898
899         log_test "FDB learning"
900 }
901
902 flood_test_do()
903 {
904         local should_flood=$1
905         local mac=$2
906         local ip=$3
907         local host1_if=$4
908         local host2_if=$5
909         local err=0
910
911         # Add an ACL on `host2_if` which will tell us whether the packet
912         # was flooded to it or not.
913         tc qdisc add dev $host2_if ingress
914         tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
915                 flower dst_mac $mac action drop
916
917         $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
918         sleep 1
919
920         tc -j -s filter show dev $host2_if ingress \
921                 | jq -e ".[] | select(.options.handle == 101) \
922                 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
923         if [[ $? -ne 0 && $should_flood == "true" || \
924               $? -eq 0 && $should_flood == "false" ]]; then
925                 err=1
926         fi
927
928         tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
929         tc qdisc del dev $host2_if ingress
930
931         return $err
932 }
933
934 flood_unicast_test()
935 {
936         local br_port=$1
937         local host1_if=$2
938         local host2_if=$3
939         local mac=de:ad:be:ef:13:37
940         local ip=192.0.2.100
941
942         RET=0
943
944         bridge link set dev $br_port flood off
945
946         flood_test_do false $mac $ip $host1_if $host2_if
947         check_err $? "Packet flooded when should not"
948
949         bridge link set dev $br_port flood on
950
951         flood_test_do true $mac $ip $host1_if $host2_if
952         check_err $? "Packet was not flooded when should"
953
954         log_test "Unknown unicast flood"
955 }
956
957 flood_multicast_test()
958 {
959         local br_port=$1
960         local host1_if=$2
961         local host2_if=$3
962         local mac=01:00:5e:00:00:01
963         local ip=239.0.0.1
964
965         RET=0
966
967         bridge link set dev $br_port mcast_flood off
968
969         flood_test_do false $mac $ip $host1_if $host2_if
970         check_err $? "Packet flooded when should not"
971
972         bridge link set dev $br_port mcast_flood on
973
974         flood_test_do true $mac $ip $host1_if $host2_if
975         check_err $? "Packet was not flooded when should"
976
977         log_test "Unregistered multicast flood"
978 }
979
980 flood_test()
981 {
982         # `br_port` is connected to `host2_if`
983         local br_port=$1
984         local host1_if=$2
985         local host2_if=$3
986
987         flood_unicast_test $br_port $host1_if $host2_if
988         flood_multicast_test $br_port $host1_if $host2_if
989 }