]>
Commit | Line | Data |
---|---|---|
73bae673 IS |
1 | #!/bin/bash |
2 | # SPDX-License-Identifier: GPL-2.0 | |
3 | ||
4 | ############################################################################## | |
5 | # Defines | |
6 | ||
7 | # Can be overridden by the configuration file. | |
8 | PING=${PING:=ping} | |
9 | PING6=${PING6:=ping6} | |
d4deb014 | 10 | MZ=${MZ:=mausezahn} |
ca70a562 | 11 | ARPING=${ARPING:=arping} |
9d9e6bde | 12 | TEAMD=${TEAMD:=teamd} |
73bae673 IS |
13 | WAIT_TIME=${WAIT_TIME:=5} |
14 | PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} | |
15 | PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} | |
59be45c3 IS |
16 | NETIF_TYPE=${NETIF_TYPE:=veth} |
17 | NETIF_CREATE=${NETIF_CREATE:=yes} | |
6d4efada ND |
18 | MCD=${MCD:=smcrouted} |
19 | MC_CLI=${MC_CLI:=smcroutectl} | |
b6a4fd68 | 20 | PING_TIMEOUT=${PING_TIMEOUT:=5} |
8f72a9cf AC |
21 | WAIT_TIMEOUT=${WAIT_TIMEOUT:=20} |
22 | INTERFACE_TIMEOUT=${INTERFACE_TIMEOUT:=600} | |
73bae673 | 23 | |
87d8fb18 YM |
24 | relative_path="${BASH_SOURCE%/*}" |
25 | if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then | |
26 | relative_path="." | |
27 | fi | |
28 | ||
29 | if [[ -f $relative_path/forwarding.config ]]; then | |
30 | source "$relative_path/forwarding.config" | |
73bae673 IS |
31 | fi |
32 | ||
33 | ############################################################################## | |
34 | # Sanity checks | |
35 | ||
198979be DA |
36 | check_tc_version() |
37 | { | |
38 | tc -j &> /dev/null | |
39 | if [[ $? -ne 0 ]]; then | |
40 | echo "SKIP: iproute2 too old; tc is missing JSON support" | |
41 | exit 1 | |
42 | fi | |
7f333cbf | 43 | } |
198979be | 44 | |
7f333cbf JP |
45 | check_tc_shblock_support() |
46 | { | |
198979be DA |
47 | tc filter help 2>&1 | grep block &> /dev/null |
48 | if [[ $? -ne 0 ]]; then | |
49 | echo "SKIP: iproute2 too old; tc is missing shared block support" | |
50 | exit 1 | |
51 | fi | |
52 | } | |
53 | ||
2d73c887 JP |
54 | check_tc_chain_support() |
55 | { | |
56 | tc help 2>&1|grep chain &> /dev/null | |
57 | if [[ $? -ne 0 ]]; then | |
58 | echo "SKIP: iproute2 too old; tc is missing chain support" | |
59 | exit 1 | |
60 | fi | |
61 | } | |
62 | ||
73bae673 IS |
63 | if [[ "$(id -u)" -ne 0 ]]; then |
64 | echo "SKIP: need root privileges" | |
65 | exit 0 | |
66 | fi | |
67 | ||
198979be DA |
68 | if [[ "$CHECK_TC" = "yes" ]]; then |
69 | check_tc_version | |
4908e24b JP |
70 | fi |
71 | ||
e094574f PM |
72 | require_command() |
73 | { | |
74 | local cmd=$1; shift | |
73bae673 | 75 | |
e094574f PM |
76 | if [[ ! -x "$(command -v "$cmd")" ]]; then |
77 | echo "SKIP: $cmd not installed" | |
78 | exit 1 | |
79 | fi | |
80 | } | |
81 | ||
82 | require_command jq | |
83 | require_command $MZ | |
d4deb014 | 84 | |
73bae673 IS |
85 | if [[ ! -v NUM_NETIFS ]]; then |
86 | echo "SKIP: importer does not define \"NUM_NETIFS\"" | |
231b85ab | 87 | exit 1 |
73bae673 IS |
88 | fi |
89 | ||
781fe631 JP |
90 | ############################################################################## |
91 | # Command line options handling | |
92 | ||
93 | count=0 | |
94 | ||
95 | while [[ $# -gt 0 ]]; do | |
96 | if [[ "$count" -eq "0" ]]; then | |
97 | unset NETIFS | |
98 | declare -A NETIFS | |
99 | fi | |
100 | count=$((count + 1)) | |
101 | NETIFS[p$count]="$1" | |
102 | shift | |
103 | done | |
104 | ||
73bae673 IS |
105 | ############################################################################## |
106 | # Network interfaces configuration | |
107 | ||
190f887c DA |
108 | create_netif_veth() |
109 | { | |
110 | local i | |
111 | ||
601bc1c1 | 112 | for ((i = 1; i <= NUM_NETIFS; ++i)); do |
190f887c DA |
113 | local j=$((i+1)) |
114 | ||
115 | ip link show dev ${NETIFS[p$i]} &> /dev/null | |
116 | if [[ $? -ne 0 ]]; then | |
117 | ip link add ${NETIFS[p$i]} type veth \ | |
118 | peer name ${NETIFS[p$j]} | |
119 | if [[ $? -ne 0 ]]; then | |
120 | echo "Failed to create netif" | |
121 | exit 1 | |
122 | fi | |
123 | fi | |
124 | i=$j | |
125 | done | |
126 | } | |
127 | ||
128 | create_netif() | |
129 | { | |
130 | case "$NETIF_TYPE" in | |
131 | veth) create_netif_veth | |
132 | ;; | |
133 | *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" | |
134 | exit 1 | |
135 | ;; | |
136 | esac | |
137 | } | |
138 | ||
139 | if [[ "$NETIF_CREATE" = "yes" ]]; then | |
140 | create_netif | |
141 | fi | |
142 | ||
601bc1c1 | 143 | for ((i = 1; i <= NUM_NETIFS; ++i)); do |
73bae673 IS |
144 | ip link show dev ${NETIFS[p$i]} &> /dev/null |
145 | if [[ $? -ne 0 ]]; then | |
146 | echo "SKIP: could not find all required interfaces" | |
231b85ab | 147 | exit 1 |
73bae673 IS |
148 | fi |
149 | done | |
150 | ||
151 | ############################################################################## | |
152 | # Helpers | |
153 | ||
154 | # Exit status to return at the end. Set in case one of the tests fails. | |
155 | EXIT_STATUS=0 | |
156 | # Per-test return value. Clear at the beginning of each test. | |
157 | RET=0 | |
158 | ||
159 | check_err() | |
160 | { | |
161 | local err=$1 | |
162 | local msg=$2 | |
163 | ||
164 | if [[ $RET -eq 0 && $err -ne 0 ]]; then | |
165 | RET=$err | |
166 | retmsg=$msg | |
167 | fi | |
168 | } | |
169 | ||
170 | check_fail() | |
171 | { | |
172 | local err=$1 | |
173 | local msg=$2 | |
174 | ||
175 | if [[ $RET -eq 0 && $err -eq 0 ]]; then | |
176 | RET=1 | |
177 | retmsg=$msg | |
178 | fi | |
179 | } | |
180 | ||
96fa91d2 PM |
181 | check_err_fail() |
182 | { | |
183 | local should_fail=$1; shift | |
184 | local err=$1; shift | |
185 | local what=$1; shift | |
186 | ||
187 | if ((should_fail)); then | |
188 | check_fail $err "$what succeeded, but should have failed" | |
189 | else | |
190 | check_err $err "$what failed" | |
191 | fi | |
192 | } | |
193 | ||
73bae673 IS |
194 | log_test() |
195 | { | |
196 | local test_name=$1 | |
197 | local opt_str=$2 | |
198 | ||
199 | if [[ $# -eq 2 ]]; then | |
200 | opt_str="($opt_str)" | |
201 | fi | |
202 | ||
203 | if [[ $RET -ne 0 ]]; then | |
204 | EXIT_STATUS=1 | |
205 | printf "TEST: %-60s [FAIL]\n" "$test_name $opt_str" | |
206 | if [[ ! -z "$retmsg" ]]; then | |
207 | printf "\t%s\n" "$retmsg" | |
208 | fi | |
209 | if [ "${PAUSE_ON_FAIL}" = "yes" ]; then | |
210 | echo "Hit enter to continue, 'q' to quit" | |
211 | read a | |
212 | [ "$a" = "q" ] && exit 1 | |
213 | fi | |
214 | return 1 | |
215 | fi | |
216 | ||
3cab0de9 | 217 | printf "TEST: %-60s [ OK ]\n" "$test_name $opt_str" |
73bae673 IS |
218 | return 0 |
219 | } | |
220 | ||
3d578d87 IS |
221 | log_info() |
222 | { | |
223 | local msg=$1 | |
224 | ||
225 | echo "INFO: $msg" | |
226 | } | |
227 | ||
c5341bcc PM |
228 | busywait() |
229 | { | |
230 | local timeout=$1; shift | |
231 | ||
232 | local start_time="$(date -u +%s%3N)" | |
233 | while true | |
234 | do | |
235 | local out | |
236 | out=$("$@") | |
237 | local ret=$? | |
238 | if ((!ret)); then | |
239 | echo -n "$out" | |
240 | return 0 | |
241 | fi | |
242 | ||
243 | local current_time="$(date -u +%s%3N)" | |
244 | if ((current_time - start_time > timeout)); then | |
245 | echo -n "$out" | |
246 | return 1 | |
247 | fi | |
248 | done | |
249 | } | |
250 | ||
05ef614c DR |
251 | not() |
252 | { | |
253 | "$@" | |
254 | [[ $? != 0 ]] | |
255 | } | |
256 | ||
257 | grep_bridge_fdb() | |
258 | { | |
259 | local addr=$1; shift | |
260 | local word | |
261 | local flag | |
262 | ||
263 | if [ "$1" == "self" ] || [ "$1" == "master" ]; then | |
264 | word=$1; shift | |
265 | if [ "$1" == "-v" ]; then | |
266 | flag=$1; shift | |
267 | fi | |
268 | fi | |
269 | ||
270 | $@ | grep $addr | grep $flag "$word" | |
271 | } | |
272 | ||
0c22f993 DR |
273 | wait_for_offload() |
274 | { | |
275 | "$@" | grep -q offload | |
276 | } | |
277 | ||
4121d947 PM |
278 | until_counter_is() |
279 | { | |
844f0556 | 280 | local expr=$1; shift |
4121d947 PM |
281 | local current=$("$@") |
282 | ||
283 | echo $((current)) | |
844f0556 | 284 | ((current $expr)) |
4121d947 PM |
285 | } |
286 | ||
287 | busywait_for_counter() | |
288 | { | |
289 | local timeout=$1; shift | |
290 | local delta=$1; shift | |
291 | ||
292 | local base=$("$@") | |
844f0556 | 293 | busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" |
4121d947 PM |
294 | } |
295 | ||
010079ba PM |
296 | setup_wait_dev() |
297 | { | |
298 | local dev=$1; shift | |
8f72a9cf | 299 | local wait_time=${1:-$WAIT_TIME}; shift |
010079ba | 300 | |
8f72a9cf AC |
301 | setup_wait_dev_with_timeout "$dev" $INTERFACE_TIMEOUT $wait_time |
302 | ||
303 | if (($?)); then | |
304 | check_err 1 | |
305 | log_test setup_wait_dev ": Interface $dev does not come up." | |
306 | exit 1 | |
307 | fi | |
308 | } | |
309 | ||
310 | setup_wait_dev_with_timeout() | |
311 | { | |
312 | local dev=$1; shift | |
313 | local max_iterations=${1:-$WAIT_TIMEOUT}; shift | |
314 | local wait_time=${1:-$WAIT_TIME}; shift | |
315 | local i | |
316 | ||
317 | for ((i = 1; i <= $max_iterations; ++i)); do | |
010079ba PM |
318 | ip link show dev $dev up \ |
319 | | grep 'state UP' &> /dev/null | |
320 | if [[ $? -ne 0 ]]; then | |
321 | sleep 1 | |
322 | else | |
8f72a9cf AC |
323 | sleep $wait_time |
324 | return 0 | |
010079ba PM |
325 | fi |
326 | done | |
8f72a9cf AC |
327 | |
328 | return 1 | |
010079ba PM |
329 | } |
330 | ||
73bae673 IS |
331 | setup_wait() |
332 | { | |
68d9cea5 | 333 | local num_netifs=${1:-$NUM_NETIFS} |
8f72a9cf | 334 | local i |
68d9cea5 PM |
335 | |
336 | for ((i = 1; i <= num_netifs; ++i)); do | |
8f72a9cf | 337 | setup_wait_dev ${NETIFS[p$i]} 0 |
73bae673 IS |
338 | done |
339 | ||
340 | # Make sure links are ready. | |
341 | sleep $WAIT_TIME | |
342 | } | |
343 | ||
d87e5edb JP |
344 | cmd_jq() |
345 | { | |
346 | local cmd=$1 | |
347 | local jq_exp=$2 | |
c04d71b5 | 348 | local jq_opts=$3 |
d87e5edb JP |
349 | local ret |
350 | local output | |
351 | ||
352 | output="$($cmd)" | |
353 | # it the command fails, return error right away | |
354 | ret=$? | |
355 | if [[ $ret -ne 0 ]]; then | |
356 | return $ret | |
357 | fi | |
c04d71b5 JP |
358 | output=$(echo $output | jq -r $jq_opts "$jq_exp") |
359 | ret=$? | |
360 | if [[ $ret -ne 0 ]]; then | |
361 | return $ret | |
362 | fi | |
d87e5edb JP |
363 | echo $output |
364 | # return success only in case of non-empty output | |
365 | [ ! -z "$output" ] | |
366 | } | |
367 | ||
989133bf PM |
368 | lldpad_app_wait_set() |
369 | { | |
370 | local dev=$1; shift | |
371 | ||
37280905 | 372 | while lldptool -t -i $dev -V APP -c app | grep -Eq "pending|unknown"; do |
989133bf PM |
373 | echo "$dev: waiting for lldpad to push pending APP updates" |
374 | sleep 5 | |
375 | done | |
376 | } | |
377 | ||
378 | lldpad_app_wait_del() | |
379 | { | |
380 | # Give lldpad a chance to push down the changes. If the device is downed | |
381 | # too soon, the updates will be left pending. However, they will have | |
382 | # been struck off the lldpad's DB already, so we won't be able to tell | |
383 | # they are pending. Then on next test iteration this would cause | |
384 | # weirdness as newly-added APP rules conflict with the old ones, | |
385 | # sometimes getting stuck in an "unknown" state. | |
386 | sleep 5 | |
387 | } | |
388 | ||
73bae673 IS |
389 | pre_cleanup() |
390 | { | |
391 | if [ "${PAUSE_ON_CLEANUP}" = "yes" ]; then | |
392 | echo "Pausing before cleanup, hit any key to continue" | |
393 | read | |
394 | fi | |
395 | } | |
396 | ||
397 | vrf_prepare() | |
398 | { | |
399 | ip -4 rule add pref 32765 table local | |
400 | ip -4 rule del pref 0 | |
401 | ip -6 rule add pref 32765 table local | |
402 | ip -6 rule del pref 0 | |
403 | } | |
404 | ||
405 | vrf_cleanup() | |
406 | { | |
407 | ip -6 rule add pref 0 table local | |
408 | ip -6 rule del pref 32765 | |
409 | ip -4 rule add pref 0 table local | |
410 | ip -4 rule del pref 32765 | |
411 | } | |
412 | ||
413 | __last_tb_id=0 | |
414 | declare -A __TB_IDS | |
415 | ||
416 | __vrf_td_id_assign() | |
417 | { | |
418 | local vrf_name=$1 | |
419 | ||
420 | __last_tb_id=$((__last_tb_id + 1)) | |
421 | __TB_IDS[$vrf_name]=$__last_tb_id | |
422 | return $__last_tb_id | |
423 | } | |
424 | ||
425 | __vrf_td_id_lookup() | |
426 | { | |
427 | local vrf_name=$1 | |
428 | ||
429 | return ${__TB_IDS[$vrf_name]} | |
430 | } | |
431 | ||
432 | vrf_create() | |
433 | { | |
434 | local vrf_name=$1 | |
435 | local tb_id | |
436 | ||
437 | __vrf_td_id_assign $vrf_name | |
438 | tb_id=$? | |
439 | ||
440 | ip link add dev $vrf_name type vrf table $tb_id | |
441 | ip -4 route add table $tb_id unreachable default metric 4278198272 | |
442 | ip -6 route add table $tb_id unreachable default metric 4278198272 | |
443 | } | |
444 | ||
445 | vrf_destroy() | |
446 | { | |
447 | local vrf_name=$1 | |
448 | local tb_id | |
449 | ||
450 | __vrf_td_id_lookup $vrf_name | |
451 | tb_id=$? | |
452 | ||
453 | ip -6 route del table $tb_id unreachable default metric 4278198272 | |
454 | ip -4 route del table $tb_id unreachable default metric 4278198272 | |
455 | ip link del dev $vrf_name | |
456 | } | |
457 | ||
458 | __addr_add_del() | |
459 | { | |
460 | local if_name=$1 | |
461 | local add_del=$2 | |
462 | local array | |
463 | ||
464 | shift | |
465 | shift | |
466 | array=("${@}") | |
467 | ||
468 | for addrstr in "${array[@]}"; do | |
469 | ip address $add_del $addrstr dev $if_name | |
470 | done | |
471 | } | |
472 | ||
3368b223 PM |
473 | __simple_if_init() |
474 | { | |
475 | local if_name=$1; shift | |
476 | local vrf_name=$1; shift | |
477 | local addrs=("${@}") | |
478 | ||
479 | ip link set dev $if_name master $vrf_name | |
480 | ip link set dev $if_name up | |
481 | ||
482 | __addr_add_del $if_name add "${addrs[@]}" | |
483 | } | |
484 | ||
485 | __simple_if_fini() | |
486 | { | |
487 | local if_name=$1; shift | |
488 | local addrs=("${@}") | |
489 | ||
490 | __addr_add_del $if_name del "${addrs[@]}" | |
491 | ||
492 | ip link set dev $if_name down | |
493 | ip link set dev $if_name nomaster | |
494 | } | |
495 | ||
73bae673 IS |
496 | simple_if_init() |
497 | { | |
498 | local if_name=$1 | |
499 | local vrf_name | |
500 | local array | |
501 | ||
502 | shift | |
503 | vrf_name=v$if_name | |
504 | array=("${@}") | |
505 | ||
506 | vrf_create $vrf_name | |
73bae673 | 507 | ip link set dev $vrf_name up |
3368b223 | 508 | __simple_if_init $if_name $vrf_name "${array[@]}" |
73bae673 IS |
509 | } |
510 | ||
511 | simple_if_fini() | |
512 | { | |
513 | local if_name=$1 | |
514 | local vrf_name | |
515 | local array | |
516 | ||
517 | shift | |
518 | vrf_name=v$if_name | |
519 | array=("${@}") | |
520 | ||
3368b223 | 521 | __simple_if_fini $if_name "${array[@]}" |
73bae673 IS |
522 | vrf_destroy $vrf_name |
523 | } | |
524 | ||
7d4cbae0 PM |
525 | tunnel_create() |
526 | { | |
527 | local name=$1; shift | |
528 | local type=$1; shift | |
529 | local local=$1; shift | |
530 | local remote=$1; shift | |
531 | ||
532 | ip link add name $name type $type \ | |
533 | local $local remote $remote "$@" | |
534 | ip link set dev $name up | |
535 | } | |
536 | ||
537 | tunnel_destroy() | |
538 | { | |
539 | local name=$1; shift | |
540 | ||
541 | ip link del dev $name | |
542 | } | |
543 | ||
0e7a504c PM |
544 | vlan_create() |
545 | { | |
546 | local if_name=$1; shift | |
547 | local vid=$1; shift | |
548 | local vrf=$1; shift | |
549 | local ips=("${@}") | |
550 | local name=$if_name.$vid | |
551 | ||
552 | ip link add name $name link $if_name type vlan id $vid | |
553 | if [ "$vrf" != "" ]; then | |
554 | ip link set dev $name master $vrf | |
555 | fi | |
556 | ip link set dev $name up | |
557 | __addr_add_del $name add "${ips[@]}" | |
558 | } | |
559 | ||
560 | vlan_destroy() | |
561 | { | |
562 | local if_name=$1; shift | |
563 | local vid=$1; shift | |
564 | local name=$if_name.$vid | |
565 | ||
566 | ip link del dev $name | |
567 | } | |
568 | ||
9d9e6bde PM |
569 | team_create() |
570 | { | |
571 | local if_name=$1; shift | |
572 | local mode=$1; shift | |
573 | ||
574 | require_command $TEAMD | |
575 | $TEAMD -t $if_name -d -c '{"runner": {"name": "'$mode'"}}' | |
576 | for slave in "$@"; do | |
577 | ip link set dev $slave down | |
578 | ip link set dev $slave master $if_name | |
579 | ip link set dev $slave up | |
580 | done | |
581 | ip link set dev $if_name up | |
582 | } | |
583 | ||
584 | team_destroy() | |
585 | { | |
586 | local if_name=$1; shift | |
587 | ||
588 | $TEAMD -t $if_name -k | |
589 | } | |
590 | ||
73bae673 IS |
591 | master_name_get() |
592 | { | |
593 | local if_name=$1 | |
594 | ||
595 | ip -j link show dev $if_name | jq -r '.[]["master"]' | |
596 | } | |
597 | ||
d1038cd0 PM |
598 | link_stats_get() |
599 | { | |
600 | local if_name=$1; shift | |
601 | local dir=$1; shift | |
602 | local stat=$1; shift | |
603 | ||
604 | ip -j -s link show dev $if_name \ | |
605 | | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' | |
606 | } | |
607 | ||
3d578d87 IS |
608 | link_stats_tx_packets_get() |
609 | { | |
d1038cd0 PM |
610 | link_stats_get $1 tx packets |
611 | } | |
3d578d87 | 612 | |
d1038cd0 PM |
613 | link_stats_rx_errors_get() |
614 | { | |
615 | link_stats_get $1 rx errors | |
3d578d87 IS |
616 | } |
617 | ||
7d4cbae0 PM |
618 | tc_rule_stats_get() |
619 | { | |
620 | local dev=$1; shift | |
621 | local pref=$1; shift | |
a66d62d8 | 622 | local dir=$1; shift |
c143139b | 623 | local selector=${1:-.packets}; shift |
7d4cbae0 | 624 | |
a66d62d8 | 625 | tc -j -s filter show dev $dev ${dir:-ingress} pref $pref \ |
c143139b | 626 | | jq ".[1].options.actions[].stats$selector" |
7d4cbae0 PM |
627 | } |
628 | ||
84ea1f85 PM |
629 | tc_rule_handle_stats_get() |
630 | { | |
631 | local id=$1; shift | |
632 | local handle=$1; shift | |
633 | local selector=${1:-.packets}; shift | |
634 | ||
635 | tc -j -s filter show $id \ | |
636 | | jq ".[] | select(.options.handle == $handle) | \ | |
637 | .options.actions[0].stats$selector" | |
638 | } | |
639 | ||
3136a369 PM |
640 | ethtool_stats_get() |
641 | { | |
642 | local dev=$1; shift | |
643 | local stat=$1; shift | |
644 | ||
645 | ethtool -S $dev | grep "^ *$stat:" | head -n 1 | cut -d: -f2 | |
646 | } | |
647 | ||
3de611b5 PM |
648 | qdisc_stats_get() |
649 | { | |
650 | local dev=$1; shift | |
651 | local handle=$1; shift | |
652 | local selector=$1; shift | |
653 | ||
654 | tc -j -s qdisc show dev "$dev" \ | |
655 | | jq '.[] | select(.handle == "'"$handle"'") | '"$selector" | |
656 | } | |
657 | ||
adc6c7ec PM |
658 | humanize() |
659 | { | |
660 | local speed=$1; shift | |
661 | ||
662 | for unit in bps Kbps Mbps Gbps; do | |
663 | if (($(echo "$speed < 1024" | bc))); then | |
664 | break | |
665 | fi | |
666 | ||
667 | speed=$(echo "scale=1; $speed / 1024" | bc) | |
668 | done | |
669 | ||
670 | echo "$speed${unit}" | |
671 | } | |
672 | ||
673 | rate() | |
674 | { | |
675 | local t0=$1; shift | |
676 | local t1=$1; shift | |
677 | local interval=$1; shift | |
678 | ||
679 | echo $((8 * (t1 - t0) / interval)) | |
680 | } | |
681 | ||
4e4272d2 JP |
682 | mac_get() |
683 | { | |
684 | local if_name=$1 | |
685 | ||
686 | ip -j link show dev $if_name | jq -r '.[]["address"]' | |
687 | } | |
688 | ||
d4deb014 IS |
689 | bridge_ageing_time_get() |
690 | { | |
691 | local bridge=$1 | |
692 | local ageing_time | |
693 | ||
694 | # Need to divide by 100 to convert to seconds. | |
695 | ageing_time=$(ip -j -d link show dev $bridge \ | |
696 | | jq '.[]["linkinfo"]["info_data"]["ageing_time"]') | |
697 | echo $((ageing_time / 100)) | |
698 | } | |
699 | ||
f5ae5778 PM |
700 | declare -A SYSCTL_ORIG |
701 | sysctl_set() | |
702 | { | |
703 | local key=$1; shift | |
704 | local value=$1; shift | |
705 | ||
706 | SYSCTL_ORIG[$key]=$(sysctl -n $key) | |
707 | sysctl -qw $key=$value | |
708 | } | |
709 | ||
710 | sysctl_restore() | |
711 | { | |
712 | local key=$1; shift | |
713 | ||
714 | sysctl -qw $key=${SYSCTL_ORIG["$key"]} | |
715 | } | |
716 | ||
7b7bc875 IS |
717 | forwarding_enable() |
718 | { | |
d51d10aa PM |
719 | sysctl_set net.ipv4.conf.all.forwarding 1 |
720 | sysctl_set net.ipv6.conf.all.forwarding 1 | |
7b7bc875 IS |
721 | } |
722 | ||
723 | forwarding_restore() | |
724 | { | |
d51d10aa PM |
725 | sysctl_restore net.ipv6.conf.all.forwarding |
726 | sysctl_restore net.ipv4.conf.all.forwarding | |
7b7bc875 IS |
727 | } |
728 | ||
a381ed12 PM |
729 | declare -A MTU_ORIG |
730 | mtu_set() | |
731 | { | |
732 | local dev=$1; shift | |
733 | local mtu=$1; shift | |
734 | ||
735 | MTU_ORIG["$dev"]=$(ip -j link show dev $dev | jq -e '.[].mtu') | |
736 | ip link set dev $dev mtu $mtu | |
737 | } | |
738 | ||
739 | mtu_restore() | |
740 | { | |
741 | local dev=$1; shift | |
742 | ||
743 | ip link set dev $dev mtu ${MTU_ORIG["$dev"]} | |
744 | } | |
745 | ||
2f19f212 JP |
746 | tc_offload_check() |
747 | { | |
68d9cea5 PM |
748 | local num_netifs=${1:-$NUM_NETIFS} |
749 | ||
750 | for ((i = 1; i <= num_netifs; ++i)); do | |
2f19f212 JP |
751 | ethtool -k ${NETIFS[p$i]} \ |
752 | | grep "hw-tc-offload: on" &> /dev/null | |
753 | if [[ $? -ne 0 ]]; then | |
754 | return 1 | |
755 | fi | |
756 | done | |
757 | ||
758 | return 0 | |
759 | } | |
760 | ||
87c0c046 | 761 | trap_install() |
7d4cbae0 PM |
762 | { |
763 | local dev=$1; shift | |
764 | local direction=$1; shift | |
765 | ||
ac0fcadf PM |
766 | # Some devices may not support or need in-hardware trapping of traffic |
767 | # (e.g. the veth pairs that this library creates for non-existent | |
768 | # loopbacks). Use continue instead, so that there is a filter in there | |
769 | # (some tests check counters), and so that other filters are still | |
770 | # processed. | |
771 | tc filter add dev $dev $direction pref 1 \ | |
772 | flower skip_sw action trap 2>/dev/null \ | |
773 | || tc filter add dev $dev $direction pref 1 \ | |
774 | flower action continue | |
7d4cbae0 PM |
775 | } |
776 | ||
87c0c046 | 777 | trap_uninstall() |
7d4cbae0 PM |
778 | { |
779 | local dev=$1; shift | |
780 | local direction=$1; shift | |
781 | ||
ac0fcadf | 782 | tc filter del dev $dev $direction pref 1 flower |
87c0c046 PM |
783 | } |
784 | ||
785 | slow_path_trap_install() | |
786 | { | |
ac0fcadf PM |
787 | # For slow-path testing, we need to install a trap to get to |
788 | # slow path the packets that would otherwise be switched in HW. | |
87c0c046 PM |
789 | if [ "${tcflags/skip_hw}" != "$tcflags" ]; then |
790 | trap_install "$@" | |
791 | fi | |
792 | } | |
793 | ||
794 | slow_path_trap_uninstall() | |
795 | { | |
7d4cbae0 | 796 | if [ "${tcflags/skip_hw}" != "$tcflags" ]; then |
87c0c046 | 797 | trap_uninstall "$@" |
7d4cbae0 PM |
798 | fi |
799 | } | |
800 | ||
801 | __icmp_capture_add_del() | |
802 | { | |
803 | local add_del=$1; shift | |
804 | local pref=$1; shift | |
805 | local vsuf=$1; shift | |
806 | local tundev=$1; shift | |
807 | local filter=$1; shift | |
808 | ||
809 | tc filter $add_del dev "$tundev" ingress \ | |
810 | proto ip$vsuf pref $pref \ | |
811 | flower ip_proto icmp$vsuf $filter \ | |
812 | action pass | |
813 | } | |
814 | ||
815 | icmp_capture_install() | |
816 | { | |
817 | __icmp_capture_add_del add 100 "" "$@" | |
818 | } | |
819 | ||
820 | icmp_capture_uninstall() | |
821 | { | |
822 | __icmp_capture_add_del del 100 "" "$@" | |
823 | } | |
824 | ||
825 | icmp6_capture_install() | |
826 | { | |
827 | __icmp_capture_add_del add 100 v6 "$@" | |
828 | } | |
829 | ||
830 | icmp6_capture_uninstall() | |
831 | { | |
832 | __icmp_capture_add_del del 100 v6 "$@" | |
833 | } | |
834 | ||
2004a9bc PM |
835 | __vlan_capture_add_del() |
836 | { | |
837 | local add_del=$1; shift | |
838 | local pref=$1; shift | |
839 | local dev=$1; shift | |
840 | local filter=$1; shift | |
841 | ||
842 | tc filter $add_del dev "$dev" ingress \ | |
843 | proto 802.1q pref $pref \ | |
844 | flower $filter \ | |
845 | action pass | |
846 | } | |
847 | ||
848 | vlan_capture_install() | |
849 | { | |
850 | __vlan_capture_add_del add 100 "$@" | |
851 | } | |
852 | ||
853 | vlan_capture_uninstall() | |
854 | { | |
855 | __vlan_capture_add_del del 100 "$@" | |
856 | } | |
857 | ||
cf608698 PM |
858 | __dscp_capture_add_del() |
859 | { | |
860 | local add_del=$1; shift | |
861 | local dev=$1; shift | |
862 | local base=$1; shift | |
863 | local dscp; | |
864 | ||
865 | for prio in {0..7}; do | |
866 | dscp=$((base + prio)) | |
867 | __icmp_capture_add_del $add_del $((dscp + 100)) "" $dev \ | |
868 | "skip_hw ip_tos $((dscp << 2))" | |
869 | done | |
870 | } | |
871 | ||
872 | dscp_capture_install() | |
873 | { | |
874 | local dev=$1; shift | |
875 | local base=$1; shift | |
876 | ||
877 | __dscp_capture_add_del add $dev $base | |
878 | } | |
879 | ||
880 | dscp_capture_uninstall() | |
881 | { | |
882 | local dev=$1; shift | |
883 | local base=$1; shift | |
884 | ||
885 | __dscp_capture_add_del del $dev $base | |
886 | } | |
887 | ||
888 | dscp_fetch_stats() | |
889 | { | |
890 | local dev=$1; shift | |
891 | local base=$1; shift | |
892 | ||
893 | for prio in {0..7}; do | |
894 | local dscp=$((base + prio)) | |
895 | local t=$(tc_rule_stats_get $dev $((dscp + 100))) | |
896 | echo "[$dscp]=$t " | |
897 | done | |
898 | } | |
899 | ||
7d4cbae0 PM |
900 | matchall_sink_create() |
901 | { | |
902 | local dev=$1; shift | |
903 | ||
904 | tc qdisc add dev $dev clsact | |
905 | tc filter add dev $dev ingress \ | |
906 | pref 10000 \ | |
907 | matchall \ | |
908 | action drop | |
909 | } | |
910 | ||
0eb8053c IS |
911 | tests_run() |
912 | { | |
913 | local current_test | |
914 | ||
915 | for current_test in ${TESTS:-$ALL_TESTS}; do | |
916 | $current_test | |
917 | done | |
918 | } | |
919 | ||
b2c47872 PM |
920 | multipath_eval() |
921 | { | |
1b86fa3b PM |
922 | local desc="$1" |
923 | local weight_rp12=$2 | |
924 | local weight_rp13=$3 | |
925 | local packets_rp12=$4 | |
926 | local packets_rp13=$5 | |
927 | local weights_ratio packets_ratio diff | |
928 | ||
929 | RET=0 | |
930 | ||
931 | if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then | |
932 | weights_ratio=$(echo "scale=2; $weight_rp12 / $weight_rp13" \ | |
933 | | bc -l) | |
934 | else | |
935 | weights_ratio=$(echo "scale=2; $weight_rp13 / $weight_rp12" \ | |
936 | | bc -l) | |
937 | fi | |
938 | ||
939 | if [[ "$packets_rp12" -eq "0" || "$packets_rp13" -eq "0" ]]; then | |
940 | check_err 1 "Packet difference is 0" | |
941 | log_test "Multipath" | |
942 | log_info "Expected ratio $weights_ratio" | |
943 | return | |
944 | fi | |
945 | ||
946 | if [[ "$weight_rp12" -gt "$weight_rp13" ]]; then | |
947 | packets_ratio=$(echo "scale=2; $packets_rp12 / $packets_rp13" \ | |
948 | | bc -l) | |
949 | else | |
950 | packets_ratio=$(echo "scale=2; $packets_rp13 / $packets_rp12" \ | |
951 | | bc -l) | |
952 | fi | |
953 | ||
954 | diff=$(echo $weights_ratio - $packets_ratio | bc -l) | |
955 | diff=${diff#-} | |
956 | ||
957 | test "$(echo "$diff / $weights_ratio > 0.15" | bc -l)" -eq 0 | |
958 | check_err $? "Too large discrepancy between expected and measured ratios" | |
959 | log_test "$desc" | |
960 | log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio" | |
b2c47872 PM |
961 | } |
962 | ||
d0540d17 PM |
963 | in_ns() |
964 | { | |
965 | local name=$1; shift | |
966 | ||
967 | ip netns exec $name bash <<-EOF | |
968 | NUM_NETIFS=0 | |
969 | source lib.sh | |
970 | $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done) | |
971 | EOF | |
972 | } | |
973 | ||
73bae673 IS |
974 | ############################################################################## |
975 | # Tests | |
976 | ||
967450c5 | 977 | ping_do() |
73bae673 IS |
978 | { |
979 | local if_name=$1 | |
980 | local dip=$2 | |
d20b0f21 | 981 | local args=$3 |
73bae673 IS |
982 | local vrf_name |
983 | ||
73bae673 | 984 | vrf_name=$(master_name_get $if_name) |
b6a4fd68 IS |
985 | ip vrf exec $vrf_name \ |
986 | $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null | |
967450c5 NA |
987 | } |
988 | ||
989 | ping_test() | |
990 | { | |
991 | RET=0 | |
992 | ||
993 | ping_do $1 $2 | |
73bae673 | 994 | check_err $? |
58c7a2d1 | 995 | log_test "ping$3" |
73bae673 IS |
996 | } |
997 | ||
967450c5 | 998 | ping6_do() |
73bae673 IS |
999 | { |
1000 | local if_name=$1 | |
1001 | local dip=$2 | |
d20b0f21 | 1002 | local args=$3 |
73bae673 IS |
1003 | local vrf_name |
1004 | ||
73bae673 | 1005 | vrf_name=$(master_name_get $if_name) |
b6a4fd68 IS |
1006 | ip vrf exec $vrf_name \ |
1007 | $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null | |
967450c5 NA |
1008 | } |
1009 | ||
1010 | ping6_test() | |
1011 | { | |
1012 | RET=0 | |
1013 | ||
1014 | ping6_do $1 $2 | |
73bae673 | 1015 | check_err $? |
58c7a2d1 | 1016 | log_test "ping6$3" |
73bae673 | 1017 | } |
d4deb014 IS |
1018 | |
1019 | learning_test() | |
1020 | { | |
1021 | local bridge=$1 | |
1022 | local br_port1=$2 # Connected to `host1_if`. | |
1023 | local host1_if=$3 | |
1024 | local host2_if=$4 | |
1025 | local mac=de:ad:be:ef:13:37 | |
1026 | local ageing_time | |
1027 | ||
1028 | RET=0 | |
1029 | ||
1030 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1031 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1032 | check_fail $? "Found FDB record when should not" | |
1033 | ||
1034 | # Disable unknown unicast flooding on `br_port1` to make sure | |
1035 | # packets are only forwarded through the port after a matching | |
1036 | # FDB entry was installed. | |
1037 | bridge link set dev $br_port1 flood off | |
1038 | ||
1039 | tc qdisc add dev $host1_if ingress | |
1040 | tc filter add dev $host1_if ingress protocol ip pref 1 handle 101 \ | |
1041 | flower dst_mac $mac action drop | |
1042 | ||
1043 | $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q | |
1044 | sleep 1 | |
1045 | ||
1046 | tc -j -s filter show dev $host1_if ingress \ | |
1047 | | jq -e ".[] | select(.options.handle == 101) \ | |
1048 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1049 | check_fail $? "Packet reached second host when should not" | |
1050 | ||
1051 | $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q | |
1052 | sleep 1 | |
1053 | ||
1054 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1055 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1056 | check_err $? "Did not find FDB record when should" | |
1057 | ||
1058 | $MZ $host2_if -c 1 -p 64 -b $mac -t ip -q | |
1059 | sleep 1 | |
1060 | ||
1061 | tc -j -s filter show dev $host1_if ingress \ | |
1062 | | jq -e ".[] | select(.options.handle == 101) \ | |
1063 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1064 | check_err $? "Packet did not reach second host when should" | |
1065 | ||
1066 | # Wait for 10 seconds after the ageing time to make sure FDB | |
1067 | # record was aged-out. | |
1068 | ageing_time=$(bridge_ageing_time_get $bridge) | |
1069 | sleep $((ageing_time + 10)) | |
1070 | ||
1071 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1072 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1073 | check_fail $? "Found FDB record when should not" | |
1074 | ||
1075 | bridge link set dev $br_port1 learning off | |
1076 | ||
1077 | $MZ $host1_if -c 1 -p 64 -a $mac -t ip -q | |
1078 | sleep 1 | |
1079 | ||
1080 | bridge -j fdb show br $bridge brport $br_port1 \ | |
1081 | | jq -e ".[] | select(.mac == \"$mac\")" &> /dev/null | |
1082 | check_fail $? "Found FDB record when should not" | |
1083 | ||
1084 | bridge link set dev $br_port1 learning on | |
1085 | ||
1086 | tc filter del dev $host1_if ingress protocol ip pref 1 handle 101 flower | |
1087 | tc qdisc del dev $host1_if ingress | |
1088 | ||
1089 | bridge link set dev $br_port1 flood on | |
1090 | ||
1091 | log_test "FDB learning" | |
1092 | } | |
236dd50b IS |
1093 | |
1094 | flood_test_do() | |
1095 | { | |
1096 | local should_flood=$1 | |
1097 | local mac=$2 | |
1098 | local ip=$3 | |
1099 | local host1_if=$4 | |
1100 | local host2_if=$5 | |
1101 | local err=0 | |
1102 | ||
1103 | # Add an ACL on `host2_if` which will tell us whether the packet | |
1104 | # was flooded to it or not. | |
1105 | tc qdisc add dev $host2_if ingress | |
1106 | tc filter add dev $host2_if ingress protocol ip pref 1 handle 101 \ | |
1107 | flower dst_mac $mac action drop | |
1108 | ||
1109 | $MZ $host1_if -c 1 -p 64 -b $mac -B $ip -t ip -q | |
1110 | sleep 1 | |
1111 | ||
1112 | tc -j -s filter show dev $host2_if ingress \ | |
1113 | | jq -e ".[] | select(.options.handle == 101) \ | |
1114 | | select(.options.actions[0].stats.packets == 1)" &> /dev/null | |
1115 | if [[ $? -ne 0 && $should_flood == "true" || \ | |
1116 | $? -eq 0 && $should_flood == "false" ]]; then | |
1117 | err=1 | |
1118 | fi | |
1119 | ||
1120 | tc filter del dev $host2_if ingress protocol ip pref 1 handle 101 flower | |
1121 | tc qdisc del dev $host2_if ingress | |
1122 | ||
1123 | return $err | |
1124 | } | |
1125 | ||
1126 | flood_unicast_test() | |
1127 | { | |
1128 | local br_port=$1 | |
1129 | local host1_if=$2 | |
1130 | local host2_if=$3 | |
1131 | local mac=de:ad:be:ef:13:37 | |
1132 | local ip=192.0.2.100 | |
1133 | ||
1134 | RET=0 | |
1135 | ||
1136 | bridge link set dev $br_port flood off | |
1137 | ||
1138 | flood_test_do false $mac $ip $host1_if $host2_if | |
1139 | check_err $? "Packet flooded when should not" | |
1140 | ||
1141 | bridge link set dev $br_port flood on | |
1142 | ||
1143 | flood_test_do true $mac $ip $host1_if $host2_if | |
1144 | check_err $? "Packet was not flooded when should" | |
1145 | ||
1146 | log_test "Unknown unicast flood" | |
1147 | } | |
1148 | ||
1149 | flood_multicast_test() | |
1150 | { | |
1151 | local br_port=$1 | |
1152 | local host1_if=$2 | |
1153 | local host2_if=$3 | |
1154 | local mac=01:00:5e:00:00:01 | |
1155 | local ip=239.0.0.1 | |
1156 | ||
1157 | RET=0 | |
1158 | ||
1159 | bridge link set dev $br_port mcast_flood off | |
1160 | ||
1161 | flood_test_do false $mac $ip $host1_if $host2_if | |
1162 | check_err $? "Packet flooded when should not" | |
1163 | ||
1164 | bridge link set dev $br_port mcast_flood on | |
1165 | ||
1166 | flood_test_do true $mac $ip $host1_if $host2_if | |
1167 | check_err $? "Packet was not flooded when should" | |
1168 | ||
1169 | log_test "Unregistered multicast flood" | |
1170 | } | |
1171 | ||
1172 | flood_test() | |
1173 | { | |
1174 | # `br_port` is connected to `host2_if` | |
1175 | local br_port=$1 | |
1176 | local host1_if=$2 | |
1177 | local host2_if=$3 | |
1178 | ||
1179 | flood_unicast_test $br_port $host1_if $host2_if | |
1180 | flood_multicast_test $br_port $host1_if $host2_if | |
1181 | } | |
4cf9b8f9 | 1182 | |
4113b048 | 1183 | __start_traffic() |
4cf9b8f9 | 1184 | { |
4113b048 | 1185 | local proto=$1; shift |
4cf9b8f9 PM |
1186 | local h_in=$1; shift # Where the traffic egresses the host |
1187 | local sip=$1; shift | |
1188 | local dip=$1; shift | |
1189 | local dmac=$1; shift | |
1190 | ||
1191 | $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ | |
4113b048 | 1192 | -a own -b $dmac -t "$proto" -q "$@" & |
4cf9b8f9 PM |
1193 | sleep 1 |
1194 | } | |
1195 | ||
4113b048 PM |
1196 | start_traffic() |
1197 | { | |
1198 | __start_traffic udp "$@" | |
1199 | } | |
1200 | ||
1201 | start_tcp_traffic() | |
1202 | { | |
1203 | __start_traffic tcp "$@" | |
1204 | } | |
1205 | ||
4cf9b8f9 PM |
1206 | stop_traffic() |
1207 | { | |
1208 | # Suppress noise from killing mausezahn. | |
1209 | { kill %% && wait %%; } 2>/dev/null | |
1210 | } |