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
754humanize()
755{
756 local speed=$1; shift
757
758 for unit in bps Kbps Mbps Gbps; do
759 if (($(echo "$speed < 1024" | bc))); then
760 break
761 fi
762
763 speed=$(echo "scale=1; $speed / 1024" | bc)
764 done
765
766 echo "$speed${unit}"
767}
768
769rate()
770{
771 local t0=$1; shift
772 local t1=$1; shift
773 local interval=$1; shift
774
775 echo $((8 * (t1 - t0) / interval))
776}
777
778packets_rate()
779{
780 local t0=$1; shift
781 local t1=$1; shift
782 local interval=$1; shift
783
784 echo $(((t1 - t0) / interval))
785}
786
787mac_get()
788{
789 local if_name=$1
790
791 ip -j link show dev $if_name | jq -r '.[]["address"]'
792}
793
794bridge_ageing_time_get()
795{
796 local bridge=$1
797 local ageing_time
798
799 # Need to divide by 100 to convert to seconds.
800 ageing_time=$(ip -j -d link show dev $bridge \
801 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
802 echo $((ageing_time / 100))
803}
804
805declare -A SYSCTL_ORIG
806sysctl_set()
807{
808 local key=$1; shift
809 local value=$1; shift
810
811 SYSCTL_ORIG[$key]=$(sysctl -n $key)
812 sysctl -qw $key=$value
813}
814
815sysctl_restore()
816{
817 local key=$1; shift
818
819 sysctl -qw $key=${SYSCTL_ORIG["$key"]}
820}
821
822forwarding_enable()
823{
824 sysctl_set net.ipv4.conf.all.forwarding 1
825 sysctl_set net.ipv6.conf.all.forwarding 1
826}
827
828forwarding_restore()
829{
830 sysctl_restore net.ipv6.conf.all.forwarding
831 sysctl_restore net.ipv4.conf.all.forwarding
832}
833
834declare -A MTU_ORIG
835mtu_set()
836{
837 local dev=$1; shift
838 local mtu=$1; shift
839
840 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
841 ip link set dev $dev mtu $mtu
842}
843
844mtu_restore()
845{
846 local dev=$1; shift
847
848 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
849}
850
851tc_offload_check()
852{
853 local num_netifs=${1:-$NUM_NETIFS}
854
855 for ((i = 1; i <= num_netifs; ++i)); do
856 ethtool -k ${NETIFS[p$i]} \
857 | grep "hw-tc-offload: on" &> /dev/null
858 if [[ $? -ne 0 ]]; then
859 return 1
860 fi
861 done
862
863 return 0
864}
865
866trap_install()
867{
868 local dev=$1; shift
869 local direction=$1; shift
870
871 # Some devices may not support or need in-hardware trapping of traffic
872 # (e.g. the veth pairs that this library creates for non-existent
873 # loopbacks). Use continue instead, so that there is a filter in there
874 # (some tests check counters), and so that other filters are still
875 # processed.
876 tc filter add dev $dev $direction pref 1 \
877 flower skip_sw action trap 2>/dev/null \
878 || tc filter add dev $dev $direction pref 1 \
879 flower action continue
880}
881
882trap_uninstall()
883{
884 local dev=$1; shift
885 local direction=$1; shift
886
887 tc filter del dev $dev $direction pref 1 flower
888}
889
890slow_path_trap_install()
891{
892 # For slow-path testing, we need to install a trap to get to
893 # slow path the packets that would otherwise be switched in HW.
894 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
895 trap_install "$@"
896 fi
897}
898
899slow_path_trap_uninstall()
900{
901 if [ "${tcflags/skip_hw}" != "$tcflags" ]; then
902 trap_uninstall "$@"
903 fi
904}
905
906__icmp_capture_add_del()
907{
908 local add_del=$1; shift
909 local pref=$1; shift
910 local vsuf=$1; shift
911 local tundev=$1; shift
912 local filter=$1; shift
913
914 tc filter $add_del dev "$tundev" ingress \
915 proto ip$vsuf pref $pref \
916 flower ip_proto icmp$vsuf $filter \
917 action pass
918}
919
920icmp_capture_install()
921{
922 __icmp_capture_add_del add 100 "" "$@"
923}
924
925icmp_capture_uninstall()
926{
927 __icmp_capture_add_del del 100 "" "$@"
928}
929
930icmp6_capture_install()
931{
932 __icmp_capture_add_del add 100 v6 "$@"
933}
934
935icmp6_capture_uninstall()
936{
937 __icmp_capture_add_del del 100 v6 "$@"
938}
939
940__vlan_capture_add_del()
941{
942 local add_del=$1; shift
943 local pref=$1; shift
944 local dev=$1; shift
945 local filter=$1; shift
946
947 tc filter $add_del dev "$dev" ingress \
948 proto 802.1q pref $pref \
949 flower $filter \
950 action pass
951}
952
953vlan_capture_install()
954{
955 __vlan_capture_add_del add 100 "$@"
956}
957
958vlan_capture_uninstall()
959{
960 __vlan_capture_add_del del 100 "$@"
961}
962
963__dscp_capture_add_del()
964{
965 local add_del=$1; shift
966 local dev=$1; shift
967 local base=$1; shift
968 local dscp;
969
970 for prio in {0..7}; do
971 dscp=$((base + prio))
972 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
973 "skip_hw ip_tos $((dscp << 2))"
974 done
975}
976
977dscp_capture_install()
978{
979 local dev=$1; shift
980 local base=$1; shift
981
982 __dscp_capture_add_del add $dev $base
983}
984
985dscp_capture_uninstall()
986{
987 local dev=$1; shift
988 local base=$1; shift
989
990 __dscp_capture_add_del del $dev $base
991}
992
993dscp_fetch_stats()
994{
995 local dev=$1; shift
996 local base=$1; shift
997
998 for prio in {0..7}; do
999 local dscp=$((base + prio))
1000 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1001 echo "[$dscp]=$t "
1002 done
1003}
1004
1005matchall_sink_create()
1006{
1007 local dev=$1; shift
1008
1009 tc qdisc add dev $dev clsact
1010 tc filter add dev $dev ingress \
1011 pref 10000 \
1012 matchall \
1013 action drop
1014}
1015
1016tests_run()
1017{
1018 local current_test
1019
1020 for current_test in ${TESTS:-$ALL_TESTS}; do
1021 $current_test
1022 done
1023}
1024
1025multipath_eval()
1026{
1027 local desc="$1"
1028 local weight_rp12=$2
1029 local weight_rp13=$3
1030 local packets_rp12=$4
1031 local packets_rp13=$5
1032 local weights_ratio packets_ratio diff
1033
1034 RET=0
1035
1036 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1037 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1038 | bc -l)
1039 else
1040 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1041 | bc -l)
1042 fi
1043
1044 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1045 check_err 1 "Packet difference is 0"
1046 log_test "Multipath"
1047 log_info "Expected ratio $weights_ratio"
1048 return
1049 fi
1050
1051 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1052 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1053 | bc -l)
1054 else
1055 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1056 | bc -l)
1057 fi
1058
1059 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1060 diff=${diff#-}
1061
1062 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1063 check_err $? "Too large discrepancy between expected and measured ratios"
1064 log_test "$desc"
1065 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1066}
1067
1068in_ns()
1069{
1070 local name=$1; shift
1071
1072 ip netns exec $name bash <<-EOF
1073 NUM_NETIFS=0
1074 source lib.sh
1075 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1076 EOF
1077}
1078
1079##############################################################################
1080# Tests
1081
1082ping_do()
1083{
1084 local if_name=$1
1085 local dip=$2
1086 local args=$3
1087 local vrf_name
1088
1089 vrf_name=$(master_name_get $if_name)
1090 ip vrf exec $vrf_name \
1091 $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1092}
1093
1094ping_test()
1095{
1096 RET=0
1097
1098 ping_do $1 $2
1099 check_err $?
1100 log_test "ping$3"
1101}
1102
1103ping6_do()
1104{
1105 local if_name=$1
1106 local dip=$2
1107 local args=$3
1108 local vrf_name
1109
1110 vrf_name=$(master_name_get $if_name)
1111 ip vrf exec $vrf_name \
1112 $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
1113}
1114
1115ping6_test()
1116{
1117 RET=0
1118
1119 ping6_do $1 $2
1120 check_err $?
1121 log_test "ping6$3"
1122}
1123
1124learning_test()
1125{
1126 local bridge=$1
1127 local br_port1=$2 # Connected to `host1_if`.
1128 local host1_if=$3
1129 local host2_if=$4
1130 local mac=de:ad:be:ef:13:37
1131 local ageing_time
1132
1133 RET=0
1134
1135 bridge -j fdb show br $bridge brport $br_port1 \
1136 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1137 check_fail $? "Found FDB record when should not"
1138
1139 # Disable unknown unicast flooding on `br_port1` to make sure
1140 # packets are only forwarded through the port after a matching
1141 # FDB entry was installed.
1142 bridge link set dev $br_port1 flood off
1143
1144 tc qdisc add dev $host1_if ingress
1145 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1146 flower dst_mac $mac action drop
1147
1148 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1149 sleep 1
1150
1151 tc -j -s filter show dev $host1_if ingress \
1152 | jq -e ".[] | select(.options.handle == 101) \
1153 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1154 check_fail $? "Packet reached second host when should not"
1155
1156 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1157 sleep 1
1158
1159 bridge -j fdb show br $bridge brport $br_port1 \
1160 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1161 check_err $? "Did not find FDB record when should"
1162
1163 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1164 sleep 1
1165
1166 tc -j -s filter show dev $host1_if ingress \
1167 | jq -e ".[] | select(.options.handle == 101) \
1168 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1169 check_err $? "Packet did not reach second host when should"
1170
1171 # Wait for 10 seconds after the ageing time to make sure FDB
1172 # record was aged-out.
1173 ageing_time=$(bridge_ageing_time_get $bridge)
1174 sleep $((ageing_time + 10))
1175
1176 bridge -j fdb show br $bridge brport $br_port1 \
1177 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1178 check_fail $? "Found FDB record when should not"
1179
1180 bridge link set dev $br_port1 learning off
1181
1182 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1183 sleep 1
1184
1185 bridge -j fdb show br $bridge brport $br_port1 \
1186 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1187 check_fail $? "Found FDB record when should not"
1188
1189 bridge link set dev $br_port1 learning on
1190
1191 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1192 tc qdisc del dev $host1_if ingress
1193
1194 bridge link set dev $br_port1 flood on
1195
1196 log_test "FDB learning"
1197}
1198
1199flood_test_do()
1200{
1201 local should_flood=$1
1202 local mac=$2
1203 local ip=$3
1204 local host1_if=$4
1205 local host2_if=$5
1206 local err=0
1207
1208 # Add an ACL on `host2_if` which will tell us whether the packet
1209 # was flooded to it or not.
1210 tc qdisc add dev $host2_if ingress
1211 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1212 flower dst_mac $mac action drop
1213
1214 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1215 sleep 1
1216
1217 tc -j -s filter show dev $host2_if ingress \
1218 | jq -e ".[] | select(.options.handle == 101) \
1219 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1220 if [[ $? -ne 0 && $should_flood == "true" || \
1221 $? -eq 0 && $should_flood == "false" ]]; then
1222 err=1
1223 fi
1224
1225 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1226 tc qdisc del dev $host2_if ingress
1227
1228 return $err
1229}
1230
1231flood_unicast_test()
1232{
1233 local br_port=$1
1234 local host1_if=$2
1235 local host2_if=$3
1236 local mac=de:ad:be:ef:13:37
1237 local ip=192.0.2.100
1238
1239 RET=0
1240
1241 bridge link set dev $br_port flood off
1242
1243 flood_test_do false $mac $ip $host1_if $host2_if
1244 check_err $? "Packet flooded when should not"
1245
1246 bridge link set dev $br_port flood on
1247
1248 flood_test_do true $mac $ip $host1_if $host2_if
1249 check_err $? "Packet was not flooded when should"
1250
1251 log_test "Unknown unicast flood"
1252}
1253
1254flood_multicast_test()
1255{
1256 local br_port=$1
1257 local host1_if=$2
1258 local host2_if=$3
1259 local mac=01:00:5e:00:00:01
1260 local ip=239.0.0.1
1261
1262 RET=0
1263
1264 bridge link set dev $br_port mcast_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 mcast_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 "Unregistered multicast flood"
1275}
1276
1277flood_test()
1278{
1279 # `br_port` is connected to `host2_if`
1280 local br_port=$1
1281 local host1_if=$2
1282 local host2_if=$3
1283
1284 flood_unicast_test $br_port $host1_if $host2_if
1285 flood_multicast_test $br_port $host1_if $host2_if
1286}
1287
1288__start_traffic()
1289{
1290 local proto=$1; shift
1291 local h_in=$1; shift # Where the traffic egresses the host
1292 local sip=$1; shift
1293 local dip=$1; shift
1294 local dmac=$1; shift
1295
1296 $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \
1297 -a own -b $dmac -t "$proto" -q "$@" &
1298 sleep 1
1299}
1300
1301start_traffic()
1302{
1303 __start_traffic udp "$@"
1304}
1305
1306start_tcp_traffic()
1307{
1308 __start_traffic tcp "$@"
1309}
1310
1311stop_traffic()
1312{
1313 # Suppress noise from killing mausezahn.
1314 { kill %% && wait %%; } 2>/dev/null
1315}
1316
1317tcpdump_start()
1318{
1319 local if_name=$1; shift
1320 local ns=$1; shift
1321
1322 capfile=$(mktemp)
1323 capout=$(mktemp)
1324
1325 if [ -z $ns ]; then
1326 ns_cmd=""
1327 else
1328 ns_cmd="ip netns exec ${ns}"
1329 fi
1330
1331 if [ -z $SUDO_USER ] ; then
1332 capuser=""
1333 else
1334 capuser="-Z $SUDO_USER"
1335 fi
1336
1337 $ns_cmd tcpdump -e -n -Q in -i $if_name \
1338 -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
1339 cappid=$!
1340
1341 sleep 1
1342}
1343
1344tcpdump_stop()
1345{
1346 $ns_cmd kill $cappid
1347 sleep 1
1348}
1349
1350tcpdump_cleanup()
1351{
1352 rm $capfile $capout
1353}
1354
1355tcpdump_show()
1356{
1357 tcpdump -e -n -r $capfile 2>&1
1358}
1359
1360# return 0 if the packet wasn't seen on host2_if or 1 if it was
1361mcast_packet_test()
1362{
1363 local mac=$1
1364 local src_ip=$2
1365 local ip=$3
1366 local host1_if=$4
1367 local host2_if=$5
1368 local seen=0
1369 local tc_proto="ip"
1370 local mz_v6arg=""
1371
1372 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1373 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1374 tc_proto="ipv6"
1375 mz_v6arg="-6"
1376 fi
1377
1378 # Add an ACL on `host2_if` which will tell us whether the packet
1379 # was received by it or not.
1380 tc qdisc add dev $host2_if ingress
1381 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1382 flower ip_proto udp dst_mac $mac action drop
1383
1384 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1385 sleep 1
1386
1387 tc -j -s filter show dev $host2_if ingress \
1388 | jq -e ".[] | select(.options.handle == 101) \
1389 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1390 if [[ $? -eq 0 ]]; then
1391 seen=1
1392 fi
1393
1394 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1395 tc qdisc del dev $host2_if ingress
1396
1397 return $seen
1398}
1399
1400brmcast_check_sg_entries()
1401{
1402 local report=$1; shift
1403 local slist=("$@")
1404 local sarg=""
1405
1406 for src in "${slist[@]}"; do
1407 sarg="${sarg} and .source_list[].address == \"$src\""
1408 done
1409 bridge -j -d -s mdb show dev br0 \
1410 | jq -e ".[].mdb[] | \
1411 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1412 check_err $? "Wrong *,G entry source list after $report report"
1413
1414 for sgent in "${slist[@]}"; do
1415 bridge -j -d -s mdb show dev br0 \
1416 | jq -e ".[].mdb[] | \
1417 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1418 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1419 done
1420}
1421
1422brmcast_check_sg_fwding()
1423{
1424 local should_fwd=$1; shift
1425 local sources=("$@")
1426
1427 for src in "${sources[@]}"; do
1428 local retval=0
1429
1430 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1431 retval=$?
1432 if [ $should_fwd -eq 1 ]; then
1433 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1434 else
1435 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1436 fi
1437 done
1438}
1439
1440brmcast_check_sg_state()
1441{
1442 local is_blocked=$1; shift
1443 local sources=("$@")
1444 local should_fail=1
1445
1446 if [ $is_blocked -eq 1 ]; then
1447 should_fail=0
1448 fi
1449
1450 for src in "${sources[@]}"; do
1451 bridge -j -d -s mdb show dev br0 \
1452 | jq -e ".[].mdb[] | \
1453 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1454 .source_list[] |
1455 select(.address == \"$src\") |
1456 select(.timer == \"0.00\")" &>/dev/null
1457 check_err_fail $should_fail $? "Entry $src has zero timer"
1458
1459 bridge -j -d -s mdb show dev br0 \
1460 | jq -e ".[].mdb[] | \
1461 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1462 .flags[] == \"blocked\")" &>/dev/null
1463 check_err_fail $should_fail $? "Entry $src has blocked flag"
1464 done
1465}