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