1#! /bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4readonly KSFT_PASS=0 5readonly KSFT_FAIL=1 6readonly KSFT_SKIP=4 7 8# shellcheck disable=SC2155 # declare and assign separately 9readonly KSFT_TEST="${MPTCP_LIB_KSFT_TEST:-$(basename "${0}" .sh)}" 10 11# These variables are used in some selftests, read-only 12declare -rx MPTCP_LIB_EVENT_ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED 13declare -rx MPTCP_LIB_EVENT_REMOVED=7 # MPTCP_EVENT_REMOVED 14declare -rx MPTCP_LIB_EVENT_SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED 15declare -rx MPTCP_LIB_EVENT_SUB_CLOSED=11 # MPTCP_EVENT_SUB_CLOSED 16declare -rx MPTCP_LIB_EVENT_LISTENER_CREATED=15 # MPTCP_EVENT_LISTENER_CREATED 17declare -rx MPTCP_LIB_EVENT_LISTENER_CLOSED=16 # MPTCP_EVENT_LISTENER_CLOSED 18 19declare -rx MPTCP_LIB_AF_INET=2 20declare -rx MPTCP_LIB_AF_INET6=10 21 22MPTCP_LIB_SUBTESTS=() 23MPTCP_LIB_SUBTESTS_DUPLICATED=0 24MPTCP_LIB_SUBTEST_FLAKY=0 25MPTCP_LIB_TEST_COUNTER=0 26MPTCP_LIB_TEST_FORMAT="%02u %-50s" 27MPTCP_LIB_IP_MPTCP=0 28 29# only if supported (or forced) and not disabled, see no-color.org 30if { [ -t 1 ] || [ "${SELFTESTS_MPTCP_LIB_COLOR_FORCE:-}" = "1" ]; } && 31 [ "${NO_COLOR:-}" != "1" ]; then 32 readonly MPTCP_LIB_COLOR_RED="\E[1;31m" 33 readonly MPTCP_LIB_COLOR_GREEN="\E[1;32m" 34 readonly MPTCP_LIB_COLOR_YELLOW="\E[1;33m" 35 readonly MPTCP_LIB_COLOR_BLUE="\E[1;34m" 36 readonly MPTCP_LIB_COLOR_RESET="\E[0m" 37else 38 readonly MPTCP_LIB_COLOR_RED= 39 readonly MPTCP_LIB_COLOR_GREEN= 40 readonly MPTCP_LIB_COLOR_YELLOW= 41 readonly MPTCP_LIB_COLOR_BLUE= 42 readonly MPTCP_LIB_COLOR_RESET= 43fi 44 45# SELFTESTS_MPTCP_LIB_OVERRIDE_FLAKY env var can be set not to ignore errors 46# from subtests marked as flaky 47mptcp_lib_override_flaky() { 48 [ "${SELFTESTS_MPTCP_LIB_OVERRIDE_FLAKY:-}" = 1 ] 49} 50 51mptcp_lib_subtest_is_flaky() { 52 [ "${MPTCP_LIB_SUBTEST_FLAKY}" = 1 ] && ! mptcp_lib_override_flaky 53} 54 55# $1: color, $2: text 56mptcp_lib_print_color() { 57 echo -e "${MPTCP_LIB_START_PRINT:-}${*}${MPTCP_LIB_COLOR_RESET}" 58} 59 60mptcp_lib_print_ok() { 61 mptcp_lib_print_color "${MPTCP_LIB_COLOR_GREEN}${*}" 62} 63 64mptcp_lib_print_warn() { 65 mptcp_lib_print_color "${MPTCP_LIB_COLOR_YELLOW}${*}" 66} 67 68mptcp_lib_print_info() { 69 mptcp_lib_print_color "${MPTCP_LIB_COLOR_BLUE}${*}" 70} 71 72mptcp_lib_print_err() { 73 mptcp_lib_print_color "${MPTCP_LIB_COLOR_RED}${*}" 74} 75 76# shellcheck disable=SC2120 # parameters are optional 77mptcp_lib_pr_ok() { 78 mptcp_lib_print_ok "[ OK ]${1:+ ${*}}" 79} 80 81mptcp_lib_pr_skip() { 82 mptcp_lib_print_warn "[SKIP]${1:+ ${*}}" 83} 84 85mptcp_lib_pr_fail() { 86 local title cmt 87 88 if mptcp_lib_subtest_is_flaky; then 89 title="IGNO" 90 cmt=" (flaky)" 91 else 92 title="FAIL" 93 fi 94 95 mptcp_lib_print_err "[${title}]${cmt}${1:+ ${*}}" 96} 97 98mptcp_lib_pr_info() { 99 mptcp_lib_print_info "INFO: ${*}" 100} 101 102# SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var can be set when validating all 103# features using the last version of the kernel and the selftests to make sure 104# a test is not being skipped by mistake. 105mptcp_lib_expect_all_features() { 106 [ "${SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES:-}" = "1" ] 107} 108 109# $1: msg 110mptcp_lib_fail_if_expected_feature() { 111 if mptcp_lib_expect_all_features; then 112 echo "ERROR: missing feature: ${*}" 113 exit ${KSFT_FAIL} 114 fi 115 116 return 1 117} 118 119# $1: file 120mptcp_lib_has_file() { 121 local f="${1}" 122 123 if [ -f "${f}" ]; then 124 return 0 125 fi 126 127 mptcp_lib_fail_if_expected_feature "${f} file not found" 128} 129 130mptcp_lib_check_mptcp() { 131 if ! mptcp_lib_has_file "/proc/sys/net/mptcp/enabled"; then 132 mptcp_lib_pr_skip "MPTCP support is not available" 133 exit ${KSFT_SKIP} 134 fi 135} 136 137mptcp_lib_check_kallsyms() { 138 if ! mptcp_lib_has_file "/proc/kallsyms"; then 139 mptcp_lib_pr_skip "CONFIG_KALLSYMS is missing" 140 exit ${KSFT_SKIP} 141 fi 142} 143 144# Internal: use mptcp_lib_kallsyms_has() instead 145__mptcp_lib_kallsyms_has() { 146 local sym="${1}" 147 148 mptcp_lib_check_kallsyms 149 150 grep -q " ${sym}" /proc/kallsyms 151} 152 153# $1: part of a symbol to look at, add '$' at the end for full name 154mptcp_lib_kallsyms_has() { 155 local sym="${1}" 156 157 if __mptcp_lib_kallsyms_has "${sym}"; then 158 return 0 159 fi 160 161 mptcp_lib_fail_if_expected_feature "${sym} symbol not found" 162} 163 164# $1: part of a symbol to look at, add '$' at the end for full name 165mptcp_lib_kallsyms_doesnt_have() { 166 local sym="${1}" 167 168 if ! __mptcp_lib_kallsyms_has "${sym}"; then 169 return 0 170 fi 171 172 mptcp_lib_fail_if_expected_feature "${sym} symbol has been found" 173} 174 175# !!!AVOID USING THIS!!! 176# Features might not land in the expected version and features can be backported 177# 178# $1: kernel version, e.g. 6.3 179mptcp_lib_kversion_ge() { 180 local exp_maj="${1%.*}" 181 local exp_min="${1#*.}" 182 local v maj min 183 184 # If the kernel has backported features, set this env var to 1: 185 if [ "${SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK:-}" = "1" ]; then 186 return 0 187 fi 188 189 v=$(uname -r | cut -d'.' -f1,2) 190 maj=${v%.*} 191 min=${v#*.} 192 193 if [ "${maj}" -gt "${exp_maj}" ] || 194 { [ "${maj}" -eq "${exp_maj}" ] && [ "${min}" -ge "${exp_min}" ]; }; then 195 return 0 196 fi 197 198 mptcp_lib_fail_if_expected_feature "kernel version ${1} lower than ${v}" 199} 200 201__mptcp_lib_result_check_duplicated() { 202 local subtest 203 204 for subtest in "${MPTCP_LIB_SUBTESTS[@]}"; do 205 if [[ "${subtest}" == *" - ${KSFT_TEST}: ${*%% #*}" ]]; then 206 MPTCP_LIB_SUBTESTS_DUPLICATED=1 207 mptcp_lib_print_err "Duplicated entry: ${*}" 208 break 209 fi 210 done 211} 212 213__mptcp_lib_result_add() { 214 local result="${1}" 215 shift 216 217 local id=$((${#MPTCP_LIB_SUBTESTS[@]} + 1)) 218 219 __mptcp_lib_result_check_duplicated "${*}" 220 221 MPTCP_LIB_SUBTESTS+=("${result} ${id} - ${KSFT_TEST}: ${*}") 222} 223 224# $1: test name 225mptcp_lib_result_pass() { 226 __mptcp_lib_result_add "ok" "${1}" 227} 228 229# $1: test name 230mptcp_lib_result_fail() { 231 if mptcp_lib_subtest_is_flaky; then 232 # It might sound better to use 'not ok # TODO' or 'ok # SKIP', 233 # but some CIs don't understand 'TODO' and treat SKIP as errors. 234 __mptcp_lib_result_add "ok" "${1} # IGNORE Flaky" 235 else 236 __mptcp_lib_result_add "not ok" "${1}" 237 fi 238} 239 240# $1: test name 241mptcp_lib_result_skip() { 242 __mptcp_lib_result_add "ok" "${1} # SKIP" 243} 244 245# $1: result code ; $2: test name 246mptcp_lib_result_code() { 247 local ret="${1}" 248 local name="${2}" 249 250 case "${ret}" in 251 "${KSFT_PASS}") 252 mptcp_lib_result_pass "${name}" 253 ;; 254 "${KSFT_FAIL}") 255 mptcp_lib_result_fail "${name}" 256 ;; 257 "${KSFT_SKIP}") 258 mptcp_lib_result_skip "${name}" 259 ;; 260 *) 261 echo "ERROR: wrong result code: ${ret}" 262 exit ${KSFT_FAIL} 263 ;; 264 esac 265} 266 267mptcp_lib_result_print_all_tap() { 268 local subtest 269 270 if [ ${#MPTCP_LIB_SUBTESTS[@]} -eq 0 ] || 271 [ "${SELFTESTS_MPTCP_LIB_NO_TAP:-}" = "1" ]; then 272 return 273 fi 274 275 printf "\nTAP version 13\n" 276 printf "1..%d\n" "${#MPTCP_LIB_SUBTESTS[@]}" 277 278 for subtest in "${MPTCP_LIB_SUBTESTS[@]}"; do 279 printf "%s\n" "${subtest}" 280 done 281 282 if [ "${MPTCP_LIB_SUBTESTS_DUPLICATED}" = 1 ] && 283 mptcp_lib_expect_all_features; then 284 mptcp_lib_print_err "Duplicated test entries" 285 exit ${KSFT_FAIL} 286 fi 287} 288 289# get the value of keyword $1 in the line marked by keyword $2 290mptcp_lib_get_info_value() { 291 grep "${2}" | sed -n 's/.*\('"${1}"':\)\([0-9a-f:.]*\).*$/\2/p;q' 292} 293 294# $1: info name ; $2: evts_ns ; [$3: event type; [$4: addr]] 295mptcp_lib_evts_get_info() { 296 grep "${4:-}" "${2}" | mptcp_lib_get_info_value "${1}" "^type:${3:-1}," 297} 298 299# $1: PID 300mptcp_lib_kill_wait() { 301 [ "${1}" -eq 0 ] && return 0 302 303 kill -SIGUSR1 "${1}" > /dev/null 2>&1 304 kill "${1}" > /dev/null 2>&1 305 wait "${1}" 2>/dev/null 306} 307 308# $1: IP address 309mptcp_lib_is_v6() { 310 [ -z "${1##*:*}" ] 311} 312 313# $1: ns, $2: MIB counter 314mptcp_lib_get_counter() { 315 local ns="${1}" 316 local counter="${2}" 317 local count 318 319 count=$(ip netns exec "${ns}" nstat -asz "${counter}" | 320 awk 'NR==1 {next} {print $2}') 321 if [ -z "${count}" ]; then 322 mptcp_lib_fail_if_expected_feature "${counter} counter" 323 return 1 324 fi 325 326 echo "${count}" 327} 328 329mptcp_lib_make_file() { 330 local name="${1}" 331 local bs="${2}" 332 local size="${3}" 333 334 dd if=/dev/urandom of="${name}" bs="${bs}" count="${size}" 2> /dev/null 335 echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "${name}" 336} 337 338# $1: file 339mptcp_lib_print_file_err() { 340 ls -l "${1}" 1>&2 341 echo "Trailing bytes are: " 342 tail -c 27 "${1}" 343} 344 345# $1: input file ; $2: output file ; $3: what kind of file 346mptcp_lib_check_transfer() { 347 local in="${1}" 348 local out="${2}" 349 local what="${3}" 350 351 if ! cmp "$in" "$out" > /dev/null 2>&1; then 352 mptcp_lib_pr_fail "$what does not match (in, out):" 353 mptcp_lib_print_file_err "$in" 354 mptcp_lib_print_file_err "$out" 355 356 return 1 357 fi 358 359 return 0 360} 361 362# $1: ns, $2: port 363mptcp_lib_wait_local_port_listen() { 364 local listener_ns="${1}" 365 local port="${2}" 366 367 local port_hex 368 port_hex="$(printf "%04X" "${port}")" 369 370 local _ 371 for _ in $(seq 10); do 372 ip netns exec "${listener_ns}" cat /proc/net/tcp* | \ 373 awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) \ 374 {rc=0; exit}} END {exit rc}" && 375 break 376 sleep 0.1 377 done 378} 379 380mptcp_lib_check_output() { 381 local err="${1}" 382 local cmd="${2}" 383 local expected="${3}" 384 local cmd_ret=0 385 local out 386 387 if ! out=$(${cmd} 2>"${err}"); then 388 cmd_ret=${?} 389 fi 390 391 if [ ${cmd_ret} -ne 0 ]; then 392 mptcp_lib_pr_fail "command execution '${cmd}' stderr" 393 cat "${err}" 394 return 2 395 elif [ "${out}" = "${expected}" ]; then 396 return 0 397 else 398 mptcp_lib_pr_fail "expected '${expected}' got '${out}'" 399 return 1 400 fi 401} 402 403mptcp_lib_check_tools() { 404 local tool 405 406 for tool in "${@}"; do 407 case "${tool}" in 408 "ip") 409 if ! ip -Version &> /dev/null; then 410 mptcp_lib_pr_skip "Could not run test without ip tool" 411 exit ${KSFT_SKIP} 412 fi 413 ;; 414 "tc") 415 if ! tc -help &> /dev/null; then 416 mptcp_lib_pr_skip "Could not run test without tc tool" 417 exit ${KSFT_SKIP} 418 fi 419 ;; 420 "ss") 421 if ! ss -h | grep -q MPTCP; then 422 mptcp_lib_pr_skip "ss tool does not support MPTCP" 423 exit ${KSFT_SKIP} 424 fi 425 ;; 426 "iptables"* | "ip6tables"*) 427 if ! "${tool}" -V &> /dev/null; then 428 mptcp_lib_pr_skip "Could not run all tests without ${tool}" 429 exit ${KSFT_SKIP} 430 fi 431 ;; 432 *) 433 mptcp_lib_pr_fail "Internal error: unsupported tool: ${tool}" 434 exit ${KSFT_FAIL} 435 ;; 436 esac 437 done 438} 439 440mptcp_lib_ns_init() { 441 local sec rndh 442 443 sec=$(date +%s) 444 rndh=$(printf %x "${sec}")-$(mktemp -u XXXXXX) 445 446 local netns 447 for netns in "${@}"; do 448 eval "${netns}=${netns}-${rndh}" 449 450 ip netns add "${!netns}" || exit ${KSFT_SKIP} 451 ip -net "${!netns}" link set lo up 452 ip netns exec "${!netns}" sysctl -q net.mptcp.enabled=1 453 ip netns exec "${!netns}" sysctl -q net.ipv4.conf.all.rp_filter=0 454 ip netns exec "${!netns}" sysctl -q net.ipv4.conf.default.rp_filter=0 455 done 456} 457 458mptcp_lib_ns_exit() { 459 local netns 460 for netns in "${@}"; do 461 ip netns del "${netns}" 462 rm -f /tmp/"${netns}".{nstat,out} 463 done 464} 465 466mptcp_lib_events() { 467 local ns="${1}" 468 local evts="${2}" 469 declare -n pid="${3}" 470 471 :>"${evts}" 472 473 mptcp_lib_kill_wait "${pid:-0}" 474 ip netns exec "${ns}" ./pm_nl_ctl events >> "${evts}" 2>&1 & 475 pid=$! 476} 477 478mptcp_lib_print_title() { 479 : "${MPTCP_LIB_TEST_COUNTER:?}" 480 : "${MPTCP_LIB_TEST_FORMAT:?}" 481 482 # shellcheck disable=SC2059 # the format is in a variable 483 printf "${MPTCP_LIB_TEST_FORMAT}" "$((++MPTCP_LIB_TEST_COUNTER))" "${*}" 484} 485 486# $1: var name ; $2: prev ret 487mptcp_lib_check_expected_one() { 488 local var="${1}" 489 local exp="e_${var}" 490 local prev_ret="${2}" 491 492 if [ "${!var}" = "${!exp}" ]; then 493 return 0 494 fi 495 496 if [ "${prev_ret}" = "0" ]; then 497 mptcp_lib_pr_fail 498 fi 499 500 mptcp_lib_print_err "Expected value for '${var}': '${!exp}', got '${!var}'." 501 return 1 502} 503 504# $@: all var names to check 505mptcp_lib_check_expected() { 506 local rc=0 507 local var 508 509 for var in "${@}"; do 510 mptcp_lib_check_expected_one "${var}" "${rc}" || rc=1 511 done 512 513 return "${rc}" 514} 515 516# shellcheck disable=SC2034 # Some variables are used below but indirectly 517mptcp_lib_verify_listener_events() { 518 local evt=${1} 519 local e_type=${2} 520 local e_family=${3} 521 local e_saddr=${4} 522 local e_sport=${5} 523 local type 524 local family 525 local saddr 526 local sport 527 local rc=0 528 529 type=$(mptcp_lib_evts_get_info type "${evt}" "${e_type}") 530 family=$(mptcp_lib_evts_get_info family "${evt}" "${e_type}") 531 if [ "${family}" ] && [ "${family}" = "${AF_INET6}" ]; then 532 saddr=$(mptcp_lib_evts_get_info saddr6 "${evt}" "${e_type}") 533 else 534 saddr=$(mptcp_lib_evts_get_info saddr4 "${evt}" "${e_type}") 535 fi 536 sport=$(mptcp_lib_evts_get_info sport "${evt}" "${e_type}") 537 538 mptcp_lib_check_expected "type" "family" "saddr" "sport" || rc="${?}" 539 return "${rc}" 540} 541 542mptcp_lib_set_ip_mptcp() { 543 MPTCP_LIB_IP_MPTCP=1 544} 545 546mptcp_lib_is_ip_mptcp() { 547 [ "${MPTCP_LIB_IP_MPTCP}" = "1" ] 548} 549 550# format: <id>,<ip>,<flags>,<dev> 551mptcp_lib_pm_nl_format_endpoints() { 552 local entry id ip flags dev port 553 554 for entry in "${@}"; do 555 IFS=, read -r id ip flags dev port <<< "${entry}" 556 if mptcp_lib_is_ip_mptcp; then 557 echo -n "${ip}" 558 [ -n "${port}" ] && echo -n " port ${port}" 559 echo -n " id ${id}" 560 [ -n "${flags}" ] && echo -n " ${flags}" 561 [ -n "${dev}" ] && echo -n " dev ${dev}" 562 echo " " # always a space at the end 563 else 564 echo -n "id ${id}" 565 echo -n " flags ${flags//" "/","}" 566 [ -n "${dev}" ] && echo -n " dev ${dev}" 567 echo -n " ${ip}" 568 [ -n "${port}" ] && echo -n " ${port}" 569 echo "" 570 fi 571 done 572} 573 574mptcp_lib_pm_nl_get_endpoint() { 575 local ns=${1} 576 local id=${2} 577 578 if mptcp_lib_is_ip_mptcp; then 579 ip -n "${ns}" mptcp endpoint show id "${id}" 580 else 581 ip netns exec "${ns}" ./pm_nl_ctl get "${id}" 582 fi 583} 584 585mptcp_lib_pm_nl_set_limits() { 586 local ns=${1} 587 local addrs=${2} 588 local subflows=${3} 589 590 if mptcp_lib_is_ip_mptcp; then 591 ip -n "${ns}" mptcp limits set add_addr_accepted "${addrs}" subflows "${subflows}" 592 else 593 ip netns exec "${ns}" ./pm_nl_ctl limits "${addrs}" "${subflows}" 594 fi 595} 596 597mptcp_lib_pm_nl_add_endpoint() { 598 local ns=${1} 599 local addr=${2} 600 local flags dev id port 601 local nr=2 602 603 local p 604 for p in "${@}"; do 605 case "${p}" in 606 "flags" | "dev" | "id" | "port") 607 eval "${p}"=\$"${nr}" 608 ;; 609 esac 610 611 nr=$((nr + 1)) 612 done 613 614 if mptcp_lib_is_ip_mptcp; then 615 # shellcheck disable=SC2086 # blanks in flags, no double quote 616 ip -n "${ns}" mptcp endpoint add "${addr}" ${flags//","/" "} \ 617 ${dev:+dev "${dev}"} ${id:+id "${id}"} ${port:+port "${port}"} 618 else 619 ip netns exec "${ns}" ./pm_nl_ctl add "${addr}" ${flags:+flags "${flags}"} \ 620 ${dev:+dev "${dev}"} ${id:+id "${id}"} ${port:+port "${port}"} 621 fi 622} 623 624mptcp_lib_pm_nl_del_endpoint() { 625 local ns=${1} 626 local id=${2} 627 local addr=${3} 628 629 if mptcp_lib_is_ip_mptcp; then 630 [ "${id}" -ne 0 ] && addr='' 631 ip -n "${ns}" mptcp endpoint delete id "${id}" ${addr:+"${addr}"} 632 else 633 ip netns exec "${ns}" ./pm_nl_ctl del "${id}" "${addr}" 634 fi 635} 636 637mptcp_lib_pm_nl_flush_endpoint() { 638 local ns=${1} 639 640 if mptcp_lib_is_ip_mptcp; then 641 ip -n "${ns}" mptcp endpoint flush 642 else 643 ip netns exec "${ns}" ./pm_nl_ctl flush 644 fi 645} 646 647mptcp_lib_pm_nl_show_endpoints() { 648 local ns=${1} 649 650 if mptcp_lib_is_ip_mptcp; then 651 ip -n "${ns}" mptcp endpoint show 652 else 653 ip netns exec "${ns}" ./pm_nl_ctl dump 654 fi 655} 656 657mptcp_lib_pm_nl_change_endpoint() { 658 local ns=${1} 659 local id=${2} 660 local flags=${3} 661 662 if mptcp_lib_is_ip_mptcp; then 663 # shellcheck disable=SC2086 # blanks in flags, no double quote 664 ip -n "${ns}" mptcp endpoint change id "${id}" ${flags//","/" "} 665 else 666 ip netns exec "${ns}" ./pm_nl_ctl set id "${id}" flags "${flags}" 667 fi 668} 669