1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4net_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") 5source "$net_dir/lib/sh/defer.sh" 6 7############################################################################## 8# Defines 9 10: "${WAIT_TIMEOUT:=20}" 11 12# Whether to pause on after a failure. 13: "${PAUSE_ON_FAIL:=no}" 14 15BUSYWAIT_TIMEOUT=$((WAIT_TIMEOUT * 1000)) # ms 16 17# Kselftest framework constants. 18ksft_pass=0 19ksft_fail=1 20ksft_xfail=2 21ksft_skip=4 22 23# namespace list created by setup_ns 24NS_LIST=() 25 26# Exit status to return at the end. Set in case one of the tests fails. 27EXIT_STATUS=0 28# Per-test return value. Clear at the beginning of each test. 29RET=0 30 31############################################################################## 32# Helpers 33 34__ksft_status_merge() 35{ 36 local a=$1; shift 37 local b=$1; shift 38 local -A weights 39 local weight=0 40 41 local i 42 for i in "$@"; do 43 weights[$i]=$((weight++)) 44 done 45 46 if [[ ${weights[$a]} > ${weights[$b]} ]]; then 47 echo "$a" 48 return 0 49 else 50 echo "$b" 51 return 1 52 fi 53} 54 55ksft_status_merge() 56{ 57 local a=$1; shift 58 local b=$1; shift 59 60 __ksft_status_merge "$a" "$b" \ 61 $ksft_pass $ksft_xfail $ksft_skip $ksft_fail 62} 63 64ksft_exit_status_merge() 65{ 66 local a=$1; shift 67 local b=$1; shift 68 69 __ksft_status_merge "$a" "$b" \ 70 $ksft_xfail $ksft_pass $ksft_skip $ksft_fail 71} 72 73loopy_wait() 74{ 75 local sleep_cmd=$1; shift 76 local timeout_ms=$1; shift 77 78 local start_time="$(date -u +%s%3N)" 79 while true 80 do 81 local out 82 if out=$("$@"); then 83 echo -n "$out" 84 return 0 85 fi 86 87 local current_time="$(date -u +%s%3N)" 88 if ((current_time - start_time > timeout_ms)); then 89 echo -n "$out" 90 return 1 91 fi 92 93 $sleep_cmd 94 done 95} 96 97busywait() 98{ 99 local timeout_ms=$1; shift 100 101 loopy_wait : "$timeout_ms" "$@" 102} 103 104# timeout in seconds 105slowwait() 106{ 107 local timeout_sec=$1; shift 108 109 loopy_wait "sleep 0.1" "$((timeout_sec * 1000))" "$@" 110} 111 112until_counter_is() 113{ 114 local expr=$1; shift 115 local current=$("$@") 116 117 echo $((current)) 118 ((current $expr)) 119} 120 121busywait_for_counter() 122{ 123 local timeout=$1; shift 124 local delta=$1; shift 125 126 local base=$("$@") 127 busywait "$timeout" until_counter_is ">= $((base + delta))" "$@" 128} 129 130slowwait_for_counter() 131{ 132 local timeout=$1; shift 133 local delta=$1; shift 134 135 local base=$("$@") 136 slowwait "$timeout" until_counter_is ">= $((base + delta))" "$@" 137} 138 139# Check for existence of tools which are built as part of selftests 140# but may also already exist in $PATH 141check_gen_prog() 142{ 143 local prog_name=$1; shift 144 145 if ! which $prog_name >/dev/null 2>/dev/null; then 146 PATH=$PWD:$PATH 147 if ! which $prog_name >/dev/null; then 148 echo "'$prog_name' command not found; skipping tests" 149 exit $ksft_skip 150 fi 151 fi 152} 153 154remove_ns_list() 155{ 156 local item=$1 157 local ns 158 local ns_list=("${NS_LIST[@]}") 159 NS_LIST=() 160 161 for ns in "${ns_list[@]}"; do 162 if [ "${ns}" != "${item}" ]; then 163 NS_LIST+=("${ns}") 164 fi 165 done 166} 167 168cleanup_ns() 169{ 170 local ns="" 171 local ret=0 172 173 for ns in "$@"; do 174 [ -z "${ns}" ] && continue 175 ip netns pids "${ns}" 2> /dev/null | xargs -r kill || true 176 ip netns delete "${ns}" &> /dev/null || true 177 if ! busywait $BUSYWAIT_TIMEOUT ip netns list \| grep -vq "^$ns$" &> /dev/null; then 178 echo "Warn: Failed to remove namespace $ns" 179 ret=1 180 else 181 remove_ns_list "${ns}" 182 fi 183 done 184 185 return $ret 186} 187 188cleanup_all_ns() 189{ 190 cleanup_ns "${NS_LIST[@]}" 191} 192 193# setup netns with given names as prefix. e.g 194# setup_ns local remote 195setup_ns() 196{ 197 local ns_name="" 198 local ns_list=() 199 for ns_name in "$@"; do 200 # avoid conflicts with local var: internal error 201 if [ "${ns_name}" = "ns_name" ]; then 202 echo "Failed to setup namespace '${ns_name}': invalid name" 203 cleanup_ns "${ns_list[@]}" 204 exit $ksft_fail 205 fi 206 207 # Some test may setup/remove same netns multi times 208 if [ -z "${!ns_name}" ]; then 209 eval "${ns_name}=${ns_name,,}-$(mktemp -u XXXXXX)" 210 else 211 cleanup_ns "${!ns_name}" 212 fi 213 214 if ! ip netns add "${!ns_name}"; then 215 echo "Failed to create namespace $ns_name" 216 cleanup_ns "${ns_list[@]}" 217 return $ksft_skip 218 fi 219 ip -n "${!ns_name}" link set lo up 220 ip netns exec "${!ns_name}" sysctl -wq net.ipv4.conf.all.rp_filter=0 221 ip netns exec "${!ns_name}" sysctl -wq net.ipv4.conf.default.rp_filter=0 222 ns_list+=("${!ns_name}") 223 done 224 NS_LIST+=("${ns_list[@]}") 225} 226 227# Create netdevsim with given id and net namespace. 228create_netdevsim() { 229 local id="$1" 230 local ns="$2" 231 232 modprobe netdevsim &> /dev/null 233 udevadm settle 234 235 echo "$id 1" | ip netns exec $ns tee /sys/bus/netdevsim/new_device >/dev/null 236 local dev=$(ip netns exec $ns ls /sys/bus/netdevsim/devices/netdevsim$id/net) 237 ip -netns $ns link set dev $dev name nsim$id 238 ip -netns $ns link set dev nsim$id up 239 240 echo nsim$id 241} 242 243# Remove netdevsim with given id. 244cleanup_netdevsim() { 245 local id="$1" 246 247 if [ -d "/sys/bus/netdevsim/devices/netdevsim$id/net" ]; then 248 echo "$id" > /sys/bus/netdevsim/del_device 249 fi 250} 251 252tc_rule_stats_get() 253{ 254 local dev=$1; shift 255 local pref=$1; shift 256 local dir=${1:-ingress}; shift 257 local selector=${1:-.packets}; shift 258 259 tc -j -s filter show dev $dev $dir pref $pref \ 260 | jq ".[1].options.actions[].stats$selector" 261} 262 263tc_rule_handle_stats_get() 264{ 265 local id=$1; shift 266 local handle=$1; shift 267 local selector=${1:-.packets}; shift 268 local netns=${1:-""}; shift 269 270 tc $netns -j -s filter show $id \ 271 | jq ".[] | select(.options.handle == $handle) | \ 272 .options.actions[0].stats$selector" 273} 274 275# attach a qdisc with two children match/no-match and a flower filter to match 276tc_set_flower_counter() { 277 local -r ns=$1 278 local -r ipver=$2 279 local -r dev=$3 280 local -r flower_expr=$4 281 282 tc -n $ns qdisc add dev $dev root handle 1: prio bands 2 \ 283 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 284 285 tc -n $ns qdisc add dev $dev parent 1:1 handle 11: pfifo 286 tc -n $ns qdisc add dev $dev parent 1:2 handle 12: pfifo 287 288 tc -n $ns filter add dev $dev parent 1: protocol ipv$ipver \ 289 flower $flower_expr classid 1:2 290} 291 292tc_get_flower_counter() { 293 local -r ns=$1 294 local -r dev=$2 295 296 tc -n $ns -j -s qdisc show dev $dev handle 12: | jq .[0].packets 297} 298 299ret_set_ksft_status() 300{ 301 local ksft_status=$1; shift 302 local msg=$1; shift 303 304 RET=$(ksft_status_merge $RET $ksft_status) 305 if (( $? )); then 306 retmsg=$msg 307 fi 308} 309 310log_test_result() 311{ 312 local test_name=$1; shift 313 local opt_str=$1; shift 314 local result=$1; shift 315 local retmsg=$1 316 317 printf "TEST: %-60s [%s]\n" "$test_name $opt_str" "$result" 318 if [[ $retmsg ]]; then 319 printf "\t%s\n" "$retmsg" 320 fi 321} 322 323pause_on_fail() 324{ 325 if [[ $PAUSE_ON_FAIL == yes ]]; then 326 echo "Hit enter to continue, 'q' to quit" 327 read a 328 [[ $a == q ]] && exit 1 329 fi 330} 331 332handle_test_result_pass() 333{ 334 local test_name=$1; shift 335 local opt_str=$1; shift 336 337 log_test_result "$test_name" "$opt_str" " OK " 338} 339 340handle_test_result_fail() 341{ 342 local test_name=$1; shift 343 local opt_str=$1; shift 344 345 log_test_result "$test_name" "$opt_str" FAIL "$retmsg" 346 pause_on_fail 347} 348 349handle_test_result_xfail() 350{ 351 local test_name=$1; shift 352 local opt_str=$1; shift 353 354 log_test_result "$test_name" "$opt_str" XFAIL "$retmsg" 355 pause_on_fail 356} 357 358handle_test_result_skip() 359{ 360 local test_name=$1; shift 361 local opt_str=$1; shift 362 363 log_test_result "$test_name" "$opt_str" SKIP "$retmsg" 364} 365 366log_test() 367{ 368 local test_name=$1 369 local opt_str=$2 370 371 if [[ $# -eq 2 ]]; then 372 opt_str="($opt_str)" 373 fi 374 375 if ((RET == ksft_pass)); then 376 handle_test_result_pass "$test_name" "$opt_str" 377 elif ((RET == ksft_xfail)); then 378 handle_test_result_xfail "$test_name" "$opt_str" 379 elif ((RET == ksft_skip)); then 380 handle_test_result_skip "$test_name" "$opt_str" 381 else 382 handle_test_result_fail "$test_name" "$opt_str" 383 fi 384 385 EXIT_STATUS=$(ksft_exit_status_merge $EXIT_STATUS $RET) 386 return $RET 387} 388 389log_test_skip() 390{ 391 RET=$ksft_skip retmsg= log_test "$@" 392} 393 394log_test_xfail() 395{ 396 RET=$ksft_xfail retmsg= log_test "$@" 397} 398 399log_info() 400{ 401 local msg=$1 402 403 echo "INFO: $msg" 404} 405 406tests_run() 407{ 408 local current_test 409 410 for current_test in ${TESTS:-$ALL_TESTS}; do 411 in_defer_scope \ 412 $current_test 413 done 414} 415 416# Whether FAILs should be interpreted as XFAILs. Internal. 417FAIL_TO_XFAIL= 418 419check_err() 420{ 421 local err=$1 422 local msg=$2 423 424 if ((err)); then 425 if [[ $FAIL_TO_XFAIL = yes ]]; then 426 ret_set_ksft_status $ksft_xfail "$msg" 427 else 428 ret_set_ksft_status $ksft_fail "$msg" 429 fi 430 fi 431} 432 433check_fail() 434{ 435 local err=$1 436 local msg=$2 437 438 check_err $((!err)) "$msg" 439} 440 441check_err_fail() 442{ 443 local should_fail=$1; shift 444 local err=$1; shift 445 local what=$1; shift 446 447 if ((should_fail)); then 448 check_fail $err "$what succeeded, but should have failed" 449 else 450 check_err $err "$what failed" 451 fi 452} 453 454xfail() 455{ 456 FAIL_TO_XFAIL=yes "$@" 457} 458 459xfail_on_slow() 460{ 461 if [[ $KSFT_MACHINE_SLOW = yes ]]; then 462 FAIL_TO_XFAIL=yes "$@" 463 else 464 "$@" 465 fi 466} 467 468omit_on_slow() 469{ 470 if [[ $KSFT_MACHINE_SLOW != yes ]]; then 471 "$@" 472 fi 473} 474 475xfail_on_veth() 476{ 477 local dev=$1; shift 478 local kind 479 480 kind=$(ip -j -d link show dev $dev | 481 jq -r '.[].linkinfo.info_kind') 482 if [[ $kind = veth ]]; then 483 FAIL_TO_XFAIL=yes "$@" 484 else 485 "$@" 486 fi 487} 488 489mac_get() 490{ 491 local if_name=$1 492 493 ip -j link show dev $if_name | jq -r '.[]["address"]' 494} 495 496kill_process() 497{ 498 local pid=$1; shift 499 500 # Suppress noise from killing the process. 501 { kill $pid && wait $pid; } 2>/dev/null 502} 503 504check_command() 505{ 506 local cmd=$1; shift 507 508 if [[ ! -x "$(command -v "$cmd")" ]]; then 509 log_test_skip "$cmd not installed" 510 return $EXIT_STATUS 511 fi 512} 513 514require_command() 515{ 516 local cmd=$1; shift 517 518 if ! check_command "$cmd"; then 519 exit $EXIT_STATUS 520 fi 521} 522 523ip_link_add() 524{ 525 local name=$1; shift 526 527 ip link add name "$name" "$@" 528 defer ip link del dev "$name" 529} 530 531ip_link_set_master() 532{ 533 local member=$1; shift 534 local master=$1; shift 535 536 ip link set dev "$member" master "$master" 537 defer ip link set dev "$member" nomaster 538} 539 540ip_link_set_addr() 541{ 542 local name=$1; shift 543 local addr=$1; shift 544 545 local old_addr=$(mac_get "$name") 546 ip link set dev "$name" address "$addr" 547 defer ip link set dev "$name" address "$old_addr" 548} 549 550ip_link_is_up() 551{ 552 local name=$1; shift 553 554 local state=$(ip -j link show "$name" | 555 jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"') 556 [[ $state == "UP" ]] 557} 558 559ip_link_set_up() 560{ 561 local name=$1; shift 562 563 if ! ip_link_is_up "$name"; then 564 ip link set dev "$name" up 565 defer ip link set dev "$name" down 566 fi 567} 568 569ip_link_set_down() 570{ 571 local name=$1; shift 572 573 if ip_link_is_up "$name"; then 574 ip link set dev "$name" down 575 defer ip link set dev "$name" up 576 fi 577} 578 579ip_addr_add() 580{ 581 local name=$1; shift 582 583 ip addr add dev "$name" "$@" 584 defer ip addr del dev "$name" "$@" 585} 586 587ip_route_add() 588{ 589 ip route add "$@" 590 defer ip route del "$@" 591} 592 593bridge_vlan_add() 594{ 595 bridge vlan add "$@" 596 defer bridge vlan del "$@" 597} 598 599wait_local_port_listen() 600{ 601 local listener_ns="${1}" 602 local port="${2}" 603 local protocol="${3}" 604 local pattern 605 local i 606 607 pattern=":$(printf "%04X" "${port}") " 608 609 # for tcp protocol additionally check the socket state 610 [ ${protocol} = "tcp" ] && pattern="${pattern}0A" 611 for i in $(seq 10); do 612 if ip netns exec "${listener_ns}" awk '{print $2" "$4}' \ 613 /proc/net/"${protocol}"* | grep -q "${pattern}"; then 614 break 615 fi 616 sleep 0.1 617 done 618} 619