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