Commit 6bf41020 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller

selftests: mptcp: update and extend fastclose test-cases

After the previous patches, the MPTCP protocol can generate
fast-closes on both ends of the connection. Rework the relevant
test-case to carefully trigger the fast-close code-path on a
single end at the time, while ensuring than a predictable amount
of data is spooled on both ends.

Additionally add another test-cases for the passive socket
fast-close.
Reviewed-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Reviewed-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d21f8348
...@@ -72,6 +72,8 @@ static int cfg_wait; ...@@ -72,6 +72,8 @@ static int cfg_wait;
static uint32_t cfg_mark; static uint32_t cfg_mark;
static char *cfg_input; static char *cfg_input;
static int cfg_repeat = 1; static int cfg_repeat = 1;
static int cfg_truncate;
static int cfg_rcv_trunc;
struct cfg_cmsg_types { struct cfg_cmsg_types {
unsigned int cmsg_enabled:1; unsigned int cmsg_enabled:1;
...@@ -95,11 +97,15 @@ static struct cfg_sockopt_types cfg_sockopt_types; ...@@ -95,11 +97,15 @@ static struct cfg_sockopt_types cfg_sockopt_types;
static void die_usage(void) static void die_usage(void)
{ {
fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-i file] [-I num] [-j] [-l] " fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-f offset] [-i file] [-I num] [-j] [-l] "
"[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] " "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] "
"[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n"); "[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n");
fprintf(stderr, "\t-6 use ipv6\n"); fprintf(stderr, "\t-6 use ipv6\n");
fprintf(stderr, "\t-c cmsg -- test cmsg type <cmsg>\n"); fprintf(stderr, "\t-c cmsg -- test cmsg type <cmsg>\n");
fprintf(stderr, "\t-f offset -- stop the I/O after receiving and sending the specified amount "
"of bytes. If there are unread bytes in the receive queue, that will cause a MPTCP "
"fastclose at close/shutdown. If offset is negative, expect the peer to close before "
"all the local data as been sent, thus toleration errors on write and EPIPE signals\n");
fprintf(stderr, "\t-i file -- read the data to send from the given file instead of stdin"); fprintf(stderr, "\t-i file -- read the data to send from the given file instead of stdin");
fprintf(stderr, "\t-I num -- repeat the transfer 'num' times. In listen mode accepts num " fprintf(stderr, "\t-I num -- repeat the transfer 'num' times. In listen mode accepts num "
"incoming connections, in client mode, disconnect and reconnect to the server\n"); "incoming connections, in client mode, disconnect and reconnect to the server\n");
...@@ -382,7 +388,7 @@ static size_t do_rnd_write(const int fd, char *buf, const size_t len) ...@@ -382,7 +388,7 @@ static size_t do_rnd_write(const int fd, char *buf, const size_t len)
bw = write(fd, buf, do_w); bw = write(fd, buf, do_w);
if (bw < 0) if (bw < 0)
perror("write"); return bw;
/* let the join handshake complete, before going on */ /* let the join handshake complete, before going on */
if (cfg_join && first) { if (cfg_join && first) {
...@@ -571,7 +577,7 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after ...@@ -571,7 +577,7 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
.fd = peerfd, .fd = peerfd,
.events = POLLIN | POLLOUT, .events = POLLIN | POLLOUT,
}; };
unsigned int woff = 0, wlen = 0; unsigned int woff = 0, wlen = 0, total_wlen = 0, total_rlen = 0;
char wbuf[8192]; char wbuf[8192];
set_nonblock(peerfd, true); set_nonblock(peerfd, true);
...@@ -597,7 +603,16 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after ...@@ -597,7 +603,16 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
} }
if (fds.revents & POLLIN) { if (fds.revents & POLLIN) {
ssize_t rb = sizeof(rbuf);
/* limit the total amount of read data to the trunc value*/
if (cfg_truncate > 0) {
if (rb + total_rlen > cfg_truncate)
rb = cfg_truncate - total_rlen;
len = read(peerfd, rbuf, rb);
} else {
len = do_rnd_read(peerfd, rbuf, sizeof(rbuf)); len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
}
if (len == 0) { if (len == 0) {
/* no more data to receive: /* no more data to receive:
* peer has closed its write side * peer has closed its write side
...@@ -612,10 +627,13 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after ...@@ -612,10 +627,13 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
/* Else, still have data to transmit */ /* Else, still have data to transmit */
} else if (len < 0) { } else if (len < 0) {
if (cfg_rcv_trunc)
return 0;
perror("read"); perror("read");
return 3; return 3;
} }
total_rlen += len;
do_write(outfd, rbuf, len); do_write(outfd, rbuf, len);
} }
...@@ -628,12 +646,21 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after ...@@ -628,12 +646,21 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
if (wlen > 0) { if (wlen > 0) {
ssize_t bw; ssize_t bw;
/* limit the total amount of written data to the trunc value */
if (cfg_truncate > 0 && wlen + total_wlen > cfg_truncate)
wlen = cfg_truncate - total_wlen;
bw = do_rnd_write(peerfd, wbuf + woff, wlen); bw = do_rnd_write(peerfd, wbuf + woff, wlen);
if (bw < 0) if (bw < 0) {
if (cfg_rcv_trunc)
return 0;
perror("write");
return 111; return 111;
}
woff += bw; woff += bw;
wlen -= bw; wlen -= bw;
total_wlen += bw;
} else if (wlen == 0) { } else if (wlen == 0) {
/* We have no more data to send. */ /* We have no more data to send. */
fds.events &= ~POLLOUT; fds.events &= ~POLLOUT;
...@@ -652,10 +679,16 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after ...@@ -652,10 +679,16 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
} }
if (fds.revents & (POLLERR | POLLNVAL)) { if (fds.revents & (POLLERR | POLLNVAL)) {
if (cfg_rcv_trunc)
return 0;
fprintf(stderr, "Unexpected revents: " fprintf(stderr, "Unexpected revents: "
"POLLERR/POLLNVAL(%x)\n", fds.revents); "POLLERR/POLLNVAL(%x)\n", fds.revents);
return 5; return 5;
} }
if (cfg_truncate > 0 && total_wlen >= cfg_truncate &&
total_rlen >= cfg_truncate)
break;
} }
/* leave some time for late join/announce */ /* leave some time for late join/announce */
...@@ -1160,11 +1193,13 @@ int main_loop(void) ...@@ -1160,11 +1193,13 @@ int main_loop(void)
} }
/* close the client socket open only if we are not going to reconnect */ /* close the client socket open only if we are not going to reconnect */
ret = copyfd_io(fd_in, fd, 1, cfg_repeat == 1); ret = copyfd_io(fd_in, fd, 1, 0);
if (ret) if (ret)
return ret; return ret;
if (--cfg_repeat > 0) { if (cfg_truncate > 0) {
xdisconnect(fd, peer->ai_addrlen);
} else if (--cfg_repeat > 0) {
xdisconnect(fd, peer->ai_addrlen); xdisconnect(fd, peer->ai_addrlen);
/* the socket could be unblocking at this point, we need the /* the socket could be unblocking at this point, we need the
...@@ -1176,7 +1211,10 @@ int main_loop(void) ...@@ -1176,7 +1211,10 @@ int main_loop(void)
if (cfg_input) if (cfg_input)
close(fd_in); close(fd_in);
goto again; goto again;
} else {
close(fd);
} }
return 0; return 0;
} }
...@@ -1262,8 +1300,19 @@ static void parse_opts(int argc, char **argv) ...@@ -1262,8 +1300,19 @@ static void parse_opts(int argc, char **argv)
{ {
int c; int c;
while ((c = getopt(argc, argv, "6c:hi:I:jlm:M:o:p:P:r:R:s:S:t:T:w:")) != -1) { while ((c = getopt(argc, argv, "6c:f:hi:I:jlm:M:o:p:P:r:R:s:S:t:T:w:")) != -1) {
switch (c) { switch (c) {
case 'f':
cfg_truncate = atoi(optarg);
/* when receiving a fastclose, ignore PIPE signals and
* all the I/O errors later in the code
*/
if (cfg_truncate < 0) {
cfg_rcv_trunc = true;
signal(SIGPIPE, handle_signal);
}
break;
case 'j': case 'j':
cfg_join = true; cfg_join = true;
cfg_mode = CFG_MODE_POLL; cfg_mode = CFG_MODE_POLL;
......
...@@ -346,10 +346,21 @@ check_transfer() ...@@ -346,10 +346,21 @@ check_transfer()
local in=$1 local in=$1
local out=$2 local out=$2
local what=$3 local what=$3
local bytes=$4
local i a b local i a b
local line local line
cmp -l "$in" "$out" | while read -r i a b; do if [ -n "$bytes" ]; then
# when truncating we must check the size explicitly
local out_size=$(wc -c $out | awk '{print $1}')
if [ $out_size -ne $bytes ]; then
echo "[ FAIL ] $what output file has wrong size ($out_size, $bytes)"
fail_test
return 1
fi
bytes="--bytes=${bytes}"
fi
cmp -l "$in" "$out" ${bytes} | while read -r i a b; do
local sum=$((0${a} + 0${b})) local sum=$((0${a} + 0${b}))
if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then
echo "[ FAIL ] $what does not match (in, out):" echo "[ FAIL ] $what does not match (in, out):"
...@@ -707,9 +718,31 @@ do_transfer() ...@@ -707,9 +718,31 @@ do_transfer()
fi fi
local flags="subflow" local flags="subflow"
local extra_cl_args=""
local extra_srv_args=""
local trunc_size=""
if [[ "${addr_nr_ns2}" = "fastclose_"* ]]; then if [[ "${addr_nr_ns2}" = "fastclose_"* ]]; then
if [ ${test_link_fail} -le 1 ]; then
echo "fastclose tests need test_link_fail argument"
fail_test
return 1
fi
# disconnect # disconnect
extra_args="$extra_args -I ${addr_nr_ns2:10}" trunc_size=${test_link_fail}
local side=${addr_nr_ns2:10}
if [ ${side} = "client" ]; then
extra_cl_args="-f ${test_link_fail}"
extra_srv_args="-f -1"
elif [ ${side} = "server" ]; then
extra_srv_args="-f ${test_link_fail}"
extra_cl_args="-f -1"
else
echo "wrong/unknown fastclose spec ${side}"
fail_test
return 1
fi
addr_nr_ns2=0 addr_nr_ns2=0
elif [[ "${addr_nr_ns2}" = "userspace_"* ]]; then elif [[ "${addr_nr_ns2}" = "userspace_"* ]]; then
userspace_pm=1 userspace_pm=1
...@@ -737,39 +770,41 @@ do_transfer() ...@@ -737,39 +770,41 @@ do_transfer()
local_addr="0.0.0.0" local_addr="0.0.0.0"
fi fi
extra_srv_args="$extra_args $extra_srv_args"
if [ "$test_link_fail" -gt 1 ];then if [ "$test_link_fail" -gt 1 ];then
timeout ${timeout_test} \ timeout ${timeout_test} \
ip netns exec ${listener_ns} \ ip netns exec ${listener_ns} \
./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ ./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \
$extra_args ${local_addr} < "$sinfail" > "$sout" & $extra_srv_args ${local_addr} < "$sinfail" > "$sout" &
else else
timeout ${timeout_test} \ timeout ${timeout_test} \
ip netns exec ${listener_ns} \ ip netns exec ${listener_ns} \
./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \ ./mptcp_connect -t ${timeout_poll} -l -p $port -s ${srv_proto} \
$extra_args ${local_addr} < "$sin" > "$sout" & $extra_srv_args ${local_addr} < "$sin" > "$sout" &
fi fi
local spid=$! local spid=$!
wait_local_port_listen "${listener_ns}" "${port}" wait_local_port_listen "${listener_ns}" "${port}"
extra_cl_args="$extra_args $extra_cl_args"
if [ "$test_link_fail" -eq 0 ];then if [ "$test_link_fail" -eq 0 ];then
timeout ${timeout_test} \ timeout ${timeout_test} \
ip netns exec ${connector_ns} \ ip netns exec ${connector_ns} \
./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \
$extra_args $connect_addr < "$cin" > "$cout" & $extra_cl_args $connect_addr < "$cin" > "$cout" &
elif [ "$test_link_fail" -eq 1 ] || [ "$test_link_fail" -eq 2 ];then elif [ "$test_link_fail" -eq 1 ] || [ "$test_link_fail" -eq 2 ];then
( cat "$cinfail" ; sleep 2; link_failure $listener_ns ; cat "$cinfail" ) | \ ( cat "$cinfail" ; sleep 2; link_failure $listener_ns ; cat "$cinfail" ) | \
tee "$cinsent" | \ tee "$cinsent" | \
timeout ${timeout_test} \ timeout ${timeout_test} \
ip netns exec ${connector_ns} \ ip netns exec ${connector_ns} \
./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \
$extra_args $connect_addr > "$cout" & $extra_cl_args $connect_addr > "$cout" &
else else
tee "$cinsent" < "$cinfail" | \ tee "$cinsent" < "$cinfail" | \
timeout ${timeout_test} \ timeout ${timeout_test} \
ip netns exec ${connector_ns} \ ip netns exec ${connector_ns} \
./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \
$extra_args $connect_addr > "$cout" & $extra_cl_args $connect_addr > "$cout" &
fi fi
local cpid=$! local cpid=$!
...@@ -971,15 +1006,15 @@ do_transfer() ...@@ -971,15 +1006,15 @@ do_transfer()
fi fi
if [ "$test_link_fail" -gt 1 ];then if [ "$test_link_fail" -gt 1 ];then
check_transfer $sinfail $cout "file received by client" check_transfer $sinfail $cout "file received by client" $trunc_size
else else
check_transfer $sin $cout "file received by client" check_transfer $sin $cout "file received by client" $trunc_size
fi fi
retc=$? retc=$?
if [ "$test_link_fail" -eq 0 ];then if [ "$test_link_fail" -eq 0 ];then
check_transfer $cin $sout "file received by server" check_transfer $cin $sout "file received by server" $trunc_size
else else
check_transfer $cinsent $sout "file received by server" check_transfer $cinsent $sout "file received by server" $trunc_size
fi fi
rets=$? rets=$?
...@@ -1188,12 +1223,23 @@ chk_fclose_nr() ...@@ -1188,12 +1223,23 @@ chk_fclose_nr()
{ {
local fclose_tx=$1 local fclose_tx=$1
local fclose_rx=$2 local fclose_rx=$2
local ns_invert=$3
local count local count
local dump_stats local dump_stats
local ns_tx=$ns2
local ns_rx=$ns1
local extra_msg=" "
if [[ $ns_invert = "invert" ]]; then
ns_tx=$ns1
ns_rx=$ns2
extra_msg=${extra_msg}"invert"
fi
printf "%-${nr_blank}s %s" " " "ctx" printf "%-${nr_blank}s %s" " " "ctx"
count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}') count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}')
[ -z "$count" ] && count=0 [ -z "$count" ] && count=0
[ "$count" != "$fclose_tx" ] && extra_msg="$extra_msg,tx=$count"
if [ "$count" != "$fclose_tx" ]; then if [ "$count" != "$fclose_tx" ]; then
echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx" echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx"
fail_test fail_test
...@@ -1203,17 +1249,20 @@ chk_fclose_nr() ...@@ -1203,17 +1249,20 @@ chk_fclose_nr()
fi fi
echo -n " - fclzrx" echo -n " - fclzrx"
count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}') count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}')
[ -z "$count" ] && count=0 [ -z "$count" ] && count=0
[ "$count" != "$fclose_rx" ] && extra_msg="$extra_msg,rx=$count"
if [ "$count" != "$fclose_rx" ]; then if [ "$count" != "$fclose_rx" ]; then
echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx" echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx"
fail_test fail_test
dump_stats=1 dump_stats=1
else else
echo "[ ok ]" echo -n "[ ok ]"
fi fi
[ "${dump_stats}" = 1 ] && dump_stats [ "${dump_stats}" = 1 ] && dump_stats
echo "$extra_msg"
} }
chk_rst_nr() chk_rst_nr()
...@@ -1236,7 +1285,7 @@ chk_rst_nr() ...@@ -1236,7 +1285,7 @@ chk_rst_nr()
printf "%-${nr_blank}s %s" " " "rtx" printf "%-${nr_blank}s %s" " " "rtx"
count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}') count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}')
[ -z "$count" ] && count=0 [ -z "$count" ] && count=0
if [ "$count" != "$rst_tx" ]; then if [ $count -lt $rst_tx ]; then
echo "[fail] got $count MP_RST[s] TX expected $rst_tx" echo "[fail] got $count MP_RST[s] TX expected $rst_tx"
fail_test fail_test
dump_stats=1 dump_stats=1
...@@ -1247,7 +1296,7 @@ chk_rst_nr() ...@@ -1247,7 +1296,7 @@ chk_rst_nr()
echo -n " - rstrx " echo -n " - rstrx "
count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}') count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}')
[ -z "$count" ] && count=0 [ -z "$count" ] && count=0
if [ "$count" != "$rst_rx" ]; then if [ "$count" -lt "$rst_rx" ]; then
echo "[fail] got $count MP_RST[s] RX expected $rst_rx" echo "[fail] got $count MP_RST[s] RX expected $rst_rx"
fail_test fail_test
dump_stats=1 dump_stats=1
...@@ -2801,11 +2850,18 @@ fullmesh_tests() ...@@ -2801,11 +2850,18 @@ fullmesh_tests()
fastclose_tests() fastclose_tests()
{ {
if reset "fastclose test"; then if reset "fastclose test"; then
run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_2 run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_client
chk_join_nr 0 0 0 chk_join_nr 0 0 0
chk_fclose_nr 1 1 chk_fclose_nr 1 1
chk_rst_nr 1 1 invert chk_rst_nr 1 1 invert
fi fi
if reset "fastclose server test"; then
run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server
chk_join_nr 0 0 0
chk_fclose_nr 1 1 invert
chk_rst_nr 1 1
fi
} }
pedit_action_pkts() pedit_action_pkts()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment