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