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# Kselftest framework requirement - SKIP code is 4.
8ksft_skip=4
9
10# Can be overridden by the configuration file.
11PING=${PING:=ping}
12PING6=${PING6:=ping6}
13MZ=${MZ:=mausezahn}
14ARPING=${ARPING:=arping}
15TEAMD=${TEAMD:=teamd}
16WAIT_TIME=${WAIT_TIME:=5}
17PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
18PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no}
19NETIF_TYPE=${NETIF_TYPE:=veth}
20NETIF_CREATE=${NETIF_CREATE:=yes}
21MCD=${MCD:=smcrouted}
22MC_CLI=${MC_CLI:=smcroutectl}
23PING_COUNT=${PING_COUNT:=10}
24PING_TIMEOUT=${PING_TIMEOUT:=5}
25WAIT_TIMEOUT=${WAIT_TIMEOUT:=20}
26INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600}
27REQUIRE_JQ=${REQUIRE_JQ:=yes}
28REQUIRE_MZ=${REQUIRE_MZ:=yes}
29
30relative_path="${BASH_SOURCE%/*}"
31if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
32 relative_path="."
33fi
34
35if [[ -f $relative_path/forwarding.config ]]; then
36 source "$relative_path/forwarding.config"
37fi
38
39##############################################################################
40# Sanity checks
41
42check_tc_version()
43{
44 tc -j &> /dev/null
45 if [[ $? -ne 0 ]]; then
46 echo "SKIP: iproute2 too old; tc is missing JSON support"
47 exit $ksft_skip
48 fi
49}
50
51# Old versions of tc don't understand "mpls_uc"
52check_tc_mpls_support()
53{
54 local dev=$1; shift
55
56 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
57 matchall action pipe &> /dev/null
58 if [[ $? -ne 0 ]]; then
59 echo "SKIP: iproute2 too old; tc is missing MPLS support"
60 return $ksft_skip
61 fi
62 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
63 matchall
64}
65
66# Old versions of tc produce invalid json output for mpls lse statistics
67check_tc_mpls_lse_stats()
68{
69 local dev=$1; shift
70 local ret;
71
72 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
73 flower mpls lse depth 2 \
74 action continue &> /dev/null
75
76 if [[ $? -ne 0 ]]; then
77 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
78 return $ksft_skip
79 fi
80
81 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
82 ret=$?
83 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
84 flower
85
86 if [[ $ret -ne 0 ]]; then
87 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
88 return $ksft_skip
89 fi
90}
91
92check_tc_shblock_support()
93{
94 tc filter help 2>&1 | grep block &> /dev/null
95 if [[ $? -ne 0 ]]; then
96 echo "SKIP: iproute2 too old; tc is missing shared block support"
97 exit $ksft_skip
98 fi
99}
100
101check_tc_chain_support()
102{
103 tc help 2>&1|grep chain &> /dev/null
104 if [[ $? -ne 0 ]]; then
105 echo "SKIP: iproute2 too old; tc is missing chain support"
106 exit $ksft_skip
107 fi
108}
109
110check_tc_action_hw_stats_support()
111{
112 tc actions help 2>&1 | grep -q hw_stats
113 if [[ $? -ne 0 ]]; then
114 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
115 exit $ksft_skip
116 fi
117}
118
119check_ethtool_lanes_support()
120{
121 ethtool --help 2>&1| grep lanes &> /dev/null
122 if [[ $? -ne 0 ]]; then
123 echo "SKIP: ethtool too old; it is missing lanes support"
124 exit $ksft_skip
125 fi
126}
127
128if [[ "$(id -u)" -ne 0 ]]; then
129 echo "SKIP: need root privileges"
130 exit $ksft_skip
131fi
132
133if [[ "$CHECK_TC" = "yes" ]]; then
134 check_tc_version
135fi
136
137require_command()
138{
139 local cmd=$1; shift
140
141 if [[ ! -x "$(command -v "$cmd")" ]]; then
142 echo "SKIP: $cmd not installed"
143 exit $ksft_skip
144 fi
145}
146
147if [[ "$REQUIRE_JQ" = "yes" ]]; then
148 require_command jq
149fi
150if [[ "$REQUIRE_MZ" = "yes" ]]; then
151 require_command $MZ
152fi
153
154if [[ ! -v NUM_NETIFS ]]; then
155 echo "SKIP: importer does not define \"NUM_NETIFS\""
156 exit $ksft_skip
157fi
158
159##############################################################################
160# Command line options handling
161
162count=0
163
164while [[ $# -gt 0 ]]; do
165 if [[ "$count" -eq "0" ]]; then
166 unset NETIFS
167 declare -A NETIFS
168 fi
169 count=$((count + 1))
170 NETIFS[p$count]="$1"
171 shift
172done
173
174##############################################################################
175# Network interfaces configuration
176
177create_netif_veth()
178{
179 local i
180
181 for ((i = 1; i <= NUM_NETIFS; ++i)); do
182 local j=$((i+1))
183
184 ip link show dev ${NETIFS[p$i]} &> /dev/null
185 if [[ $? -ne 0 ]]; then
186 ip link add ${NETIFS[p$i]} type veth \
187 peer name ${NETIFS[p$j]}
188 if [[ $? -ne 0 ]]; then
189 echo "Failed to create netif"
190 exit 1
191 fi
192 fi
193 i=$j
194 done
195}
196
197create_netif()
198{
199 case "$NETIF_TYPE" in
200 veth) create_netif_veth
201 ;;
202 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
203 exit 1
204 ;;
205 esac
206}
207
208if [[ "$NETIF_CREATE" = "yes" ]]; then
209 create_netif
210fi
211
212for ((i = 1; i <= NUM_NETIFS; ++i)); do
213 ip link show dev ${NETIFS[p$i]} &> /dev/null
214 if [[ $? -ne 0 ]]; then
215 echo "SKIP: could not find all required interfaces"
216 exit $ksft_skip
217 fi
218done
219
220##############################################################################
221# Helpers
222
223# Exit status to return at the end. Set in case one of the tests fails.
224EXIT_STATUS=0
225# Per-test return value. Clear at the beginning of each test.
226RET=0
227
228check_err()
229{
230 local err=$1
231 local msg=$2
232
233 if [[ $RET -eq 0 && $err -ne 0 ]]; then
234 RET=$err
235 retmsg=$msg
236 fi
237}
238
239check_fail()
240{
241 local err=$1
242 local msg=$2
243
244 if [[ $RET -eq 0 && $err -eq 0 ]]; then
245 RET=1
246 retmsg=$msg
247 fi
248}
249
250check_err_fail()
251{
252 local should_fail=$1; shift
253 local err=$1; shift
254 local what=$1; shift
255
256 if ((should_fail)); then
257 check_fail $err "$what succeeded, but should have failed"
258 else
259 check_err $err "$what failed"
260 fi
261}
262
263log_test()
264{
265 local test_name=$1
266 local opt_str=$2
267
268 if [[ $# -eq 2 ]]; then
269 opt_str="($opt_str)"
270 fi
271
272 if [[ $RET -ne 0 ]]; then
273 EXIT_STATUS=1
274 printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str"
275 if [[ ! -z "$retmsg" ]]; then
276 printf "\t%s\n" "$retmsg"
277 fi
278 if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
279 echo "Hit enter to continue, 'q' to quit"
280 read a
281 [ "$a" = "q" ] && exit 1
282 fi
283 return 1
284 fi
285
286 printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str"
287 return 0
288}
289
290log_test_skip()
291{
292 local test_name=$1
293 local opt_str=$2
294
295 printf "TEST: %-60s [SKIP]\n" "$test_name $opt_str"
296 return 0
297}
298
299log_info()
300{
301 local msg=$1
302
303 echo "INFO: $msg"
304}
305
306busywait()
307{
308 local timeout=$1; shift
309
310 local start_time="$(date -u +%s%3N)"
311 while true
312 do
313 local out
314 out=$("$@")
315 local ret=$?
316 if ((!ret)); then
317 echo -n "$out"
318 return 0
319 fi
320
321 local current_time="$(date -u +%s%3N)"
322 if ((current_time - start_time > timeout)); then
323 echo -n "$out"
324 return 1
325 fi
326 done
327}
328
329not()
330{
331 "$@"
332 [[ $? != 0 ]]
333}
334
335get_max()
336{
337 local arr=("$@")
338
339 max=${arr[0]}
340 for cur in ${arr[@]}; do
341 if [[ $cur -gt $max ]]; then
342 max=$cur
343 fi
344 done
345
346 echo $max
347}
348
349grep_bridge_fdb()
350{
351 local addr=$1; shift
352 local word
353 local flag
354
355 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
356 word=$1; shift
357 if [ "$1" == "-v" ]; then
358 flag=$1; shift
359 fi
360 fi
361
362 $@ | grep $addr | grep $flag "$word"
363}
364
365wait_for_port_up()
366{
367 "$@" | grep -q "Link detected: yes"
368}
369
370wait_for_offload()
371{
372 "$@" | grep -q offload
373}
374
375wait_for_trap()
376{
377 "$@" | grep -q trap
378}
379
380until_counter_is()
381{
382 local expr=$1; shift
383 local current=$("$@")
384
385 echo $((current))
386 ((current $expr))
387}
388
389busywait_for_counter()
390{
391 local timeout=$1; shift
392 local delta=$1; shift
393
394 local base=$("$@")
395 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@"
396}
397
398setup_wait_dev()
399{
400 local dev=$1; shift
401 local wait_time=${1:-$WAIT_TIME}; shift
402
403 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
404
405 if (($?)); then
406 check_err 1
407 log_test setup_wait_dev ": Interface $dev does not come up."
408 exit 1
409 fi
410}
411
412setup_wait_dev_with_timeout()
413{
414 local dev=$1; shift
415 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
416 local wait_time=${1:-$WAIT_TIME}; shift
417 local i
418
419 for ((i = 1; i <= $max_iterations; ++i)); do
420 ip link show dev $dev up \
421 | grep 'state UP' &> /dev/null
422 if [[ $? -ne 0 ]]; then
423 sleep 1
424 else
425 sleep $wait_time
426 return 0
427 fi
428 done
429
430 return 1
431}
432
433setup_wait()
434{
435 local num_netifs=${1:-$NUM_NETIFS}
436 local i
437
438 for ((i = 1; i <= num_netifs; ++i)); do
439 setup_wait_dev ${NETIFS[p$i]} 0
440 done
441
442 # Make sure links are ready.
443 sleep $WAIT_TIME
444}
445
446cmd_jq()
447{
448 local cmd=$1
449 local jq_exp=$2
450 local jq_opts=$3
451 local ret
452 local output
453
454 output="$($cmd)"
455 # it the command fails, return error right away
456 ret=$?
457 if [[ $ret -ne 0 ]]; then
458 return $ret
459 fi
460 output=$(echo $output | jq -r $jq_opts "$jq_exp")
461 ret=$?
462 if [[ $ret -ne 0 ]]; then
463 return $ret
464 fi
465 echo $output
466 # return success only in case of non-empty output
467 [ ! -z "$output" ]
468}
469
470lldpad_app_wait_set()
471{
472 local dev=$1; shift
473
474 while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do
475 echo "$dev: waiting for lldpad to push pending APP updates"
476 sleep 5
477 done
478}
479
480lldpad_app_wait_del()
481{
482 # Give lldpad a chance to push down the changes. If the device is downed
483 # too soon, the updates will be left pending. However, they will have
484 # been struck off the lldpad's DB already, so we won't be able to tell
485 # they are pending. Then on next test iteration this would cause
486 # weirdness as newly-added APP rules conflict with the old ones,
487 # sometimes getting stuck in an "unknown" state.
488 sleep 5
489}
490
491pre_cleanup()
492{
493 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
494 echo "Pausing before cleanup, hit any key to continue"
495 read
496 fi
497}
498
499vrf_prepare()
500{
501 ip -4 rule add pref 32765 table local
502 ip -4 rule del pref 0
503 ip -6 rule add pref 32765 table local
504 ip -6 rule del pref 0
505}
506
507vrf_cleanup()
508{
509 ip -6 rule add pref 0 table local
510 ip -6 rule del pref 32765
511 ip -4 rule add pref 0 table local
512 ip -4 rule del pref 32765
513}
514
515__last_tb_id=0
516declare -A __TB_IDS
517
518__vrf_td_id_assign()
519{
520 local vrf_name=$1
521
522 __last_tb_id=$((__last_tb_id + 1))
523 __TB_IDS[$vrf_name]=$__last_tb_id
524 return $__last_tb_id
525}
526
527__vrf_td_id_lookup()
528{
529 local vrf_name=$1
530
531 return ${__TB_IDS[$vrf_name]}
532}
533
534vrf_create()
535{
536 local vrf_name=$1
537 local tb_id
538
539 __vrf_td_id_assign $vrf_name
540 tb_id=$?
541
542 ip link add dev $vrf_name type vrf table $tb_id
543 ip -4 route add table $tb_id unreachable default metric 4278198272
544 ip -6 route add table $tb_id unreachable default metric 4278198272
545}
546
547vrf_destroy()
548{
549 local vrf_name=$1
550 local tb_id
551
552 __vrf_td_id_lookup $vrf_name
553 tb_id=$?
554
555 ip -6 route del table $tb_id unreachable default metric 4278198272
556 ip -4 route del table $tb_id unreachable default metric 4278198272
557 ip link del dev $vrf_name
558}
559
560__addr_add_del()
561{
562 local if_name=$1
563 local add_del=$2
564 local array
565
566 shift
567 shift
568 array=("${@}")
569
570 for addrstr in "${array[@]}"; do
571 ip address $add_del $addrstr dev $if_name
572 done
573}
574
575__simple_if_init()
576{
577 local if_name=$1; shift
578 local vrf_name=$1; shift
579 local addrs=("${@}")
580
581 ip link set dev $if_name master $vrf_name
582 ip link set dev $if_name up
583
584 __addr_add_del $if_name add "${addrs[@]}"
585}
586
587__simple_if_fini()
588{
589 local if_name=$1; shift
590 local addrs=("${@}")
591
592 __addr_add_del $if_name del "${addrs[@]}"
593
594 ip link set dev $if_name down
595 ip link set dev $if_name nomaster
596}
597
598simple_if_init()
599{
600 local if_name=$1
601 local vrf_name
602 local array
603
604 shift
605 vrf_name=v$if_name
606 array=("${@}")
607
608 vrf_create $vrf_name
609 ip link set dev $vrf_name up
610 __simple_if_init $if_name $vrf_name "${array[@]}"
611}
612
613simple_if_fini()
614{
615 local if_name=$1
616 local vrf_name
617 local array
618
619 shift
620 vrf_name=v$if_name
621 array=("${@}")
622
623 __simple_if_fini $if_name "${array[@]}"
624 vrf_destroy $vrf_name
625}
626
627tunnel_create()
628{
629 local name=$1; shift
630 local type=$1; shift
631 local local=$1; shift
632 local remote=$1; shift
633
634 ip link add name $name type $type \
635 local $local remote $remote "$@"
636 ip link set dev $name up
637}
638
639tunnel_destroy()
640{
641 local name=$1; shift
642
643 ip link del dev $name
644}
645
646vlan_create()
647{
648 local if_name=$1; shift
649 local vid=$1; shift
650 local vrf=$1; shift
651 local ips=("${@}")
652 local name=$if_name.$vid
653
654 ip link add name $name link $if_name type vlan id $vid
655 if [ "$vrf" != "" ]; then
656 ip link set dev $name master $vrf
657 fi
658 ip link set dev $name up
659 __addr_add_del $name add "${ips[@]}"
660}
661
662vlan_destroy()
663{
664 local if_name=$1; shift
665 local vid=$1; shift
666 local name=$if_name.$vid
667
668 ip link del dev $name
669}
670
671team_create()
672{
673 local if_name=$1; shift
674 local mode=$1; shift
675
676 require_command $TEAMD
677 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
678 for slave in "$@"; do
679 ip link set dev $slave down
680 ip link set dev $slave master $if_name
681 ip link set dev $slave up
682 done
683 ip link set dev $if_name up
684}
685
686team_destroy()
687{
688 local if_name=$1; shift
689
690 $TEAMD -t $if_name -k
691}
692
693master_name_get()
694{
695 local if_name=$1
696
697 ip -j link show dev $if_name | jq -r '.[]["master"]'
698}
699
700link_stats_get()
701{
702 local if_name=$1; shift
703 local dir=$1; shift
704 local stat=$1; shift
705
706 ip -j -s link show dev $if_name \
707 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
708}
709
710link_stats_tx_packets_get()
711{
712 link_stats_get $1 tx packets
713}
714
715link_stats_rx_errors_get()
716{
717 link_stats_get $1 rx errors
718}
719
720tc_rule_stats_get()
721{
722 local dev=$1; shift
723 local pref=$1; shift
724 local dir=$1; shift
725 local selector=${1:-.packets}; shift
726
727 tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \
728 | jq ".[1].options.actions[].stats$selector"
729}
730
731tc_rule_handle_stats_get()
732{
733 local id=$1; shift
734 local handle=$1; shift
735 local selector=${1:-.packets}; shift
736
737 tc -j -s filter show $id \
738 | jq ".[] | select(.options.handle == $handle) | \
739 .options.actions[0].stats$selector"
740}
741
742ethtool_stats_get()
743{
744 local dev=$1; shift
745 local stat=$1; shift
746
747 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
748}
749
750qdisc_stats_get()
751{
752 local dev=$1; shift
753 local handle=$1; shift
754 local selector=$1; shift
755
756 tc -j -s qdisc show dev "$dev" \
757 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
758}
759
760qdisc_parent_stats_get()
761{
762 local dev=$1; shift
763 local parent=$1; shift
764 local selector=$1; shift
765
766 tc -j -s qdisc show dev "$dev" invisible \
767 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
768}
769
770ipv6_stats_get()
771{
772 local dev=$1; shift
773 local stat=$1; shift
774
775 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
776}
777
778humanize()
779{
780 local speed=$1; shift
781
782 for unit in bps Kbps Mbps Gbps; do
783 if (($(echo "$speed < 1024" | bc))); then
784 break
785 fi
786
787 speed=$(echo "scale=1; $speed / 1024" | bc)
788 done
789
790 echo "$speed${unit}"
791}
792
793rate()
794{
795 local t0=$1; shift
796 local t1=$1; shift
797 local interval=$1; shift
798
799 echo $((8 * (t1 - t0) / interval))
800}
801
802packets_rate()
803{
804 local t0=$1; shift
805 local t1=$1; shift
806 local interval=$1; shift
807
808 echo $(((t1 - t0) / interval))
809}
810
811mac_get()
812{
813 local if_name=$1
814
815 ip -j link show dev $if_name | jq -r '.[]["address"]'
816}
817
818bridge_ageing_time_get()
819{
820 local bridge=$1
821 local ageing_time
822
823 # Need to divide by 100 to convert to seconds.
824 ageing_time=$(ip -j -d link show dev $bridge \
825 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
826 echo $((ageing_time / 100))
827}
828
829declare -A SYSCTL_ORIG
830sysctl_set()
831{
832 local key=$1; shift
833 local value=$1; shift
834
835 SYSCTL_ORIG[$key]=$(sysctl -n $key)
836 sysctl -qw $key=$value
837}
838
839sysctl_restore()
840{
841 local key=$1; shift
842
843 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
844}
845
846forwarding_enable()
847{
848 sysctl_set net.ipv4.conf.all.forwarding 1
849 sysctl_set net.ipv6.conf.all.forwarding 1
850}
851
852forwarding_restore()
853{
854 sysctl_restore net.ipv6.conf.all.forwarding
855 sysctl_restore net.ipv4.conf.all.forwarding
856}
857
858declare -A MTU_ORIG
859mtu_set()
860{
861 local dev=$1; shift
862 local mtu=$1; shift
863
864 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
865 ip link set dev $dev mtu $mtu
866}
867
868mtu_restore()
869{
870 local dev=$1; shift
871
872 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
873}
874
875tc_offload_check()
876{
877 local num_netifs=${1:-$NUM_NETIFS}
878
879 for ((i = 1; i <= num_netifs; ++i)); do
880 ethtool -k ${NETIFS[p$i]} \
881 | grep "hw-tc-offload: on" &> /dev/null
882 if [[ $? -ne 0 ]]; then
883 return 1
884 fi
885 done
886
887 return 0
888}
889
890trap_install()
891{
892 local dev=$1; shift
893 local direction=$1; shift
894
895 # Some devices may not support or need in-hardware trapping of traffic
896 # (e.g. the veth pairs that this library creates for non-existent
897 # loopbacks). Use continue instead, so that there is a filter in there
898 # (some tests check counters), and so that other filters are still
899 # processed.
900 tc filter add dev $dev $direction pref 1 \
901 flower skip_sw action trap 2>/dev/null \
902 || tc filter add dev $dev $direction pref 1 \
903 flower action continue
904}
905
906trap_uninstall()
907{
908 local dev=$1; shift
909 local direction=$1; shift
910
911 tc filter del dev $dev $direction pref 1 flower
912}
913
914slow_path_trap_install()
915{
916 # For slow-path testing, we need to install a trap to get to
917 # slow path the packets that would otherwise be switched in HW.
918 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
919 trap_install "$@"
920 fi
921}
922
923slow_path_trap_uninstall()
924{
925 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
926 trap_uninstall "$@"
927 fi
928}
929
930__icmp_capture_add_del()
931{
932 local add_del=$1; shift
933 local pref=$1; shift
934 local vsuf=$1; shift
935 local tundev=$1; shift
936 local filter=$1; shift
937
938 tc filter $add_del dev "$tundev" ingress \
939 proto ip$vsuf pref $pref \
940 flower ip_proto icmp$vsuf $filter \
941 action pass
942}
943
944icmp_capture_install()
945{
946 __icmp_capture_add_del add 100 "" "$@"
947}
948
949icmp_capture_uninstall()
950{
951 __icmp_capture_add_del del 100 "" "$@"
952}
953
954icmp6_capture_install()
955{
956 __icmp_capture_add_del add 100 v6 "$@"
957}
958
959icmp6_capture_uninstall()
960{
961 __icmp_capture_add_del del 100 v6 "$@"
962}
963
964__vlan_capture_add_del()
965{
966 local add_del=$1; shift
967 local pref=$1; shift
968 local dev=$1; shift
969 local filter=$1; shift
970
971 tc filter $add_del dev "$dev" ingress \
972 proto 802.1q pref $pref \
973 flower $filter \
974 action pass
975}
976
977vlan_capture_install()
978{
979 __vlan_capture_add_del add 100 "$@"
980}
981
982vlan_capture_uninstall()
983{
984 __vlan_capture_add_del del 100 "$@"
985}
986
987__dscp_capture_add_del()
988{
989 local add_del=$1; shift
990 local dev=$1; shift
991 local base=$1; shift
992 local dscp;
993
994 for prio in {0..7}; do
995 dscp=$((base + prio))
996 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
997 "skip_hw ip_tos $((dscp << 2))"
998 done
999}
1000
1001dscp_capture_install()
1002{
1003 local dev=$1; shift
1004 local base=$1; shift
1005
1006 __dscp_capture_add_del add $dev $base
1007}
1008
1009dscp_capture_uninstall()
1010{
1011 local dev=$1; shift
1012 local base=$1; shift
1013
1014 __dscp_capture_add_del del $dev $base
1015}
1016
1017dscp_fetch_stats()
1018{
1019 local dev=$1; shift
1020 local base=$1; shift
1021
1022 for prio in {0..7}; do
1023 local dscp=$((base + prio))
1024 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1025 echo "[$dscp]=$t "
1026 done
1027}
1028
1029matchall_sink_create()
1030{
1031 local dev=$1; shift
1032
1033 tc qdisc add dev $dev clsact
1034 tc filter add dev $dev ingress \
1035 pref 10000 \
1036 matchall \
1037 action drop
1038}
1039
1040tests_run()
1041{
1042 local current_test
1043
1044 for current_test in ${TESTS:-$ALL_TESTS}; do
1045 $current_test
1046 done
1047}
1048
1049multipath_eval()
1050{
1051 local desc="$1"
1052 local weight_rp12=$2
1053 local weight_rp13=$3
1054 local packets_rp12=$4
1055 local packets_rp13=$5
1056 local weights_ratio packets_ratio diff
1057
1058 RET=0
1059
1060 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1061 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1062 | bc -l)
1063 else
1064 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1065 | bc -l)
1066 fi
1067
1068 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1069 check_err 1 "Packet difference is 0"
1070 log_test "Multipath"
1071 log_info "Expected ratio $weights_ratio"
1072 return
1073 fi
1074
1075 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1076 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1077 | bc -l)
1078 else
1079 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1080 | bc -l)
1081 fi
1082
1083 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1084 diff=${diff#-}
1085
1086 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1087 check_err $? "Too large discrepancy between expected and measured ratios"
1088 log_test "$desc"
1089 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1090}
1091
1092in_ns()
1093{
1094 local name=$1; shift
1095
1096 ip netns exec $name bash <<-EOF
1097 NUM_NETIFS=0
1098 source lib.sh
1099 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1100 EOF
1101}
1102
1103##############################################################################
1104# Tests
1105
1106ping_do()
1107{
1108 local if_name=$1
1109 local dip=$2
1110 local args=$3
1111 local vrf_name
1112
1113 vrf_name=$(master_name_get $if_name)
1114 ip vrf exec $vrf_name \
1115 $PING $args $dip -c $PING_COUNT -i 0.1 \
1116 -w $PING_TIMEOUT &> /dev/null
1117}
1118
1119ping_test()
1120{
1121 RET=0
1122
1123 ping_do $1 $2
1124 check_err $?
1125 log_test "ping$3"
1126}
1127
1128ping6_do()
1129{
1130 local if_name=$1
1131 local dip=$2
1132 local args=$3
1133 local vrf_name
1134
1135 vrf_name=$(master_name_get $if_name)
1136 ip vrf exec $vrf_name \
1137 $PING6 $args $dip -c $PING_COUNT -i 0.1 \
1138 -w $PING_TIMEOUT &> /dev/null
1139}
1140
1141ping6_test()
1142{
1143 RET=0
1144
1145 ping6_do $1 $2
1146 check_err $?
1147 log_test "ping6$3"
1148}
1149
1150learning_test()
1151{
1152 local bridge=$1
1153 local br_port1=$2 # Connected to `host1_if`.
1154 local host1_if=$3
1155 local host2_if=$4
1156 local mac=de:ad:be:ef:13:37
1157 local ageing_time
1158
1159 RET=0
1160
1161 bridge -j fdb show br $bridge brport $br_port1 \
1162 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1163 check_fail $? "Found FDB record when should not"
1164
1165 # Disable unknown unicast flooding on `br_port1` to make sure
1166 # packets are only forwarded through the port after a matching
1167 # FDB entry was installed.
1168 bridge link set dev $br_port1 flood off
1169
1170 tc qdisc add dev $host1_if ingress
1171 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1172 flower dst_mac $mac action drop
1173
1174 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1175 sleep 1
1176
1177 tc -j -s filter show dev $host1_if ingress \
1178 | jq -e ".[] | select(.options.handle == 101) \
1179 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1180 check_fail $? "Packet reached second host when should not"
1181
1182 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1183 sleep 1
1184
1185 bridge -j fdb show br $bridge brport $br_port1 \
1186 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1187 check_err $? "Did not find FDB record when should"
1188
1189 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1190 sleep 1
1191
1192 tc -j -s filter show dev $host1_if ingress \
1193 | jq -e ".[] | select(.options.handle == 101) \
1194 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1195 check_err $? "Packet did not reach second host when should"
1196
1197 # Wait for 10 seconds after the ageing time to make sure FDB
1198 # record was aged-out.
1199 ageing_time=$(bridge_ageing_time_get $bridge)
1200 sleep $((ageing_time + 10))
1201
1202 bridge -j fdb show br $bridge brport $br_port1 \
1203 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1204 check_fail $? "Found FDB record when should not"
1205
1206 bridge link set dev $br_port1 learning off
1207
1208 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1209 sleep 1
1210
1211 bridge -j fdb show br $bridge brport $br_port1 \
1212 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1213 check_fail $? "Found FDB record when should not"
1214
1215 bridge link set dev $br_port1 learning on
1216
1217 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1218 tc qdisc del dev $host1_if ingress
1219
1220 bridge link set dev $br_port1 flood on
1221
1222 log_test "FDB learning"
1223}
1224
1225flood_test_do()
1226{
1227 local should_flood=$1
1228 local mac=$2
1229 local ip=$3
1230 local host1_if=$4
1231 local host2_if=$5
1232 local err=0
1233
1234 # Add an ACL on `host2_if` which will tell us whether the packet
1235 # was flooded to it or not.
1236 tc qdisc add dev $host2_if ingress
1237 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1238 flower dst_mac $mac action drop
1239
1240 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1241 sleep 1
1242
1243 tc -j -s filter show dev $host2_if ingress \
1244 | jq -e ".[] | select(.options.handle == 101) \
1245 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1246 if [[ $? -ne 0 && $should_flood == "true" || \
1247 $? -eq 0 && $should_flood == "false" ]]; then
1248 err=1
1249 fi
1250
1251 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1252 tc qdisc del dev $host2_if ingress
1253
1254 return $err
1255}
1256
1257flood_unicast_test()
1258{
1259 local br_port=$1
1260 local host1_if=$2
1261 local host2_if=$3
1262 local mac=de:ad:be:ef:13:37
1263 local ip=192.0.2.100
1264
1265 RET=0
1266
1267 bridge link set dev $br_port flood off
1268
1269 flood_test_do false $mac $ip $host1_if $host2_if
1270 check_err $? "Packet flooded when should not"
1271
1272 bridge link set dev $br_port flood on
1273
1274 flood_test_do true $mac $ip $host1_if $host2_if
1275 check_err $? "Packet was not flooded when should"
1276
1277 log_test "Unknown unicast flood"
1278}
1279
1280flood_multicast_test()
1281{
1282 local br_port=$1
1283 local host1_if=$2
1284 local host2_if=$3
1285 local mac=01:00:5e:00:00:01
1286 local ip=239.0.0.1
1287
1288 RET=0
1289
1290 bridge link set dev $br_port mcast_flood off
1291
1292 flood_test_do false $mac $ip $host1_if $host2_if
1293 check_err $? "Packet flooded when should not"
1294
1295 bridge link set dev $br_port mcast_flood on
1296
1297 flood_test_do true $mac $ip $host1_if $host2_if
1298 check_err $? "Packet was not flooded when should"
1299
1300 log_test "Unregistered multicast flood"
1301}
1302
1303flood_test()
1304{
1305 # `br_port` is connected to `host2_if`
1306 local br_port=$1
1307 local host1_if=$2
1308 local host2_if=$3
1309
1310 flood_unicast_test $br_port $host1_if $host2_if
1311 flood_multicast_test $br_port $host1_if $host2_if
1312}
1313
1314__start_traffic()
1315{
1316 local proto=$1; shift
1317 local h_in=$1; shift # Where the traffic egresses the host
1318 local sip=$1; shift
1319 local dip=$1; shift
1320 local dmac=$1; shift
1321
1322 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
1323 -a own -b $dmac -t "$proto" -q "$@" &
1324 sleep 1
1325}
1326
1327start_traffic()
1328{
1329 __start_traffic udp "$@"
1330}
1331
1332start_tcp_traffic()
1333{
1334 __start_traffic tcp "$@"
1335}
1336
1337stop_traffic()
1338{
1339 # Suppress noise from killing mausezahn.
1340 { kill %% && wait %%; } 2>/dev/null
1341}
1342
1343tcpdump_start()
1344{
1345 local if_name=$1; shift
1346 local ns=$1; shift
1347
1348 capfile=$(mktemp)
1349 capout=$(mktemp)
1350
1351 if [ -z $ns ]; then
1352 ns_cmd=""
1353 else
1354 ns_cmd="ip netns exec ${ns}"
1355 fi
1356
1357 if [ -z $SUDO_USER ] ; then
1358 capuser=""
1359 else
1360 capuser="-Z $SUDO_USER"
1361 fi
1362
1363 $ns_cmd tcpdump -e -n -Q in -i $if_name \
1364 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
1365 cappid=$!
1366
1367 sleep 1
1368}
1369
1370tcpdump_stop()
1371{
1372 $ns_cmd kill $cappid
1373 sleep 1
1374}
1375
1376tcpdump_cleanup()
1377{
1378 rm $capfile $capout
1379}
1380
1381tcpdump_show()
1382{
1383 tcpdump -e -n -r $capfile 2>&1
1384}
1385
1386# return 0 if the packet wasn't seen on host2_if or 1 if it was
1387mcast_packet_test()
1388{
1389 local mac=$1
1390 local src_ip=$2
1391 local ip=$3
1392 local host1_if=$4
1393 local host2_if=$5
1394 local seen=0
1395 local tc_proto="ip"
1396 local mz_v6arg=""
1397
1398 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1399 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1400 tc_proto="ipv6"
1401 mz_v6arg="-6"
1402 fi
1403
1404 # Add an ACL on `host2_if` which will tell us whether the packet
1405 # was received by it or not.
1406 tc qdisc add dev $host2_if ingress
1407 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1408 flower ip_proto udp dst_mac $mac action drop
1409
1410 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1411 sleep 1
1412
1413 tc -j -s filter show dev $host2_if ingress \
1414 | jq -e ".[] | select(.options.handle == 101) \
1415 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1416 if [[ $? -eq 0 ]]; then
1417 seen=1
1418 fi
1419
1420 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1421 tc qdisc del dev $host2_if ingress
1422
1423 return $seen
1424}
1425
1426brmcast_check_sg_entries()
1427{
1428 local report=$1; shift
1429 local slist=("$@")
1430 local sarg=""
1431
1432 for src in "${slist[@]}"; do
1433 sarg="${sarg} and .source_list[].address == \"$src\""
1434 done
1435 bridge -j -d -s mdb show dev br0 \
1436 | jq -e ".[].mdb[] | \
1437 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1438 check_err $? "Wrong *,G entry source list after $report report"
1439
1440 for sgent in "${slist[@]}"; do
1441 bridge -j -d -s mdb show dev br0 \
1442 | jq -e ".[].mdb[] | \
1443 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1444 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1445 done
1446}
1447
1448brmcast_check_sg_fwding()
1449{
1450 local should_fwd=$1; shift
1451 local sources=("$@")
1452
1453 for src in "${sources[@]}"; do
1454 local retval=0
1455
1456 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1457 retval=$?
1458 if [ $should_fwd -eq 1 ]; then
1459 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1460 else
1461 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1462 fi
1463 done
1464}
1465
1466brmcast_check_sg_state()
1467{
1468 local is_blocked=$1; shift
1469 local sources=("$@")
1470 local should_fail=1
1471
1472 if [ $is_blocked -eq 1 ]; then
1473 should_fail=0
1474 fi
1475
1476 for src in "${sources[@]}"; do
1477 bridge -j -d -s mdb show dev br0 \
1478 | jq -e ".[].mdb[] | \
1479 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1480 .source_list[] |
1481 select(.address == \"$src\") |
1482 select(.timer == \"0.00\")" &>/dev/null
1483 check_err_fail $should_fail $? "Entry $src has zero timer"
1484
1485 bridge -j -d -s mdb show dev br0 \
1486 | jq -e ".[].mdb[] | \
1487 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1488 .flags[] == \"blocked\")" &>/dev/null
1489 check_err_fail $should_fail $? "Entry $src has blocked flag"
1490 done
1491}