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