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