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