| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| |
| # Double quotes to prevent globbing and word splitting is recommended in new |
| # code but we accept it, especially because there were too many before having |
| # address all other issues detected by shellcheck. |
| #shellcheck disable=SC2086 |
| |
| . "$(dirname "${0}")/mptcp_lib.sh" |
| |
| ret=0 |
| sin="" |
| sout="" |
| cin="" |
| cout="" |
| timeout_poll=30 |
| timeout_test=$((timeout_poll * 2 + 1)) |
| iptables="iptables" |
| ip6tables="ip6tables" |
| |
| ns1="" |
| ns2="" |
| ns_sbox="" |
| |
| add_mark_rules() |
| { |
| local ns=$1 |
| local m=$2 |
| |
| local t |
| for t in ${iptables} ${ip6tables}; do |
| # just to debug: check we have multiple subflows connection requests |
| ip netns exec $ns $t -A OUTPUT -p tcp --syn -m mark --mark $m -j ACCEPT |
| |
| # RST packets might be handled by a internal dummy socket |
| ip netns exec $ns $t -A OUTPUT -p tcp --tcp-flags RST RST -m mark --mark 0 -j ACCEPT |
| |
| ip netns exec $ns $t -A OUTPUT -p tcp -m mark --mark $m -j ACCEPT |
| ip netns exec $ns $t -A OUTPUT -p tcp -m mark --mark 0 -j DROP |
| done |
| } |
| |
| init() |
| { |
| mptcp_lib_ns_init ns1 ns2 ns_sbox |
| |
| local i |
| for i in $(seq 1 4); do |
| ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2" |
| ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i |
| ip -net "$ns1" addr add dead:beef:$i::1/64 dev ns1eth$i nodad |
| ip -net "$ns1" link set ns1eth$i up |
| |
| ip -net "$ns2" addr add 10.0.$i.2/24 dev ns2eth$i |
| ip -net "$ns2" addr add dead:beef:$i::2/64 dev ns2eth$i nodad |
| ip -net "$ns2" link set ns2eth$i up |
| |
| # let $ns2 reach any $ns1 address from any interface |
| ip -net "$ns2" route add default via 10.0.$i.1 dev ns2eth$i metric 10$i |
| |
| ip netns exec $ns1 ./pm_nl_ctl add 10.0.$i.1 flags signal |
| ip netns exec $ns1 ./pm_nl_ctl add dead:beef:$i::1 flags signal |
| |
| ip netns exec $ns2 ./pm_nl_ctl add 10.0.$i.2 flags signal |
| ip netns exec $ns2 ./pm_nl_ctl add dead:beef:$i::2 flags signal |
| done |
| |
| ip netns exec $ns1 ./pm_nl_ctl limits 8 8 |
| ip netns exec $ns2 ./pm_nl_ctl limits 8 8 |
| |
| add_mark_rules $ns1 1 |
| add_mark_rules $ns2 2 |
| } |
| |
| # This function is used in the cleanup trap |
| #shellcheck disable=SC2317 |
| cleanup() |
| { |
| mptcp_lib_ns_exit "${ns1}" "${ns2}" "${ns_sbox}" |
| rm -f "$cin" "$cout" |
| rm -f "$sin" "$sout" |
| } |
| |
| mptcp_lib_check_mptcp |
| mptcp_lib_check_kallsyms |
| mptcp_lib_check_tools ip "${iptables}" "${ip6tables}" |
| |
| check_mark() |
| { |
| local ns=$1 |
| local af=$2 |
| |
| local tables=${iptables} |
| |
| if [ $af -eq 6 ];then |
| tables=${ip6tables} |
| fi |
| |
| local counters values |
| counters=$(ip netns exec $ns $tables -v -L OUTPUT | grep DROP) |
| values=${counters%DROP*} |
| |
| local v |
| for v in $values; do |
| if [ $v -ne 0 ]; then |
| mptcp_lib_pr_fail "got $tables $values in ns $ns," \ |
| "not 0 - not all expected packets marked" |
| ret=${KSFT_FAIL} |
| return 1 |
| fi |
| done |
| |
| return 0 |
| } |
| |
| print_title() |
| { |
| mptcp_lib_print_title "${@}" |
| } |
| |
| do_transfer() |
| { |
| local listener_ns="$1" |
| local connector_ns="$2" |
| local cl_proto="$3" |
| local srv_proto="$4" |
| local connect_addr="$5" |
| |
| local port=12001 |
| |
| :> "$cout" |
| :> "$sout" |
| |
| local mptcp_connect="./mptcp_connect -r 20" |
| |
| local local_addr ip |
| if mptcp_lib_is_v6 "${connect_addr}"; then |
| local_addr="::" |
| ip=ipv6 |
| else |
| local_addr="0.0.0.0" |
| ip=ipv4 |
| fi |
| |
| cmsg="TIMESTAMPNS" |
| if mptcp_lib_kallsyms_has "mptcp_ioctl$"; then |
| cmsg+=",TCPINQ" |
| fi |
| |
| timeout ${timeout_test} \ |
| ip netns exec ${listener_ns} \ |
| $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c "${cmsg}" \ |
| ${local_addr} < "$sin" > "$sout" & |
| local spid=$! |
| |
| sleep 1 |
| |
| timeout ${timeout_test} \ |
| ip netns exec ${connector_ns} \ |
| $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c "${cmsg}" \ |
| $connect_addr < "$cin" > "$cout" & |
| |
| local cpid=$! |
| |
| wait $cpid |
| local retc=$? |
| wait $spid |
| local rets=$? |
| |
| print_title "Transfer ${ip:2}" |
| if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then |
| mptcp_lib_pr_fail "client exit code $retc, server $rets" |
| echo -e "\nnetns ${listener_ns} socket stat for ${port}:" 1>&2 |
| ip netns exec ${listener_ns} ss -Menita 1>&2 -o "sport = :$port" |
| |
| echo -e "\nnetns ${connector_ns} socket stat for ${port}:" 1>&2 |
| ip netns exec ${connector_ns} ss -Menita 1>&2 -o "dport = :$port" |
| |
| mptcp_lib_result_fail "transfer ${ip}" |
| |
| ret=${KSFT_FAIL} |
| return 1 |
| fi |
| if ! mptcp_lib_check_transfer $cin $sout "file received by server"; then |
| rets=1 |
| else |
| mptcp_lib_pr_ok |
| fi |
| mptcp_lib_result_code "${rets}" "transfer ${ip}" |
| |
| print_title "Mark ${ip:2}" |
| if [ $local_addr = "::" ];then |
| check_mark $listener_ns 6 || retc=1 |
| check_mark $connector_ns 6 || retc=1 |
| else |
| check_mark $listener_ns 4 || retc=1 |
| check_mark $connector_ns 4 || retc=1 |
| fi |
| |
| mptcp_lib_result_code "${retc}" "mark ${ip}" |
| |
| if [ $retc -eq 0 ] && [ $rets -eq 0 ];then |
| mptcp_lib_pr_ok |
| return 0 |
| fi |
| mptcp_lib_pr_fail |
| |
| return 1 |
| } |
| |
| make_file() |
| { |
| local name=$1 |
| local who=$2 |
| local size=$3 |
| |
| mptcp_lib_make_file $name 1024 $size |
| |
| echo "Created $name (size $size KB) containing data sent by $who" |
| } |
| |
| do_mptcp_sockopt_tests() |
| { |
| local lret=0 |
| |
| if ! mptcp_lib_kallsyms_has "mptcp_diag_fill_info$"; then |
| mptcp_lib_pr_skip "MPTCP sockopt not supported" |
| mptcp_lib_result_skip "sockopt" |
| return |
| fi |
| |
| ip netns exec "$ns_sbox" ./mptcp_sockopt |
| lret=$? |
| |
| print_title "SOL_MPTCP sockopt v4" |
| if [ $lret -ne 0 ]; then |
| mptcp_lib_pr_fail |
| mptcp_lib_result_fail "sockopt v4" |
| ret=$lret |
| return |
| fi |
| mptcp_lib_pr_ok |
| mptcp_lib_result_pass "sockopt v4" |
| |
| ip netns exec "$ns_sbox" ./mptcp_sockopt -6 |
| lret=$? |
| |
| print_title "SOL_MPTCP sockopt v6" |
| if [ $lret -ne 0 ]; then |
| mptcp_lib_pr_fail |
| mptcp_lib_result_fail "sockopt v6" |
| ret=$lret |
| return |
| fi |
| mptcp_lib_pr_ok |
| mptcp_lib_result_pass "sockopt v6" |
| } |
| |
| run_tests() |
| { |
| local listener_ns="$1" |
| local connector_ns="$2" |
| local connect_addr="$3" |
| local lret=0 |
| |
| do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} |
| |
| lret=$? |
| |
| if [ $lret -ne 0 ]; then |
| ret=$lret |
| return |
| fi |
| } |
| |
| do_tcpinq_test() |
| { |
| print_title "TCP_INQ cmsg/ioctl $*" |
| ip netns exec "$ns_sbox" ./mptcp_inq "$@" |
| local lret=$? |
| if [ $lret -ne 0 ];then |
| ret=$lret |
| mptcp_lib_pr_fail |
| mptcp_lib_result_fail "TCP_INQ: $*" |
| return $lret |
| fi |
| |
| mptcp_lib_pr_ok |
| mptcp_lib_result_pass "TCP_INQ: $*" |
| return $lret |
| } |
| |
| do_tcpinq_tests() |
| { |
| local lret=0 |
| |
| if ! mptcp_lib_kallsyms_has "mptcp_ioctl$"; then |
| mptcp_lib_pr_skip "TCP_INQ not supported" |
| mptcp_lib_result_skip "TCP_INQ" |
| return |
| fi |
| |
| local args |
| for args in "-t tcp" "-r tcp"; do |
| do_tcpinq_test $args |
| lret=$? |
| if [ $lret -ne 0 ] ; then |
| return $lret |
| fi |
| do_tcpinq_test -6 $args |
| lret=$? |
| if [ $lret -ne 0 ] ; then |
| return $lret |
| fi |
| done |
| |
| do_tcpinq_test -r tcp -t tcp |
| |
| return $? |
| } |
| |
| sin=$(mktemp) |
| sout=$(mktemp) |
| cin=$(mktemp) |
| cout=$(mktemp) |
| init |
| make_file "$cin" "client" 1 |
| make_file "$sin" "server" 1 |
| trap cleanup EXIT |
| |
| run_tests $ns1 $ns2 10.0.1.1 |
| run_tests $ns1 $ns2 dead:beef:1::1 |
| |
| do_mptcp_sockopt_tests |
| do_tcpinq_tests |
| |
| mptcp_lib_result_print_all_tap |
| exit $ret |