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