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