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