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
517omit_on_slow()
518{
519 if [[ $KSFT_MACHINE_SLOW != yes ]]; then
520 "$@"
521 fi
522}
523
524xfail_on_veth()
525{
526 local dev=$1; shift
527 local kind
528
529 kind=$(ip -j -d link show dev $dev |
530 jq -r '.[].linkinfo.info_kind')
531 if [[ $kind = veth ]]; then
532 FAIL_TO_XFAIL=yes "$@"
533 else
534 "$@"
535 fi
536}
537
538log_test_result()
539{
540 local test_name=$1; shift
541 local opt_str=$1; shift
542 local result=$1; shift
543 local retmsg=$1; shift
544
545 printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result"
546 if [[ $retmsg ]]; then
547 printf "\t%s\n" "$retmsg"
548 fi
549}
550
551pause_on_fail()
552{
553 if [[ $PAUSE_ON_FAIL == yes ]]; then
554 echo "Hit enter to continue, 'q' to quit"
555 read a
556 [[ $a == q ]] && exit 1
557 fi
558}
559
560handle_test_result_pass()
561{
562 local test_name=$1; shift
563 local opt_str=$1; shift
564
565 log_test_result "$test_name" "$opt_str" " OK "
566}
567
568handle_test_result_fail()
569{
570 local test_name=$1; shift
571 local opt_str=$1; shift
572
573 log_test_result "$test_name" "$opt_str" FAIL "$retmsg"
574 pause_on_fail
575}
576
577handle_test_result_xfail()
578{
579 local test_name=$1; shift
580 local opt_str=$1; shift
581
582 log_test_result "$test_name" "$opt_str" XFAIL "$retmsg"
583 pause_on_fail
584}
585
586handle_test_result_skip()
587{
588 local test_name=$1; shift
589 local opt_str=$1; shift
590
591 log_test_result "$test_name" "$opt_str" SKIP "$retmsg"
592}
593
594log_test()
595{
596 local test_name=$1
597 local opt_str=$2
598
599 if [[ $# -eq 2 ]]; then
600 opt_str="($opt_str)"
601 fi
602
603 if ((RET == ksft_pass)); then
604 handle_test_result_pass "$test_name" "$opt_str"
605 elif ((RET == ksft_xfail)); then
606 handle_test_result_xfail "$test_name" "$opt_str"
607 elif ((RET == ksft_skip)); then
608 handle_test_result_skip "$test_name" "$opt_str"
609 else
610 handle_test_result_fail "$test_name" "$opt_str"
611 fi
612
613 EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET)
614 return $RET
615}
616
617log_test_skip()
618{
619 RET=$ksft_skip retmsg= log_test "$@"
620}
621
622log_test_xfail()
623{
624 RET=$ksft_xfail retmsg= log_test "$@"
625}
626
627log_info()
628{
629 local msg=$1
630
631 echo "INFO: $msg"
632}
633
634not()
635{
636 "$@"
637 [[ $? != 0 ]]
638}
639
640get_max()
641{
642 local arr=("$@")
643
644 max=${arr[0]}
645 for cur in ${arr[@]}; do
646 if [[ $cur -gt $max ]]; then
647 max=$cur
648 fi
649 done
650
651 echo $max
652}
653
654grep_bridge_fdb()
655{
656 local addr=$1; shift
657 local word
658 local flag
659
660 if [ "$1" == "self" ] || [ "$1" == "master" ]; then
661 word=$1; shift
662 if [ "$1" == "-v" ]; then
663 flag=$1; shift
664 fi
665 fi
666
667 $@ | grep $addr | grep $flag "$word"
668}
669
670wait_for_port_up()
671{
672 "$@" | grep -q "Link detected: yes"
673}
674
675wait_for_offload()
676{
677 "$@" | grep -q offload
678}
679
680wait_for_trap()
681{
682 "$@" | grep -q trap
683}
684
685setup_wait_dev()
686{
687 local dev=$1; shift
688 local wait_time=${1:-$WAIT_TIME}; shift
689
690 setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time
691
692 if (($?)); then
693 check_err 1
694 log_test setup_wait_dev ": Interface $dev does not come up."
695 exit 1
696 fi
697}
698
699setup_wait_dev_with_timeout()
700{
701 local dev=$1; shift
702 local max_iterations=${1:-$WAIT_TIMEOUT}; shift
703 local wait_time=${1:-$WAIT_TIME}; shift
704 local i
705
706 for ((i = 1; i <= $max_iterations; ++i)); do
707 ip link show dev $dev up \
708 | grep 'state UP' &> /dev/null
709 if [[ $? -ne 0 ]]; then
710 sleep 1
711 else
712 sleep $wait_time
713 return 0
714 fi
715 done
716
717 return 1
718}
719
720setup_wait()
721{
722 local num_netifs=${1:-$NUM_NETIFS}
723 local i
724
725 for ((i = 1; i <= num_netifs; ++i)); do
726 setup_wait_dev ${NETIFS[p$i]} 0
727 done
728
729 # Make sure links are ready.
730 sleep $WAIT_TIME
731}
732
733wait_for_dev()
734{
735 local dev=$1; shift
736 local timeout=${1:-$WAIT_TIMEOUT}; shift
737
738 slowwait $timeout ip link show dev $dev &> /dev/null
739 if (( $? )); then
740 check_err 1
741 log_test wait_for_dev "Interface $dev did not appear."
742 exit $EXIT_STATUS
743 fi
744}
745
746cmd_jq()
747{
748 local cmd=$1
749 local jq_exp=$2
750 local jq_opts=$3
751 local ret
752 local output
753
754 output="$($cmd)"
755 # it the command fails, return error right away
756 ret=$?
757 if [[ $ret -ne 0 ]]; then
758 return $ret
759 fi
760 output=$(echo $output | jq -r $jq_opts "$jq_exp")
761 ret=$?
762 if [[ $ret -ne 0 ]]; then
763 return $ret
764 fi
765 echo $output
766 # return success only in case of non-empty output
767 [ ! -z "$output" ]
768}
769
770pre_cleanup()
771{
772 if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then
773 echo "Pausing before cleanup, hit any key to continue"
774 read
775 fi
776
777 if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then
778 mac_addr_restore
779 fi
780}
781
782vrf_prepare()
783{
784 ip -4 rule add pref 32765 table local
785 ip -4 rule del pref 0
786 ip -6 rule add pref 32765 table local
787 ip -6 rule del pref 0
788}
789
790vrf_cleanup()
791{
792 ip -6 rule add pref 0 table local
793 ip -6 rule del pref 32765
794 ip -4 rule add pref 0 table local
795 ip -4 rule del pref 32765
796}
797
798__last_tb_id=0
799declare -A __TB_IDS
800
801__vrf_td_id_assign()
802{
803 local vrf_name=$1
804
805 __last_tb_id=$((__last_tb_id + 1))
806 __TB_IDS[$vrf_name]=$__last_tb_id
807 return $__last_tb_id
808}
809
810__vrf_td_id_lookup()
811{
812 local vrf_name=$1
813
814 return ${__TB_IDS[$vrf_name]}
815}
816
817vrf_create()
818{
819 local vrf_name=$1
820 local tb_id
821
822 __vrf_td_id_assign $vrf_name
823 tb_id=$?
824
825 ip link add dev $vrf_name type vrf table $tb_id
826 ip -4 route add table $tb_id unreachable default metric 4278198272
827 ip -6 route add table $tb_id unreachable default metric 4278198272
828}
829
830vrf_destroy()
831{
832 local vrf_name=$1
833 local tb_id
834
835 __vrf_td_id_lookup $vrf_name
836 tb_id=$?
837
838 ip -6 route del table $tb_id unreachable default metric 4278198272
839 ip -4 route del table $tb_id unreachable default metric 4278198272
840 ip link del dev $vrf_name
841}
842
843__addr_add_del()
844{
845 local if_name=$1
846 local add_del=$2
847 local array
848
849 shift
850 shift
851 array=("${@}")
852
853 for addrstr in "${array[@]}"; do
854 ip address $add_del $addrstr dev $if_name
855 done
856}
857
858__simple_if_init()
859{
860 local if_name=$1; shift
861 local vrf_name=$1; shift
862 local addrs=("${@}")
863
864 ip link set dev $if_name master $vrf_name
865 ip link set dev $if_name up
866
867 __addr_add_del $if_name add "${addrs[@]}"
868}
869
870__simple_if_fini()
871{
872 local if_name=$1; shift
873 local addrs=("${@}")
874
875 __addr_add_del $if_name del "${addrs[@]}"
876
877 ip link set dev $if_name down
878 ip link set dev $if_name nomaster
879}
880
881simple_if_init()
882{
883 local if_name=$1
884 local vrf_name
885 local array
886
887 shift
888 vrf_name=v$if_name
889 array=("${@}")
890
891 vrf_create $vrf_name
892 ip link set dev $vrf_name up
893 __simple_if_init $if_name $vrf_name "${array[@]}"
894}
895
896simple_if_fini()
897{
898 local if_name=$1
899 local vrf_name
900 local array
901
902 shift
903 vrf_name=v$if_name
904 array=("${@}")
905
906 __simple_if_fini $if_name "${array[@]}"
907 vrf_destroy $vrf_name
908}
909
910tunnel_create()
911{
912 local name=$1; shift
913 local type=$1; shift
914 local local=$1; shift
915 local remote=$1; shift
916
917 ip link add name $name type $type \
918 local $local remote $remote "$@"
919 ip link set dev $name up
920}
921
922tunnel_destroy()
923{
924 local name=$1; shift
925
926 ip link del dev $name
927}
928
929vlan_create()
930{
931 local if_name=$1; shift
932 local vid=$1; shift
933 local vrf=$1; shift
934 local ips=("${@}")
935 local name=$if_name.$vid
936
937 ip link add name $name link $if_name type vlan id $vid
938 if [ "$vrf" != "" ]; then
939 ip link set dev $name master $vrf
940 fi
941 ip link set dev $name up
942 __addr_add_del $name add "${ips[@]}"
943}
944
945vlan_destroy()
946{
947 local if_name=$1; shift
948 local vid=$1; shift
949 local name=$if_name.$vid
950
951 ip link del dev $name
952}
953
954team_create()
955{
956 local if_name=$1; shift
957 local mode=$1; shift
958
959 require_command $TEAMD
960 $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}'
961 for slave in "$@"; do
962 ip link set dev $slave down
963 ip link set dev $slave master $if_name
964 ip link set dev $slave up
965 done
966 ip link set dev $if_name up
967}
968
969team_destroy()
970{
971 local if_name=$1; shift
972
973 $TEAMD -t $if_name -k
974}
975
976master_name_get()
977{
978 local if_name=$1
979
980 ip -j link show dev $if_name | jq -r '.[]["master"]'
981}
982
983link_stats_get()
984{
985 local if_name=$1; shift
986 local dir=$1; shift
987 local stat=$1; shift
988
989 ip -j -s link show dev $if_name \
990 | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
991}
992
993link_stats_tx_packets_get()
994{
995 link_stats_get $1 tx packets
996}
997
998link_stats_rx_errors_get()
999{
1000 link_stats_get $1 rx errors
1001}
1002
1003ethtool_stats_get()
1004{
1005 local dev=$1; shift
1006 local stat=$1; shift
1007
1008 ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2
1009}
1010
1011ethtool_std_stats_get()
1012{
1013 local dev=$1; shift
1014 local grp=$1; shift
1015 local name=$1; shift
1016 local src=$1; shift
1017
1018 ethtool --json -S $dev --groups $grp -- --src $src | \
1019 jq '.[]."'"$grp"'"."'$name'"'
1020}
1021
1022qdisc_stats_get()
1023{
1024 local dev=$1; shift
1025 local handle=$1; shift
1026 local selector=$1; shift
1027
1028 tc -j -s qdisc show dev "$dev" \
1029 | jq '.[] | select(.handle == "'"$handle"'") | '"$selector"
1030}
1031
1032qdisc_parent_stats_get()
1033{
1034 local dev=$1; shift
1035 local parent=$1; shift
1036 local selector=$1; shift
1037
1038 tc -j -s qdisc show dev "$dev" invisible \
1039 | jq '.[] | select(.parent == "'"$parent"'") | '"$selector"
1040}
1041
1042ipv6_stats_get()
1043{
1044 local dev=$1; shift
1045 local stat=$1; shift
1046
1047 cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2
1048}
1049
1050hw_stats_get()
1051{
1052 local suite=$1; shift
1053 local if_name=$1; shift
1054 local dir=$1; shift
1055 local stat=$1; shift
1056
1057 ip -j stats show dev $if_name group offload subgroup $suite |
1058 jq ".[0].stats64.$dir.$stat"
1059}
1060
1061__nh_stats_get()
1062{
1063 local key=$1; shift
1064 local group_id=$1; shift
1065 local member_id=$1; shift
1066
1067 ip -j -s -s nexthop show id $group_id |
1068 jq --argjson member_id "$member_id" --arg key "$key" \
1069 '.[].group_stats[] | select(.id == $member_id) | .[$key]'
1070}
1071
1072nh_stats_get()
1073{
1074 local group_id=$1; shift
1075 local member_id=$1; shift
1076
1077 __nh_stats_get packets "$group_id" "$member_id"
1078}
1079
1080nh_stats_get_hw()
1081{
1082 local group_id=$1; shift
1083 local member_id=$1; shift
1084
1085 __nh_stats_get packets_hw "$group_id" "$member_id"
1086}
1087
1088humanize()
1089{
1090 local speed=$1; shift
1091
1092 for unit in bps Kbps Mbps Gbps; do
1093 if (($(echo "$speed < 1024" | bc))); then
1094 break
1095 fi
1096
1097 speed=$(echo "scale=1; $speed / 1024" | bc)
1098 done
1099
1100 echo "$speed${unit}"
1101}
1102
1103rate()
1104{
1105 local t0=$1; shift
1106 local t1=$1; shift
1107 local interval=$1; shift
1108
1109 echo $((8 * (t1 - t0) / interval))
1110}
1111
1112packets_rate()
1113{
1114 local t0=$1; shift
1115 local t1=$1; shift
1116 local interval=$1; shift
1117
1118 echo $(((t1 - t0) / interval))
1119}
1120
1121mac_get()
1122{
1123 local if_name=$1
1124
1125 ip -j link show dev $if_name | jq -r '.[]["address"]'
1126}
1127
1128ether_addr_to_u64()
1129{
1130 local addr="$1"
1131 local order="$((1 << 40))"
1132 local val=0
1133 local byte
1134
1135 addr="${addr//:/ }"
1136
1137 for byte in $addr; do
1138 byte="0x$byte"
1139 val=$((val + order * byte))
1140 order=$((order >> 8))
1141 done
1142
1143 printf "0x%x" $val
1144}
1145
1146u64_to_ether_addr()
1147{
1148 local val=$1
1149 local byte
1150 local i
1151
1152 for ((i = 40; i >= 0; i -= 8)); do
1153 byte=$(((val & (0xff << i)) >> i))
1154 printf "%02x" $byte
1155 if [ $i -ne 0 ]; then
1156 printf ":"
1157 fi
1158 done
1159}
1160
1161ipv6_lladdr_get()
1162{
1163 local if_name=$1
1164
1165 ip -j addr show dev $if_name | \
1166 jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \
1167 head -1
1168}
1169
1170bridge_ageing_time_get()
1171{
1172 local bridge=$1
1173 local ageing_time
1174
1175 # Need to divide by 100 to convert to seconds.
1176 ageing_time=$(ip -j -d link show dev $bridge \
1177 | jq '.[]["linkinfo"]["info_data"]["ageing_time"]')
1178 echo $((ageing_time / 100))
1179}
1180
1181declare -A SYSCTL_ORIG
1182sysctl_save()
1183{
1184 local key=$1; shift
1185
1186 SYSCTL_ORIG[$key]=$(sysctl -n $key)
1187}
1188
1189sysctl_set()
1190{
1191 local key=$1; shift
1192 local value=$1; shift
1193
1194 sysctl_save "$key"
1195 sysctl -qw $key="$value"
1196}
1197
1198sysctl_restore()
1199{
1200 local key=$1; shift
1201
1202 sysctl -qw $key="${SYSCTL_ORIG[$key]}"
1203}
1204
1205forwarding_enable()
1206{
1207 sysctl_set net.ipv4.conf.all.forwarding 1
1208 sysctl_set net.ipv6.conf.all.forwarding 1
1209}
1210
1211forwarding_restore()
1212{
1213 sysctl_restore net.ipv6.conf.all.forwarding
1214 sysctl_restore net.ipv4.conf.all.forwarding
1215}
1216
1217declare -A MTU_ORIG
1218mtu_set()
1219{
1220 local dev=$1; shift
1221 local mtu=$1; shift
1222
1223 MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu')
1224 ip link set dev $dev mtu $mtu
1225}
1226
1227mtu_restore()
1228{
1229 local dev=$1; shift
1230
1231 ip link set dev $dev mtu ${MTU_ORIG["$dev"]}
1232}
1233
1234tc_offload_check()
1235{
1236 local num_netifs=${1:-$NUM_NETIFS}
1237
1238 for ((i = 1; i <= num_netifs; ++i)); do
1239 ethtool -k ${NETIFS[p$i]} \
1240 | grep "hw-tc-offload: on" &> /dev/null
1241 if [[ $? -ne 0 ]]; then
1242 return 1
1243 fi
1244 done
1245
1246 return 0
1247}
1248
1249trap_install()
1250{
1251 local dev=$1; shift
1252 local direction=$1; shift
1253
1254 # Some devices may not support or need in-hardware trapping of traffic
1255 # (e.g. the veth pairs that this library creates for non-existent
1256 # loopbacks). Use continue instead, so that there is a filter in there
1257 # (some tests check counters), and so that other filters are still
1258 # processed.
1259 tc filter add dev $dev $direction pref 1 \
1260 flower skip_sw action trap 2>/dev/null \
1261 || tc filter add dev $dev $direction pref 1 \
1262 flower action continue
1263}
1264
1265trap_uninstall()
1266{
1267 local dev=$1; shift
1268 local direction=$1; shift
1269
1270 tc filter del dev $dev $direction pref 1 flower
1271}
1272
1273__icmp_capture_add_del()
1274{
1275 local add_del=$1; shift
1276 local pref=$1; shift
1277 local vsuf=$1; shift
1278 local tundev=$1; shift
1279 local filter=$1; shift
1280
1281 tc filter $add_del dev "$tundev" ingress \
1282 proto ip$vsuf pref $pref \
1283 flower ip_proto icmp$vsuf $filter \
1284 action pass
1285}
1286
1287icmp_capture_install()
1288{
1289 local tundev=$1; shift
1290 local filter=$1; shift
1291
1292 __icmp_capture_add_del add 100 "" "$tundev" "$filter"
1293}
1294
1295icmp_capture_uninstall()
1296{
1297 local tundev=$1; shift
1298 local filter=$1; shift
1299
1300 __icmp_capture_add_del del 100 "" "$tundev" "$filter"
1301}
1302
1303icmp6_capture_install()
1304{
1305 local tundev=$1; shift
1306 local filter=$1; shift
1307
1308 __icmp_capture_add_del add 100 v6 "$tundev" "$filter"
1309}
1310
1311icmp6_capture_uninstall()
1312{
1313 local tundev=$1; shift
1314 local filter=$1; shift
1315
1316 __icmp_capture_add_del del 100 v6 "$tundev" "$filter"
1317}
1318
1319__vlan_capture_add_del()
1320{
1321 local add_del=$1; shift
1322 local pref=$1; shift
1323 local dev=$1; shift
1324 local filter=$1; shift
1325
1326 tc filter $add_del dev "$dev" ingress \
1327 proto 802.1q pref $pref \
1328 flower $filter \
1329 action pass
1330}
1331
1332vlan_capture_install()
1333{
1334 local dev=$1; shift
1335 local filter=$1; shift
1336
1337 __vlan_capture_add_del add 100 "$dev" "$filter"
1338}
1339
1340vlan_capture_uninstall()
1341{
1342 local dev=$1; shift
1343 local filter=$1; shift
1344
1345 __vlan_capture_add_del del 100 "$dev" "$filter"
1346}
1347
1348__dscp_capture_add_del()
1349{
1350 local add_del=$1; shift
1351 local dev=$1; shift
1352 local base=$1; shift
1353 local dscp;
1354
1355 for prio in {0..7}; do
1356 dscp=$((base + prio))
1357 __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \
1358 "skip_hw ip_tos $((dscp << 2))"
1359 done
1360}
1361
1362dscp_capture_install()
1363{
1364 local dev=$1; shift
1365 local base=$1; shift
1366
1367 __dscp_capture_add_del add $dev $base
1368}
1369
1370dscp_capture_uninstall()
1371{
1372 local dev=$1; shift
1373 local base=$1; shift
1374
1375 __dscp_capture_add_del del $dev $base
1376}
1377
1378dscp_fetch_stats()
1379{
1380 local dev=$1; shift
1381 local base=$1; shift
1382
1383 for prio in {0..7}; do
1384 local dscp=$((base + prio))
1385 local t=$(tc_rule_stats_get $dev $((dscp + 100)))
1386 echo "[$dscp]=$t "
1387 done
1388}
1389
1390matchall_sink_create()
1391{
1392 local dev=$1; shift
1393
1394 tc qdisc add dev $dev clsact
1395 tc filter add dev $dev ingress \
1396 pref 10000 \
1397 matchall \
1398 action drop
1399}
1400
1401tests_run()
1402{
1403 local current_test
1404
1405 for current_test in ${TESTS:-$ALL_TESTS}; do
1406 $current_test
1407 done
1408}
1409
1410multipath_eval()
1411{
1412 local desc="$1"
1413 local weight_rp12=$2
1414 local weight_rp13=$3
1415 local packets_rp12=$4
1416 local packets_rp13=$5
1417 local weights_ratio packets_ratio diff
1418
1419 RET=0
1420
1421 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1422 weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \
1423 | bc -l)
1424 else
1425 weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \
1426 | bc -l)
1427 fi
1428
1429 if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then
1430 check_err 1 "Packet difference is 0"
1431 log_test "Multipath"
1432 log_info "Expected ratio $weights_ratio"
1433 return
1434 fi
1435
1436 if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then
1437 packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \
1438 | bc -l)
1439 else
1440 packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \
1441 | bc -l)
1442 fi
1443
1444 diff=$(echo $weights_ratio - $packets_ratio | bc -l)
1445 diff=${diff#-}
1446
1447 test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0
1448 check_err $? "Too large discrepancy between expected and measured ratios"
1449 log_test "$desc"
1450 log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
1451}
1452
1453in_ns()
1454{
1455 local name=$1; shift
1456
1457 ip netns exec $name bash <<-EOF
1458 NUM_NETIFS=0
1459 source lib.sh
1460 $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
1461 EOF
1462}
1463
1464##############################################################################
1465# Tests
1466
1467ping_do()
1468{
1469 local if_name=$1
1470 local dip=$2
1471 local args=$3
1472 local vrf_name
1473
1474 vrf_name=$(master_name_get $if_name)
1475 ip vrf exec $vrf_name \
1476 $PING $args $dip -c $PING_COUNT -i 0.1 \
1477 -w $PING_TIMEOUT &> /dev/null
1478}
1479
1480ping_test()
1481{
1482 RET=0
1483
1484 ping_do $1 $2
1485 check_err $?
1486 log_test "ping$3"
1487}
1488
1489ping_test_fails()
1490{
1491 RET=0
1492
1493 ping_do $1 $2
1494 check_fail $?
1495 log_test "ping fails$3"
1496}
1497
1498ping6_do()
1499{
1500 local if_name=$1
1501 local dip=$2
1502 local args=$3
1503 local vrf_name
1504
1505 vrf_name=$(master_name_get $if_name)
1506 ip vrf exec $vrf_name \
1507 $PING6 $args $dip -c $PING_COUNT -i 0.1 \
1508 -w $PING_TIMEOUT &> /dev/null
1509}
1510
1511ping6_test()
1512{
1513 RET=0
1514
1515 ping6_do $1 $2
1516 check_err $?
1517 log_test "ping6$3"
1518}
1519
1520ping6_test_fails()
1521{
1522 RET=0
1523
1524 ping6_do $1 $2
1525 check_fail $?
1526 log_test "ping6 fails$3"
1527}
1528
1529learning_test()
1530{
1531 local bridge=$1
1532 local br_port1=$2 # Connected to `host1_if`.
1533 local host1_if=$3
1534 local host2_if=$4
1535 local mac=de:ad:be:ef:13:37
1536 local ageing_time
1537
1538 RET=0
1539
1540 bridge -j fdb show br $bridge brport $br_port1 \
1541 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1542 check_fail $? "Found FDB record when should not"
1543
1544 # Disable unknown unicast flooding on `br_port1` to make sure
1545 # packets are only forwarded through the port after a matching
1546 # FDB entry was installed.
1547 bridge link set dev $br_port1 flood off
1548
1549 ip link set $host1_if promisc on
1550 tc qdisc add dev $host1_if ingress
1551 tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \
1552 flower dst_mac $mac action drop
1553
1554 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1555 sleep 1
1556
1557 tc -j -s filter show dev $host1_if ingress \
1558 | jq -e ".[] | select(.options.handle == 101) \
1559 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1560 check_fail $? "Packet reached first host when should not"
1561
1562 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1563 sleep 1
1564
1565 bridge -j fdb show br $bridge brport $br_port1 \
1566 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1567 check_err $? "Did not find FDB record when should"
1568
1569 $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q
1570 sleep 1
1571
1572 tc -j -s filter show dev $host1_if ingress \
1573 | jq -e ".[] | select(.options.handle == 101) \
1574 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1575 check_err $? "Packet did not reach second host when should"
1576
1577 # Wait for 10 seconds after the ageing time to make sure FDB
1578 # record was aged-out.
1579 ageing_time=$(bridge_ageing_time_get $bridge)
1580 sleep $((ageing_time + 10))
1581
1582 bridge -j fdb show br $bridge brport $br_port1 \
1583 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1584 check_fail $? "Found FDB record when should not"
1585
1586 bridge link set dev $br_port1 learning off
1587
1588 $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q
1589 sleep 1
1590
1591 bridge -j fdb show br $bridge brport $br_port1 \
1592 | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null
1593 check_fail $? "Found FDB record when should not"
1594
1595 bridge link set dev $br_port1 learning on
1596
1597 tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower
1598 tc qdisc del dev $host1_if ingress
1599 ip link set $host1_if promisc off
1600
1601 bridge link set dev $br_port1 flood on
1602
1603 log_test "FDB learning"
1604}
1605
1606flood_test_do()
1607{
1608 local should_flood=$1
1609 local mac=$2
1610 local ip=$3
1611 local host1_if=$4
1612 local host2_if=$5
1613 local err=0
1614
1615 # Add an ACL on `host2_if` which will tell us whether the packet
1616 # was flooded to it or not.
1617 ip link set $host2_if promisc on
1618 tc qdisc add dev $host2_if ingress
1619 tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \
1620 flower dst_mac $mac action drop
1621
1622 $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q
1623 sleep 1
1624
1625 tc -j -s filter show dev $host2_if ingress \
1626 | jq -e ".[] | select(.options.handle == 101) \
1627 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1628 if [[ $? -ne 0 && $should_flood == "true" || \
1629 $? -eq 0 && $should_flood == "false" ]]; then
1630 err=1
1631 fi
1632
1633 tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower
1634 tc qdisc del dev $host2_if ingress
1635 ip link set $host2_if promisc off
1636
1637 return $err
1638}
1639
1640flood_unicast_test()
1641{
1642 local br_port=$1
1643 local host1_if=$2
1644 local host2_if=$3
1645 local mac=de:ad:be:ef:13:37
1646 local ip=192.0.2.100
1647
1648 RET=0
1649
1650 bridge link set dev $br_port flood off
1651
1652 flood_test_do false $mac $ip $host1_if $host2_if
1653 check_err $? "Packet flooded when should not"
1654
1655 bridge link set dev $br_port flood on
1656
1657 flood_test_do true $mac $ip $host1_if $host2_if
1658 check_err $? "Packet was not flooded when should"
1659
1660 log_test "Unknown unicast flood"
1661}
1662
1663flood_multicast_test()
1664{
1665 local br_port=$1
1666 local host1_if=$2
1667 local host2_if=$3
1668 local mac=01:00:5e:00:00:01
1669 local ip=239.0.0.1
1670
1671 RET=0
1672
1673 bridge link set dev $br_port mcast_flood off
1674
1675 flood_test_do false $mac $ip $host1_if $host2_if
1676 check_err $? "Packet flooded when should not"
1677
1678 bridge link set dev $br_port mcast_flood on
1679
1680 flood_test_do true $mac $ip $host1_if $host2_if
1681 check_err $? "Packet was not flooded when should"
1682
1683 log_test "Unregistered multicast flood"
1684}
1685
1686flood_test()
1687{
1688 # `br_port` is connected to `host2_if`
1689 local br_port=$1
1690 local host1_if=$2
1691 local host2_if=$3
1692
1693 flood_unicast_test $br_port $host1_if $host2_if
1694 flood_multicast_test $br_port $host1_if $host2_if
1695}
1696
1697__start_traffic()
1698{
1699 local pktsize=$1; shift
1700 local proto=$1; shift
1701 local h_in=$1; shift # Where the traffic egresses the host
1702 local sip=$1; shift
1703 local dip=$1; shift
1704 local dmac=$1; shift
1705 local -a mz_args=("$@")
1706
1707 $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \
1708 -a own -b $dmac -t "$proto" -q "${mz_args[@]}" &
1709 sleep 1
1710}
1711
1712start_traffic_pktsize()
1713{
1714 local pktsize=$1; shift
1715 local h_in=$1; shift
1716 local sip=$1; shift
1717 local dip=$1; shift
1718 local dmac=$1; shift
1719 local -a mz_args=("$@")
1720
1721 __start_traffic $pktsize udp "$h_in" "$sip" "$dip" "$dmac" \
1722 "${mz_args[@]}"
1723}
1724
1725start_tcp_traffic_pktsize()
1726{
1727 local pktsize=$1; shift
1728 local h_in=$1; shift
1729 local sip=$1; shift
1730 local dip=$1; shift
1731 local dmac=$1; shift
1732 local -a mz_args=("$@")
1733
1734 __start_traffic $pktsize tcp "$h_in" "$sip" "$dip" "$dmac" \
1735 "${mz_args[@]}"
1736}
1737
1738start_traffic()
1739{
1740 local h_in=$1; shift
1741 local sip=$1; shift
1742 local dip=$1; shift
1743 local dmac=$1; shift
1744 local -a mz_args=("$@")
1745
1746 start_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1747 "${mz_args[@]}"
1748}
1749
1750start_tcp_traffic()
1751{
1752 local h_in=$1; shift
1753 local sip=$1; shift
1754 local dip=$1; shift
1755 local dmac=$1; shift
1756 local -a mz_args=("$@")
1757
1758 start_tcp_traffic_pktsize 8000 "$h_in" "$sip" "$dip" "$dmac" \
1759 "${mz_args[@]}"
1760}
1761
1762stop_traffic()
1763{
1764 # Suppress noise from killing mausezahn.
1765 { kill %% && wait %%; } 2>/dev/null
1766}
1767
1768declare -A cappid
1769declare -A capfile
1770declare -A capout
1771
1772tcpdump_start()
1773{
1774 local if_name=$1; shift
1775 local ns=$1; shift
1776
1777 capfile[$if_name]=$(mktemp)
1778 capout[$if_name]=$(mktemp)
1779
1780 if [ -z $ns ]; then
1781 ns_cmd=""
1782 else
1783 ns_cmd="ip netns exec ${ns}"
1784 fi
1785
1786 if [ -z $SUDO_USER ] ; then
1787 capuser=""
1788 else
1789 capuser="-Z $SUDO_USER"
1790 fi
1791
1792 $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \
1793 -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \
1794 > "${capout[$if_name]}" 2>&1 &
1795 cappid[$if_name]=$!
1796
1797 sleep 1
1798}
1799
1800tcpdump_stop()
1801{
1802 local if_name=$1
1803 local pid=${cappid[$if_name]}
1804
1805 $ns_cmd kill "$pid" && wait "$pid"
1806 sleep 1
1807}
1808
1809tcpdump_cleanup()
1810{
1811 local if_name=$1
1812
1813 rm ${capfile[$if_name]} ${capout[$if_name]}
1814}
1815
1816tcpdump_show()
1817{
1818 local if_name=$1
1819
1820 tcpdump -e -n -r ${capfile[$if_name]} 2>&1
1821}
1822
1823# return 0 if the packet wasn't seen on host2_if or 1 if it was
1824mcast_packet_test()
1825{
1826 local mac=$1
1827 local src_ip=$2
1828 local ip=$3
1829 local host1_if=$4
1830 local host2_if=$5
1831 local seen=0
1832 local tc_proto="ip"
1833 local mz_v6arg=""
1834
1835 # basic check to see if we were passed an IPv4 address, if not assume IPv6
1836 if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
1837 tc_proto="ipv6"
1838 mz_v6arg="-6"
1839 fi
1840
1841 # Add an ACL on `host2_if` which will tell us whether the packet
1842 # was received by it or not.
1843 tc qdisc add dev $host2_if ingress
1844 tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \
1845 flower ip_proto udp dst_mac $mac action drop
1846
1847 $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q
1848 sleep 1
1849
1850 tc -j -s filter show dev $host2_if ingress \
1851 | jq -e ".[] | select(.options.handle == 101) \
1852 | select(.options.actions[0].stats.packets == 1)" &> /dev/null
1853 if [[ $? -eq 0 ]]; then
1854 seen=1
1855 fi
1856
1857 tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower
1858 tc qdisc del dev $host2_if ingress
1859
1860 return $seen
1861}
1862
1863brmcast_check_sg_entries()
1864{
1865 local report=$1; shift
1866 local slist=("$@")
1867 local sarg=""
1868
1869 for src in "${slist[@]}"; do
1870 sarg="${sarg} and .source_list[].address == \"$src\""
1871 done
1872 bridge -j -d -s mdb show dev br0 \
1873 | jq -e ".[].mdb[] | \
1874 select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null
1875 check_err $? "Wrong *,G entry source list after $report report"
1876
1877 for sgent in "${slist[@]}"; do
1878 bridge -j -d -s mdb show dev br0 \
1879 | jq -e ".[].mdb[] | \
1880 select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null
1881 check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)"
1882 done
1883}
1884
1885brmcast_check_sg_fwding()
1886{
1887 local should_fwd=$1; shift
1888 local sources=("$@")
1889
1890 for src in "${sources[@]}"; do
1891 local retval=0
1892
1893 mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1
1894 retval=$?
1895 if [ $should_fwd -eq 1 ]; then
1896 check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)"
1897 else
1898 check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)"
1899 fi
1900 done
1901}
1902
1903brmcast_check_sg_state()
1904{
1905 local is_blocked=$1; shift
1906 local sources=("$@")
1907 local should_fail=1
1908
1909 if [ $is_blocked -eq 1 ]; then
1910 should_fail=0
1911 fi
1912
1913 for src in "${sources[@]}"; do
1914 bridge -j -d -s mdb show dev br0 \
1915 | jq -e ".[].mdb[] | \
1916 select(.grp == \"$TEST_GROUP\" and .source_list != null) |
1917 .source_list[] |
1918 select(.address == \"$src\") |
1919 select(.timer == \"0.00\")" &>/dev/null
1920 check_err_fail $should_fail $? "Entry $src has zero timer"
1921
1922 bridge -j -d -s mdb show dev br0 \
1923 | jq -e ".[].mdb[] | \
1924 select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \
1925 .flags[] == \"blocked\")" &>/dev/null
1926 check_err_fail $should_fail $? "Entry $src has blocked flag"
1927 done
1928}
1929
1930mc_join()
1931{
1932 local if_name=$1
1933 local group=$2
1934 local vrf_name=$(master_name_get $if_name)
1935
1936 # We don't care about actual reception, just about joining the
1937 # IP multicast group and adding the L2 address to the device's
1938 # MAC filtering table
1939 ip vrf exec $vrf_name \
1940 mreceive -g $group -I $if_name > /dev/null 2>&1 &
1941 mreceive_pid=$!
1942
1943 sleep 1
1944}
1945
1946mc_leave()
1947{
1948 kill "$mreceive_pid" && wait "$mreceive_pid"
1949}
1950
1951mc_send()
1952{
1953 local if_name=$1
1954 local groups=$2
1955 local vrf_name=$(master_name_get $if_name)
1956
1957 ip vrf exec $vrf_name \
1958 msend -g $groups -I $if_name -c 1 > /dev/null 2>&1
1959}
1960
1961start_ip_monitor()
1962{
1963 local mtype=$1; shift
1964 local ip=${1-ip}; shift
1965
1966 # start the monitor in the background
1967 tmpfile=`mktemp /var/run/nexthoptestXXX`
1968 mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
1969 sleep 0.2
1970 echo "$mpid $tmpfile"
1971}
1972
1973stop_ip_monitor()
1974{
1975 local mpid=$1; shift
1976 local tmpfile=$1; shift
1977 local el=$1; shift
1978 local what=$1; shift
1979
1980 sleep 0.2
1981 kill $mpid
1982 local lines=`grep '^\w' $tmpfile | wc -l`
1983 test $lines -eq $el
1984 check_err $? "$what: $lines lines of events, expected $el"
1985 rm -rf $tmpfile
1986}
1987
1988hw_stats_monitor_test()
1989{
1990 local dev=$1; shift
1991 local type=$1; shift
1992 local make_suitable=$1; shift
1993 local make_unsuitable=$1; shift
1994 local ip=${1-ip}; shift
1995
1996 RET=0
1997
1998 # Expect a notification about enablement.
1999 local ipmout=$(start_ip_monitor stats "$ip")
2000 $ip stats set dev $dev ${type}_stats on
2001 stop_ip_monitor $ipmout 1 "${type}_stats enablement"
2002
2003 # Expect a notification about offload.
2004 local ipmout=$(start_ip_monitor stats "$ip")
2005 $make_suitable
2006 stop_ip_monitor $ipmout 1 "${type}_stats installation"
2007
2008 # Expect a notification about loss of offload.
2009 local ipmout=$(start_ip_monitor stats "$ip")
2010 $make_unsuitable
2011 stop_ip_monitor $ipmout 1 "${type}_stats deinstallation"
2012
2013 # Expect a notification about disablement
2014 local ipmout=$(start_ip_monitor stats "$ip")
2015 $ip stats set dev $dev ${type}_stats off
2016 stop_ip_monitor $ipmout 1 "${type}_stats disablement"
2017
2018 log_test "${type}_stats notifications"
2019}
2020
2021ipv4_to_bytes()
2022{
2023 local IP=$1; shift
2024
2025 printf '%02x:' ${IP//./ } |
2026 sed 's/:$//'
2027}
2028
2029# Convert a given IPv6 address, `IP' such that the :: token, if present, is
2030# expanded, and each 16-bit group is padded with zeroes to be 4 hexadecimal
2031# digits. An optional `BYTESEP' parameter can be given to further separate
2032# individual bytes of each 16-bit group.
2033expand_ipv6()
2034{
2035 local IP=$1; shift
2036 local bytesep=$1; shift
2037
2038 local cvt_ip=${IP/::/_}
2039 local colons=${cvt_ip//[^:]/}
2040 local allcol=:::::::
2041 # IP where :: -> the appropriate number of colons:
2042 local allcol_ip=${cvt_ip/_/${allcol:${#colons}}}
2043
2044 echo $allcol_ip | tr : '\n' |
2045 sed s/^/0000/ |
2046 sed 's/.*\(..\)\(..\)/\1'"$bytesep"'\2/' |
2047 tr '\n' : |
2048 sed 's/:$//'
2049}
2050
2051ipv6_to_bytes()
2052{
2053 local IP=$1; shift
2054
2055 expand_ipv6 "$IP" :
2056}
2057
2058u16_to_bytes()
2059{
2060 local u16=$1; shift
2061
2062 printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/'
2063}
2064
2065# Given a mausezahn-formatted payload (colon-separated bytes given as %02x),
2066# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be,
2067# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any)
2068# stands for 00:00.
2069payload_template_calc_checksum()
2070{
2071 local payload=$1; shift
2072
2073 (
2074 # Set input radix.
2075 echo "16i"
2076 # Push zero for the initial checksum.
2077 echo 0
2078
2079 # Pad the payload with a terminating 00: in case we get an odd
2080 # number of bytes.
2081 echo "${payload%:}:00:" |
2082 sed 's/CHECKSUM/00:00/g' |
2083 tr '[:lower:]' '[:upper:]' |
2084 # Add the word to the checksum.
2085 sed 's/\(..\):\(..\):/\1\2+\n/g' |
2086 # Strip the extra odd byte we pushed if left unconverted.
2087 sed 's/\(..\):$//'
2088
2089 echo "10000 ~ +" # Calculate and add carry.
2090 echo "FFFF r - p" # Bit-flip and print.
2091 ) |
2092 dc |
2093 tr '[:upper:]' '[:lower:]'
2094}
2095
2096payload_template_expand_checksum()
2097{
2098 local payload=$1; shift
2099 local checksum=$1; shift
2100
2101 local ckbytes=$(u16_to_bytes $checksum)
2102
2103 echo "$payload" | sed "s/CHECKSUM/$ckbytes/g"
2104}
2105
2106payload_template_nbytes()
2107{
2108 local payload=$1; shift
2109
2110 payload_template_expand_checksum "${payload%:}" 0 |
2111 sed 's/:/\n/g' | wc -l
2112}
2113
2114igmpv3_is_in_get()
2115{
2116 local GRP=$1; shift
2117 local sources=("$@")
2118
2119 local igmpv3
2120 local nsources=$(u16_to_bytes ${#sources[@]})
2121
2122 # IS_IN ( $sources )
2123 igmpv3=$(:
2124 )"22:"$( : Type - Membership Report
2125 )"00:"$( : Reserved
2126 )"CHECKSUM:"$( : Checksum
2127 )"00:00:"$( : Reserved
2128 )"00:01:"$( : Number of Group Records
2129 )"01:"$( : Record Type - IS_IN
2130 )"00:"$( : Aux Data Len
2131 )"${nsources}:"$( : Number of Sources
2132 )"$(ipv4_to_bytes $GRP):"$( : Multicast Address
2133 )"$(for src in "${sources[@]}"; do
2134 ipv4_to_bytes $src
2135 echo -n :
2136 done)"$( : Source Addresses
2137 )
2138 local checksum=$(payload_template_calc_checksum "$igmpv3")
2139
2140 payload_template_expand_checksum "$igmpv3" $checksum
2141}
2142
2143igmpv2_leave_get()
2144{
2145 local GRP=$1; shift
2146
2147 local payload=$(:
2148 )"17:"$( : Type - Leave Group
2149 )"00:"$( : Max Resp Time - not meaningful
2150 )"CHECKSUM:"$( : Checksum
2151 )"$(ipv4_to_bytes $GRP)"$( : Group Address
2152 )
2153 local checksum=$(payload_template_calc_checksum "$payload")
2154
2155 payload_template_expand_checksum "$payload" $checksum
2156}
2157
2158mldv2_is_in_get()
2159{
2160 local SIP=$1; shift
2161 local GRP=$1; shift
2162 local sources=("$@")
2163
2164 local hbh
2165 local icmpv6
2166 local nsources=$(u16_to_bytes ${#sources[@]})
2167
2168 hbh=$(:
2169 )"3a:"$( : Next Header - ICMPv6
2170 )"00:"$( : Hdr Ext Len
2171 )"00:00:00:00:00:00:"$( : Options and Padding
2172 )
2173
2174 icmpv6=$(:
2175 )"8f:"$( : Type - MLDv2 Report
2176 )"00:"$( : Code
2177 )"CHECKSUM:"$( : Checksum
2178 )"00:00:"$( : Reserved
2179 )"00:01:"$( : Number of Group Records
2180 )"01:"$( : Record Type - IS_IN
2181 )"00:"$( : Aux Data Len
2182 )"${nsources}:"$( : Number of Sources
2183 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2184 )"$(for src in "${sources[@]}"; do
2185 ipv6_to_bytes $src
2186 echo -n :
2187 done)"$( : Source Addresses
2188 )
2189
2190 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2191 local sudohdr=$(:
2192 )"$(ipv6_to_bytes $SIP):"$( : SIP
2193 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2194 )"${len}:"$( : Upper-layer length
2195 )"00:3a:"$( : Zero and next-header
2196 )
2197 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2198
2199 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2200}
2201
2202mldv1_done_get()
2203{
2204 local SIP=$1; shift
2205 local GRP=$1; shift
2206
2207 local hbh
2208 local icmpv6
2209
2210 hbh=$(:
2211 )"3a:"$( : Next Header - ICMPv6
2212 )"00:"$( : Hdr Ext Len
2213 )"00:00:00:00:00:00:"$( : Options and Padding
2214 )
2215
2216 icmpv6=$(:
2217 )"84:"$( : Type - MLDv1 Done
2218 )"00:"$( : Code
2219 )"CHECKSUM:"$( : Checksum
2220 )"00:00:"$( : Max Resp Delay - not meaningful
2221 )"00:00:"$( : Reserved
2222 )"$(ipv6_to_bytes $GRP):"$( : Multicast address
2223 )
2224
2225 local len=$(u16_to_bytes $(payload_template_nbytes $icmpv6))
2226 local sudohdr=$(:
2227 )"$(ipv6_to_bytes $SIP):"$( : SIP
2228 )"$(ipv6_to_bytes $GRP):"$( : DIP is multicast address
2229 )"${len}:"$( : Upper-layer length
2230 )"00:3a:"$( : Zero and next-header
2231 )
2232 local checksum=$(payload_template_calc_checksum ${sudohdr}${icmpv6})
2233
2234 payload_template_expand_checksum "$hbh$icmpv6" $checksum
2235}
2236
2237bail_on_lldpad()
2238{
2239 local reason1="$1"; shift
2240 local reason2="$1"; shift
2241 local caller=${FUNCNAME[1]}
2242 local src=${BASH_SOURCE[1]}
2243
2244 if systemctl is-active --quiet lldpad; then
2245
2246 cat >/dev/stderr <<-EOF
2247 WARNING: lldpad is running
2248
2249 lldpad will likely $reason1, and this test will
2250 $reason2. Both are not supported at the same time,
2251 one of them is arbitrarily going to overwrite the
2252 other. That will cause spurious failures (or, unlikely,
2253 passes) of this test.
2254 EOF
2255
2256 if [[ -z $ALLOW_LLDPAD ]]; then
2257 cat >/dev/stderr <<-EOF
2258
2259 If you want to run the test anyway, please set
2260 an environment variable ALLOW_LLDPAD to a
2261 non-empty string.
2262 EOF
2263 log_test_skip $src:$caller
2264 exit $EXIT_STATUS
2265 else
2266 return
2267 fi
2268 fi
2269}
2270
2271absval()
2272{
2273 local v=$1; shift
2274
2275 echo $((v > 0 ? v : -v))
2276}
2277
2278has_unicast_flt()
2279{
2280 local dev=$1; shift
2281 local mac_addr=$(mac_get $dev)
2282 local tmp=$(ether_addr_to_u64 $mac_addr)
2283 local promisc
2284
2285 ip link set $dev up
2286 ip link add link $dev name macvlan-tmp type macvlan mode private
2287 ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
2288 ip link set macvlan-tmp up
2289
2290 promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
2291
2292 ip link del macvlan-tmp
2293
2294 [[ $promisc == 1 ]] && echo "no" || echo "yes"
2295}