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# Topology description. p1 looped back to p2, p3 to p4 and so on.
6
7declare -A NETIFS=(
8 [p1]=veth0
9 [p2]=veth1
10 [p3]=veth2
11 [p4]=veth3
12 [p5]=veth4
13 [p6]=veth5
14 [p7]=veth6
15 [p8]=veth7
16 [p9]=veth8
17 [p10]=veth9
18)
19
20# Port that does not have a cable connected.
21: "${NETIF_NO_CABLE:=eth8}"
22
23##############################################################################
24# Defines
25
26# Networking utilities.
27: "${PING:=ping}"
28: "${PING6:=ping6}" # Some distros just use ping.
29: "${ARPING:=arping}"
30: "${TROUTE6:=traceroute6}"
31
32# Packet generator.
33: "${MZ:=mausezahn}" # Some distributions use 'mz'.
34: "${MZ_DELAY:=0}"
35
36# Host configuration tools.
37: "${TEAMD:=teamd}"
38: "${MCD:=smcrouted}"
39: "${MC_CLI:=smcroutectl}"
40
41# Constants for netdevice bring-up:
42# Default time in seconds to wait for an interface to come up before giving up
43# and bailing out. Used during initial setup.
44: "${INTERFACE_TIMEOUT:=600}"
45# Like INTERFACE_TIMEOUT, but default for ad-hoc waiting in testing scripts.
46: "${WAIT_TIMEOUT:=20}"
47# Time to wait after interfaces participating in the test are all UP.
48: "${WAIT_TIME:=5}"
49
50# Whether to pause on, respectively, after a failure and before cleanup.
51: "${PAUSE_ON_FAIL:=no}"
52: "${PAUSE_ON_CLEANUP:=no}"
53
54# Whether to create virtual interfaces, and what netdevice type they should be.
55: "${NETIF_CREATE:=yes}"
56: "${NETIF_TYPE:=veth}"
57
58# Constants for ping tests:
59# How many packets should be sent.
60: "${PING_COUNT:=10}"
61# Timeout (in seconds) before ping exits regardless of how many packets have
62# been sent or received
63: "${PING_TIMEOUT:=5}"
64
65# Minimum ageing_time (in centiseconds) supported by hardware
66: "${LOW_AGEING_TIME:=1000}"
67
68# Whether to check for availability of certain tools.
69: "${REQUIRE_JQ:=yes}"
70: "${REQUIRE_MZ:=yes}"
71: "${REQUIRE_MTOOLS:=no}"
72
73# Whether to override MAC addresses on interfaces participating in the test.
74: "${STABLE_MAC_ADDRS:=no}"
75
76# Flags for tcpdump
77: "${TCPDUMP_EXTRA_FLAGS:=}"
78
79# Flags for TC filters.
80: "${TC_FLAG:=skip_hw}"
81
82# Whether the machine is "slow" -- i.e. might be incapable of running tests
83# involving heavy traffic. This might be the case on a debug kernel, a VM, or
84# e.g. a low-power board.
85: "${KSFT_MACHINE_SLOW:=no}"
86
87##############################################################################
88# Find netifs by test-specified driver name
89
90driver_name_get()
91{
92 local dev=$1; shift
93 local driver_path="/sys/class/net/$dev/device/driver"
94
95 if [[ -L $driver_path ]]; then
96 basename `realpath $driver_path`
97 fi
98}
99
100netif_find_driver()
101{
102 local ifnames=`ip -j link show | jq -r ".[].ifname"`
103 local count=0
104
105 for ifname in $ifnames
106 do
107 local driver_name=`driver_name_get $ifname`
108 if [[ ! -z $driver_name && $driver_name == $NETIF_FIND_DRIVER ]]; then
109 count=$((count + 1))
110 NETIFS[p$count]="$ifname"
111 fi
112 done
113}
114
115# Whether to find netdevice according to the driver speficied by the importer
116: "${NETIF_FIND_DRIVER:=}"
117
118if [[ $NETIF_FIND_DRIVER ]]; then
119 unset NETIFS
120 declare -A NETIFS
121 netif_find_driver
122fi
123
124net_forwarding_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
125
126if [[ -f $net_forwarding_dir/forwarding.config ]]; then
127 source "$net_forwarding_dir/forwarding.config"
128fi
129
130source "$net_forwarding_dir/../lib.sh"
131
132##############################################################################
133# Sanity checks
134
135check_tc_version()
136{
137 tc -j &> /dev/null
138 if [[ $? -ne 0 ]]; then
139 echo "SKIP: iproute2 too old; tc is missing JSON support"
140 exit $ksft_skip
141 fi
142}
143
144# Old versions of tc don't understand "mpls_uc"
145check_tc_mpls_support()
146{
147 local dev=$1; shift
148
149 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
150 matchall action pipe &> /dev/null
151 if [[ $? -ne 0 ]]; then
152 echo "SKIP: iproute2 too old; tc is missing MPLS support"
153 return $ksft_skip
154 fi
155 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
156 matchall
157}
158
159# Old versions of tc produce invalid json output for mpls lse statistics
160check_tc_mpls_lse_stats()
161{
162 local dev=$1; shift
163 local ret;
164
165 tc filter add dev $dev ingress protocol mpls_uc pref 1 handle 1 \
166 flower mpls lse depth 2 \
167 action continue &> /dev/null
168
169 if [[ $? -ne 0 ]]; then
170 echo "SKIP: iproute2 too old; tc-flower is missing extended MPLS support"
171 return $ksft_skip
172 fi
173
174 tc -j filter show dev $dev ingress protocol mpls_uc | jq . &> /dev/null
175 ret=$?
176 tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \
177 flower
178
179 if [[ $ret -ne 0 ]]; then
180 echo "SKIP: iproute2 too old; tc-flower produces invalid json output for extended MPLS filters"
181 return $ksft_skip
182 fi
183}
184
185check_tc_shblock_support()
186{
187 tc filter help 2>&1 | grep block &> /dev/null
188 if [[ $? -ne 0 ]]; then
189 echo "SKIP: iproute2 too old; tc is missing shared block support"
190 exit $ksft_skip
191 fi
192}
193
194check_tc_chain_support()
195{
196 tc help 2>&1|grep chain &> /dev/null
197 if [[ $? -ne 0 ]]; then
198 echo "SKIP: iproute2 too old; tc is missing chain support"
199 exit $ksft_skip
200 fi
201}
202
203check_tc_action_hw_stats_support()
204{
205 tc actions help 2>&1 | grep -q hw_stats
206 if [[ $? -ne 0 ]]; then
207 echo "SKIP: iproute2 too old; tc is missing action hw_stats support"
208 exit $ksft_skip
209 fi
210}
211
212check_tc_fp_support()
213{
214 tc qdisc add dev lo mqprio help 2>&1 | grep -q "fp "
215 if [[ $? -ne 0 ]]; then
216 echo "SKIP: iproute2 too old; tc is missing frame preemption support"
217 exit $ksft_skip
218 fi
219}
220
221check_ethtool_lanes_support()
222{
223 ethtool --help 2>&1| grep lanes &> /dev/null
224 if [[ $? -ne 0 ]]; then
225 echo "SKIP: ethtool too old; it is missing lanes support"
226 exit $ksft_skip
227 fi
228}
229
230check_ethtool_mm_support()
231{
232 ethtool --help 2>&1| grep -- '--show-mm' &> /dev/null
233 if [[ $? -ne 0 ]]; then
234 echo "SKIP: ethtool too old; it is missing MAC Merge layer support"
235 exit $ksft_skip
236 fi
237}
238
239check_ethtool_counter_group_support()
240{
241 ethtool --help 2>&1| grep -- '--all-groups' &> /dev/null
242 if [[ $? -ne 0 ]]; then
243 echo "SKIP: ethtool too old; it is missing standard counter group support"
244 exit $ksft_skip
245 fi
246}
247
248check_ethtool_pmac_std_stats_support()
249{
250 local dev=$1; shift
251 local grp=$1; shift
252
253 [ 0 -ne $(ethtool --json -S $dev --all-groups --src pmac 2>/dev/null \
254 | jq ".[].\"$grp\" | length") ]
255}
256
257check_locked_port_support()
258{
259 if ! bridge -d link show | grep -q " locked"; then
260 echo "SKIP: iproute2 too old; Locked port feature not supported."
261 return $ksft_skip
262 fi
263}
264
265check_port_mab_support()
266{
267 if ! bridge -d link show | grep -q "mab"; then
268 echo "SKIP: iproute2 too old; MacAuth feature not supported."
269 return $ksft_skip
270 fi
271}
272
273if [[ "$(id -u)" -ne 0 ]]; then
274 echo "SKIP: need root privileges"
275 exit $ksft_skip
276fi
277
278check_driver()
279{
280 local dev=$1; shift
281 local expected=$1; shift
282 local driver_name=`driver_name_get $dev`
283
284 if [[ $driver_name != $expected ]]; then
285 echo "SKIP: expected driver $expected for $dev, got $driver_name instead"
286 exit $ksft_skip
287 fi
288}
289
290if [[ "$CHECK_TC" = "yes" ]]; then
291 check_tc_version
292fi
293
294require_command()
295{
296 local cmd=$1; shift
297
298 if [[ ! -x "$(command -v "$cmd")" ]]; then
299 echo "SKIP: $cmd not installed"
300 exit $ksft_skip
301 fi
302}
303
304# IPv6 support was added in v3.0
305check_mtools_version()
306{
307 local version="$(msend -v)"
308 local major
309
310 version=${version##msend version }
311 major=$(echo $version | cut -d. -f1)
312
313 if [ $major -lt 3 ]; then
314 echo "SKIP: expected mtools version 3.0, got $version"
315 exit $ksft_skip
316 fi
317}
318
319if [[ "$REQUIRE_JQ" = "yes" ]]; then
320 require_command jq
321fi
322if [[ "$REQUIRE_MZ" = "yes" ]]; then
323 require_command $MZ
324fi
325if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then
326 # https://github.com/troglobit/mtools
327 require_command msend
328 require_command mreceive
329 check_mtools_version
330fi
331
332##############################################################################
333# Command line options handling
334
335count=0
336
337while [[ $# -gt 0 ]]; do
338 if [[ "$count" -eq "0" ]]; then
339 unset NETIFS
340 declare -A NETIFS
341 fi
342 count=$((count + 1))
343 NETIFS[p$count]="$1"
344 shift
345done
346
347##############################################################################
348# Network interfaces configuration
349
350if [[ ! -v NUM_NETIFS ]]; then
351 echo "SKIP: importer does not define \"NUM_NETIFS\""
352 exit $ksft_skip
353fi
354
355if (( NUM_NETIFS > ${#NETIFS[@]} )); then
356 echo "SKIP: Importer requires $NUM_NETIFS NETIFS, but only ${#NETIFS[@]} are defined (${NETIFS[@]})"
357 exit $ksft_skip
358fi
359
360for i in $(seq ${#NETIFS[@]}); do
361 if [[ ! ${NETIFS[p$i]} ]]; then
362 echo "SKIP: NETIFS[p$i] not given"
363 exit $ksft_skip
364 fi
365done
366
367create_netif_veth()
368{
369 local i
370
371 for ((i = 1; i <= NUM_NETIFS; ++i)); do
372 local j=$((i+1))
373
374 if [ -z ${NETIFS[p$i]} ]; then
375 echo "SKIP: Cannot create interface. Name not specified"
376 exit $ksft_skip
377 fi
378
379 ip link show dev ${NETIFS[p$i]} &> /dev/null
380 if [[ $? -ne 0 ]]; then
381 ip link add ${NETIFS[p$i]} type veth \
382 peer name ${NETIFS[p$j]}
383 if [[ $? -ne 0 ]]; then
384 echo "Failed to create netif"
385 exit 1
386 fi
387 fi
388 i=$j
389 done
390}
391
392create_netif()
393{
394 case "$NETIF_TYPE" in
395 veth) create_netif_veth
396 ;;
397 *) echo "Can not create interfaces of type \'$NETIF_TYPE\'"
398 exit 1
399 ;;
400 esac
401}
402
403declare -A MAC_ADDR_ORIG
404mac_addr_prepare()
405{
406 local new_addr=
407 local dev=
408
409 for ((i = 1; i <= NUM_NETIFS; ++i)); do
410 dev=${NETIFS[p$i]}
411 new_addr=$(printf "00:01:02:03:04:%02x" $i)
412
413 MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address')
414 # Strip quotes
415 MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/}
416 ip link set dev $dev address $new_addr
417 done
418}
419
420mac_addr_restore()
421{
422 local dev=
423
424 for ((i = 1; i <= NUM_NETIFS; ++i)); do
425 dev=${NETIFS[p$i]}
426 ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]}
427 done
428}
429
430if [[ "$NETIF_CREATE" = "yes" ]]; then
431 create_netif
432fi
433
434if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
435 mac_addr_prepare
436fi
437
438for ((i = 1; i <= NUM_NETIFS; ++i)); do
439 ip link show dev ${NETIFS[p$i]} &> /dev/null
440 if [[ $? -ne 0 ]]; then
441 echo "SKIP: could not find all required interfaces"
442 exit $ksft_skip
443 fi
444done
445
446##############################################################################
447# Helpers
448
449# Exit status to return at the end. Set in case one of the tests fails.
450EXIT_STATUS=0
451# Per-test return value. Clear at the beginning of each test.
452RET=0
453
454ret_set_ksft_status()
455{
456 local ksft_status=$1; shift
457 local msg=$1; shift
458
459 RET=$(ksft_status_merge $RET $ksft_status)
460 if (( $? )); then
461 retmsg=$msg
462 fi
463}
464
465# Whether FAILs should be interpreted as XFAILs. Internal.
466FAIL_TO_XFAIL=
467
468check_err()
469{
470 local err=$1
471 local msg=$2
472
473 if ((err)); then
474 if [[ $FAIL_TO_XFAIL = yes ]]; then
475 ret_set_ksft_status $ksft_xfail "$msg"
476 else
477 ret_set_ksft_status $ksft_fail "$msg"
478 fi
479 fi
480}
481
482check_fail()
483{
484 local err=$1
485 local msg=$2
486
487 check_err $((!err)) "$msg"
488}
489
490check_err_fail()
491{
492 local should_fail=$1; shift
493 local err=$1; shift
494 local what=$1; shift
495
496 if ((should_fail)); then
497 check_fail $err "$what succeeded, but should have failed"
498 else
499 check_err $err "$what failed"
500 fi
501}
502
503xfail()
504{
505 FAIL_TO_XFAIL=yes "$@"
506}
507
508xfail_on_slow()
509{
510 if [[ $KSFT_MACHINE_SLOW = yes ]]; then
511 FAIL_TO_XFAIL=yes "$@"
512 else
513 "$@"
514 fi
515}
516
517xfail_on_veth()
518{
519 local dev=$1; shift
520 local kind
521
522 kind=$(ip -j -d link show dev $dev |
523 jq -r '.[].linkinfo.info_kind')
524 if [[ $kind = veth ]]; then
525 FAIL_TO_XFAIL=yes "$@"
526 else
527 "$@"
528 fi
529}
530
531log_test_result()
532{
533 local test_name=$1; shift
534 local opt_str=$1; shift
535 local result=$1; shift
536 local retmsg=$1; shift
537
538 printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result"
539 if [[ $retmsg ]]; then
540 printf "\t%s\n" "$retmsg"
541 fi
542}
543
544pause_on_fail()
545{
546 if [[ $PAUSE_ON_FAIL == yes ]]; then
547 echo "Hit enter to continue, 'q' to quit"
548 read a
549 [[ $a == q ]] && exit 1
550 fi
551}
552
553handle_test_result_pass()
554{
555 local test_name=$1; shift
556 local opt_str=$1; shift
557
558 log_test_result "$test_name" "$opt_str" " OK "
559}
560
561handle_test_result_fail()
562{
563 local test_name=$1; shift
564 local opt_str=$1; shift
565
566 log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
567 pause_on_fail
568}
569
570handle_test_result_xfail()
571{
572 local test_name=$1; shift
573 local opt_str=$1; shift
574
575 log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
576 pause_on_fail
577}
578
579handle_test_result_skip()
580{
581 local test_name=$1; shift
582 local opt_str=$1; shift
583
584 log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
585}
586
587log_test()
588{
589 local test_name=$1
590 local opt_str=$2
591
592 if [[ $# -eq 2 ]]; then
593 opt_str="($opt_str)"
594 fi
595
596 if ((RET == ksft_pass)); then
597 handle_test_result_pass "$test_name" "$opt_str"
598 elif ((RET == ksft_xfail)); then
599 handle_test_result_xfail "$test_name" "$opt_str"
600 elif ((RET == ksft_skip)); then
601 handle_test_result_skip "$test_name" "$opt_str"
602 else
603 handle_test_result_fail "$test_name" "$opt_str"
604 fi
605
606 EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
607 return $RET
608}
609
610log_test_skip()
611{
612 RET=$ksft_skip retmsg= log_test "$@"
613}
614
615log_test_xfail()
616{
617 RET=$ksft_xfail retmsg= log_test "$@"
618}
619
620log_info()
621{
622 local msg=$1
623
624 echo "INFO: $msg"
625}
626
627not()
628{
629 "$@"
630 [[ $? != 0 ]]
631}
632
633get_max()
634{
635 local arr=("$@")
636
637 max=${arr[0]}
638 for cur in ${arr[@]}; do
639 if [[ $cur -gt $max ]]; then
640 max=$cur
641 fi
642 done
643
644 echo $max
645}
646
647grep_bridge_fdb()
648{
649 local addr=$1; shift
650 local word
651 local flag
652
653 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
654 word=$1; shift
655 if [ "$1" == "-v" ]; then
656 flag=$1; shift
657 fi
658 fi
659
660 $@ | grep $addr | grep $flag "$word"
661}
662
663wait_for_port_up()
664{
665 "$@" | grep -q "Link detected: yes"
666}
667
668wait_for_offload()
669{
670 "$@" | grep -q offload
671}
672
673wait_for_trap()
674{
675 "$@" | grep -q trap
676}
677
678setup_wait_dev()
679{
680 local dev=$1; shift
681 local wait_time=${1:-$WAIT_TIME}; shift
682
683 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
684
685 if (($?)); then
686 check_err 1
687 log_test setup_wait_dev ": Interface $dev does not come up."
688 exit 1
689 fi
690}
691
692setup_wait_dev_with_timeout()
693{
694 local dev=$1; shift
695 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
696 local wait_time=${1:-$WAIT_TIME}; shift
697 local i
698
699 for ((i = 1; i <= $max_iterations; ++i)); do
700 ip link show dev $dev up \
701 | grep 'state UP' &> /dev/null
702 if [[ $? -ne 0 ]]; then
703 sleep 1
704 else
705 sleep $wait_time
706 return 0
707 fi
708 done
709
710 return 1
711}
712
713setup_wait()
714{
715 local num_netifs=${1:-$NUM_NETIFS}
716 local i
717
718 for ((i = 1; i <= num_netifs; ++i)); do
719 setup_wait_dev ${NETIFS[p$i]} 0
720 done
721
722 # Make sure links are ready.
723 sleep $WAIT_TIME
724}
725
726wait_for_dev()
727{
728 local dev=$1; shift
729 local timeout=${1:-$WAIT_TIMEOUT}; shift
730
731 slowwait $timeout ip link show dev $dev &> /dev/null
732 if (( $? )); then
733 check_err 1
734 log_test wait_for_dev "Interface $dev did not appear."
735 exit $EXIT_STATUS
736 fi
737}
738
739cmd_jq()
740{
741 local cmd=$1
742 local jq_exp=$2
743 local jq_opts=$3
744 local ret
745 local output
746
747 output="$($cmd)"
748 # it the command fails, return error right away
749 ret=$?
750 if [[ $ret -ne 0 ]]; then
751 return $ret
752 fi
753 output=$(echo $output | jq -r $jq_opts "$jq_exp")
754 ret=$?
755 if [[ $ret -ne 0 ]]; then
756 return $ret
757 fi
758 echo $output
759 # return success only in case of non-empty output
760 [ ! -z "$output" ]
761}
762
763pre_cleanup()
764{
765 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
766 echo "Pausing before cleanup, hit any key to continue"
767 read
768 fi
769
770 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
771 mac_addr_restore
772 fi
773}
774
775vrf_prepare()
776{
777 ip -4 rule add pref 32765 table local
778 ip -4 rule del pref 0
779 ip -6 rule add pref 32765 table local
780 ip -6 rule del pref 0
781}
782
783vrf_cleanup()
784{
785 ip -6 rule add pref 0 table local
786 ip -6 rule del pref 32765
787 ip -4 rule add pref 0 table local
788 ip -4 rule del pref 32765
789}
790
791__last_tb_id=0
792declare -A __TB_IDS
793
794__vrf_td_id_assign()
795{
796 local vrf_name=$1
797
798 __last_tb_id=$((__last_tb_id + 1))
799 __TB_IDS[$vrf_name]=$__last_tb_id
800 return $__last_tb_id
801}
802
803__vrf_td_id_lookup()
804{
805 local vrf_name=$1
806
807 return ${__TB_IDS[$vrf_name]}
808}
809
810vrf_create()
811{
812 local vrf_name=$1
813 local tb_id
814
815 __vrf_td_id_assign $vrf_name
816 tb_id=$?
817
818 ip link add dev $vrf_name type vrf table $tb_id
819 ip -4 route add table $tb_id unreachable default metric 4278198272
820 ip -6 route add table $tb_id unreachable default metric 4278198272
821}
822
823vrf_destroy()
824{
825 local vrf_name=$1
826 local tb_id
827
828 __vrf_td_id_lookup $vrf_name
829 tb_id=$?
830
831 ip -6 route del table $tb_id unreachable default metric 4278198272
832 ip -4 route del table $tb_id unreachable default metric 4278198272
833 ip link del dev $vrf_name
834}
835
836__addr_add_del()
837{
838 local if_name=$1
839 local add_del=$2
840 local array
841
842 shift
843 shift
844 array=("${@}")
845
846 for addrstr in "${array[@]}"; do
847 ip address $add_del $addrstr dev $if_name
848 done
849}
850
851__simple_if_init()
852{
853 local if_name=$1; shift
854 local vrf_name=$1; shift
855 local addrs=("${@}")
856
857 ip link set dev $if_name master $vrf_name
858 ip link set dev $if_name up
859
860 __addr_add_del $if_name add "${addrs[@]}"
861}
862
863__simple_if_fini()
864{
865 local if_name=$1; shift
866 local addrs=("${@}")
867
868 __addr_add_del $if_name del "${addrs[@]}"
869
870 ip link set dev $if_name down
871 ip link set dev $if_name nomaster
872}
873
874simple_if_init()
875{
876 local if_name=$1
877 local vrf_name
878 local array
879
880 shift
881 vrf_name=v$if_name
882 array=("${@}")
883
884 vrf_create $vrf_name
885 ip link set dev $vrf_name up
886 __simple_if_init $if_name $vrf_name "${array[@]}"
887}
888
889simple_if_fini()
890{
891 local if_name=$1
892 local vrf_name
893 local array
894
895 shift
896 vrf_name=v$if_name
897 array=("${@}")
898
899 __simple_if_fini $if_name "${array[@]}"
900 vrf_destroy $vrf_name
901}
902
903tunnel_create()
904{
905 local name=$1; shift
906 local type=$1; shift
907 local local=$1; shift
908 local remote=$1; shift
909
910 ip link add name $name type $type \
911 local $local remote $remote "$@"
912 ip link set dev $name up
913}
914
915tunnel_destroy()
916{
917 local name=$1; shift
918
919 ip link del dev $name
920}
921
922vlan_create()
923{
924 local if_name=$1; shift
925 local vid=$1; shift
926 local vrf=$1; shift
927 local ips=("${@}")
928 local name=$if_name.$vid
929
930 ip link add name $name link $if_name type vlan id $vid
931 if [ "$vrf" != "" ]; then
932 ip link set dev $name master $vrf
933 fi
934 ip link set dev $name up
935 __addr_add_del $name add "${ips[@]}"
936}
937
938vlan_destroy()
939{
940 local if_name=$1; shift
941 local vid=$1; shift
942 local name=$if_name.$vid
943
944 ip link del dev $name
945}
946
947team_create()
948{
949 local if_name=$1; shift
950 local mode=$1; shift
951
952 require_command $TEAMD
953 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
954 for slave in "$@"; do
955 ip link set dev $slave down
956 ip link set dev $slave master $if_name
957 ip link set dev $slave up
958 done
959 ip link set dev $if_name up
960}
961
962team_destroy()
963{
964 local if_name=$1; shift
965
966 $TEAMD -t $if_name -k
967}
968
969master_name_get()
970{
971 local if_name=$1
972
973 ip -j link show dev $if_name | jq -r '.[]["master"]'
974}
975
976link_stats_get()
977{
978 local if_name=$1; shift
979 local dir=$1; shift
980 local stat=$1; shift
981
982 ip -j -s link show dev $if_name \
983 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
984}
985
986link_stats_tx_packets_get()
987{
988 link_stats_get $1 tx packets
989}
990
991link_stats_rx_errors_get()
992{
993 link_stats_get $1 rx errors
994}
995
996ethtool_stats_get()
997{
998 local dev=$1; shift
999 local stat=$1; shift
1000
1001 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
1002}
1003
1004ethtool_std_stats_get()
1005{
1006 local dev=$1; shift
1007 local grp=$1; shift
1008 local name=$1; shift
1009 local src=$1; shift
1010
1011 ethtool --json -S $dev --groups $grp -- --src $src | \
1012 jq '.[]."'"$grp"'"."'$name'"'
1013}
1014
1015qdisc_stats_get()
1016{
1017 local dev=$1; shift
1018 local handle=$1; shift
1019 local selector=$1; shift
1020
1021 tc -j -s qdisc show dev "$dev" \
1022 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
1023}
1024
1025qdisc_parent_stats_get()
1026{
1027 local dev=$1; shift
1028 local parent=$1; shift
1029 local selector=$1; shift
1030
1031 tc -j -s qdisc show dev "$dev" invisible \
1032 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
1033}
1034
1035ipv6_stats_get()
1036{
1037 local dev=$1; shift
1038 local stat=$1; shift
1039
1040 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
1041}
1042
1043hw_stats_get()
1044{
1045 local suite=$1; shift
1046 local if_name=$1; shift
1047 local dir=$1; shift
1048 local stat=$1; shift
1049
1050 ip -j stats show dev $if_name group offload subgroup $suite |
1051 jq ".[0].stats64.$dir.$stat"
1052}
1053
1054__nh_stats_get()
1055{
1056 local key=$1; shift
1057 local group_id=$1; shift
1058 local member_id=$1; shift
1059
1060 ip -j -s -s nexthop show id $group_id |
1061 jq --argjson member_id "$member_id" --arg key "$key" \
1062 '.[].group_stats[] | select(.id == $member_id) | .[$key]'
1063}
1064
1065nh_stats_get()
1066{
1067 local group_id=$1; shift
1068 local member_id=$1; shift
1069
1070 __nh_stats_get packets "$group_id" "$member_id"
1071}
1072
1073nh_stats_get_hw()
1074{
1075 local group_id=$1; shift
1076 local member_id=$1; shift
1077
1078 __nh_stats_get packets_hw "$group_id" "$member_id"
1079}
1080
1081humanize()
1082{
1083 local speed=$1; shift
1084
1085 for unit in bps Kbps Mbps Gbps; do
1086 if (($(echo "$speed < 1024" | bc))); then
1087 break
1088 fi
1089
1090 speed=$(echo "scale=1; $speed / 1024" | bc)
1091 done
1092
1093 echo "$speed${unit}"
1094}
1095
1096rate()
1097{
1098 local t0=$1; shift
1099 local t1=$1; shift
1100 local interval=$1; shift
1101
1102 echo $((8 * (t1 - t0) / interval))
1103}
1104
1105packets_rate()
1106{
1107 local t0=$1; shift
1108 local t1=$1; shift
1109 local interval=$1; shift
1110
1111 echo $(((t1 - t0) / interval))
1112}
1113
1114mac_get()
1115{
1116 local if_name=$1
1117
1118 ip -j link show dev $if_name | jq -r '.[]["address"]'
1119}
1120
1121ether_addr_to_u64()
1122{
1123 local addr="$1"
1124 local order="$((1 << 40))"
1125 local val=0
1126 local byte
1127
1128 addr="${addr//:/ }"
1129
1130 for byte in $addr; do
1131 byte="0x$byte"
1132 val=$((val + order * byte))
1133 order=$((order >> 8))
1134 done
1135
1136 printf "0x%x" $val
1137}
1138
1139u64_to_ether_addr()
1140{
1141 local val=$1
1142 local byte
1143 local i
1144
1145 for ((i = 40; i >= 0; i -= 8)); do
1146 byte=$(((val & (0xff << i)) >> i))
1147 printf "%02x" $byte
1148 if [ $i -ne 0 ]; then
1149 printf ":"
1150 fi
1151 done
1152}
1153
1154ipv6_lladdr_get()
1155{
1156 local if_name=$1
1157
1158 ip -j addr show dev $if_name | \
1159 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
1160 head -1
1161}
1162
1163bridge_ageing_time_get()
1164{
1165 local bridge=$1
1166 local ageing_time
1167
1168 # Need to divide by 100 to convert to seconds.
1169 ageing_time=$(ip -j -d link show dev $bridge \
1170 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
1171 echo $((ageing_time / 100))
1172}
1173
1174declare -A SYSCTL_ORIG
1175sysctl_save()
1176{
1177 local key=$1; shift
1178
1179 SYSCTL_ORIG[$key]=$(sysctl -n $key)
1180}
1181
1182sysctl_set()
1183{
1184 local key=$1; shift
1185 local value=$1; shift
1186
1187 sysctl_save "$key"
1188 sysctl -qw $key="$value"
1189}
1190
1191sysctl_restore()
1192{
1193 local key=$1; shift
1194
1195 sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1196}
1197
1198forwarding_enable()
1199{
1200 sysctl_set net.ipv4.conf.all.forwarding 1
1201 sysctl_set net.ipv6.conf.all.forwarding 1
1202}
1203
1204forwarding_restore()
1205{
1206 sysctl_restore net.ipv6.conf.all.forwarding
1207 sysctl_restore net.ipv4.conf.all.forwarding
1208}
1209
1210declare -A MTU_ORIG
1211mtu_set()
1212{
1213 local dev=$1; shift
1214 local mtu=$1; shift
1215
1216 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
1217 ip link set dev $dev mtu $mtu
1218}
1219
1220mtu_restore()
1221{
1222 local dev=$1; shift
1223
1224 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1225}
1226
1227tc_offload_check()
1228{
1229 local num_netifs=${1:-$NUM_NETIFS}
1230
1231 for ((i = 1; i <= num_netifs; ++i)); do
1232 ethtool -k ${NETIFS[p$i]} \
1233 | grep "hw-tc-offload: on" &> /dev/null
1234 if [[ $? -ne 0 ]]; then
1235 return 1
1236 fi
1237 done
1238
1239 return 0
1240}
1241
1242trap_install()
1243{
1244 local dev=$1; shift
1245 local direction=$1; shift
1246
1247 # Some devices may not support or need in-hardware trapping of traffic
1248 # (e.g. the veth pairs that this library creates for non-existent
1249 # loopbacks). Use continue instead, so that there is a filter in there
1250 # (some tests check counters), and so that other filters are still
1251 # processed.
1252 tc filter add dev $dev $direction pref 1 \
1253 flower skip_sw action trap 2>/dev/null \
1254 || tc filter add dev $dev $direction pref 1 \
1255 flower action continue
1256}
1257
1258trap_uninstall()
1259{
1260 local dev=$1; shift
1261 local direction=$1; shift
1262
1263 tc filter del dev $dev $direction pref 1 flower
1264}
1265
1266__icmp_capture_add_del()
1267{
1268 local add_del=$1; shift
1269 local pref=$1; shift
1270 local vsuf=$1; shift
1271 local tundev=$1; shift
1272 local filter=$1; shift
1273
1274 tc filter $add_del dev "$tundev" ingress \
1275 proto ip$vsuf pref $pref \
1276 flower ip_proto icmp$vsuf $filter \
1277 action pass
1278}
1279
1280icmp_capture_install()
1281{
1282 local tundev=$1; shift
1283 local filter=$1; shift
1284
1285 __icmp_capture_add_del add 100 "" "$tundev" "$filter"
1286}
1287
1288icmp_capture_uninstall()
1289{
1290 local tundev=$1; shift
1291 local filter=$1; shift
1292
1293 __icmp_capture_add_del del 100 "" "$tundev" "$filter"
1294}
1295
1296icmp6_capture_install()
1297{
1298 local tundev=$1; shift
1299 local filter=$1; shift
1300
1301 __icmp_capture_add_del add 100 v6 "$tundev" "$filter"
1302}
1303
1304icmp6_capture_uninstall()
1305{
1306 local tundev=$1; shift
1307 local filter=$1; shift
1308
1309 __icmp_capture_add_del del 100 v6 "$tundev" "$filter"
1310}
1311
1312__vlan_capture_add_del()
1313{
1314 local add_del=$1; shift
1315 local pref=$1; shift
1316 local dev=$1; shift
1317 local filter=$1; shift
1318
1319 tc filter $add_del dev "$dev" ingress \
1320 proto 802.1q pref $pref \
1321 flower $filter \
1322 action pass
1323}
1324
1325vlan_capture_install()
1326{
1327 local dev=$1; shift
1328 local filter=$1; shift
1329
1330 __vlan_capture_add_del add 100 "$dev" "$filter"
1331}
1332
1333vlan_capture_uninstall()
1334{
1335 local dev=$1; shift
1336 local filter=$1; shift
1337
1338 __vlan_capture_add_del del 100 "$dev" "$filter"
1339}
1340
1341__dscp_capture_add_del()
1342{
1343 local add_del=$1; shift
1344 local dev=$1; shift
1345 local base=$1; shift
1346 local dscp;
1347
1348 for prio in {0..7}; do
1349 dscp=$((base + prio))
1350 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1351 "skip_hw ip_tos $((dscp << 2))"
1352 done
1353}
1354
1355dscp_capture_install()
1356{
1357 local dev=$1; shift
1358 local base=$1; shift
1359
1360 __dscp_capture_add_del add $dev $base
1361}
1362
1363dscp_capture_uninstall()
1364{
1365 local dev=$1; shift
1366 local base=$1; shift
1367
1368 __dscp_capture_add_del del $dev $base
1369}
1370
1371dscp_fetch_stats()
1372{
1373 local dev=$1; shift
1374 local base=$1; shift
1375
1376 for prio in {0..7}; do
1377 local dscp=$((base + prio))
1378 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1379 echo "[$dscp]=$t "
1380 done
1381}
1382
1383matchall_sink_create()
1384{
1385 local dev=$1; shift
1386
1387 tc qdisc add dev $dev clsact
1388 tc filter add dev $dev ingress \
1389 pref 10000 \
1390 matchall \
1391 action drop
1392}
1393
1394tests_run()
1395{
1396 local current_test
1397
1398 for current_test in ${TESTS:-$ALL_TESTS}; do
1399 $current_test
1400 done
1401}
1402
1403multipath_eval()
1404{
1405 local desc="$1"
1406 local weight_rp12=$2
1407 local weight_rp13=$3
1408 local packets_rp12=$4
1409 local packets_rp13=$5
1410 local weights_ratio packets_ratio diff
1411
1412 RET=0
1413
1414 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1415 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1416 | bc -l)
1417 else
1418 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1419 | bc -l)
1420 fi
1421
1422 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1423 check_err 1 "Packet difference is 0"
1424 log_test "Multipath"
1425 log_info "Expected ratio $weights_ratio"
1426 return
1427 fi
1428
1429 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1430 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1431 | bc -l)
1432 else
1433 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1434 | bc -l)
1435 fi
1436
1437 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1438 diff=${diff#-}
1439
1440 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1441 check_err $? "Too large discrepancy between expected and measured ratios"
1442 log_test "$desc"
1443 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1444}
1445
1446in_ns()
1447{
1448 local name=$1; shift
1449
1450 ip netns exec $name bash <<-EOF
1451 NUM_NETIFS=0
1452 source lib.sh
1453 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1454 EOF
1455}
1456
1457##############################################################################
1458# Tests
1459
1460ping_do()
1461{
1462 local if_name=$1
1463 local dip=$2
1464 local args=$3
1465 local vrf_name
1466
1467 vrf_name=$(master_name_get $if_name)
1468 ip vrf exec $vrf_name \
1469 $PING $args $dip -c $PING_COUNT -i 0.1 \
1470 -w $PING_TIMEOUT &> /dev/null
1471}
1472
1473ping_test()
1474{
1475 RET=0
1476
1477 ping_do $1 $2
1478 check_err $?
1479 log_test "ping$3"
1480}
1481
1482ping_test_fails()
1483{
1484 RET=0
1485
1486 ping_do $1 $2
1487 check_fail $?
1488 log_test "ping fails$3"
1489}
1490
1491ping6_do()
1492{
1493 local if_name=$1
1494 local dip=$2
1495 local args=$3
1496 local vrf_name
1497
1498 vrf_name=$(master_name_get $if_name)
1499 ip vrf exec $vrf_name \
1500 $PING6 $args $dip -c $PING_COUNT -i 0.1 \
1501 -w $PING_TIMEOUT &> /dev/null
1502}
1503
1504ping6_test()
1505{
1506 RET=0
1507
1508 ping6_do $1 $2
1509 check_err $?
1510 log_test "ping6$3"
1511}
1512
1513ping6_test_fails()
1514{
1515 RET=0
1516
1517 ping6_do $1 $2
1518 check_fail $?
1519 log_test "ping6 fails$3"
1520}
1521
1522learning_test()
1523{
1524 local bridge=$1
1525 local br_port1=$2 # Connected to `host1_if`.
1526 local host1_if=$3
1527 local host2_if=$4
1528 local mac=de:ad:be:ef:13:37
1529 local ageing_time
1530
1531 RET=0
1532
1533 bridge -j fdb show br $bridge brport $br_port1 \
1534 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1535 check_fail $? "Found FDB record when should not"
1536
1537 # Disable unknown unicast flooding on `br_port1` to make sure
1538 # packets are only forwarded through the port after a matching
1539 # FDB entry was installed.
1540 bridge link set dev $br_port1 flood off
1541
1542 ip link set $host1_if promisc on
1543 tc qdisc add dev $host1_if ingress
1544 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1545 flower dst_mac $mac action drop
1546
1547 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1548 sleep 1
1549
1550 tc -j -s filter show dev $host1_if ingress \
1551 | jq -e ".[] | select(.options.handle == 101) \
1552 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1553 check_fail $? "Packet reached first host when should not"
1554
1555 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1556 sleep 1
1557
1558 bridge -j fdb show br $bridge brport $br_port1 \
1559 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1560 check_err $? "Did not find FDB record when should"
1561
1562 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1563 sleep 1
1564
1565 tc -j -s filter show dev $host1_if ingress \
1566 | jq -e ".[] | select(.options.handle == 101) \
1567 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1568 check_err $? "Packet did not reach second host when should"
1569
1570 # Wait for 10 seconds after the ageing time to make sure FDB
1571 # record was aged-out.
1572 ageing_time=$(bridge_ageing_time_get $bridge)
1573 sleep $((ageing_time + 10))
1574
1575 bridge -j fdb show br $bridge brport $br_port1 \
1576 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1577 check_fail $? "Found FDB record when should not"
1578
1579 bridge link set dev $br_port1 learning off
1580
1581 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1582 sleep 1
1583
1584 bridge -j fdb show br $bridge brport $br_port1 \
1585 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1586 check_fail $? "Found FDB record when should not"
1587
1588 bridge link set dev $br_port1 learning on
1589
1590 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1591 tc qdisc del dev $host1_if ingress
1592 ip link set $host1_if promisc off
1593
1594 bridge link set dev $br_port1 flood on
1595
1596 log_test "FDB learning"
1597}
1598
1599flood_test_do()
1600{
1601 local should_flood=$1
1602 local mac=$2
1603 local ip=$3
1604 local host1_if=$4
1605 local host2_if=$5
1606 local err=0
1607
1608 # Add an ACL on `host2_if` which will tell us whether the packet
1609 # was flooded to it or not.
1610 ip link set $host2_if promisc on
1611 tc qdisc add dev $host2_if ingress
1612 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1613 flower dst_mac $mac action drop
1614
1615 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1616 sleep 1
1617
1618 tc -j -s filter show dev $host2_if ingress \
1619 | jq -e ".[] | select(.options.handle == 101) \
1620 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1621 if [[ $? -ne 0 && $should_flood == "true" || \
1622 $? -eq 0 && $should_flood == "false" ]]; then
1623 err=1
1624 fi
1625
1626 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1627 tc qdisc del dev $host2_if ingress
1628 ip link set $host2_if promisc off
1629
1630 return $err
1631}
1632
1633flood_unicast_test()
1634{
1635 local br_port=$1
1636 local host1_if=$2
1637 local host2_if=$3
1638 local mac=de:ad:be:ef:13:37
1639 local ip=192.0.2.100
1640
1641 RET=0
1642
1643 bridge link set dev $br_port flood off
1644
1645 flood_test_do false $mac $ip $host1_if $host2_if
1646 check_err $? "Packet flooded when should not"
1647
1648 bridge link set dev $br_port flood on
1649
1650 flood_test_do true $mac $ip $host1_if $host2_if
1651 check_err $? "Packet was not flooded when should"
1652
1653 log_test "Unknown unicast flood"
1654}
1655
1656flood_multicast_test()
1657{
1658 local br_port=$1
1659 local host1_if=$2
1660 local host2_if=$3
1661 local mac=01:00:5e:00:00:01
1662 local ip=239.0.0.1
1663
1664 RET=0
1665
1666 bridge link set dev $br_port mcast_flood off
1667
1668 flood_test_do false $mac $ip $host1_if $host2_if
1669 check_err $? "Packet flooded when should not"
1670
1671 bridge link set dev $br_port mcast_flood on
1672
1673 flood_test_do true $mac $ip $host1_if $host2_if
1674 check_err $? "Packet was not flooded when should"
1675
1676 log_test "Unregistered multicast flood"
1677}
1678
1679flood_test()
1680{
1681 # `br_port` is connected to `host2_if`
1682 local br_port=$1
1683 local host1_if=$2
1684 local host2_if=$3
1685
1686 flood_unicast_test $br_port $host1_if $host2_if
1687 flood_multicast_test $br_port $host1_if $host2_if
1688}
1689
1690__start_traffic()
1691{
1692 local pktsize=$1; shift
1693 local proto=$1; shift
1694 local h_in=$1; shift # Where the traffic egresses the host
1695 local sip=$1; shift
1696 local dip=$1; shift
1697 local dmac=$1; shift
1698 local -a mz_args=("$@")
1699
1700 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1701 -a own -b $dmac -t "$proto" -q "${mz_args[@]}" &
1702 sleep 1
1703}
1704
1705start_traffic_pktsize()
1706{
1707 local pktsize=$1; shift
1708 local h_in=$1; shift
1709 local sip=$1; shift
1710 local dip=$1; shift
1711 local dmac=$1; shift
1712 local -a mz_args=("$@")
1713
1714 __start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \
1715 "${mz_args[@]}"
1716}
1717
1718start_tcp_traffic_pktsize()
1719{
1720 local pktsize=$1; shift
1721 local h_in=$1; shift
1722 local sip=$1; shift
1723 local dip=$1; shift
1724 local dmac=$1; shift
1725 local -a mz_args=("$@")
1726
1727 __start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \
1728 "${mz_args[@]}"
1729}
1730
1731start_traffic()
1732{
1733 local h_in=$1; shift
1734 local sip=$1; shift
1735 local dip=$1; shift
1736 local dmac=$1; shift
1737 local -a mz_args=("$@")
1738
1739 start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1740 "${mz_args[@]}"
1741}
1742
1743start_tcp_traffic()
1744{
1745 local h_in=$1; shift
1746 local sip=$1; shift
1747 local dip=$1; shift
1748 local dmac=$1; shift
1749 local -a mz_args=("$@")
1750
1751 start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1752 "${mz_args[@]}"
1753}
1754
1755stop_traffic()
1756{
1757 # Suppress noise from killing mausezahn.
1758 { kill %% && wait %%; } 2>/dev/null
1759}
1760
1761declare -A cappid
1762declare -A capfile
1763declare -A capout
1764
1765tcpdump_start()
1766{
1767 local if_name=$1; shift
1768 local ns=$1; shift
1769
1770 capfile[$if_name]=$(mktemp)
1771 capout[$if_name]=$(mktemp)
1772
1773 if [ -z $ns ]; then
1774 ns_cmd=""
1775 else
1776 ns_cmd="ip netns exec ${ns}"
1777 fi
1778
1779 if [ -z $SUDO_USER ] ; then
1780 capuser=""
1781 else
1782 capuser="-Z $SUDO_USER"
1783 fi
1784
1785 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1786 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1787 > "${capout[$if_name]}" 2>&1 &
1788 cappid[$if_name]=$!
1789
1790 sleep 1
1791}
1792
1793tcpdump_stop()
1794{
1795 local if_name=$1
1796 local pid=${cappid[$if_name]}
1797
1798 $ns_cmd kill "$pid" && wait "$pid"
1799 sleep 1
1800}
1801
1802tcpdump_cleanup()
1803{
1804 local if_name=$1
1805
1806 rm ${capfile[$if_name]} ${capout[$if_name]}
1807}
1808
1809tcpdump_show()
1810{
1811 local if_name=$1
1812
1813 tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1814}
1815
1816# return 0 if the packet wasn't seen on host2_if or 1 if it was
1817mcast_packet_test()
1818{
1819 local mac=$1
1820 local src_ip=$2
1821 local ip=$3
1822 local host1_if=$4
1823 local host2_if=$5
1824 local seen=0
1825 local tc_proto="ip"
1826 local mz_v6arg=""
1827
1828 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1829 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1830 tc_proto="ipv6"
1831 mz_v6arg="-6"
1832 fi
1833
1834 # Add an ACL on `host2_if` which will tell us whether the packet
1835 # was received by it or not.
1836 tc qdisc add dev $host2_if ingress
1837 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1838 flower ip_proto udp dst_mac $mac action drop
1839
1840 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1841 sleep 1
1842
1843 tc -j -s filter show dev $host2_if ingress \
1844 | jq -e ".[] | select(.options.handle == 101) \
1845 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1846 if [[ $? -eq 0 ]]; then
1847 seen=1
1848 fi
1849
1850 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1851 tc qdisc del dev $host2_if ingress
1852
1853 return $seen
1854}
1855
1856brmcast_check_sg_entries()
1857{
1858 local report=$1; shift
1859 local slist=("$@")
1860 local sarg=""
1861
1862 for src in "${slist[@]}"; do
1863 sarg="${sarg} and .source_list[].address == \"$src\""
1864 done
1865 bridge -j -d -s mdb show dev br0 \
1866 | jq -e ".[].mdb[] | \
1867 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1868 check_err $? "Wrong *,G entry source list after $report report"
1869
1870 for sgent in "${slist[@]}"; do
1871 bridge -j -d -s mdb show dev br0 \
1872 | jq -e ".[].mdb[] | \
1873 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1874 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1875 done
1876}
1877
1878brmcast_check_sg_fwding()
1879{
1880 local should_fwd=$1; shift
1881 local sources=("$@")
1882
1883 for src in "${sources[@]}"; do
1884 local retval=0
1885
1886 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1887 retval=$?
1888 if [ $should_fwd -eq 1 ]; then
1889 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1890 else
1891 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1892 fi
1893 done
1894}
1895
1896brmcast_check_sg_state()
1897{
1898 local is_blocked=$1; shift
1899 local sources=("$@")
1900 local should_fail=1
1901
1902 if [ $is_blocked -eq 1 ]; then
1903 should_fail=0
1904 fi
1905
1906 for src in "${sources[@]}"; do
1907 bridge -j -d -s mdb show dev br0 \
1908 | jq -e ".[].mdb[] | \
1909 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1910 .source_list[] |
1911 select(.address == \"$src\") |
1912 select(.timer == \"0.00\")" &>/dev/null
1913 check_err_fail $should_fail $? "Entry $src has zero timer"
1914
1915 bridge -j -d -s mdb show dev br0 \
1916 | jq -e ".[].mdb[] | \
1917 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1918 .flags[] == \"blocked\")" &>/dev/null
1919 check_err_fail $should_fail $? "Entry $src has blocked flag"
1920 done
1921}
1922
1923mc_join()
1924{
1925 local if_name=$1
1926 local group=$2
1927 local vrf_name=$(master_name_get $if_name)
1928
1929 # We don't care about actual reception, just about joining the
1930 # IP multicast group and adding the L2 address to the device's
1931 # MAC filtering table
1932 ip vrf exec $vrf_name \
1933 mreceive -g $group -I $if_name > /dev/null 2>&1 &
1934 mreceive_pid=$!
1935
1936 sleep 1
1937}
1938
1939mc_leave()
1940{
1941 kill "$mreceive_pid" && wait "$mreceive_pid"
1942}
1943
1944mc_send()
1945{
1946 local if_name=$1
1947 local groups=$2
1948 local vrf_name=$(master_name_get $if_name)
1949
1950 ip vrf exec $vrf_name \
1951 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1952}
1953
1954start_ip_monitor()
1955{
1956 local mtype=$1; shift
1957 local ip=${1-ip}; shift
1958
1959 # start the monitor in the background
1960 tmpfile=`mktemp /var/run/nexthoptestXXX`
1961 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1962 sleep 0.2
1963 echo "$mpid $tmpfile"
1964}
1965
1966stop_ip_monitor()
1967{
1968 local mpid=$1; shift
1969 local tmpfile=$1; shift
1970 local el=$1; shift
1971 local what=$1; shift
1972
1973 sleep 0.2
1974 kill $mpid
1975 local lines=`grep '^\w' $tmpfile | wc -l`
1976 test $lines -eq $el
1977 check_err $? "$what: $lines lines of events, expected $el"
1978 rm -rf $tmpfile
1979}
1980
1981hw_stats_monitor_test()
1982{
1983 local dev=$1; shift
1984 local type=$1; shift
1985 local make_suitable=$1; shift
1986 local make_unsuitable=$1; shift
1987 local ip=${1-ip}; shift
1988
1989 RET=0
1990
1991 # Expect a notification about enablement.
1992 local ipmout=$(start_ip_monitor stats "$ip")
1993 $ip stats set dev $dev ${type}_stats on
1994 stop_ip_monitor $ipmout 1 "${type}_stats enablement"
1995
1996 # Expect a notification about offload.
1997 local ipmout=$(start_ip_monitor stats "$ip")
1998 $make_suitable
1999 stop_ip_monitor $ipmout 1 "${type}_stats installation"
2000
2001 # Expect a notification about loss of offload.
2002 local ipmout=$(start_ip_monitor stats "$ip")
2003 $make_unsuitable
2004 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
2005
2006 # Expect a notification about disablement
2007 local ipmout=$(start_ip_monitor stats "$ip")
2008 $ip stats set dev $dev ${type}_stats off
2009 stop_ip_monitor $ipmout 1 "${type}_stats disablement"
2010
2011 log_test "${type}_stats notifications"
2012}
2013
2014ipv4_to_bytes()
2015{
2016 local IP=$1; shift
2017
2018 printf '%02x:' ${IP//./ } |
2019 sed 's/:$//'
2020}
2021
2022# Convert a given IPv6 address, `IP' such that the :: token, if present, is
2023# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal
2024# digits. An optional `BYTESEP' parameter can be given to further separate
2025# individual bytes of each 16-bit group.
2026expand_ipv6()
2027{
2028 local IP=$1; shift
2029 local bytesep=$1; shift
2030
2031 local cvt_ip=${IP/::/_}
2032 local colons=${cvt_ip//[^:]/}
2033 local allcol=:::::::
2034 # IP where :: -> the appropriate number of colons:
2035 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}
2036
2037 echo $allcol_ip | tr : '\n' |
2038 sed s/^/0000/ |
2039 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |
2040 tr '\n' : |
2041 sed 's/:$//'
2042}
2043
2044ipv6_to_bytes()
2045{
2046 local IP=$1; shift
2047
2048 expand_ipv6 "$IP" :
2049}
2050
2051u16_to_bytes()
2052{
2053 local u16=$1; shift
2054
2055 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
2056}
2057
2058# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
2059# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
2060# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
2061# stands for 00:00.
2062payload_template_calc_checksum()
2063{
2064 local payload=$1; shift
2065
2066 (
2067 # Set input radix.
2068 echo "16i"
2069 # Push zero for the initial checksum.
2070 echo 0
2071
2072 # Pad the payload with a terminating 00: in case we get an odd
2073 # number of bytes.
2074 echo "${payload%:}:00:" |
2075 sed 's/CHECKSUM/00:00/g' |
2076 tr '[:lower:]' '[:upper:]' |
2077 # Add the word to the checksum.
2078 sed 's/\(..\):\(..\):/\1\2+\n/g' |
2079 # Strip the extra odd byte we pushed if left unconverted.
2080 sed 's/\(..\):$//'
2081
2082 echo "10000 ~ +" # Calculate and add carry.
2083 echo "FFFF r - p" # Bit-flip and print.
2084 ) |
2085 dc |
2086 tr '[:upper:]' '[:lower:]'
2087}
2088
2089payload_template_expand_checksum()
2090{
2091 local payload=$1; shift
2092 local checksum=$1; shift
2093
2094 local ckbytes=$(u16_to_bytes $checksum)
2095
2096 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
2097}
2098
2099payload_template_nbytes()
2100{
2101 local payload=$1; shift
2102
2103 payload_template_expand_checksum "${payload%:}" 0 |
2104 sed 's/:/\n/g' | wc -l
2105}
2106
2107igmpv3_is_in_get()
2108{
2109 local GRP=$1; shift
2110 local sources=("$@")
2111
2112 local igmpv3
2113 local nsources=$(u16_to_bytes ${#sources[@]})
2114
2115 # IS_IN ( $sources )
2116 igmpv3=$(:
2117 )"22:"$( : Type - Membership Report
2118 )"00:"$( : Reserved
2119 )"CHECKSUM:"$( : Checksum
2120 )"00:00:"$( : Reserved
2121 )"00:01:"$( : Number of Group Records
2122 )"01:"$( : Record Type - IS_IN
2123 )"00:"$( : Aux Data Len
2124 )"${nsources}:"$( : Number of Sources
2125 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address
2126 )"$(for src in "${sources[@]}"; do
2127 ipv4_to_bytes $src
2128 echo -n :
2129 done)"$( : Source Addresses
2130 )
2131 local checksum=$(payload_template_calc_checksum "$igmpv3")
2132
2133 payload_template_expand_checksum "$igmpv3" $checksum
2134}
2135
2136igmpv2_leave_get()
2137{
2138 local GRP=$1; shift
2139
2140 local payload=$(:
2141 )"17:"$( : Type - Leave Group
2142 )"00:"$( : Max Resp Time - not meaningful
2143 )"CHECKSUM:"$( : Checksum
2144 )"$(ipv4_to_bytes $GRP)"$( : Group Address
2145 )
2146 local checksum=$(payload_template_calc_checksum "$payload")
2147
2148 payload_template_expand_checksum "$payload" $checksum
2149}
2150
2151mldv2_is_in_get()
2152{
2153 local SIP=$1; shift
2154 local GRP=$1; shift
2155 local sources=("$@")
2156
2157 local hbh
2158 local icmpv6
2159 local nsources=$(u16_to_bytes ${#sources[@]})
2160
2161 hbh=$(:
2162 )"3a:"$( : Next Header - ICMPv6
2163 )"00:"$( : Hdr Ext Len
2164 )"00:00:00:00:00:00:"$( : Options and Padding
2165 )
2166
2167 icmpv6=$(:
2168 )"8f:"$( : Type - MLDv2 Report
2169 )"00:"$( : Code
2170 )"CHECKSUM:"$( : Checksum
2171 )"00:00:"$( : Reserved
2172 )"00:01:"$( : Number of Group Records
2173 )"01:"$( : Record Type - IS_IN
2174 )"00:"$( : Aux Data Len
2175 )"${nsources}:"$( : Number of Sources
2176 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2177 )"$(for src in "${sources[@]}"; do
2178 ipv6_to_bytes $src
2179 echo -n :
2180 done)"$( : Source Addresses
2181 )
2182
2183 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2184 local sudohdr=$(:
2185 )"$(ipv6_to_bytes $SIP):"$( : SIP
2186 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2187 )"${len}:"$( : Upper-layer length
2188 )"00:3a:"$( : Zero and next-header
2189 )
2190 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2191
2192 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2193}
2194
2195mldv1_done_get()
2196{
2197 local SIP=$1; shift
2198 local GRP=$1; shift
2199
2200 local hbh
2201 local icmpv6
2202
2203 hbh=$(:
2204 )"3a:"$( : Next Header - ICMPv6
2205 )"00:"$( : Hdr Ext Len
2206 )"00:00:00:00:00:00:"$( : Options and Padding
2207 )
2208
2209 icmpv6=$(:
2210 )"84:"$( : Type - MLDv1 Done
2211 )"00:"$( : Code
2212 )"CHECKSUM:"$( : Checksum
2213 )"00:00:"$( : Max Resp Delay - not meaningful
2214 )"00:00:"$( : Reserved
2215 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2216 )
2217
2218 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2219 local sudohdr=$(:
2220 )"$(ipv6_to_bytes $SIP):"$( : SIP
2221 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2222 )"${len}:"$( : Upper-layer length
2223 )"00:3a:"$( : Zero and next-header
2224 )
2225 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2226
2227 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2228}
2229
2230bail_on_lldpad()
2231{
2232 local reason1="$1"; shift
2233 local reason2="$1"; shift
2234 local caller=${FUNCNAME[1]}
2235 local src=${BASH_SOURCE[1]}
2236
2237 if systemctl is-active --quiet lldpad; then
2238
2239 cat >/dev/stderr <<-EOF
2240 WARNING: lldpad is running
2241
2242 lldpad will likely $reason1, and this test will
2243 $reason2. Both are not supported at the same time,
2244 one of them is arbitrarily going to overwrite the
2245 other. That will cause spurious failures (or, unlikely,
2246 passes) of this test.
2247 EOF
2248
2249 if [[ -z $ALLOW_LLDPAD ]]; then
2250 cat >/dev/stderr <<-EOF
2251
2252 If you want to run the test anyway, please set
2253 an environment variable ALLOW_LLDPAD to a
2254 non-empty string.
2255 EOF
2256 log_test_skip $src:$caller
2257 exit $EXIT_STATUS
2258 else
2259 return
2260 fi
2261 fi
2262}
2263
2264absval()
2265{
2266 local v=$1; shift
2267
2268 echo $((v > 0 ? v : -v))
2269}
2270
2271has_unicast_flt()
2272{
2273 local dev=$1; shift
2274 local mac_addr=$(mac_get $dev)
2275 local tmp=$(ether_addr_to_u64 $mac_addr)
2276 local promisc
2277
2278 ip link set $dev up
2279 ip link add link $dev name macvlan-tmp type macvlan mode private
2280 ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
2281 ip link set macvlan-tmp up
2282
2283 promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
2284
2285 ip link del macvlan-tmp
2286
2287 [[ $promisc == 1 ]] && echo "no" || echo "yes"
2288}