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}" 2>/dev/null | 335 sed -n 's/.*\('"${1}"':\)\([0-9a-f:.]*\).*$/\2/p;q' 336 # the ';q' at the end limits to the first matched entry. 337} 338 339# $1: info name ; $2: evts_ns ; [$3: event type; [$4: addr]] 340mptcp_lib_evts_get_info() { 341 grep "${4:-}" "${2}" 2>/dev/null | 342 mptcp_lib_get_info_value "${1}" "^type:${3:-1}," 343} 344 345# $1: PID 346mptcp_lib_kill_wait() { 347 [ "${1}" -eq 0 ] && return 0 348 349 kill -SIGUSR1 "${1}" > /dev/null 2>&1 350 kill "${1}" > /dev/null 2>&1 351 wait "${1}" 2>/dev/null 352} 353 354# $1: IP address 355mptcp_lib_is_v6() { 356 [ -z "${1##*:*}" ] 357} 358 359# $1: ns, $2: MIB counter 360mptcp_lib_get_counter() { 361 local ns="${1}" 362 local counter="${2}" 363 local count 364 365 count=$(ip netns exec "${ns}" nstat -asz "${counter}" | 366 awk 'NR==1 {next} {print $2}') 367 if [ -z "${count}" ]; then 368 mptcp_lib_fail_if_expected_feature "${counter} counter" 369 return 1 370 fi 371 372 echo "${count}" 373} 374 375mptcp_lib_make_file() { 376 local name="${1}" 377 local bs="${2}" 378 local size="${3}" 379 380 dd if=/dev/urandom of="${name}" bs="${bs}" count="${size}" 2> /dev/null 381 echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "${name}" 382} 383 384# $1: file 385mptcp_lib_print_file_err() { 386 ls -l "${1}" 1>&2 387 echo "Trailing bytes are: " 388 tail -c 27 "${1}" 389} 390 391# $1: input file ; $2: output file ; $3: what kind of file 392mptcp_lib_check_transfer() { 393 local in="${1}" 394 local out="${2}" 395 local what="${3}" 396 397 if ! cmp "$in" "$out" > /dev/null 2>&1; then 398 mptcp_lib_pr_fail "$what does not match (in, out):" 399 mptcp_lib_print_file_err "$in" 400 mptcp_lib_print_file_err "$out" 401 402 return 1 403 fi 404 405 return 0 406} 407 408# $1: ns, $2: port 409mptcp_lib_wait_local_port_listen() { 410 wait_local_port_listen "${@}" "tcp" 411} 412 413mptcp_lib_check_output() { 414 local err="${1}" 415 local cmd="${2}" 416 local expected="${3}" 417 local cmd_ret=0 418 local out 419 420 if ! out=$(${cmd} 2>"${err}"); then 421 cmd_ret=${?} 422 fi 423 424 if [ ${cmd_ret} -ne 0 ]; then 425 mptcp_lib_pr_fail "command execution '${cmd}' stderr" 426 cat "${err}" 427 return 2 428 elif [ "${out}" = "${expected}" ]; then 429 return 0 430 else 431 mptcp_lib_pr_fail "expected '${expected}' got '${out}'" 432 return 1 433 fi 434} 435 436mptcp_lib_check_tools() { 437 local tool 438 439 for tool in "${@}"; do 440 case "${tool}" in 441 "ip") 442 if ! ip -Version &> /dev/null; then 443 mptcp_lib_pr_skip "Could not run test without ip tool" 444 exit ${KSFT_SKIP} 445 fi 446 ;; 447 "tc") 448 if ! tc -help &> /dev/null; then 449 mptcp_lib_pr_skip "Could not run test without tc tool" 450 exit ${KSFT_SKIP} 451 fi 452 ;; 453 "ss") 454 if ! ss -h | grep -q MPTCP; then 455 mptcp_lib_pr_skip "ss tool does not support MPTCP" 456 exit ${KSFT_SKIP} 457 fi 458 ;; 459 "iptables"* | "ip6tables"*) 460 if ! "${tool}" -V &> /dev/null; then 461 mptcp_lib_pr_skip "Could not run all tests without ${tool}" 462 exit ${KSFT_SKIP} 463 fi 464 ;; 465 *) 466 mptcp_lib_pr_fail "Internal error: unsupported tool: ${tool}" 467 exit ${KSFT_FAIL} 468 ;; 469 esac 470 done 471} 472 473mptcp_lib_ns_init() { 474 if ! setup_ns "${@}"; then 475 mptcp_lib_pr_fail "Failed to setup namespaces ${*}" 476 exit ${KSFT_FAIL} 477 fi 478 479 local netns 480 for netns in "${@}"; do 481 ip netns exec "${!netns}" sysctl -q net.mptcp.enabled=1 482 done 483} 484 485mptcp_lib_ns_exit() { 486 cleanup_ns "${@}" 487 488 local netns 489 for netns in "${@}"; do 490 rm -f /tmp/"${netns}".{nstat,out} 491 done 492} 493 494mptcp_lib_events() { 495 local ns="${1}" 496 local evts="${2}" 497 declare -n pid="${3}" 498 499 :>"${evts}" 500 501 mptcp_lib_kill_wait "${pid:-0}" 502 ip netns exec "${ns}" ./pm_nl_ctl events >> "${evts}" 2>&1 & 503 pid=$! 504} 505 506mptcp_lib_print_title() { 507 : "${MPTCP_LIB_TEST_COUNTER:?}" 508 : "${MPTCP_LIB_TEST_FORMAT:?}" 509 510 # shellcheck disable=SC2059 # the format is in a variable 511 printf "${MPTCP_LIB_TEST_FORMAT}" "$((++MPTCP_LIB_TEST_COUNTER))" "${*}" 512} 513 514# $1: var name ; $2: prev ret 515mptcp_lib_check_expected_one() { 516 local var="${1}" 517 local exp="e_${var}" 518 local prev_ret="${2}" 519 520 if [ "${!var}" = "${!exp}" ]; then 521 return 0 522 fi 523 524 if [ "${prev_ret}" = "0" ]; then 525 mptcp_lib_pr_fail 526 fi 527 528 mptcp_lib_print_err "Expected value for '${var}': '${!exp}', got '${!var}'." 529 return 1 530} 531 532# $@: all var names to check 533mptcp_lib_check_expected() { 534 local rc=0 535 local var 536 537 for var in "${@}"; do 538 mptcp_lib_check_expected_one "${var}" "${rc}" || rc=1 539 done 540 541 return "${rc}" 542} 543 544# shellcheck disable=SC2034 # Some variables are used below but indirectly 545mptcp_lib_verify_listener_events() { 546 local evt=${1} 547 local e_type=${2} 548 local e_family=${3} 549 local e_saddr=${4} 550 local e_sport=${5} 551 local type 552 local family 553 local saddr 554 local sport 555 local rc=0 556 557 type=$(mptcp_lib_evts_get_info type "${evt}" "${e_type}") 558 family=$(mptcp_lib_evts_get_info family "${evt}" "${e_type}") 559 if [ "${family}" ] && [ "${family}" = "${AF_INET6}" ]; then 560 saddr=$(mptcp_lib_evts_get_info saddr6 "${evt}" "${e_type}") 561 else 562 saddr=$(mptcp_lib_evts_get_info saddr4 "${evt}" "${e_type}") 563 fi 564 sport=$(mptcp_lib_evts_get_info sport "${evt}" "${e_type}") 565 566 mptcp_lib_check_expected "type" "family" "saddr" "sport" || rc="${?}" 567 return "${rc}" 568} 569 570mptcp_lib_set_ip_mptcp() { 571 MPTCP_LIB_IP_MPTCP=1 572} 573 574mptcp_lib_is_ip_mptcp() { 575 [ "${MPTCP_LIB_IP_MPTCP}" = "1" ] 576} 577 578# format: <id>,<ip>,<flags>,<dev> 579mptcp_lib_pm_nl_format_endpoints() { 580 local entry id ip flags dev port 581 582 for entry in "${@}"; do 583 IFS=, read -r id ip flags dev port <<< "${entry}" 584 if mptcp_lib_is_ip_mptcp; then 585 echo -n "${ip}" 586 [ -n "${port}" ] && echo -n " port ${port}" 587 echo -n " id ${id}" 588 [ -n "${flags}" ] && echo -n " ${flags}" 589 [ -n "${dev}" ] && echo -n " dev ${dev}" 590 echo " " # always a space at the end 591 else 592 echo -n "id ${id}" 593 echo -n " flags ${flags//" "/","}" 594 [ -n "${dev}" ] && echo -n " dev ${dev}" 595 echo -n " ${ip}" 596 [ -n "${port}" ] && echo -n " ${port}" 597 echo "" 598 fi 599 done 600} 601 602mptcp_lib_pm_nl_get_endpoint() { 603 local ns=${1} 604 local id=${2} 605 606 if mptcp_lib_is_ip_mptcp; then 607 ip -n "${ns}" mptcp endpoint show id "${id}" 608 else 609 ip netns exec "${ns}" ./pm_nl_ctl get "${id}" 610 fi 611} 612 613mptcp_lib_pm_nl_set_limits() { 614 local ns=${1} 615 local addrs=${2} 616 local subflows=${3} 617 618 if mptcp_lib_is_ip_mptcp; then 619 ip -n "${ns}" mptcp limits set add_addr_accepted "${addrs}" subflows "${subflows}" 620 else 621 ip netns exec "${ns}" ./pm_nl_ctl limits "${addrs}" "${subflows}" 622 fi 623} 624 625mptcp_lib_pm_nl_add_endpoint() { 626 local ns=${1} 627 local addr=${2} 628 local flags dev id port 629 local nr=2 630 631 local p 632 for p in "${@}"; do 633 case "${p}" in 634 "flags" | "dev" | "id" | "port") 635 eval "${p}"=\$"${nr}" 636 ;; 637 esac 638 639 nr=$((nr + 1)) 640 done 641 642 if mptcp_lib_is_ip_mptcp; then 643 # shellcheck disable=SC2086 # blanks in flags, no double quote 644 ip -n "${ns}" mptcp endpoint add "${addr}" ${flags//","/" "} \ 645 ${dev:+dev "${dev}"} ${id:+id "${id}"} ${port:+port "${port}"} 646 else 647 ip netns exec "${ns}" ./pm_nl_ctl add "${addr}" ${flags:+flags "${flags}"} \ 648 ${dev:+dev "${dev}"} ${id:+id "${id}"} ${port:+port "${port}"} 649 fi 650} 651 652mptcp_lib_pm_nl_del_endpoint() { 653 local ns=${1} 654 local id=${2} 655 local addr=${3} 656 657 if mptcp_lib_is_ip_mptcp; then 658 [ "${id}" -ne 0 ] && addr='' 659 ip -n "${ns}" mptcp endpoint delete id "${id}" ${addr:+"${addr}"} 660 else 661 ip netns exec "${ns}" ./pm_nl_ctl del "${id}" "${addr}" 662 fi 663} 664 665mptcp_lib_pm_nl_flush_endpoint() { 666 local ns=${1} 667 668 if mptcp_lib_is_ip_mptcp; then 669 ip -n "${ns}" mptcp endpoint flush 670 else 671 ip netns exec "${ns}" ./pm_nl_ctl flush 672 fi 673} 674 675mptcp_lib_pm_nl_show_endpoints() { 676 local ns=${1} 677 678 if mptcp_lib_is_ip_mptcp; then 679 ip -n "${ns}" mptcp endpoint show 680 else 681 ip netns exec "${ns}" ./pm_nl_ctl dump 682 fi 683} 684 685mptcp_lib_pm_nl_change_endpoint() { 686 local ns=${1} 687 local id=${2} 688 local flags=${3} 689 690 if mptcp_lib_is_ip_mptcp; then 691 # shellcheck disable=SC2086 # blanks in flags, no double quote 692 ip -n "${ns}" mptcp endpoint change id "${id}" ${flags//","/" "} 693 else 694 ip netns exec "${ns}" ./pm_nl_ctl set id "${id}" flags "${flags}" 695 fi 696} 697