1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# Double quotes to prevent globbing and word splitting is recommended in new 5# code but we accept it, especially because there were too many before having 6# address all other issues detected by shellcheck. 7#shellcheck disable=SC2086 8 9. "$(dirname "${0}")/mptcp_lib.sh" 10 11time_start=$(date +%s) 12 13optstring="S:R:d:e:l:r:h4cm:f:tC" 14ret=0 15final_ret=0 16sin="" 17sout="" 18cin_disconnect="" 19cin="" 20cout="" 21capture=false 22timeout_poll=30 23timeout_test=$((timeout_poll * 2 + 1)) 24ipv6=true 25ethtool_random_on=true 26tc_delay="$((RANDOM%50))" 27tc_loss=$((RANDOM%101)) 28testmode="" 29sndbuf=0 30rcvbuf=0 31options_log=true 32do_tcp=0 33checksum=false 34filesize=0 35connect_per_transfer=1 36 37if [ $tc_loss -eq 100 ];then 38 tc_loss=1% 39elif [ $tc_loss -ge 10 ]; then 40 tc_loss=0.$tc_loss% 41elif [ $tc_loss -ge 1 ]; then 42 tc_loss=0.0$tc_loss% 43else 44 tc_loss="" 45fi 46 47usage() { 48 echo "Usage: $0 [ -a ]" 49 echo -e "\t-d: tc/netem delay in milliseconds, e.g. \"-d 10\" (default random)" 50 echo -e "\t-l: tc/netem loss percentage, e.g. \"-l 0.02\" (default random)" 51 echo -e "\t-r: tc/netem reorder mode, e.g. \"-r 25% 50% gap 5\", use "-r 0" to disable reordering (default random)" 52 echo -e "\t-e: ethtool features to disable, e.g.: \"-e tso -e gso\" (default: randomly disable any of tso/gso/gro)" 53 echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)" 54 echo -e "\t-c: capture packets for each test using tcpdump (default: no capture)" 55 echo -e "\t-f: size of file to transfer in bytes (default random)" 56 echo -e "\t-S: set sndbuf value (default: use kernel default)" 57 echo -e "\t-R: set rcvbuf value (default: use kernel default)" 58 echo -e "\t-m: test mode (poll, sendfile; default: poll)" 59 echo -e "\t-t: also run tests with TCP (use twice to non-fallback tcp)" 60 echo -e "\t-C: enable the MPTCP data checksum" 61} 62 63while getopts "$optstring" option;do 64 case "$option" in 65 "h") 66 usage $0 67 exit 0 68 ;; 69 "d") 70 if [ $OPTARG -ge 0 ];then 71 tc_delay="$OPTARG" 72 else 73 echo "-d requires numeric argument, got \"$OPTARG\"" 1>&2 74 exit 1 75 fi 76 ;; 77 "e") 78 ethtool_args="$ethtool_args $OPTARG off" 79 ethtool_random_on=false 80 ;; 81 "l") 82 tc_loss="$OPTARG" 83 ;; 84 "r") 85 tc_reorder="$OPTARG" 86 ;; 87 "4") 88 ipv6=false 89 ;; 90 "c") 91 capture=true 92 ;; 93 "S") 94 if [ $OPTARG -ge 0 ];then 95 sndbuf="$OPTARG" 96 else 97 echo "-S requires numeric argument, got \"$OPTARG\"" 1>&2 98 exit 1 99 fi 100 ;; 101 "R") 102 if [ $OPTARG -ge 0 ];then 103 rcvbuf="$OPTARG" 104 else 105 echo "-R requires numeric argument, got \"$OPTARG\"" 1>&2 106 exit 1 107 fi 108 ;; 109 "m") 110 testmode="$OPTARG" 111 ;; 112 "f") 113 filesize="$OPTARG" 114 ;; 115 "t") 116 do_tcp=$((do_tcp+1)) 117 ;; 118 "C") 119 checksum=true 120 ;; 121 "?") 122 usage $0 123 exit 1 124 ;; 125 esac 126done 127 128sec=$(date +%s) 129rndh=$(printf %x $sec)-$(mktemp -u XXXXXX) 130ns1="ns1-$rndh" 131ns2="ns2-$rndh" 132ns3="ns3-$rndh" 133ns4="ns4-$rndh" 134 135TEST_COUNT=0 136TEST_GROUP="" 137 138# This function is used in the cleanup trap 139#shellcheck disable=SC2317 140cleanup() 141{ 142 rm -f "$cin_disconnect" "$cout_disconnect" 143 rm -f "$cin" "$cout" 144 rm -f "$sin" "$sout" 145 rm -f "$capout" 146 147 local netns 148 for netns in "$ns1" "$ns2" "$ns3" "$ns4";do 149 ip netns del $netns 150 rm -f /tmp/$netns.{nstat,out} 151 done 152} 153 154mptcp_lib_check_mptcp 155mptcp_lib_check_kallsyms 156 157ip -Version > /dev/null 2>&1 158if [ $? -ne 0 ];then 159 echo "SKIP: Could not run test without ip tool" 160 exit $ksft_skip 161fi 162 163sin=$(mktemp) 164sout=$(mktemp) 165cin=$(mktemp) 166cout=$(mktemp) 167capout=$(mktemp) 168cin_disconnect="$cin".disconnect 169cout_disconnect="$cout".disconnect 170trap cleanup EXIT 171 172for i in "$ns1" "$ns2" "$ns3" "$ns4";do 173 ip netns add $i || exit $ksft_skip 174 ip -net $i link set lo up 175done 176 177# "$ns1" ns2 ns3 ns4 178# ns1eth2 ns2eth1 ns2eth3 ns3eth2 ns3eth4 ns4eth3 179# - drop 1% -> reorder 25% 180# <- TSO off - 181 182ip link add ns1eth2 netns "$ns1" type veth peer name ns2eth1 netns "$ns2" 183ip link add ns2eth3 netns "$ns2" type veth peer name ns3eth2 netns "$ns3" 184ip link add ns3eth4 netns "$ns3" type veth peer name ns4eth3 netns "$ns4" 185 186ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth2 187ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth2 nodad 188 189ip -net "$ns1" link set ns1eth2 up 190ip -net "$ns1" route add default via 10.0.1.2 191ip -net "$ns1" route add default via dead:beef:1::2 192 193ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1 194ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad 195ip -net "$ns2" link set ns2eth1 up 196 197ip -net "$ns2" addr add 10.0.2.1/24 dev ns2eth3 198ip -net "$ns2" addr add dead:beef:2::1/64 dev ns2eth3 nodad 199ip -net "$ns2" link set ns2eth3 up 200ip -net "$ns2" route add default via 10.0.2.2 201ip -net "$ns2" route add default via dead:beef:2::2 202ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1 203ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1 204 205ip -net "$ns3" addr add 10.0.2.2/24 dev ns3eth2 206ip -net "$ns3" addr add dead:beef:2::2/64 dev ns3eth2 nodad 207ip -net "$ns3" link set ns3eth2 up 208 209ip -net "$ns3" addr add 10.0.3.2/24 dev ns3eth4 210ip -net "$ns3" addr add dead:beef:3::2/64 dev ns3eth4 nodad 211ip -net "$ns3" link set ns3eth4 up 212ip -net "$ns3" route add default via 10.0.2.1 213ip -net "$ns3" route add default via dead:beef:2::1 214ip netns exec "$ns3" sysctl -q net.ipv4.ip_forward=1 215ip netns exec "$ns3" sysctl -q net.ipv6.conf.all.forwarding=1 216 217ip -net "$ns4" addr add 10.0.3.1/24 dev ns4eth3 218ip -net "$ns4" addr add dead:beef:3::1/64 dev ns4eth3 nodad 219ip -net "$ns4" link set ns4eth3 up 220ip -net "$ns4" route add default via 10.0.3.2 221ip -net "$ns4" route add default via dead:beef:3::2 222 223if $checksum; then 224 for i in "$ns1" "$ns2" "$ns3" "$ns4";do 225 ip netns exec $i sysctl -q net.mptcp.checksum_enabled=1 226 done 227fi 228 229set_ethtool_flags() { 230 local ns="$1" 231 local dev="$2" 232 local flags="$3" 233 234 if ip netns exec $ns ethtool -K $dev $flags 2>/dev/null; then 235 echo "INFO: set $ns dev $dev: ethtool -K $flags" 236 fi 237} 238 239set_random_ethtool_flags() { 240 local flags="" 241 local r=$RANDOM 242 243 local pick1=$((r & 1)) 244 local pick2=$((r & 2)) 245 local pick3=$((r & 4)) 246 247 [ $pick1 -ne 0 ] && flags="tso off" 248 [ $pick2 -ne 0 ] && flags="$flags gso off" 249 [ $pick3 -ne 0 ] && flags="$flags gro off" 250 251 [ -z "$flags" ] && return 252 253 set_ethtool_flags "$1" "$2" "$flags" 254} 255 256if $ethtool_random_on;then 257 set_random_ethtool_flags "$ns3" ns3eth2 258 set_random_ethtool_flags "$ns4" ns4eth3 259else 260 set_ethtool_flags "$ns3" ns3eth2 "$ethtool_args" 261 set_ethtool_flags "$ns4" ns4eth3 "$ethtool_args" 262fi 263 264print_file_err() 265{ 266 ls -l "$1" 1>&2 267 echo "Trailing bytes are: " 268 tail -c 27 "$1" 269} 270 271check_transfer() 272{ 273 local in=$1 274 local out=$2 275 local what=$3 276 277 cmp "$in" "$out" > /dev/null 2>&1 278 if [ $? -ne 0 ] ;then 279 echo "[ FAIL ] $what does not match (in, out):" 280 print_file_err "$in" 281 print_file_err "$out" 282 283 return 1 284 fi 285 286 return 0 287} 288 289check_mptcp_disabled() 290{ 291 local disabled_ns="ns_disabled-$rndh" 292 ip netns add ${disabled_ns} || exit $ksft_skip 293 294 # net.mptcp.enabled should be enabled by default 295 if [ "$(ip netns exec ${disabled_ns} sysctl net.mptcp.enabled | awk '{ print $3 }')" -ne 1 ]; then 296 echo -e "net.mptcp.enabled sysctl is not 1 by default\t\t[ FAIL ]" 297 mptcp_lib_result_fail "net.mptcp.enabled sysctl is not 1 by default" 298 ret=1 299 return 1 300 fi 301 ip netns exec ${disabled_ns} sysctl -q net.mptcp.enabled=0 302 303 local err=0 304 LC_ALL=C ip netns exec ${disabled_ns} ./mptcp_connect -p 10000 -s MPTCP 127.0.0.1 < "$cin" 2>&1 | \ 305 grep -q "^socket: Protocol not available$" && err=1 306 ip netns delete ${disabled_ns} 307 308 if [ ${err} -eq 0 ]; then 309 echo -e "New MPTCP socket cannot be blocked via sysctl\t\t[ FAIL ]" 310 mptcp_lib_result_fail "New MPTCP socket cannot be blocked via sysctl" 311 ret=1 312 return 1 313 fi 314 315 echo -e "New MPTCP socket can be blocked via sysctl\t\t[ OK ]" 316 mptcp_lib_result_pass "New MPTCP socket can be blocked via sysctl" 317 return 0 318} 319 320do_ping() 321{ 322 local listener_ns="$1" 323 local connector_ns="$2" 324 local connect_addr="$3" 325 local ping_args="-q -c 1" 326 local rc=0 327 328 if mptcp_lib_is_v6 "${connect_addr}"; then 329 $ipv6 || return 0 330 ping_args="${ping_args} -6" 331 fi 332 333 ip netns exec ${connector_ns} ping ${ping_args} $connect_addr >/dev/null || rc=1 334 335 if [ $rc -ne 0 ] ; then 336 echo "$listener_ns -> $connect_addr connectivity [ FAIL ]" 1>&2 337 ret=1 338 339 return 1 340 fi 341 342 return 0 343} 344 345# $1: ns, $2: port 346wait_local_port_listen() 347{ 348 local listener_ns="${1}" 349 local port="${2}" 350 351 local port_hex i 352 353 port_hex="$(printf "%04X" "${port}")" 354 for i in $(seq 10); do 355 ip netns exec "${listener_ns}" cat /proc/net/tcp* | \ 356 awk "BEGIN {rc=1} {if (\$2 ~ /:${port_hex}\$/ && \$4 ~ /0A/) {rc=0; exit}} END {exit rc}" && 357 break 358 sleep 0.1 359 done 360} 361 362do_transfer() 363{ 364 local listener_ns="$1" 365 local connector_ns="$2" 366 local cl_proto="$3" 367 local srv_proto="$4" 368 local connect_addr="$5" 369 local local_addr="$6" 370 local extra_args="$7" 371 372 local port 373 port=$((10000+TEST_COUNT)) 374 TEST_COUNT=$((TEST_COUNT+1)) 375 376 if [ "$rcvbuf" -gt 0 ]; then 377 extra_args+=" -R $rcvbuf" 378 fi 379 380 if [ "$sndbuf" -gt 0 ]; then 381 extra_args+=" -S $sndbuf" 382 fi 383 384 if [ -n "$testmode" ]; then 385 extra_args+=" -m $testmode" 386 fi 387 388 if [ -n "$extra_args" ] && $options_log; then 389 echo "INFO: extra options: $extra_args" 390 fi 391 options_log=false 392 393 :> "$cout" 394 :> "$sout" 395 :> "$capout" 396 397 local addr_port 398 addr_port=$(printf "%s:%d" ${connect_addr} ${port}) 399 local result_msg 400 result_msg="$(printf "%.3s %-5s -> %.3s (%-20s) %-5s" ${connector_ns} ${cl_proto} ${listener_ns} ${addr_port} ${srv_proto})" 401 printf "%s\t" "${result_msg}" 402 403 if $capture; then 404 local capuser 405 if [ -z $SUDO_USER ] ; then 406 capuser="" 407 else 408 capuser="-Z $SUDO_USER" 409 fi 410 411 local capfile="${rndh}-${connector_ns:0:3}-${listener_ns:0:3}-${cl_proto}-${srv_proto}-${connect_addr}-${port}" 412 local capopt="-i any -s 65535 -B 32768 ${capuser}" 413 414 ip netns exec ${listener_ns} tcpdump ${capopt} -w "${capfile}-listener.pcap" >> "${capout}" 2>&1 & 415 local cappid_listener=$! 416 417 ip netns exec ${connector_ns} tcpdump ${capopt} -w "${capfile}-connector.pcap" >> "${capout}" 2>&1 & 418 local cappid_connector=$! 419 420 sleep 1 421 fi 422 423 NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \ 424 nstat -n 425 if [ ${listener_ns} != ${connector_ns} ]; then 426 NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \ 427 nstat -n 428 fi 429 430 local stat_synrx_last_l 431 local stat_ackrx_last_l 432 local stat_cookietx_last 433 local stat_cookierx_last 434 local stat_csum_err_s 435 local stat_csum_err_c 436 local stat_tcpfb_last_l 437 stat_synrx_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableSYNRX") 438 stat_ackrx_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableACKRX") 439 stat_cookietx_last=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesSent") 440 stat_cookierx_last=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesRecv") 441 stat_csum_err_s=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtDataCsumErr") 442 stat_csum_err_c=$(mptcp_lib_get_counter "${connector_ns}" "MPTcpExtDataCsumErr") 443 stat_tcpfb_last_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableFallbackACK") 444 445 timeout ${timeout_test} \ 446 ip netns exec ${listener_ns} \ 447 ./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ 448 $extra_args $local_addr < "$sin" > "$sout" & 449 local spid=$! 450 451 wait_local_port_listen "${listener_ns}" "${port}" 452 453 local start 454 start=$(date +%s%3N) 455 timeout ${timeout_test} \ 456 ip netns exec ${connector_ns} \ 457 ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ 458 $extra_args $connect_addr < "$cin" > "$cout" & 459 local cpid=$! 460 461 wait $cpid 462 local retc=$? 463 wait $spid 464 local rets=$? 465 466 local stop 467 stop=$(date +%s%3N) 468 469 if $capture; then 470 sleep 1 471 kill ${cappid_listener} 472 kill ${cappid_connector} 473 fi 474 475 NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \ 476 nstat | grep Tcp > /tmp/${listener_ns}.out 477 if [ ${listener_ns} != ${connector_ns} ]; then 478 NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \ 479 nstat | grep Tcp > /tmp/${connector_ns}.out 480 fi 481 482 local duration 483 duration=$((stop-start)) 484 result_msg+=" # time=${duration}ms" 485 printf "(duration %05sms) " "${duration}" 486 if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then 487 echo "[ FAIL ] client exit code $retc, server $rets" 1>&2 488 echo -e "\nnetns ${listener_ns} socket stat for ${port}:" 1>&2 489 ip netns exec ${listener_ns} ss -Menita 1>&2 -o "sport = :$port" 490 cat /tmp/${listener_ns}.out 491 echo -e "\nnetns ${connector_ns} socket stat for ${port}:" 1>&2 492 ip netns exec ${connector_ns} ss -Menita 1>&2 -o "dport = :$port" 493 [ ${listener_ns} != ${connector_ns} ] && cat /tmp/${connector_ns}.out 494 495 echo 496 cat "$capout" 497 mptcp_lib_result_fail "${TEST_GROUP}: ${result_msg}" 498 return 1 499 fi 500 501 check_transfer $sin $cout "file received by client" 502 retc=$? 503 check_transfer $cin $sout "file received by server" 504 rets=$? 505 506 local extra="" 507 local stat_synrx_now_l 508 local stat_ackrx_now_l 509 local stat_cookietx_now 510 local stat_cookierx_now 511 local stat_ooo_now 512 local stat_tcpfb_now_l 513 stat_synrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableSYNRX") 514 stat_ackrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableACKRX") 515 stat_cookietx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesSent") 516 stat_cookierx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesRecv") 517 stat_ooo_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtTCPOFOQueue") 518 stat_tcpfb_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableFallbackACK") 519 520 expect_synrx=$((stat_synrx_last_l)) 521 expect_ackrx=$((stat_ackrx_last_l)) 522 523 cookies=$(ip netns exec ${listener_ns} sysctl net.ipv4.tcp_syncookies) 524 cookies=${cookies##*=} 525 526 if [ ${cl_proto} = "MPTCP" ] && [ ${srv_proto} = "MPTCP" ]; then 527 expect_synrx=$((stat_synrx_last_l+connect_per_transfer)) 528 expect_ackrx=$((stat_ackrx_last_l+connect_per_transfer)) 529 fi 530 531 if [ ${stat_synrx_now_l} -lt ${expect_synrx} ]; then 532 printf "[ FAIL ] lower MPC SYN rx (%d) than expected (%d)\n" \ 533 "${stat_synrx_now_l}" "${expect_synrx}" 1>&2 534 retc=1 535 fi 536 if [ ${stat_ackrx_now_l} -lt ${expect_ackrx} ] && [ ${stat_ooo_now} -eq 0 ]; then 537 if [ ${stat_ooo_now} -eq 0 ]; then 538 printf "[ FAIL ] lower MPC ACK rx (%d) than expected (%d)\n" \ 539 "${stat_ackrx_now_l}" "${expect_ackrx}" 1>&2 540 rets=1 541 else 542 extra+=" [ Note ] fallback due to TCP OoO" 543 fi 544 fi 545 546 if $checksum; then 547 local csum_err_s 548 local csum_err_c 549 csum_err_s=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtDataCsumErr") 550 csum_err_c=$(mptcp_lib_get_counter "${connector_ns}" "MPTcpExtDataCsumErr") 551 552 local csum_err_s_nr=$((csum_err_s - stat_csum_err_s)) 553 if [ $csum_err_s_nr -gt 0 ]; then 554 printf "[ FAIL ]\nserver got %d data checksum error[s]" ${csum_err_s_nr} 555 rets=1 556 fi 557 558 local csum_err_c_nr=$((csum_err_c - stat_csum_err_c)) 559 if [ $csum_err_c_nr -gt 0 ]; then 560 printf "[ FAIL ]\nclient got %d data checksum error[s]" ${csum_err_c_nr} 561 retc=1 562 fi 563 fi 564 565 if [ ${stat_ooo_now} -eq 0 ] && [ ${stat_tcpfb_last_l} -ne ${stat_tcpfb_now_l} ]; then 566 mptcp_lib_pr_fail "unexpected fallback to TCP" 567 rets=1 568 fi 569 570 if [ $cookies -eq 2 ];then 571 if [ $stat_cookietx_last -ge $stat_cookietx_now ] ;then 572 extra+=" WARN: CookieSent: did not advance" 573 fi 574 if [ $stat_cookierx_last -ge $stat_cookierx_now ] ;then 575 extra+=" WARN: CookieRecv: did not advance" 576 fi 577 else 578 if [ $stat_cookietx_last -ne $stat_cookietx_now ] ;then 579 extra+=" WARN: CookieSent: changed" 580 fi 581 if [ $stat_cookierx_last -ne $stat_cookierx_now ] ;then 582 extra+=" WARN: CookieRecv: changed" 583 fi 584 fi 585 586 if [ ${stat_synrx_now_l} -gt ${expect_synrx} ]; then 587 extra+=" WARN: SYNRX: expect ${expect_synrx}," 588 extra+=" got ${stat_synrx_now_l} (probably retransmissions)" 589 fi 590 if [ ${stat_ackrx_now_l} -gt ${expect_ackrx} ]; then 591 extra+=" WARN: ACKRX: expect ${expect_ackrx}," 592 extra+=" got ${stat_ackrx_now_l} (probably retransmissions)" 593 fi 594 595 if [ $retc -eq 0 ] && [ $rets -eq 0 ]; then 596 printf "[ OK ]%s\n" "${extra}" 597 mptcp_lib_result_pass "${TEST_GROUP}: ${result_msg}" 598 else 599 if [ -n "${extra}" ]; then 600 printf "%s\n" "${extra:1}" 601 fi 602 mptcp_lib_result_fail "${TEST_GROUP}: ${result_msg}" 603 fi 604 605 cat "$capout" 606 [ $retc -eq 0 ] && [ $rets -eq 0 ] 607} 608 609make_file() 610{ 611 local name=$1 612 local who=$2 613 local SIZE=$filesize 614 local ksize 615 local rem 616 617 if [ $SIZE -eq 0 ]; then 618 local MAXSIZE=$((1024 * 1024 * 8)) 619 local MINSIZE=$((1024 * 256)) 620 621 SIZE=$(((RANDOM * RANDOM + MINSIZE) % MAXSIZE)) 622 fi 623 624 ksize=$((SIZE / 1024)) 625 rem=$((SIZE - (ksize * 1024))) 626 627 dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null 628 dd if=/dev/urandom conv=notrunc of="$name" bs=1 count=$rem 2> /dev/null 629 echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name" 630 631 echo "Created $name (size $(du -b "$name")) containing data sent by $who" 632} 633 634run_tests_lo() 635{ 636 local listener_ns="$1" 637 local connector_ns="$2" 638 local connect_addr="$3" 639 local loopback="$4" 640 local extra_args="$5" 641 local lret=0 642 643 # skip if test programs are running inside same netns for subsequent runs. 644 if [ $loopback -eq 0 ] && [ ${listener_ns} = ${connector_ns} ]; then 645 return 0 646 fi 647 648 # skip if we don't want v6 649 if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then 650 return 0 651 fi 652 653 local local_addr 654 if mptcp_lib_is_v6 "${connect_addr}"; then 655 local_addr="::" 656 else 657 local_addr="0.0.0.0" 658 fi 659 660 do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \ 661 ${connect_addr} ${local_addr} "${extra_args}" 662 lret=$? 663 if [ $lret -ne 0 ]; then 664 ret=$lret 665 return 1 666 fi 667 668 if [ $do_tcp -eq 0 ]; then 669 # don't bother testing fallback tcp except for loopback case. 670 if [ ${listener_ns} != ${connector_ns} ]; then 671 return 0 672 fi 673 fi 674 675 do_transfer ${listener_ns} ${connector_ns} MPTCP TCP \ 676 ${connect_addr} ${local_addr} "${extra_args}" 677 lret=$? 678 if [ $lret -ne 0 ]; then 679 ret=$lret 680 return 1 681 fi 682 683 do_transfer ${listener_ns} ${connector_ns} TCP MPTCP \ 684 ${connect_addr} ${local_addr} "${extra_args}" 685 lret=$? 686 if [ $lret -ne 0 ]; then 687 ret=$lret 688 return 1 689 fi 690 691 if [ $do_tcp -gt 1 ] ;then 692 do_transfer ${listener_ns} ${connector_ns} TCP TCP \ 693 ${connect_addr} ${local_addr} "${extra_args}" 694 lret=$? 695 if [ $lret -ne 0 ]; then 696 ret=$lret 697 return 1 698 fi 699 fi 700 701 return 0 702} 703 704run_tests() 705{ 706 run_tests_lo $1 $2 $3 0 707} 708 709run_test_transparent() 710{ 711 local connect_addr="$1" 712 local msg="$2" 713 714 local connector_ns="$ns1" 715 local listener_ns="$ns2" 716 local lret=0 717 local r6flag="" 718 719 TEST_GROUP="${msg}" 720 721 # skip if we don't want v6 722 if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then 723 return 0 724 fi 725 726 # IP(V6)_TRANSPARENT has been added after TOS support which came with 727 # the required infrastructure in MPTCP sockopt code. To support TOS, the 728 # following function has been exported (T). Not great but better than 729 # checking for a specific kernel version. 730 if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then 731 echo "INFO: ${msg} not supported by the kernel: SKIP" 732 mptcp_lib_result_skip "${TEST_GROUP}" 733 return 734 fi 735 736 if ! ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF" 737flush ruleset 738table inet mangle { 739 chain divert { 740 type filter hook prerouting priority -150; 741 742 meta l4proto tcp socket transparent 1 meta mark set 1 accept 743 tcp dport 20000 tproxy to :20000 meta mark set 1 accept 744 } 745} 746EOF 747 then 748 echo "SKIP: $msg, could not load nft ruleset" 749 mptcp_lib_fail_if_expected_feature "nft rules" 750 mptcp_lib_result_skip "${TEST_GROUP}" 751 return 752 fi 753 754 local local_addr 755 if mptcp_lib_is_v6 "${connect_addr}"; then 756 local_addr="::" 757 r6flag="-6" 758 else 759 local_addr="0.0.0.0" 760 fi 761 762 if ! ip -net "$listener_ns" $r6flag rule add fwmark 1 lookup 100; then 763 ip netns exec "$listener_ns" nft flush ruleset 764 echo "SKIP: $msg, ip $r6flag rule failed" 765 mptcp_lib_fail_if_expected_feature "ip rule" 766 mptcp_lib_result_skip "${TEST_GROUP}" 767 return 768 fi 769 770 if ! ip -net "$listener_ns" route add local $local_addr/0 dev lo table 100; then 771 ip netns exec "$listener_ns" nft flush ruleset 772 ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100 773 echo "SKIP: $msg, ip route add local $local_addr failed" 774 mptcp_lib_fail_if_expected_feature "ip route" 775 mptcp_lib_result_skip "${TEST_GROUP}" 776 return 777 fi 778 779 echo "INFO: test $msg" 780 781 TEST_COUNT=10000 782 local extra_args="-o TRANSPARENT" 783 do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \ 784 ${connect_addr} ${local_addr} "${extra_args}" 785 lret=$? 786 787 ip netns exec "$listener_ns" nft flush ruleset 788 ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100 789 ip -net "$listener_ns" route del local $local_addr/0 dev lo table 100 790 791 if [ $lret -ne 0 ]; then 792 echo "FAIL: $msg, mptcp connection error" 1>&2 793 ret=$lret 794 return 1 795 fi 796 797 echo "PASS: $msg" 798 return 0 799} 800 801run_tests_peekmode() 802{ 803 local peekmode="$1" 804 805 TEST_GROUP="peek mode: ${peekmode}" 806 echo "INFO: with peek mode: ${peekmode}" 807 run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-P ${peekmode}" 808 run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-P ${peekmode}" 809} 810 811run_tests_mptfo() 812{ 813 TEST_GROUP="MPTFO" 814 815 if ! mptcp_lib_kallsyms_has "mptcp_fastopen_"; then 816 echo "INFO: TFO not supported by the kernel: SKIP" 817 mptcp_lib_result_skip "${TEST_GROUP}" 818 return 819 fi 820 821 echo "INFO: with MPTFO start" 822 ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2 823 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1 824 825 run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" 826 run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" 827 828 run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" 829 run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" 830 831 ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=0 832 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=0 833 echo "INFO: with MPTFO end" 834} 835 836run_tests_disconnect() 837{ 838 local old_cin=$cin 839 local old_sin=$sin 840 841 TEST_GROUP="full disconnect" 842 843 if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then 844 echo "INFO: Full disconnect not supported: SKIP" 845 mptcp_lib_result_skip "${TEST_GROUP}" 846 return 847 fi 848 849 cat $cin $cin $cin > "$cin".disconnect 850 851 # force do_transfer to cope with the multiple transmissions 852 sin="$cin.disconnect" 853 cin="$cin.disconnect" 854 cin_disconnect="$old_cin" 855 connect_per_transfer=3 856 857 echo "INFO: disconnect" 858 run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-I 3 -i $old_cin" 859 run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-I 3 -i $old_cin" 860 861 # restore previous status 862 sin=$old_sin 863 cin=$old_cin 864 cin_disconnect="$cin".disconnect 865 connect_per_transfer=1 866} 867 868display_time() 869{ 870 time_end=$(date +%s) 871 time_run=$((time_end-time_start)) 872 873 echo "Time: ${time_run} seconds" 874} 875 876log_if_error() 877{ 878 local msg="$1" 879 880 if [ ${ret} -ne 0 ]; then 881 echo "FAIL: ${msg}" 1>&2 882 883 final_ret=${ret} 884 ret=0 885 886 return ${final_ret} 887 fi 888} 889 890stop_if_error() 891{ 892 if ! log_if_error "${@}"; then 893 display_time 894 mptcp_lib_result_print_all_tap 895 exit ${final_ret} 896 fi 897} 898 899make_file "$cin" "client" 900make_file "$sin" "server" 901 902check_mptcp_disabled 903 904stop_if_error "The kernel configuration is not valid for MPTCP" 905 906echo "INFO: validating network environment with pings" 907for sender in "$ns1" "$ns2" "$ns3" "$ns4";do 908 do_ping "$ns1" $sender 10.0.1.1 909 do_ping "$ns1" $sender dead:beef:1::1 910 911 do_ping "$ns2" $sender 10.0.1.2 912 do_ping "$ns2" $sender dead:beef:1::2 913 do_ping "$ns2" $sender 10.0.2.1 914 do_ping "$ns2" $sender dead:beef:2::1 915 916 do_ping "$ns3" $sender 10.0.2.2 917 do_ping "$ns3" $sender dead:beef:2::2 918 do_ping "$ns3" $sender 10.0.3.2 919 do_ping "$ns3" $sender dead:beef:3::2 920 921 do_ping "$ns4" $sender 10.0.3.1 922 do_ping "$ns4" $sender dead:beef:3::1 923done 924 925mptcp_lib_result_code "${ret}" "ping tests" 926 927stop_if_error "Could not even run ping tests" 928 929[ -n "$tc_loss" ] && tc -net "$ns2" qdisc add dev ns2eth3 root netem loss random $tc_loss delay ${tc_delay}ms 930tc_info="loss of $tc_loss " 931test "$tc_delay" -gt 0 && tc_info+="delay $tc_delay ms " 932 933reorder_delay=$((tc_delay / 4)) 934 935if [ -z "${tc_reorder}" ]; then 936 reorder1=$((RANDOM%10)) 937 reorder1=$((100 - reorder1)) 938 reorder2=$((RANDOM%100)) 939 940 if [ $reorder_delay -gt 0 ] && [ $reorder1 -lt 100 ] && [ $reorder2 -gt 0 ]; then 941 tc_reorder="reorder ${reorder1}% ${reorder2}%" 942 tc_info+="$tc_reorder with delay ${reorder_delay}ms " 943 fi 944elif [ "$tc_reorder" = "0" ];then 945 tc_reorder="" 946elif [ "$reorder_delay" -gt 0 ];then 947 # reordering requires some delay 948 tc_reorder="reorder $tc_reorder" 949 tc_info+="$tc_reorder with delay ${reorder_delay}ms " 950fi 951 952echo "INFO: Using ${tc_info}on ns3eth4" 953 954tc -net "$ns3" qdisc add dev ns3eth4 root netem delay ${reorder_delay}ms $tc_reorder 955 956TEST_GROUP="loopback v4" 957run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 958stop_if_error "Could not even run loopback test" 959 960TEST_GROUP="loopback v6" 961run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 962stop_if_error "Could not even run loopback v6 test" 963 964TEST_GROUP="multihosts" 965for sender in $ns1 $ns2 $ns3 $ns4;do 966 # ns1<->ns2 is not subject to reordering/tc delays. Use it to test 967 # mptcp syncookie support. 968 if [ $sender = $ns1 ]; then 969 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2 970 else 971 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=1 972 fi 973 974 run_tests "$ns1" $sender 10.0.1.1 975 run_tests "$ns1" $sender dead:beef:1::1 976 977 run_tests "$ns2" $sender 10.0.1.2 978 run_tests "$ns2" $sender dead:beef:1::2 979 run_tests "$ns2" $sender 10.0.2.1 980 run_tests "$ns2" $sender dead:beef:2::1 981 982 run_tests "$ns3" $sender 10.0.2.2 983 run_tests "$ns3" $sender dead:beef:2::2 984 run_tests "$ns3" $sender 10.0.3.2 985 run_tests "$ns3" $sender dead:beef:3::2 986 987 run_tests "$ns4" $sender 10.0.3.1 988 run_tests "$ns4" $sender dead:beef:3::1 989 990 log_if_error "Tests with $sender as a sender have failed" 991done 992 993run_tests_peekmode "saveWithPeek" 994run_tests_peekmode "saveAfterPeek" 995log_if_error "Tests with peek mode have failed" 996 997# MPTFO (MultiPath TCP Fatopen tests) 998run_tests_mptfo 999log_if_error "Tests with MPTFO have failed" 1000 1001# connect to ns4 ip address, ns2 should intercept/proxy 1002run_test_transparent 10.0.3.1 "tproxy ipv4" 1003run_test_transparent dead:beef:3::1 "tproxy ipv6" 1004log_if_error "Tests with tproxy have failed" 1005 1006run_tests_disconnect 1007log_if_error "Tests of the full disconnection have failed" 1008 1009display_time 1010mptcp_lib_result_print_all_tap 1011exit ${final_ret} 1012