Commit | Line | Data |
---|---|---|
73bae673 IS |
1 | #!/bin/bash |
2 | # SPDX-License-Identifier: GPL-2.0 | |
3 | ||
4 | ############################################################################## | |
5 | # Defines | |
6 | ||
7844ec21 PHL |
7 | # Kselftest framework requirement - SKIP code is 4. |
8 | ksft_skip=4 | |
9 | ||
73bae673 IS |
10 | # Can be overridden by the configuration file. |
11 | PING=${PING:=ping} | |
12 | PING6=${PING6:=ping6} | |
d4deb014 | 13 | MZ=${MZ:=mausezahn} |
ca70a562 | 14 | ARPING=${ARPING:=arping} |
9d9e6bde | 15 | TEAMD=${TEAMD:=teamd} |
73bae673 IS |
16 | WAIT_TIME=${WAIT_TIME:=5} |
17 | PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} | |
18 | PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} | |
59be45c3 IS |
19 | NETIF_TYPE=${NETIF_TYPE:=veth} |
20 | NETIF_CREATE=${NETIF_CREATE:=yes} | |
6d4efada ND |
21 | MCD=${MCD:=smcrouted} |
22 | MC_CLI=${MC_CLI:=smcroutectl} | |
0cd0b1f7 | 23 | PING_COUNT=${PING_COUNT:=10} |
b6a4fd68 | 24 | PING_TIMEOUT=${PING_TIMEOUT:=5} |
8f72a9cf AC |
25 | WAIT_TIMEOUT=${WAIT_TIMEOUT:=20} |
26 | INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600} | |
08119759 | 27 | LOW_AGEING_TIME=${LOW_AGEING_TIME:=1000} |
d70b51f2 VO |
28 | REQUIRE_JQ=${REQUIRE_JQ:=yes} |
29 | REQUIRE_MZ=${REQUIRE_MZ:=yes} | |
f23cddc7 | 30 | REQUIRE_MTOOLS=${REQUIRE_MTOOLS:=no} |
b343734e | 31 | STABLE_MAC_ADDRS=${STABLE_MAC_ADDRS:=no} |
fe32dffd | 32 | TCPDUMP_EXTRA_FLAGS=${TCPDUMP_EXTRA_FLAGS:=} |
73bae673 | 33 | |
87d8fb18 YM |
34 | relative_path="${BASH_SOURCE%/*}" |
35 | if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then | |
36 | relative_path="." | |
37 | fi | |
38 | ||
39 | if [[ -f $relative_path/forwarding.config ]]; then | |
40 | source "$relative_path/forwarding.config" | |
73bae673 IS |
41 | fi |
42 | ||
43 | ############################################################################## | |
44 | # Sanity checks | |
45 | ||
198979be DA |
46 | check_tc_version() |
47 | { | |
48 | tc -j &> /dev/null | |
49 | if [[ $? -ne 0 ]]; then | |
50 | echo "SKIP: iproute2 too old; tc is missing JSON support" | |
7844ec21 | 51 | exit $ksft_skip |
198979be | 52 | fi |
7f333cbf | 53 | } |
198979be | 54 | |
203ee5cd GN |
55 | # Old versions of tc don't understand "mpls_uc" |
56 | check_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" | |
7844ec21 | 64 | return $ksft_skip |
203ee5cd GN |
65 | fi |
66 | tc filter del dev $dev ingress protocol mpls_uc pref 1 handle 1 \ | |
67 | matchall | |
68 | } | |
69 | ||
c09bfd9a GN |
70 | # Old versions of tc produce invalid json output for mpls lse statistics |
71 | check_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" | |
7844ec21 | 82 | return $ksft_skip |
c09bfd9a GN |
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" | |
7844ec21 | 92 | return $ksft_skip |
c09bfd9a GN |
93 | fi |
94 | } | |
95 | ||
7f333cbf JP |
96 | check_tc_shblock_support() |
97 | { | |
198979be DA |
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" | |
7844ec21 | 101 | exit $ksft_skip |
198979be DA |
102 | fi |
103 | } | |
104 | ||
2d73c887 JP |
105 | check_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" | |
7844ec21 | 110 | exit $ksft_skip |
2d73c887 JP |
111 | fi |
112 | } | |
113 | ||
ee4848ac JP |
114 | check_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" | |
7844ec21 | 119 | exit $ksft_skip |
ee4848ac JP |
120 | fi |
121 | } | |
122 | ||
f72e2f48 DR |
123 | check_ethtool_lanes_support() |
124 | { | |
125 | ethtool --help 2>&1| grep lanes &> /dev/null | |
126 | if [[ $? -ne 0 ]]; then | |
127 | echo "SKIP: ethtool too old; it is missing lanes support" | |
7844ec21 | 128 | exit $ksft_skip |
f72e2f48 DR |
129 | fi |
130 | } | |
131 | ||
b2b681a4 HS |
132 | check_locked_port_support() |
133 | { | |
134 | if ! bridge -d link show | grep -q " locked"; then | |
135 | echo "SKIP: iproute2 too old; Locked port feature not supported." | |
136 | return $ksft_skip | |
137 | fi | |
138 | } | |
139 | ||
73bae673 IS |
140 | if [[ "$(id -u)" -ne 0 ]]; then |
141 | echo "SKIP: need root privileges" | |
7844ec21 | 142 | exit $ksft_skip |
73bae673 IS |
143 | fi |
144 | ||
198979be DA |
145 | if [[ "$CHECK_TC" = "yes" ]]; then |
146 | check_tc_version | |
4908e24b JP |
147 | fi |
148 | ||
e094574f PM |
149 | require_command() |
150 | { | |
151 | local cmd=$1; shift | |
73bae673 | 152 | |
e094574f PM |
153 | if [[ ! -x "$(command -v "$cmd")" ]]; then |
154 | echo "SKIP: $cmd not installed" | |
7844ec21 | 155 | exit $ksft_skip |
e094574f PM |
156 | fi |
157 | } | |
158 | ||
d70b51f2 VO |
159 | if [[ "$REQUIRE_JQ" = "yes" ]]; then |
160 | require_command jq | |
161 | fi | |
162 | if [[ "$REQUIRE_MZ" = "yes" ]]; then | |
163 | require_command $MZ | |
164 | fi | |
f23cddc7 VO |
165 | if [[ "$REQUIRE_MTOOLS" = "yes" ]]; then |
166 | # https://github.com/vladimiroltean/mtools/ | |
167 | # patched for IPv6 support | |
168 | require_command msend | |
169 | require_command mreceive | |
170 | fi | |
d4deb014 | 171 | |
73bae673 IS |
172 | if [[ ! -v NUM_NETIFS ]]; then |
173 | echo "SKIP: importer does not define \"NUM_NETIFS\"" | |
7844ec21 | 174 | exit $ksft_skip |
73bae673 IS |
175 | fi |
176 | ||
781fe631 JP |
177 | ############################################################################## |
178 | # Command line options handling | |
179 | ||
180 | count=0 | |
181 | ||
182 | while [[ $# -gt 0 ]]; do | |
183 | if [[ "$count" -eq "0" ]]; then | |
184 | unset NETIFS | |
185 | declare -A NETIFS | |
186 | fi | |
187 | count=$((count + 1)) | |
188 | NETIFS[p$count]="$1" | |
189 | shift | |
190 | done | |
191 | ||
73bae673 IS |
192 | ############################################################################## |
193 | # Network interfaces configuration | |
194 | ||
190f887c DA |
195 | create_netif_veth() |
196 | { | |
197 | local i | |
198 | ||
601bc1c1 | 199 | for ((i = 1; i <= NUM_NETIFS; ++i)); do |
190f887c DA |
200 | local j=$((i+1)) |
201 | ||
202 | ip link show dev ${NETIFS[p$i]} &> /dev/null | |
203 | if [[ $? -ne 0 ]]; then | |
204 | ip link add ${NETIFS[p$i]} type veth \ | |
205 | peer name ${NETIFS[p$j]} | |
206 | if [[ $? -ne 0 ]]; then | |
207 | echo "Failed to create netif" | |
208 | exit 1 | |
209 | fi | |
210 | fi | |
211 | i=$j | |
212 | done | |
213 | } | |
214 | ||
215 | create_netif() | |
216 | { | |
217 | case "$NETIF_TYPE" in | |
218 | veth) create_netif_veth | |
219 | ;; | |
220 | *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" | |
221 | exit 1 | |
222 | ;; | |
223 | esac | |
224 | } | |
225 | ||
b343734e VO |
226 | declare -A MAC_ADDR_ORIG |
227 | mac_addr_prepare() | |
228 | { | |
229 | local new_addr= | |
230 | local dev= | |
231 | ||
232 | for ((i = 1; i <= NUM_NETIFS; ++i)); do | |
233 | dev=${NETIFS[p$i]} | |
234 | new_addr=$(printf "00:01:02:03:04:%02x" $i) | |
235 | ||
236 | MAC_ADDR_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].address') | |
237 | # Strip quotes | |
238 | MAC_ADDR_ORIG["$dev"]=${MAC_ADDR_ORIG["$dev"]//\"/} | |
239 | ip link set dev $dev address $new_addr | |
240 | done | |
241 | } | |
242 | ||
243 | mac_addr_restore() | |
244 | { | |
245 | local dev= | |
246 | ||
247 | for ((i = 1; i <= NUM_NETIFS; ++i)); do | |
248 | dev=${NETIFS[p$i]} | |
249 | ip link set dev $dev address ${MAC_ADDR_ORIG["$dev"]} | |
250 | done | |
251 | } | |
252 | ||
190f887c DA |
253 | if [[ "$NETIF_CREATE" = "yes" ]]; then |
254 | create_netif | |
255 | fi | |
256 | ||
b343734e VO |
257 | if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then |
258 | mac_addr_prepare | |
259 | fi | |
260 | ||
601bc1c1 | 261 | for ((i = 1; i <= NUM_NETIFS; ++i)); do |
73bae673 IS |
262 | ip link show dev ${NETIFS[p$i]} &> /dev/null |
263 | if [[ $? -ne 0 ]]; then | |
264 | echo "SKIP: could not find all required interfaces" | |
7844ec21 | 265 | exit $ksft_skip |
73bae673 IS |
266 | fi |
267 | done | |
268 | ||
269 | ############################################################################## | |
270 | # Helpers | |
271 | ||
272 | # Exit status to return at the end. Set in case one of the tests fails. | |
273 | EXIT_STATUS=0 | |
274 | # Per-test return value. Clear at the beginning of each test. | |
275 | RET=0 | |
276 | ||
277 | check_err() | |
278 | { | |
279 | local err=$1 | |
280 | local msg=$2 | |
281 | ||
282 | if [[ $RET -eq 0 && $err -ne 0 ]]; then | |
283 | RET=$err | |
284 | retmsg=$msg | |
285 | fi | |
286 | } | |
287 | ||
288 | check_fail() | |
289 | { | |
290 | local err=$1 | |
291 | local msg=$2 | |
292 | ||
293 | if [[ $RET -eq 0 && $err -eq 0 ]]; then | |
294 | RET=1 | |
295 | retmsg=$msg | |
296 | fi | |
297 | } | |
298 | ||
96fa91d2 PM |
299 | check_err_fail() |
300 | { | |
301 | local should_fail=$1; shift | |
302 | local err=$1; shift | |
303 | local what=$1; shift | |
304 | ||
305 | if ((should_fail)); then | |
306 | check_fail $err "$what succeeded, but should have failed" | |
307 | else | |
308 | check_err $err "$what failed" | |
309 | fi | |
310 | } | |
311 | ||
73bae673 IS |
312 | log_test() |
313 | { | |
314 | local test_name=$1 | |
315 | local opt_str=$2 | |
316 | ||
317 | if [[ $# -eq 2 ]]; then | |
318 | opt_str="($opt_str)" | |
319 | fi | |
320 | ||
321 | if [[ $RET -ne 0 ]]; then | |
322 | EXIT_STATUS=1 | |
323 | printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" | |
324 | if [[ ! -z "$retmsg" ]]; then | |
325 | printf "\t%s\n" "$retmsg" | |
326 | fi | |
327 | if [ "${PAUSE_ON_FAIL}" = "yes" ]; then | |
328 | echo "Hit enter to continue, 'q' to quit" | |
329 | read a | |
330 | [ "$a" = "q" ] && exit 1 | |
331 | fi | |
332 | return 1 | |
333 | fi | |
334 | ||
3cab0de9 | 335 | printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str" |
73bae673 IS |
336 | return 0 |
337 | } | |
338 | ||
b8bfafe4 PM |
339 | log_test_skip() |
340 | { | |
341 | local test_name=$1 | |
342 | local opt_str=$2 | |
343 | ||
344 | printf "TEST: %-60s [SKIP]\n" "$test_name $opt_str" | |
345 | return 0 | |
346 | } | |
347 | ||
3d578d87 IS |
348 | log_info() |
349 | { | |
350 | local msg=$1 | |
351 | ||
352 | echo "INFO: $msg" | |
353 | } | |
354 | ||
c5341bcc PM |
355 | busywait() |
356 | { | |
357 | local timeout=$1; shift | |
358 | ||
359 | local start_time="$(date -u +%s%3N)" | |
360 | while true | |
361 | do | |
362 | local out | |
363 | out=$("$@") | |
364 | local ret=$? | |
365 | if ((!ret)); then | |
366 | echo -n "$out" | |
367 | return 0 | |
368 | fi | |
369 | ||
370 | local current_time="$(date -u +%s%3N)" | |
371 | if ((current_time - start_time > timeout)); then | |
372 | echo -n "$out" | |
373 | return 1 | |
374 | fi | |
375 | done | |
376 | } | |
377 | ||
05ef614c DR |
378 | not() |
379 | { | |
380 | "$@" | |
381 | [[ $? != 0 ]] | |
382 | } | |
383 | ||
f72e2f48 DR |
384 | get_max() |
385 | { | |
386 | local arr=("$@") | |
387 | ||
388 | max=${arr[0]} | |
389 | for cur in ${arr[@]}; do | |
390 | if [[ $cur -gt $max ]]; then | |
391 | max=$cur | |
392 | fi | |
393 | done | |
394 | ||
395 | echo $max | |
396 | } | |
397 | ||
05ef614c DR |
398 | grep_bridge_fdb() |
399 | { | |
400 | local addr=$1; shift | |
401 | local word | |
402 | local flag | |
403 | ||
404 | if [ "$1" == "self" ] || [ "$1" == "master" ]; then | |
405 | word=$1; shift | |
406 | if [ "$1" == "-v" ]; then | |
407 | flag=$1; shift | |
408 | fi | |
409 | fi | |
410 | ||
411 | $@ | grep $addr | grep $flag "$word" | |
412 | } | |
413 | ||
f72e2f48 DR |
414 | wait_for_port_up() |
415 | { | |
416 | "$@" | grep -q "Link detected: yes" | |
417 | } | |
418 | ||
0c22f993 DR |
419 | wait_for_offload() |
420 | { | |
421 | "$@" | grep -q offload | |
422 | } | |
423 | ||
ffd3e9b0 IS |
424 | wait_for_trap() |
425 | { | |
426 | "$@" | grep -q trap | |
427 | } | |
428 | ||
4121d947 PM |
429 | until_counter_is() |
430 | { | |
844f0556 | 431 | local expr=$1; shift |
4121d947 PM |
432 | local current=$("$@") |
433 | ||
434 | echo $((current)) | |
844f0556 | 435 | ((current $expr)) |
4121d947 PM |
436 | } |
437 | ||
438 | busywait_for_counter() | |
439 | { | |
440 | local timeout=$1; shift | |
441 | local delta=$1; shift | |
442 | ||
443 | local base=$("$@") | |
844f0556 | 444 | busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" |
4121d947 PM |
445 | } |
446 | ||
010079ba PM |
447 | setup_wait_dev() |
448 | { | |
449 | local dev=$1; shift | |
8f72a9cf | 450 | local wait_time=${1:-$WAIT_TIME}; shift |
010079ba | 451 | |
8f72a9cf AC |
452 | setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time |
453 | ||
454 | if (($?)); then | |
455 | check_err 1 | |
456 | log_test setup_wait_dev ": Interface $dev does not come up." | |
457 | exit 1 | |
458 | fi | |
459 | } | |
460 | ||
461 | setup_wait_dev_with_timeout() | |
462 | { | |
463 | local dev=$1; shift | |
464 | local max_iterations=${1:-$WAIT_TIMEOUT}; shift | |
465 | local wait_time=${1:-$WAIT_TIME}; shift | |
466 | local i | |
467 | ||
468 | for ((i = 1; i <= $max_iterations; ++i)); do | |
010079ba PM |
469 | ip link show dev $dev up \ |
470 | | grep 'state UP' &> /dev/null | |
471 | if [[ $? -ne 0 ]]; then | |
472 | sleep 1 | |
473 | else | |
8f72a9cf AC |
474 | sleep $wait_time |
475 | return 0 | |
010079ba PM |
476 | fi |
477 | done | |
8f72a9cf AC |
478 | |
479 | return 1 | |
010079ba PM |
480 | } |
481 | ||
73bae673 IS |
482 | setup_wait() |
483 | { | |
68d9cea5 | 484 | local num_netifs=${1:-$NUM_NETIFS} |
8f72a9cf | 485 | local i |
68d9cea5 PM |
486 | |
487 | for ((i = 1; i <= num_netifs; ++i)); do | |
8f72a9cf | 488 | setup_wait_dev ${NETIFS[p$i]} 0 |
73bae673 IS |
489 | done |
490 | ||
491 | # Make sure links are ready. | |
492 | sleep $WAIT_TIME | |
493 | } | |
494 | ||
d87e5edb JP |
495 | cmd_jq() |
496 | { | |
497 | local cmd=$1 | |
498 | local jq_exp=$2 | |
c04d71b5 | 499 | local jq_opts=$3 |
d87e5edb JP |
500 | local ret |
501 | local output | |
502 | ||
503 | output="$($cmd)" | |
504 | # it the command fails, return error right away | |
505 | ret=$? | |
506 | if [[ $ret -ne 0 ]]; then | |
507 | return $ret | |
508 | fi | |
c04d71b5 JP |
509 | output=$(echo $output | jq -r $jq_opts "$jq_exp") |
510 | ret=$? | |
511 | if [[ $ret -ne 0 ]]; then | |
512 | return $ret | |
513 | fi | |
d87e5edb JP |
514 | echo $output |
515 | # return success only in case of non-empty output | |
516 | [ ! -z "$output" ] | |
517 | } | |
518 | ||
989133bf PM |
519 | lldpad_app_wait_set() |
520 | { | |
521 | local dev=$1; shift | |
522 | ||
37280905 | 523 | while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do |
989133bf PM |
524 | echo "$dev: waiting for lldpad to push pending APP updates" |
525 | sleep 5 | |
526 | done | |
527 | } | |
528 | ||
529 | lldpad_app_wait_del() | |
530 | { | |
531 | # Give lldpad a chance to push down the changes. If the device is downed | |
532 | # too soon, the updates will be left pending. However, they will have | |
533 | # been struck off the lldpad's DB already, so we won't be able to tell | |
534 | # they are pending. Then on next test iteration this would cause | |
535 | # weirdness as newly-added APP rules conflict with the old ones, | |
536 | # sometimes getting stuck in an "unknown" state. | |
537 | sleep 5 | |
538 | } | |
539 | ||
73bae673 IS |
540 | pre_cleanup() |
541 | { | |
542 | if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then | |
543 | echo "Pausing before cleanup, hit any key to continue" | |
544 | read | |
545 | fi | |
b343734e VO |
546 | |
547 | if [[ "$STABLE_MAC_ADDRS" = "yes" ]]; then | |
548 | mac_addr_restore | |
549 | fi | |
73bae673 IS |
550 | } |
551 | ||
552 | vrf_prepare() | |
553 | { | |
554 | ip -4 rule add pref 32765 table local | |
555 | ip -4 rule del pref 0 | |
556 | ip -6 rule add pref 32765 table local | |
557 | ip -6 rule del pref 0 | |
558 | } | |
559 | ||
560 | vrf_cleanup() | |
561 | { | |
562 | ip -6 rule add pref 0 table local | |
563 | ip -6 rule del pref 32765 | |
564 | ip -4 rule add pref 0 table local | |
565 | ip -4 rule del pref 32765 | |
566 | } | |
567 | ||
568 | __last_tb_id=0 | |
569 | declare -A __TB_IDS | |
570 | ||
571 | __vrf_td_id_assign() | |
572 | { | |
573 | local vrf_name=$1 | |
574 | ||
575 | __last_tb_id=$((__last_tb_id + 1)) | |
576 | __TB_IDS[$vrf_name]=$__last_tb_id | |
577 | return $__last_tb_id | |
578 | } | |
579 | ||
580 | __vrf_td_id_lookup() | |
581 | { | |
582 | local vrf_name=$1 | |
583 | ||
584 | return ${__TB_IDS[$vrf_name]} | |
585 | } | |
586 | ||
587 | vrf_create() | |
588 | { | |
589 | local vrf_name=$1 | |
590 | local tb_id | |
591 | ||
592 | __vrf_td_id_assign $vrf_name | |
593 | tb_id=$? | |
594 | ||
595 | ip link add dev $vrf_name type vrf table $tb_id | |
596 | ip -4 route add table $tb_id unreachable default metric 4278198272 | |
597 | ip -6 route add table $tb_id unreachable default metric 4278198272 | |
598 | } | |
599 | ||
600 | vrf_destroy() | |
601 | { | |
602 | local vrf_name=$1 | |
603 | local tb_id | |
604 | ||
605 | __vrf_td_id_lookup $vrf_name | |
606 | tb_id=$? | |
607 | ||
608 | ip -6 route del table $tb_id unreachable default metric 4278198272 | |
609 | ip -4 route del table $tb_id unreachable default metric 4278198272 | |
610 | ip link del dev $vrf_name | |
611 | } | |
612 | ||
613 | __addr_add_del() | |
614 | { | |
615 | local if_name=$1 | |
616 | local add_del=$2 | |
617 | local array | |
618 | ||
619 | shift | |
620 | shift | |
621 | array=("${@}") | |
622 | ||
623 | for addrstr in "${array[@]}"; do | |
624 | ip address $add_del $addrstr dev $if_name | |
625 | done | |
626 | } | |
627 | ||
3368b223 PM |
628 | __simple_if_init() |
629 | { | |
630 | local if_name=$1; shift | |
631 | local vrf_name=$1; shift | |
632 | local addrs=("${@}") | |
633 | ||
634 | ip link set dev $if_name master $vrf_name | |
635 | ip link set dev $if_name up | |
636 | ||
637 | __addr_add_del $if_name add "${addrs[@]}" | |
638 | } | |
639 | ||
640 | __simple_if_fini() | |
641 | { | |
642 | local if_name=$1; shift | |
643 | local addrs=("${@}") | |
644 | ||
645 | __addr_add_del $if_name del "${addrs[@]}" | |
646 | ||
647 | ip link set dev $if_name down | |
648 | ip link set dev $if_name nomaster | |
649 | } | |
650 | ||
73bae673 IS |
651 | simple_if_init() |
652 | { | |
653 | local if_name=$1 | |
654 | local vrf_name | |
655 | local array | |
656 | ||
657 | shift | |
658 | vrf_name=v$if_name | |
659 | array=("${@}") | |
660 | ||
661 | vrf_create $vrf_name | |
73bae673 | 662 | ip link set dev $vrf_name up |
3368b223 | 663 | __simple_if_init $if_name $vrf_name "${array[@]}" |
73bae673 IS |
664 | } |
665 | ||
666 | simple_if_fini() | |
667 | { | |
668 | local if_name=$1 | |
669 | local vrf_name | |
670 | local array | |
671 | ||
672 | shift | |
673 | vrf_name=v$if_name | |
674 | array=("${@}") | |
675 | ||
3368b223 | 676 | __simple_if_fini $if_name "${array[@]}" |
73bae673 IS |
677 | vrf_destroy $vrf_name |
678 | } | |
679 | ||
7d4cbae0 PM |
680 | tunnel_create() |
681 | { | |
682 | local name=$1; shift | |
683 | local type=$1; shift | |
684 | local local=$1; shift | |
685 | local remote=$1; shift | |
686 | ||
687 | ip link add name $name type $type \ | |
688 | local $local remote $remote "$@" | |
689 | ip link set dev $name up | |
690 | } | |
691 | ||
692 | tunnel_destroy() | |
693 | { | |
694 | local name=$1; shift | |
695 | ||
696 | ip link del dev $name | |
697 | } | |
698 | ||
0e7a504c PM |
699 | vlan_create() |
700 | { | |
701 | local if_name=$1; shift | |
702 | local vid=$1; shift | |
703 | local vrf=$1; shift | |
704 | local ips=("${@}") | |
705 | local name=$if_name.$vid | |
706 | ||
707 | ip link add name $name link $if_name type vlan id $vid | |
708 | if [ "$vrf" != "" ]; then | |
709 | ip link set dev $name master $vrf | |
710 | fi | |
711 | ip link set dev $name up | |
712 | __addr_add_del $name add "${ips[@]}" | |
713 | } | |
714 | ||
715 | vlan_destroy() | |
716 | { | |
717 | local if_name=$1; shift | |
718 | local vid=$1; shift | |
719 | local name=$if_name.$vid | |
720 | ||
721 | ip link del dev $name | |
722 | } | |
723 | ||
9d9e6bde PM |
724 | team_create() |
725 | { | |
726 | local if_name=$1; shift | |
727 | local mode=$1; shift | |
728 | ||
729 | require_command $TEAMD | |
730 | $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' | |
731 | for slave in "$@"; do | |
732 | ip link set dev $slave down | |
733 | ip link set dev $slave master $if_name | |
734 | ip link set dev $slave up | |
735 | done | |
736 | ip link set dev $if_name up | |
737 | } | |
738 | ||
739 | team_destroy() | |
740 | { | |
741 | local if_name=$1; shift | |
742 | ||
743 | $TEAMD -t $if_name -k | |
744 | } | |
745 | ||
73bae673 IS |
746 | master_name_get() |
747 | { | |
748 | local if_name=$1 | |
749 | ||
750 | ip -j link show dev $if_name | jq -r '.[]["master"]' | |
751 | } | |
752 | ||
d1038cd0 PM |
753 | link_stats_get() |
754 | { | |
755 | local if_name=$1; shift | |
756 | local dir=$1; shift | |
757 | local stat=$1; shift | |
758 | ||
759 | ip -j -s link show dev $if_name \ | |
760 | | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' | |
761 | } | |
762 | ||
3d578d87 IS |
763 | link_stats_tx_packets_get() |
764 | { | |
d1038cd0 PM |
765 | link_stats_get $1 tx packets |
766 | } | |
3d578d87 | 767 | |
d1038cd0 PM |
768 | link_stats_rx_errors_get() |
769 | { | |
770 | link_stats_get $1 rx errors | |
3d578d87 IS |
771 | } |
772 | ||
7d4cbae0 PM |
773 | tc_rule_stats_get() |
774 | { | |
775 | local dev=$1; shift | |
776 | local pref=$1; shift | |
a66d62d8 | 777 | local dir=$1; shift |
c143139b | 778 | local selector=${1:-.packets}; shift |
7d4cbae0 | 779 | |
a66d62d8 | 780 | tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ |
c143139b | 781 | | jq ".[1].options.actions[].stats$selector" |
7d4cbae0 PM |
782 | } |
783 | ||
84ea1f85 PM |
784 | tc_rule_handle_stats_get() |
785 | { | |
786 | local id=$1; shift | |
787 | local handle=$1; shift | |
788 | local selector=${1:-.packets}; shift | |
789 | ||
790 | tc -j -s filter show $id \ | |
791 | | jq ".[] | select(.options.handle == $handle) | \ | |
792 | .options.actions[0].stats$selector" | |
793 | } | |
794 | ||
3136a369 PM |
795 | ethtool_stats_get() |
796 | { | |
797 | local dev=$1; shift | |
798 | local stat=$1; shift | |
799 | ||
800 | ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 | |
801 | } | |
802 | ||
3de611b5 PM |
803 | qdisc_stats_get() |
804 | { | |
805 | local dev=$1; shift | |
806 | local handle=$1; shift | |
807 | local selector=$1; shift | |
808 | ||
809 | tc -j -s qdisc show dev "$dev" \ | |
810 | | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" | |
811 | } | |
812 | ||
b9b72999 PM |
813 | qdisc_parent_stats_get() |
814 | { | |
815 | local dev=$1; shift | |
816 | local parent=$1; shift | |
817 | local selector=$1; shift | |
818 | ||
819 | tc -j -s qdisc show dev "$dev" invisible \ | |
820 | | jq '.[] | select(.parent == "'"$parent"'") | '"$selector" | |
821 | } | |
822 | ||
0857d6f8 SS |
823 | ipv6_stats_get() |
824 | { | |
825 | local dev=$1; shift | |
826 | local stat=$1; shift | |
827 | ||
828 | cat /proc/net/dev_snmp6/$dev | grep "^$stat" | cut -f2 | |
829 | } | |
830 | ||
32fb67a3 PM |
831 | hw_stats_get() |
832 | { | |
833 | local suite=$1; shift | |
834 | local if_name=$1; shift | |
835 | local dir=$1; shift | |
836 | local stat=$1; shift | |
837 | ||
838 | ip -j stats show dev $if_name group offload subgroup $suite | | |
839 | jq ".[0].stats64.$dir.$stat" | |
840 | } | |
841 | ||
adc6c7ec PM |
842 | humanize() |
843 | { | |
844 | local speed=$1; shift | |
845 | ||
846 | for unit in bps Kbps Mbps Gbps; do | |
847 | if (($(echo "$speed < 1024" | bc))); then | |
848 | break | |
849 | fi | |
850 | ||
851 | speed=$(echo "scale=1; $speed / 1024" | bc) | |
852 | done | |
853 | ||
854 | echo "$speed${unit}" | |
855 | } | |
856 | ||
857 | rate() | |
858 | { | |
859 | local t0=$1; shift | |
860 | local t1=$1; shift | |
861 | local interval=$1; shift | |
862 | ||
863 | echo $((8 * (t1 - t0) / interval)) | |
864 | } | |
865 | ||
53b61f29 BZ |
866 | packets_rate() |
867 | { | |
868 | local t0=$1; shift | |
869 | local t1=$1; shift | |
870 | local interval=$1; shift | |
871 | ||
872 | echo $(((t1 - t0) / interval)) | |
873 | } | |
874 | ||
4e4272d2 JP |
875 | mac_get() |
876 | { | |
877 | local if_name=$1 | |
878 | ||
879 | ip -j link show dev $if_name | jq -r '.[]["address"]' | |
880 | } | |
881 | ||
a5114df6 VO |
882 | ipv6_lladdr_get() |
883 | { | |
884 | local if_name=$1 | |
885 | ||
886 | ip -j addr show dev $if_name | \ | |
887 | jq -r '.[]["addr_info"][] | select(.scope == "link").local' | \ | |
888 | head -1 | |
889 | } | |
890 | ||
d4deb014 IS |
891 | bridge_ageing_time_get() |
892 | { | |
893 | local bridge=$1 | |
894 | local ageing_time | |
895 | ||
896 | # Need to divide by 100 to convert to seconds. | |
897 | ageing_time=$(ip -j -d link show dev $bridge \ | |
898 | | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') | |
899 | echo $((ageing_time / 100)) | |
900 | } | |
901 | ||
f5ae5778 PM |
902 | declare -A SYSCTL_ORIG |
903 | sysctl_set() | |
904 | { | |
905 | local key=$1; shift | |
906 | local value=$1; shift | |
907 | ||
908 | SYSCTL_ORIG[$key]=$(sysctl -n $key) | |
909 | sysctl -qw $key=$value | |
910 | } | |
911 | ||
912 | sysctl_restore() | |
913 | { | |
914 | local key=$1; shift | |
915 | ||
916 | sysctl -qw $key=${SYSCTL_ORIG["$key"]} | |
917 | } | |
918 | ||
7b7bc875 IS |
919 | forwarding_enable() |
920 | { | |
d51d10aa PM |
921 | sysctl_set net.ipv4.conf.all.forwarding 1 |
922 | sysctl_set net.ipv6.conf.all.forwarding 1 | |
7b7bc875 IS |
923 | } |
924 | ||
925 | forwarding_restore() | |
926 | { | |
d51d10aa PM |
927 | sysctl_restore net.ipv6.conf.all.forwarding |
928 | sysctl_restore net.ipv4.conf.all.forwarding | |
7b7bc875 IS |
929 | } |
930 | ||
a381ed12 PM |
931 | declare -A MTU_ORIG |
932 | mtu_set() | |
933 | { | |
934 | local dev=$1; shift | |
935 | local mtu=$1; shift | |
936 | ||
937 | MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') | |
938 | ip link set dev $dev mtu $mtu | |
939 | } | |
940 | ||
941 | mtu_restore() | |
942 | { | |
943 | local dev=$1; shift | |
944 | ||
945 | ip link set dev $dev mtu ${MTU_ORIG["$dev"]} | |
946 | } | |
947 | ||
2f19f212 JP |
948 | tc_offload_check() |
949 | { | |
68d9cea5 PM |
950 | local num_netifs=${1:-$NUM_NETIFS} |
951 | ||
952 | for ((i = 1; i <= num_netifs; ++i)); do | |
2f19f212 JP |
953 | ethtool -k ${NETIFS[p$i]} \ |
954 | | grep "hw-tc-offload: on" &> /dev/null | |
955 | if [[ $? -ne 0 ]]; then | |
956 | return 1 | |
957 | fi | |
958 | done | |
959 | ||
960 | return 0 | |
961 | } | |
962 | ||
87c0c046 | 963 | trap_install() |
7d4cbae0 PM |
964 | { |
965 | local dev=$1; shift | |
966 | local direction=$1; shift | |
967 | ||
ac0fcadf PM |
968 | # Some devices may not support or need in-hardware trapping of traffic |
969 | # (e.g. the veth pairs that this library creates for non-existent | |
970 | # loopbacks). Use continue instead, so that there is a filter in there | |
971 | # (some tests check counters), and so that other filters are still | |
972 | # processed. | |
973 | tc filter add dev $dev $direction pref 1 \ | |
974 | flower skip_sw action trap 2>/dev/null \ | |
975 | || tc filter add dev $dev $direction pref 1 \ | |
976 | flower action continue | |
7d4cbae0 PM |
977 | } |
978 | ||
87c0c046 | 979 | trap_uninstall() |
7d4cbae0 PM |
980 | { |
981 | local dev=$1; shift | |
982 | local direction=$1; shift | |
983 | ||
ac0fcadf | 984 | tc filter del dev $dev $direction pref 1 flower |
87c0c046 PM |
985 | } |
986 | ||
987 | slow_path_trap_install() | |
988 | { | |
ac0fcadf PM |
989 | # For slow-path testing, we need to install a trap to get to |
990 | # slow path the packets that would otherwise be switched in HW. | |
87c0c046 PM |
991 | if [ "${tcflags/skip_hw}" != "$tcflags" ]; then |
992 | trap_install "$@" | |
993 | fi | |
994 | } | |
995 | ||
996 | slow_path_trap_uninstall() | |
997 | { | |
7d4cbae0 | 998 | if [ "${tcflags/skip_hw}" != "$tcflags" ]; then |
87c0c046 | 999 | trap_uninstall "$@" |
7d4cbae0 PM |
1000 | fi |
1001 | } | |
1002 | ||
1003 | __icmp_capture_add_del() | |
1004 | { | |
1005 | local add_del=$1; shift | |
1006 | local pref=$1; shift | |
1007 | local vsuf=$1; shift | |
1008 | local tundev=$1; shift | |
1009 | local filter=$1; shift | |
1010 | ||
1011 | tc filter $add_del dev "$tundev" ingress \ | |
1012 | proto ip$vsuf pref $pref \ | |
1013 | flower ip_proto icmp$vsuf $filter \ | |
1014 | action pass | |
1015 | } | |
1016 | ||
1017 | icmp_capture_install() | |
1018 | { | |
1019 | __icmp_capture_add_del add 100 "" "$@" | |
1020 | } | |
1021 | ||
1022 | icmp_capture_uninstall() | |
1023 | { | |
1024 | __icmp_capture_add_del del 100 "" "$@" | |
1025 | } | |
1026 | ||
1027 | icmp6_capture_install() | |
1028 | { | |
1029 | __icmp_capture_add_del add 100 v6 "$@" | |
1030 | } | |
1031 | ||
1032 | icmp6_capture_uninstall() | |
1033 | { | |
1034 | __icmp_capture_add_del del 100 v6 "$@" | |
1035 | } | |
1036 | ||
2004a9bc PM |
1037 | __vlan_capture_add_del() |
1038 | { | |
1039 | local add_del=$1; shift | |
1040 | local pref=$1; shift | |
1041 | local dev=$1; shift | |
1042 | local filter=$1; shift | |
1043 | ||
1044 | tc filter $add_del dev "$dev" ingress \ | |
1045 | proto 802.1q pref $pref \ | |
1046 | flower $filter \ | |
1047 | action pass | |
1048 | } | |
1049 | ||
1050 | vlan_capture_install() | |
1051 | { | |
1052 | __vlan_capture_add_del add 100 "$@" | |
1053 | } | |
1054 | ||
1055 | vlan_capture_uninstall() | |
1056 | { | |
1057 | __vlan_capture_add_del del 100 "$@" | |
1058 | } | |
1059 | ||
cf608698 PM |
1060 | __dscp_capture_add_del() |
1061 | { | |
1062 | local add_del=$1; shift | |
1063 | local dev=$1; shift | |
1064 | local base=$1; shift | |
1065 | local dscp; | |
1066 | ||
1067 | for prio in {0..7}; do | |
1068 | dscp=$((base + prio)) | |
1069 | __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ | |
1070 | "skip_hw ip_tos $((dscp << 2))" | |
1071 | done | |
1072 | } | |
1073 | ||
1074 | dscp_capture_install() | |
1075 | { | |
1076 | local dev=$1; shift | |
1077 | local base=$1; shift | |
1078 | ||
1079 | __dscp_capture_add_del add $dev $base | |
1080 | } | |
1081 | ||
1082 | dscp_capture_uninstall() | |
1083 | { | |
1084 | local dev=$1; shift | |
1085 | local base=$1; shift | |
1086 | ||
1087 | __dscp_capture_add_del del $dev $base | |
1088 | } | |
1089 | ||
1090 | dscp_fetch_stats() | |
1091 | { | |
1092 | local dev=$1; shift | |
1093 | local base=$1; shift | |
1094 | ||
1095 | for prio in {0..7}; do | |
1096 | local dscp=$((base + prio)) | |
1097 | local t=$(tc_rule_stats_get $dev $((dscp + 100))) | |
1098 | echo "[$dscp]=$t " | |
1099 | done | |
1100 | } | |
1101 | ||
7d4cbae0 PM |
1102 | matchall_sink_create() |
1103 | { | |
1104 | local dev=$1; shift | |
1105 | ||
1106 | tc qdisc add dev $dev clsact | |
1107 | tc filter add dev $dev ingress \ | |
1108 | pref 10000 \ | |
1109 | matchall \ | |
1110 | action drop | |
1111 | } | |
1112 | ||
0eb8053c IS |
1113 | tests_run() |
1114 | { | |
1115 | local current_test | |
1116 | ||
1117 | for current_test in ${TESTS:-$ALL_TESTS}; do | |
1118 | $current_test | |
1119 | done | |
1120 | } | |
1121 | ||
b2c47872 PM |
1122 | multipath_eval() |
1123 | { | |
1b86fa3b PM |
1124 | local desc="$1" |
1125 | local weight_rp12=$2 | |
1126 | local weight_rp13=$3 | |
1127 | local packets_rp12=$4 | |
1128 | local packets_rp13=$5 | |
1129 | local weights_ratio packets_ratio diff | |
1130 | ||
1131 | RET=0 | |
1132 | ||
1133 | if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then | |
1134 | weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ | |
1135 | | bc -l) | |
1136 | else | |
1137 | weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ | |
1138 | | bc -l) | |
1139 | fi | |
1140 | ||
1141 | if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then | |
1142 | check_err 1 "Packet difference is 0" | |
1143 | log_test "Multipath" | |
1144 | log_info "Expected ratio $weights_ratio" | |
1145 | return | |
1146 | fi | |
1147 | ||
1148 | if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then | |
1149 | packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ | |
1150 | | bc -l) | |
1151 | else | |
1152 | packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ | |
1153 | | bc -l) | |
1154 | fi | |
1155 | ||
1156 | diff=$(echo $weights_ratio - $packets_ratio | bc -l) | |
1157 | diff=${diff#-} | |
1158 | ||
1159 | test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 | |
1160 | check_err $? "Too large discrepancy between expected and measured ratios" | |
1161 | log_test "$desc" | |
1162 | log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" | |
b2c47872 PM |
1163 | } |
1164 | ||
d0540d17 PM |
1165 | in_ns() |
1166 | { | |
1167 | local name=$1; shift | |
1168 | ||
1169 | ip netns exec $name bash <<-EOF | |
1170 | NUM_NETIFS=0 | |
1171 | source lib.sh | |
1172 | $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) | |
1173 | EOF | |
1174 | } | |
1175 | ||
73bae673 IS |
1176 | ############################################################################## |
1177 | # Tests | |
1178 | ||
967450c5 | 1179 | ping_do() |
73bae673 IS |
1180 | { |
1181 | local if_name=$1 | |
1182 | local dip=$2 | |
d20b0f21 | 1183 | local args=$3 |
73bae673 IS |
1184 | local vrf_name |
1185 | ||
73bae673 | 1186 | vrf_name=$(master_name_get $if_name) |
b6a4fd68 | 1187 | ip vrf exec $vrf_name \ |
0cd0b1f7 AC |
1188 | $PING $args $dip -c $PING_COUNT -i 0.1 \ |
1189 | -w $PING_TIMEOUT &> /dev/null | |
967450c5 NA |
1190 | } |
1191 | ||
1192 | ping_test() | |
1193 | { | |
1194 | RET=0 | |
1195 | ||
1196 | ping_do $1 $2 | |
73bae673 | 1197 | check_err $? |
58c7a2d1 | 1198 | log_test "ping$3" |
73bae673 IS |
1199 | } |
1200 | ||
967450c5 | 1201 | ping6_do() |
73bae673 IS |
1202 | { |
1203 | local if_name=$1 | |
1204 | local dip=$2 | |
d20b0f21 | 1205 | local args=$3 |
73bae673 IS |
1206 | local vrf_name |
1207 | ||
73bae673 | 1208 | vrf_name=$(master_name_get $if_name) |
b6a4fd68 | 1209 | ip vrf exec $vrf_name \ |
0cd0b1f7 AC |
1210 | $PING6 $args $dip -c $PING_COUNT -i 0.1 \ |
1211 | -w $PING_TIMEOUT &> /dev/null | |
967450c5 NA |
1212 | } |
1213 | ||
1214 | ping6_test() | |
1215 | { | |
1216 | RET=0 | |
1217 | ||
1218 | ping6_do $1 $2 | |
73bae673 | 1219 | check_err $? |
58c7a2d1 | 1220 | log_test "ping6$3" |
73bae673 | 1221 | } |
d4deb014 IS |
1222 | |
1223 | learning_test() | |
1224 | { | |
1225 | local bridge=$1 | |
1226 | local br_port1=$2 # Connected to `host1_if`. | |
1227 | local host1_if=$3 | |
1228 | local host2_if=$4 | |
1229 | local mac=de:ad:be:ef:13:37 | |
1230 | local ageing_time | |
1231 | ||
1232 | RET=0 | |
1233 | ||
1234 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1235 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1236 | check_fail $? "Found FDB record when should not" | |
1237 | ||
1238 | # Disable unknown unicast flooding on `br_port1` to make sure | |
1239 | # packets are only forwarded through the port after a matching | |
1240 | # FDB entry was installed. | |
1241 | bridge link set dev $br_port1 flood off | |
1242 | ||
1a635d3e | 1243 | ip link set $host1_if promisc on |
d4deb014 IS |
1244 | tc qdisc add dev $host1_if ingress |
1245 | tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ | |
1246 | flower dst_mac $mac action drop | |
1247 | ||
1248 | $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q | |
1249 | sleep 1 | |
1250 | ||
1251 | tc -j -s filter show dev $host1_if ingress \ | |
1252 | | jq -e ".[] | select(.options.handle == 101) \ | |
1253 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
83844aac | 1254 | check_fail $? "Packet reached first host when should not" |
d4deb014 IS |
1255 | |
1256 | $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q | |
1257 | sleep 1 | |
1258 | ||
1259 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1260 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1261 | check_err $? "Did not find FDB record when should" | |
1262 | ||
1263 | $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q | |
1264 | sleep 1 | |
1265 | ||
1266 | tc -j -s filter show dev $host1_if ingress \ | |
1267 | | jq -e ".[] | select(.options.handle == 101) \ | |
1268 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1269 | check_err $? "Packet did not reach second host when should" | |
1270 | ||
1271 | # Wait for 10 seconds after the ageing time to make sure FDB | |
1272 | # record was aged-out. | |
1273 | ageing_time=$(bridge_ageing_time_get $bridge) | |
1274 | sleep $((ageing_time + 10)) | |
1275 | ||
1276 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1277 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1278 | check_fail $? "Found FDB record when should not" | |
1279 | ||
1280 | bridge link set dev $br_port1 learning off | |
1281 | ||
1282 | $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q | |
1283 | sleep 1 | |
1284 | ||
1285 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1286 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1287 | check_fail $? "Found FDB record when should not" | |
1288 | ||
1289 | bridge link set dev $br_port1 learning on | |
1290 | ||
1291 | tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower | |
1292 | tc qdisc del dev $host1_if ingress | |
1a635d3e | 1293 | ip link set $host1_if promisc off |
d4deb014 IS |
1294 | |
1295 | bridge link set dev $br_port1 flood on | |
1296 | ||
1297 | log_test "FDB learning" | |
1298 | } | |
236dd50b IS |
1299 | |
1300 | flood_test_do() | |
1301 | { | |
1302 | local should_flood=$1 | |
1303 | local mac=$2 | |
1304 | local ip=$3 | |
1305 | local host1_if=$4 | |
1306 | local host2_if=$5 | |
1307 | local err=0 | |
1308 | ||
1309 | # Add an ACL on `host2_if` which will tell us whether the packet | |
1310 | # was flooded to it or not. | |
b8e629b0 | 1311 | ip link set $host2_if promisc on |
236dd50b IS |
1312 | tc qdisc add dev $host2_if ingress |
1313 | tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ | |
1314 | flower dst_mac $mac action drop | |
1315 | ||
1316 | $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q | |
1317 | sleep 1 | |
1318 | ||
1319 | tc -j -s filter show dev $host2_if ingress \ | |
1320 | | jq -e ".[] | select(.options.handle == 101) \ | |
1321 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1322 | if [[ $? -ne 0 && $should_flood == "true" || \ | |
1323 | $? -eq 0 && $should_flood == "false" ]]; then | |
1324 | err=1 | |
1325 | fi | |
1326 | ||
1327 | tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower | |
1328 | tc qdisc del dev $host2_if ingress | |
b8e629b0 | 1329 | ip link set $host2_if promisc off |
236dd50b IS |
1330 | |
1331 | return $err | |
1332 | } | |
1333 | ||
1334 | flood_unicast_test() | |
1335 | { | |
1336 | local br_port=$1 | |
1337 | local host1_if=$2 | |
1338 | local host2_if=$3 | |
1339 | local mac=de:ad:be:ef:13:37 | |
1340 | local ip=192.0.2.100 | |
1341 | ||
1342 | RET=0 | |
1343 | ||
1344 | bridge link set dev $br_port flood off | |
1345 | ||
1346 | flood_test_do false $mac $ip $host1_if $host2_if | |
1347 | check_err $? "Packet flooded when should not" | |
1348 | ||
1349 | bridge link set dev $br_port flood on | |
1350 | ||
1351 | flood_test_do true $mac $ip $host1_if $host2_if | |
1352 | check_err $? "Packet was not flooded when should" | |
1353 | ||
1354 | log_test "Unknown unicast flood" | |
1355 | } | |
1356 | ||
1357 | flood_multicast_test() | |
1358 | { | |
1359 | local br_port=$1 | |
1360 | local host1_if=$2 | |
1361 | local host2_if=$3 | |
1362 | local mac=01:00:5e:00:00:01 | |
1363 | local ip=239.0.0.1 | |
1364 | ||
1365 | RET=0 | |
1366 | ||
1367 | bridge link set dev $br_port mcast_flood off | |
1368 | ||
1369 | flood_test_do false $mac $ip $host1_if $host2_if | |
1370 | check_err $? "Packet flooded when should not" | |
1371 | ||
1372 | bridge link set dev $br_port mcast_flood on | |
1373 | ||
1374 | flood_test_do true $mac $ip $host1_if $host2_if | |
1375 | check_err $? "Packet was not flooded when should" | |
1376 | ||
1377 | log_test "Unregistered multicast flood" | |
1378 | } | |
1379 | ||
1380 | flood_test() | |
1381 | { | |
1382 | # `br_port` is connected to `host2_if` | |
1383 | local br_port=$1 | |
1384 | local host1_if=$2 | |
1385 | local host2_if=$3 | |
1386 | ||
1387 | flood_unicast_test $br_port $host1_if $host2_if | |
1388 | flood_multicast_test $br_port $host1_if $host2_if | |
1389 | } | |
4cf9b8f9 | 1390 | |
4113b048 | 1391 | __start_traffic() |
4cf9b8f9 | 1392 | { |
1531cc63 | 1393 | local pktsize=$1; shift |
4113b048 | 1394 | local proto=$1; shift |
4cf9b8f9 PM |
1395 | local h_in=$1; shift # Where the traffic egresses the host |
1396 | local sip=$1; shift | |
1397 | local dip=$1; shift | |
1398 | local dmac=$1; shift | |
1399 | ||
1531cc63 | 1400 | $MZ $h_in -p $pktsize -A $sip -B $dip -c 0 \ |
4113b048 | 1401 | -a own -b $dmac -t "$proto" -q "$@" & |
4cf9b8f9 PM |
1402 | sleep 1 |
1403 | } | |
1404 | ||
1531cc63 PM |
1405 | start_traffic_pktsize() |
1406 | { | |
1407 | local pktsize=$1; shift | |
1408 | ||
1409 | __start_traffic $pktsize udp "$@" | |
1410 | } | |
1411 | ||
1412 | start_tcp_traffic_pktsize() | |
1413 | { | |
1414 | local pktsize=$1; shift | |
1415 | ||
1416 | __start_traffic $pktsize tcp "$@" | |
1417 | } | |
1418 | ||
4113b048 PM |
1419 | start_traffic() |
1420 | { | |
1531cc63 | 1421 | start_traffic_pktsize 8000 "$@" |
4113b048 PM |
1422 | } |
1423 | ||
1424 | start_tcp_traffic() | |
1425 | { | |
1531cc63 | 1426 | start_tcp_traffic_pktsize 8000 "$@" |
4113b048 PM |
1427 | } |
1428 | ||
4cf9b8f9 PM |
1429 | stop_traffic() |
1430 | { | |
1431 | # Suppress noise from killing mausezahn. | |
1432 | { kill %% && wait %%; } 2>/dev/null | |
1433 | } | |
8cd6b020 | 1434 | |
6182c5c5 JW |
1435 | declare -A cappid |
1436 | declare -A capfile | |
1437 | declare -A capout | |
1438 | ||
8cd6b020 VO |
1439 | tcpdump_start() |
1440 | { | |
1441 | local if_name=$1; shift | |
1442 | local ns=$1; shift | |
1443 | ||
6182c5c5 JW |
1444 | capfile[$if_name]=$(mktemp) |
1445 | capout[$if_name]=$(mktemp) | |
8cd6b020 VO |
1446 | |
1447 | if [ -z $ns ]; then | |
1448 | ns_cmd="" | |
1449 | else | |
1450 | ns_cmd="ip netns exec ${ns}" | |
1451 | fi | |
1452 | ||
1453 | if [ -z $SUDO_USER ] ; then | |
1454 | capuser="" | |
1455 | else | |
1456 | capuser="-Z $SUDO_USER" | |
1457 | fi | |
1458 | ||
fe32dffd | 1459 | $ns_cmd tcpdump $TCPDUMP_EXTRA_FLAGS -e -n -Q in -i $if_name \ |
6182c5c5 JW |
1460 | -s 65535 -B 32768 $capuser -w ${capfile[$if_name]} \ |
1461 | > "${capout[$if_name]}" 2>&1 & | |
1462 | cappid[$if_name]=$! | |
8cd6b020 VO |
1463 | |
1464 | sleep 1 | |
1465 | } | |
1466 | ||
1467 | tcpdump_stop() | |
1468 | { | |
6182c5c5 JW |
1469 | local if_name=$1 |
1470 | local pid=${cappid[$if_name]} | |
1471 | ||
1472 | $ns_cmd kill "$pid" && wait "$pid" | |
8cd6b020 VO |
1473 | sleep 1 |
1474 | } | |
1475 | ||
1476 | tcpdump_cleanup() | |
1477 | { | |
6182c5c5 JW |
1478 | local if_name=$1 |
1479 | ||
1480 | rm ${capfile[$if_name]} ${capout[$if_name]} | |
8cd6b020 VO |
1481 | } |
1482 | ||
1483 | tcpdump_show() | |
1484 | { | |
6182c5c5 JW |
1485 | local if_name=$1 |
1486 | ||
1487 | tcpdump -e -n -r ${capfile[$if_name]} 2>&1 | |
8cd6b020 | 1488 | } |
537e4825 NA |
1489 | |
1490 | # return 0 if the packet wasn't seen on host2_if or 1 if it was | |
1491 | mcast_packet_test() | |
1492 | { | |
1493 | local mac=$1 | |
1494 | local src_ip=$2 | |
1495 | local ip=$3 | |
1496 | local host1_if=$4 | |
1497 | local host2_if=$5 | |
1498 | local seen=0 | |
450b0b84 NA |
1499 | local tc_proto="ip" |
1500 | local mz_v6arg="" | |
1501 | ||
1502 | # basic check to see if we were passed an IPv4 address, if not assume IPv6 | |
1503 | if [[ ! $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then | |
1504 | tc_proto="ipv6" | |
1505 | mz_v6arg="-6" | |
1506 | fi | |
537e4825 NA |
1507 | |
1508 | # Add an ACL on `host2_if` which will tell us whether the packet | |
1509 | # was received by it or not. | |
1510 | tc qdisc add dev $host2_if ingress | |
450b0b84 | 1511 | tc filter add dev $host2_if ingress protocol $tc_proto pref 1 handle 101 \ |
537e4825 NA |
1512 | flower ip_proto udp dst_mac $mac action drop |
1513 | ||
450b0b84 | 1514 | $MZ $host1_if $mz_v6arg -c 1 -p 64 -b $mac -A $src_ip -B $ip -t udp "dp=4096,sp=2048" -q |
537e4825 NA |
1515 | sleep 1 |
1516 | ||
1517 | tc -j -s filter show dev $host2_if ingress \ | |
1518 | | jq -e ".[] | select(.options.handle == 101) \ | |
1519 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1520 | if [[ $? -eq 0 ]]; then | |
1521 | seen=1 | |
1522 | fi | |
1523 | ||
450b0b84 | 1524 | tc filter del dev $host2_if ingress protocol $tc_proto pref 1 handle 101 flower |
537e4825 NA |
1525 | tc qdisc del dev $host2_if ingress |
1526 | ||
1527 | return $seen | |
1528 | } | |
95e6f430 NA |
1529 | |
1530 | brmcast_check_sg_entries() | |
1531 | { | |
1532 | local report=$1; shift | |
1533 | local slist=("$@") | |
1534 | local sarg="" | |
1535 | ||
1536 | for src in "${slist[@]}"; do | |
1537 | sarg="${sarg} and .source_list[].address == \"$src\"" | |
1538 | done | |
1539 | bridge -j -d -s mdb show dev br0 \ | |
1540 | | jq -e ".[].mdb[] | \ | |
1541 | select(.grp == \"$TEST_GROUP\" and .source_list != null $sarg)" &>/dev/null | |
1542 | check_err $? "Wrong *,G entry source list after $report report" | |
1543 | ||
1544 | for sgent in "${slist[@]}"; do | |
1545 | bridge -j -d -s mdb show dev br0 \ | |
1546 | | jq -e ".[].mdb[] | \ | |
1547 | select(.grp == \"$TEST_GROUP\" and .src == \"$sgent\")" &>/dev/null | |
1548 | check_err $? "Missing S,G entry ($sgent, $TEST_GROUP)" | |
1549 | done | |
1550 | } | |
1551 | ||
1552 | brmcast_check_sg_fwding() | |
1553 | { | |
1554 | local should_fwd=$1; shift | |
1555 | local sources=("$@") | |
1556 | ||
1557 | for src in "${sources[@]}"; do | |
1558 | local retval=0 | |
1559 | ||
1560 | mcast_packet_test $TEST_GROUP_MAC $src $TEST_GROUP $h2 $h1 | |
1561 | retval=$? | |
1562 | if [ $should_fwd -eq 1 ]; then | |
1563 | check_fail $retval "Didn't forward traffic from S,G ($src, $TEST_GROUP)" | |
1564 | else | |
1565 | check_err $retval "Forwarded traffic for blocked S,G ($src, $TEST_GROUP)" | |
1566 | fi | |
1567 | done | |
1568 | } | |
1569 | ||
1570 | brmcast_check_sg_state() | |
1571 | { | |
1572 | local is_blocked=$1; shift | |
1573 | local sources=("$@") | |
1574 | local should_fail=1 | |
1575 | ||
1576 | if [ $is_blocked -eq 1 ]; then | |
1577 | should_fail=0 | |
1578 | fi | |
1579 | ||
1580 | for src in "${sources[@]}"; do | |
1581 | bridge -j -d -s mdb show dev br0 \ | |
1582 | | jq -e ".[].mdb[] | \ | |
1583 | select(.grp == \"$TEST_GROUP\" and .source_list != null) | | |
1584 | .source_list[] | | |
1585 | select(.address == \"$src\") | | |
1586 | select(.timer == \"0.00\")" &>/dev/null | |
1587 | check_err_fail $should_fail $? "Entry $src has zero timer" | |
1588 | ||
1589 | bridge -j -d -s mdb show dev br0 \ | |
1590 | | jq -e ".[].mdb[] | \ | |
1591 | select(.grp == \"$TEST_GROUP\" and .src == \"$src\" and \ | |
1592 | .flags[] == \"blocked\")" &>/dev/null | |
1593 | check_err_fail $should_fail $? "Entry $src has blocked flag" | |
1594 | done | |
1595 | } | |
9b18942e | 1596 | |
f23cddc7 VO |
1597 | mc_join() |
1598 | { | |
1599 | local if_name=$1 | |
1600 | local group=$2 | |
1601 | local vrf_name=$(master_name_get $if_name) | |
1602 | ||
1603 | # We don't care about actual reception, just about joining the | |
1604 | # IP multicast group and adding the L2 address to the device's | |
1605 | # MAC filtering table | |
1606 | ip vrf exec $vrf_name \ | |
1607 | mreceive -g $group -I $if_name > /dev/null 2>&1 & | |
1608 | mreceive_pid=$! | |
1609 | ||
1610 | sleep 1 | |
1611 | } | |
1612 | ||
1613 | mc_leave() | |
1614 | { | |
1615 | kill "$mreceive_pid" && wait "$mreceive_pid" | |
1616 | } | |
1617 | ||
1618 | mc_send() | |
1619 | { | |
1620 | local if_name=$1 | |
1621 | local groups=$2 | |
1622 | local vrf_name=$(master_name_get $if_name) | |
1623 | ||
1624 | ip vrf exec $vrf_name \ | |
1625 | msend -g $groups -I $if_name -c 1 > /dev/null 2>&1 | |
1626 | } | |
1627 | ||
9b18942e PM |
1628 | start_ip_monitor() |
1629 | { | |
1630 | local mtype=$1; shift | |
1631 | local ip=${1-ip}; shift | |
1632 | ||
1633 | # start the monitor in the background | |
1634 | tmpfile=`mktemp /var/run/nexthoptestXXX` | |
1635 | mpid=`($ip monitor $mtype > $tmpfile & echo $!) 2>/dev/null` | |
1636 | sleep 0.2 | |
1637 | echo "$mpid $tmpfile" | |
1638 | } | |
1639 | ||
1640 | stop_ip_monitor() | |
1641 | { | |
1642 | local mpid=$1; shift | |
1643 | local tmpfile=$1; shift | |
1644 | local el=$1; shift | |
1645 | local what=$1; shift | |
1646 | ||
1647 | sleep 0.2 | |
1648 | kill $mpid | |
1649 | local lines=`grep '^\w' $tmpfile | wc -l` | |
1650 | test $lines -eq $el | |
1651 | check_err $? "$what: $lines lines of events, expected $el" | |
1652 | rm -rf $tmpfile | |
1653 | } | |
1654 | ||
1655 | hw_stats_monitor_test() | |
1656 | { | |
1657 | local dev=$1; shift | |
1658 | local type=$1; shift | |
1659 | local make_suitable=$1; shift | |
1660 | local make_unsuitable=$1; shift | |
1661 | local ip=${1-ip}; shift | |
1662 | ||
1663 | RET=0 | |
1664 | ||
1665 | # Expect a notification about enablement. | |
1666 | local ipmout=$(start_ip_monitor stats "$ip") | |
1667 | $ip stats set dev $dev ${type}_stats on | |
1668 | stop_ip_monitor $ipmout 1 "${type}_stats enablement" | |
1669 | ||
1670 | # Expect a notification about offload. | |
1671 | local ipmout=$(start_ip_monitor stats "$ip") | |
1672 | $make_suitable | |
1673 | stop_ip_monitor $ipmout 1 "${type}_stats installation" | |
1674 | ||
1675 | # Expect a notification about loss of offload. | |
1676 | local ipmout=$(start_ip_monitor stats "$ip") | |
1677 | $make_unsuitable | |
1678 | stop_ip_monitor $ipmout 1 "${type}_stats deinstallation" | |
1679 | ||
1680 | # Expect a notification about disablement | |
1681 | local ipmout=$(start_ip_monitor stats "$ip") | |
1682 | $ip stats set dev $dev ${type}_stats off | |
1683 | stop_ip_monitor $ipmout 1 "${type}_stats disablement" | |
1684 | ||
1685 | log_test "${type}_stats notifications" | |
1686 | } |