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