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="$extra_args -R $rcvbuf" 378 fi 379 380 if [ "$sndbuf" -gt 0 ]; then 381 extra_args="$extra_args -S $sndbuf" 382 fi 383 384 if [ -n "$testmode" ]; then 385 extra_args="$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 stat_synrx_now_l 507 local stat_ackrx_now_l 508 local stat_cookietx_now 509 local stat_cookierx_now 510 local stat_ooo_now 511 local stat_tcpfb_now_l 512 stat_synrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableSYNRX") 513 stat_ackrx_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableACKRX") 514 stat_cookietx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesSent") 515 stat_cookierx_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtSyncookiesRecv") 516 stat_ooo_now=$(mptcp_lib_get_counter "${listener_ns}" "TcpExtTCPOFOQueue") 517 stat_tcpfb_now_l=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtMPCapableFallbackACK") 518 519 expect_synrx=$((stat_synrx_last_l)) 520 expect_ackrx=$((stat_ackrx_last_l)) 521 522 cookies=$(ip netns exec ${listener_ns} sysctl net.ipv4.tcp_syncookies) 523 cookies=${cookies##*=} 524 525 if [ ${cl_proto} = "MPTCP" ] && [ ${srv_proto} = "MPTCP" ]; then 526 expect_synrx=$((stat_synrx_last_l+connect_per_transfer)) 527 expect_ackrx=$((stat_ackrx_last_l+connect_per_transfer)) 528 fi 529 530 if [ ${stat_synrx_now_l} -lt ${expect_synrx} ]; then 531 printf "[ FAIL ] lower MPC SYN rx (%d) than expected (%d)\n" \ 532 "${stat_synrx_now_l}" "${expect_synrx}" 1>&2 533 retc=1 534 fi 535 if [ ${stat_ackrx_now_l} -lt ${expect_ackrx} ] && [ ${stat_ooo_now} -eq 0 ]; then 536 if [ ${stat_ooo_now} -eq 0 ]; then 537 printf "[ FAIL ] lower MPC ACK rx (%d) than expected (%d)\n" \ 538 "${stat_ackrx_now_l}" "${expect_ackrx}" 1>&2 539 rets=1 540 else 541 printf "[ Note ] fallback due to TCP OoO" 542 fi 543 fi 544 545 if $checksum; then 546 local csum_err_s 547 local csum_err_c 548 csum_err_s=$(mptcp_lib_get_counter "${listener_ns}" "MPTcpExtDataCsumErr") 549 csum_err_c=$(mptcp_lib_get_counter "${connector_ns}" "MPTcpExtDataCsumErr") 550 551 local csum_err_s_nr=$((csum_err_s - stat_csum_err_s)) 552 if [ $csum_err_s_nr -gt 0 ]; then 553 printf "[ FAIL ]\nserver got %d data checksum error[s]" ${csum_err_s_nr} 554 rets=1 555 fi 556 557 local csum_err_c_nr=$((csum_err_c - stat_csum_err_c)) 558 if [ $csum_err_c_nr -gt 0 ]; then 559 printf "[ FAIL ]\nclient got %d data checksum error[s]" ${csum_err_c_nr} 560 retc=1 561 fi 562 fi 563 564 if [ $retc -eq 0 ] && [ $rets -eq 0 ]; then 565 printf "[ OK ]" 566 mptcp_lib_result_pass "${TEST_GROUP}: ${result_msg}" 567 else 568 mptcp_lib_result_fail "${TEST_GROUP}: ${result_msg}" 569 fi 570 571 if [ ${stat_ooo_now} -eq 0 ] && [ ${stat_tcpfb_last_l} -ne ${stat_tcpfb_now_l} ]; then 572 mptcp_lib_pr_fail "unexpected fallback to TCP" 573 rets=1 574 fi 575 576 if [ $cookies -eq 2 ];then 577 if [ $stat_cookietx_last -ge $stat_cookietx_now ] ;then 578 printf " WARN: CookieSent: did not advance" 579 fi 580 if [ $stat_cookierx_last -ge $stat_cookierx_now ] ;then 581 printf " WARN: CookieRecv: did not advance" 582 fi 583 else 584 if [ $stat_cookietx_last -ne $stat_cookietx_now ] ;then 585 printf " WARN: CookieSent: changed" 586 fi 587 if [ $stat_cookierx_last -ne $stat_cookierx_now ] ;then 588 printf " WARN: CookieRecv: changed" 589 fi 590 fi 591 592 if [ ${stat_synrx_now_l} -gt ${expect_synrx} ]; then 593 printf " WARN: SYNRX: expect %d, got %d (probably retransmissions)" \ 594 "${expect_synrx}" "${stat_synrx_now_l}" 595 fi 596 if [ ${stat_ackrx_now_l} -gt ${expect_ackrx} ]; then 597 printf " WARN: ACKRX: expect %d, got %d (probably retransmissions)" \ 598 "${expect_ackrx}" "${stat_ackrx_now_l}" 599 fi 600 601 echo 602 cat "$capout" 603 [ $retc -eq 0 ] && [ $rets -eq 0 ] 604} 605 606make_file() 607{ 608 local name=$1 609 local who=$2 610 local SIZE=$filesize 611 local ksize 612 local rem 613 614 if [ $SIZE -eq 0 ]; then 615 local MAXSIZE=$((1024 * 1024 * 8)) 616 local MINSIZE=$((1024 * 256)) 617 618 SIZE=$(((RANDOM * RANDOM + MINSIZE) % MAXSIZE)) 619 fi 620 621 ksize=$((SIZE / 1024)) 622 rem=$((SIZE - (ksize * 1024))) 623 624 dd if=/dev/urandom of="$name" bs=1024 count=$ksize 2> /dev/null 625 dd if=/dev/urandom conv=notrunc of="$name" bs=1 count=$rem 2> /dev/null 626 echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name" 627 628 echo "Created $name (size $(du -b "$name")) containing data sent by $who" 629} 630 631run_tests_lo() 632{ 633 local listener_ns="$1" 634 local connector_ns="$2" 635 local connect_addr="$3" 636 local loopback="$4" 637 local extra_args="$5" 638 local lret=0 639 640 # skip if test programs are running inside same netns for subsequent runs. 641 if [ $loopback -eq 0 ] && [ ${listener_ns} = ${connector_ns} ]; then 642 return 0 643 fi 644 645 # skip if we don't want v6 646 if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then 647 return 0 648 fi 649 650 local local_addr 651 if mptcp_lib_is_v6 "${connect_addr}"; then 652 local_addr="::" 653 else 654 local_addr="0.0.0.0" 655 fi 656 657 do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \ 658 ${connect_addr} ${local_addr} "${extra_args}" 659 lret=$? 660 if [ $lret -ne 0 ]; then 661 ret=$lret 662 return 1 663 fi 664 665 if [ $do_tcp -eq 0 ]; then 666 # don't bother testing fallback tcp except for loopback case. 667 if [ ${listener_ns} != ${connector_ns} ]; then 668 return 0 669 fi 670 fi 671 672 do_transfer ${listener_ns} ${connector_ns} MPTCP TCP \ 673 ${connect_addr} ${local_addr} "${extra_args}" 674 lret=$? 675 if [ $lret -ne 0 ]; then 676 ret=$lret 677 return 1 678 fi 679 680 do_transfer ${listener_ns} ${connector_ns} TCP MPTCP \ 681 ${connect_addr} ${local_addr} "${extra_args}" 682 lret=$? 683 if [ $lret -ne 0 ]; then 684 ret=$lret 685 return 1 686 fi 687 688 if [ $do_tcp -gt 1 ] ;then 689 do_transfer ${listener_ns} ${connector_ns} TCP TCP \ 690 ${connect_addr} ${local_addr} "${extra_args}" 691 lret=$? 692 if [ $lret -ne 0 ]; then 693 ret=$lret 694 return 1 695 fi 696 fi 697 698 return 0 699} 700 701run_tests() 702{ 703 run_tests_lo $1 $2 $3 0 704} 705 706run_test_transparent() 707{ 708 local connect_addr="$1" 709 local msg="$2" 710 711 local connector_ns="$ns1" 712 local listener_ns="$ns2" 713 local lret=0 714 local r6flag="" 715 716 TEST_GROUP="${msg}" 717 718 # skip if we don't want v6 719 if ! $ipv6 && mptcp_lib_is_v6 "${connect_addr}"; then 720 return 0 721 fi 722 723 # IP(V6)_TRANSPARENT has been added after TOS support which came with 724 # the required infrastructure in MPTCP sockopt code. To support TOS, the 725 # following function has been exported (T). Not great but better than 726 # checking for a specific kernel version. 727 if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then 728 echo "INFO: ${msg} not supported by the kernel: SKIP" 729 mptcp_lib_result_skip "${TEST_GROUP}" 730 return 731 fi 732 733 if ! ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF" 734flush ruleset 735table inet mangle { 736 chain divert { 737 type filter hook prerouting priority -150; 738 739 meta l4proto tcp socket transparent 1 meta mark set 1 accept 740 tcp dport 20000 tproxy to :20000 meta mark set 1 accept 741 } 742} 743EOF 744 then 745 echo "SKIP: $msg, could not load nft ruleset" 746 mptcp_lib_fail_if_expected_feature "nft rules" 747 mptcp_lib_result_skip "${TEST_GROUP}" 748 return 749 fi 750 751 local local_addr 752 if mptcp_lib_is_v6 "${connect_addr}"; then 753 local_addr="::" 754 r6flag="-6" 755 else 756 local_addr="0.0.0.0" 757 fi 758 759 if ! ip -net "$listener_ns" $r6flag rule add fwmark 1 lookup 100; then 760 ip netns exec "$listener_ns" nft flush ruleset 761 echo "SKIP: $msg, ip $r6flag rule failed" 762 mptcp_lib_fail_if_expected_feature "ip rule" 763 mptcp_lib_result_skip "${TEST_GROUP}" 764 return 765 fi 766 767 if ! ip -net "$listener_ns" route add local $local_addr/0 dev lo table 100; then 768 ip netns exec "$listener_ns" nft flush ruleset 769 ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100 770 echo "SKIP: $msg, ip route add local $local_addr failed" 771 mptcp_lib_fail_if_expected_feature "ip route" 772 mptcp_lib_result_skip "${TEST_GROUP}" 773 return 774 fi 775 776 echo "INFO: test $msg" 777 778 TEST_COUNT=10000 779 local extra_args="-o TRANSPARENT" 780 do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP \ 781 ${connect_addr} ${local_addr} "${extra_args}" 782 lret=$? 783 784 ip netns exec "$listener_ns" nft flush ruleset 785 ip -net "$listener_ns" $r6flag rule del fwmark 1 lookup 100 786 ip -net "$listener_ns" route del local $local_addr/0 dev lo table 100 787 788 if [ $lret -ne 0 ]; then 789 echo "FAIL: $msg, mptcp connection error" 1>&2 790 ret=$lret 791 return 1 792 fi 793 794 echo "PASS: $msg" 795 return 0 796} 797 798run_tests_peekmode() 799{ 800 local peekmode="$1" 801 802 TEST_GROUP="peek mode: ${peekmode}" 803 echo "INFO: with peek mode: ${peekmode}" 804 run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-P ${peekmode}" 805 run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-P ${peekmode}" 806} 807 808run_tests_mptfo() 809{ 810 TEST_GROUP="MPTFO" 811 812 if ! mptcp_lib_kallsyms_has "mptcp_fastopen_"; then 813 echo "INFO: TFO not supported by the kernel: SKIP" 814 mptcp_lib_result_skip "${TEST_GROUP}" 815 return 816 fi 817 818 echo "INFO: with MPTFO start" 819 ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2 820 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1 821 822 run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" 823 run_tests_lo "$ns1" "$ns2" 10.0.1.1 0 "-o MPTFO" 824 825 run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" 826 run_tests_lo "$ns1" "$ns2" dead:beef:1::1 0 "-o MPTFO" 827 828 ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=0 829 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=0 830 echo "INFO: with MPTFO end" 831} 832 833run_tests_disconnect() 834{ 835 local old_cin=$cin 836 local old_sin=$sin 837 838 TEST_GROUP="full disconnect" 839 840 if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then 841 echo "INFO: Full disconnect not supported: SKIP" 842 mptcp_lib_result_skip "${TEST_GROUP}" 843 return 844 fi 845 846 cat $cin $cin $cin > "$cin".disconnect 847 848 # force do_transfer to cope with the multiple transmissions 849 sin="$cin.disconnect" 850 cin="$cin.disconnect" 851 cin_disconnect="$old_cin" 852 connect_per_transfer=3 853 854 echo "INFO: disconnect" 855 run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-I 3 -i $old_cin" 856 run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-I 3 -i $old_cin" 857 858 # restore previous status 859 sin=$old_sin 860 cin=$old_cin 861 cin_disconnect="$cin".disconnect 862 connect_per_transfer=1 863} 864 865display_time() 866{ 867 time_end=$(date +%s) 868 time_run=$((time_end-time_start)) 869 870 echo "Time: ${time_run} seconds" 871} 872 873log_if_error() 874{ 875 local msg="$1" 876 877 if [ ${ret} -ne 0 ]; then 878 echo "FAIL: ${msg}" 1>&2 879 880 final_ret=${ret} 881 ret=0 882 883 return ${final_ret} 884 fi 885} 886 887stop_if_error() 888{ 889 if ! log_if_error "${@}"; then 890 display_time 891 mptcp_lib_result_print_all_tap 892 exit ${final_ret} 893 fi 894} 895 896make_file "$cin" "client" 897make_file "$sin" "server" 898 899check_mptcp_disabled 900 901stop_if_error "The kernel configuration is not valid for MPTCP" 902 903echo "INFO: validating network environment with pings" 904for sender in "$ns1" "$ns2" "$ns3" "$ns4";do 905 do_ping "$ns1" $sender 10.0.1.1 906 do_ping "$ns1" $sender dead:beef:1::1 907 908 do_ping "$ns2" $sender 10.0.1.2 909 do_ping "$ns2" $sender dead:beef:1::2 910 do_ping "$ns2" $sender 10.0.2.1 911 do_ping "$ns2" $sender dead:beef:2::1 912 913 do_ping "$ns3" $sender 10.0.2.2 914 do_ping "$ns3" $sender dead:beef:2::2 915 do_ping "$ns3" $sender 10.0.3.2 916 do_ping "$ns3" $sender dead:beef:3::2 917 918 do_ping "$ns4" $sender 10.0.3.1 919 do_ping "$ns4" $sender dead:beef:3::1 920done 921 922mptcp_lib_result_code "${ret}" "ping tests" 923 924stop_if_error "Could not even run ping tests" 925 926[ -n "$tc_loss" ] && tc -net "$ns2" qdisc add dev ns2eth3 root netem loss random $tc_loss delay ${tc_delay}ms 927echo -n "INFO: Using loss of $tc_loss " 928test "$tc_delay" -gt 0 && echo -n "delay $tc_delay ms " 929 930reorder_delay=$((tc_delay / 4)) 931 932if [ -z "${tc_reorder}" ]; then 933 reorder1=$((RANDOM%10)) 934 reorder1=$((100 - reorder1)) 935 reorder2=$((RANDOM%100)) 936 937 if [ $reorder_delay -gt 0 ] && [ $reorder1 -lt 100 ] && [ $reorder2 -gt 0 ]; then 938 tc_reorder="reorder ${reorder1}% ${reorder2}%" 939 echo -n "$tc_reorder with delay ${reorder_delay}ms " 940 fi 941elif [ "$tc_reorder" = "0" ];then 942 tc_reorder="" 943elif [ "$reorder_delay" -gt 0 ];then 944 # reordering requires some delay 945 tc_reorder="reorder $tc_reorder" 946 echo -n "$tc_reorder with delay ${reorder_delay}ms " 947fi 948 949echo "on ns3eth4" 950 951tc -net "$ns3" qdisc add dev ns3eth4 root netem delay ${reorder_delay}ms $tc_reorder 952 953TEST_GROUP="loopback v4" 954run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 955stop_if_error "Could not even run loopback test" 956 957TEST_GROUP="loopback v6" 958run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 959stop_if_error "Could not even run loopback v6 test" 960 961TEST_GROUP="multihosts" 962for sender in $ns1 $ns2 $ns3 $ns4;do 963 # ns1<->ns2 is not subject to reordering/tc delays. Use it to test 964 # mptcp syncookie support. 965 if [ $sender = $ns1 ]; then 966 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=2 967 else 968 ip netns exec "$ns2" sysctl -q net.ipv4.tcp_syncookies=1 969 fi 970 971 run_tests "$ns1" $sender 10.0.1.1 972 run_tests "$ns1" $sender dead:beef:1::1 973 974 run_tests "$ns2" $sender 10.0.1.2 975 run_tests "$ns2" $sender dead:beef:1::2 976 run_tests "$ns2" $sender 10.0.2.1 977 run_tests "$ns2" $sender dead:beef:2::1 978 979 run_tests "$ns3" $sender 10.0.2.2 980 run_tests "$ns3" $sender dead:beef:2::2 981 run_tests "$ns3" $sender 10.0.3.2 982 run_tests "$ns3" $sender dead:beef:3::2 983 984 run_tests "$ns4" $sender 10.0.3.1 985 run_tests "$ns4" $sender dead:beef:3::1 986 987 log_if_error "Tests with $sender as a sender have failed" 988done 989 990run_tests_peekmode "saveWithPeek" 991run_tests_peekmode "saveAfterPeek" 992log_if_error "Tests with peek mode have failed" 993 994# MPTFO (MultiPath TCP Fatopen tests) 995run_tests_mptfo 996log_if_error "Tests with MPTFO have failed" 997 998# connect to ns4 ip address, ns2 should intercept/proxy 999run_test_transparent 10.0.3.1 "tproxy ipv4" 1000run_test_transparent dead:beef:3::1 "tproxy ipv6" 1001log_if_error "Tests with tproxy have failed" 1002 1003run_tests_disconnect 1004log_if_error "Tests of the full disconnection have failed" 1005 1006display_time 1007mptcp_lib_result_print_all_tap 1008exit ${final_ret} 1009