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