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