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