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