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