Skip to content

Commit 05be5e2

Browse files
Paolo Abenidavem330
authored andcommitted
selftests: mptcp: add disconnect tests
Performs several disconnect/reconnect on the same socket, ensuring the overall transfer is succesful. The new test leverages ioctl(SIOCOUTQ) to ensure all the pending data is acked before disconnecting. Additionally order alphabetically the test program arguments list for better maintainability. Signed-off-by: Paolo Abeni <[email protected]> Signed-off-by: Mat Martineau <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3d1d6d6 commit 05be5e2

File tree

2 files changed

+160
-27
lines changed

2 files changed

+160
-27
lines changed

tools/testing/selftests/net/mptcp/mptcp_connect.c

Lines changed: 123 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <unistd.h>
1717
#include <time.h>
1818

19+
#include <sys/ioctl.h>
1920
#include <sys/poll.h>
2021
#include <sys/sendfile.h>
2122
#include <sys/stat.h>
@@ -28,6 +29,7 @@
2829

2930
#include <linux/tcp.h>
3031
#include <linux/time_types.h>
32+
#include <linux/sockios.h>
3133

3234
extern int optind;
3335

@@ -68,6 +70,8 @@ static unsigned int cfg_time;
6870
static unsigned int cfg_do_w;
6971
static int cfg_wait;
7072
static uint32_t cfg_mark;
73+
static char *cfg_input;
74+
static int cfg_repeat = 1;
7175

7276
struct cfg_cmsg_types {
7377
unsigned int cmsg_enabled:1;
@@ -91,22 +95,31 @@ static struct cfg_sockopt_types cfg_sockopt_types;
9195

9296
static void die_usage(void)
9397
{
94-
fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
95-
"[-l] [-w sec] [-t num] [-T num] connect_address\n");
98+
fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-i file] [-I num] [-j] [-l] "
99+
"[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] "
100+
"[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n");
96101
fprintf(stderr, "\t-6 use ipv6\n");
97-
fprintf(stderr, "\t-t num -- set poll timeout to num\n");
98-
fprintf(stderr, "\t-T num -- set expected runtime to num ms\n");
99-
fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
100-
fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
101-
fprintf(stderr, "\t-p num -- use port num\n");
102-
fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
102+
fprintf(stderr, "\t-c cmsg -- test cmsg type <cmsg>\n");
103+
fprintf(stderr, "\t-i file -- read the data to send from the given file instead of stdin");
104+
fprintf(stderr, "\t-I num -- repeat the transfer 'num' times. In listen mode accepts num "
105+
"incoming connections, in client mode, disconnect and reconnect to the server\n");
106+
fprintf(stderr, "\t-j -- add additional sleep at connection start and tear down "
107+
"-- for MPJ tests\n");
108+
fprintf(stderr, "\t-l -- listens mode, accepts incoming connection\n");
103109
fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
104110
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
105-
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
106-
fprintf(stderr, "\t-c cmsg -- test cmsg type <cmsg>\n");
107111
fprintf(stderr, "\t-o option -- test sockopt <option>\n");
112+
fprintf(stderr, "\t-p num -- use port num\n");
108113
fprintf(stderr,
109114
"\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n");
115+
fprintf(stderr, "\t-t num -- set poll timeout to num\n");
116+
fprintf(stderr, "\t-T num -- set expected runtime to num ms\n");
117+
fprintf(stderr, "\t-r num -- enable slow mode, limiting each write to num bytes "
118+
"-- for remove addr tests\n");
119+
fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
120+
fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
121+
fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
122+
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
110123
exit(1);
111124
}
112125

@@ -310,7 +323,8 @@ static int sock_listen_mptcp(const char * const listenaddr,
310323
}
311324

312325
static int sock_connect_mptcp(const char * const remoteaddr,
313-
const char * const port, int proto)
326+
const char * const port, int proto,
327+
struct addrinfo **peer)
314328
{
315329
struct addrinfo hints = {
316330
.ai_protocol = IPPROTO_TCP,
@@ -334,8 +348,10 @@ static int sock_connect_mptcp(const char * const remoteaddr,
334348
if (cfg_mark)
335349
set_mark(sock, cfg_mark);
336350

337-
if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
351+
if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) {
352+
*peer = a;
338353
break; /* success */
354+
}
339355

340356
perror("connect()");
341357
close(sock);
@@ -524,14 +540,17 @@ static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
524540
return ret;
525541
}
526542

527-
static void set_nonblock(int fd)
543+
static void set_nonblock(int fd, bool nonblock)
528544
{
529545
int flags = fcntl(fd, F_GETFL);
530546

531547
if (flags == -1)
532548
return;
533549

534-
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
550+
if (nonblock)
551+
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
552+
else
553+
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
535554
}
536555

537556
static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after_out)
@@ -543,7 +562,7 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
543562
unsigned int woff = 0, wlen = 0;
544563
char wbuf[8192];
545564

546-
set_nonblock(peerfd);
565+
set_nonblock(peerfd, true);
547566

548567
for (;;) {
549568
char rbuf[8192];
@@ -638,7 +657,6 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after
638657
if (cfg_remove)
639658
usleep(cfg_wait);
640659

641-
close(peerfd);
642660
return 0;
643661
}
644662

@@ -780,7 +798,7 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd,
780798
return err;
781799
}
782800

783-
static int copyfd_io(int infd, int peerfd, int outfd)
801+
static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd)
784802
{
785803
bool in_closed_after_out = false;
786804
struct timespec start, end;
@@ -819,6 +837,9 @@ static int copyfd_io(int infd, int peerfd, int outfd)
819837
if (ret)
820838
return ret;
821839

840+
if (close_peerfd)
841+
close(peerfd);
842+
822843
if (cfg_time) {
823844
unsigned int delta_ms;
824845

@@ -930,7 +951,7 @@ static void maybe_close(int fd)
930951
{
931952
unsigned int r = rand();
932953

933-
if (!(cfg_join || cfg_remove) && (r & 1))
954+
if (!(cfg_join || cfg_remove || cfg_repeat > 1) && (r & 1))
934955
close(fd);
935956
}
936957

@@ -940,7 +961,9 @@ int main_loop_s(int listensock)
940961
struct pollfd polls;
941962
socklen_t salen;
942963
int remotesock;
964+
int fd = 0;
943965

966+
again:
944967
polls.fd = listensock;
945968
polls.events = POLLIN;
946969

@@ -961,14 +984,27 @@ int main_loop_s(int listensock)
961984
check_sockaddr(pf, &ss, salen);
962985
check_getpeername(remotesock, &ss, salen);
963986

987+
if (cfg_input) {
988+
fd = open(cfg_input, O_RDONLY);
989+
if (fd < 0)
990+
xerror("can't open %s: %d", cfg_input, errno);
991+
}
992+
964993
SOCK_TEST_TCPULP(remotesock, 0);
965994

966-
return copyfd_io(0, remotesock, 1);
995+
copyfd_io(fd, remotesock, 1, true);
996+
} else {
997+
perror("accept");
998+
return 1;
967999
}
9681000

969-
perror("accept");
1001+
if (--cfg_repeat > 0) {
1002+
if (cfg_input)
1003+
close(fd);
1004+
goto again;
1005+
}
9701006

971-
return 1;
1007+
return 0;
9721008
}
9731009

9741010
static void init_rng(void)
@@ -1057,15 +1093,47 @@ static void parse_setsock_options(const char *name)
10571093
exit(1);
10581094
}
10591095

1096+
void xdisconnect(int fd, int addrlen)
1097+
{
1098+
struct sockaddr_storage empty;
1099+
int msec_sleep = 10;
1100+
int queued = 1;
1101+
int i;
1102+
1103+
shutdown(fd, SHUT_WR);
1104+
1105+
/* while until the pending data is completely flushed, the later
1106+
* disconnect will bypass/ignore/drop any pending data.
1107+
*/
1108+
for (i = 0; ; i += msec_sleep) {
1109+
if (ioctl(fd, SIOCOUTQ, &queued) < 0)
1110+
xerror("can't query out socket queue: %d", errno);
1111+
1112+
if (!queued)
1113+
break;
1114+
1115+
if (i > poll_timeout)
1116+
xerror("timeout while waiting for spool to complete");
1117+
usleep(msec_sleep * 1000);
1118+
}
1119+
1120+
memset(&empty, 0, sizeof(empty));
1121+
empty.ss_family = AF_UNSPEC;
1122+
if (connect(fd, (struct sockaddr *)&empty, addrlen) < 0)
1123+
xerror("can't disconnect: %d", errno);
1124+
}
1125+
10601126
int main_loop(void)
10611127
{
1062-
int fd;
1128+
int fd, ret, fd_in = 0;
1129+
struct addrinfo *peer;
10631130

10641131
/* listener is ready. */
1065-
fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
1132+
fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto, &peer);
10661133
if (fd < 0)
10671134
return 2;
10681135

1136+
again:
10691137
check_getpeername_connect(fd);
10701138

10711139
SOCK_TEST_TCPULP(fd, cfg_sock_proto);
@@ -1077,7 +1145,31 @@ int main_loop(void)
10771145
if (cfg_cmsg_types.cmsg_enabled)
10781146
apply_cmsg_types(fd, &cfg_cmsg_types);
10791147

1080-
return copyfd_io(0, fd, 1);
1148+
if (cfg_input) {
1149+
fd_in = open(cfg_input, O_RDONLY);
1150+
if (fd < 0)
1151+
xerror("can't open %s:%d", cfg_input, errno);
1152+
}
1153+
1154+
/* close the client socket open only if we are not going to reconnect */
1155+
ret = copyfd_io(fd_in, fd, 1, cfg_repeat == 1);
1156+
if (ret)
1157+
return ret;
1158+
1159+
if (--cfg_repeat > 0) {
1160+
xdisconnect(fd, peer->ai_addrlen);
1161+
1162+
/* the socket could be unblocking at this point, we need the
1163+
* connect to be blocking
1164+
*/
1165+
set_nonblock(fd, false);
1166+
if (connect(fd, peer->ai_addr, peer->ai_addrlen))
1167+
xerror("can't reconnect: %d", errno);
1168+
if (cfg_input)
1169+
close(fd_in);
1170+
goto again;
1171+
}
1172+
return 0;
10811173
}
10821174

10831175
int parse_proto(const char *proto)
@@ -1162,7 +1254,7 @@ static void parse_opts(int argc, char **argv)
11621254
{
11631255
int c;
11641256

1165-
while ((c = getopt(argc, argv, "6jr:lp:s:ht:T:m:S:R:w:M:P:c:o:")) != -1) {
1257+
while ((c = getopt(argc, argv, "6c:hi:I:jlm:M:o:p:P:r:R:s:S:t:T:w:")) != -1) {
11661258
switch (c) {
11671259
case 'j':
11681260
cfg_join = true;
@@ -1176,6 +1268,12 @@ static void parse_opts(int argc, char **argv)
11761268
if (cfg_do_w <= 0)
11771269
cfg_do_w = 50;
11781270
break;
1271+
case 'i':
1272+
cfg_input = optarg;
1273+
break;
1274+
case 'I':
1275+
cfg_repeat = atoi(optarg);
1276+
break;
11791277
case 'l':
11801278
listen_mode = true;
11811279
break;

tools/testing/selftests/net/mptcp/mptcp_connect.sh

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ optstring="S:R:d:e:l:r:h4cm:f:tC"
77
ret=0
88
sin=""
99
sout=""
10+
cin_disconnect=""
1011
cin=""
1112
cout=""
1213
ksft_skip=4
@@ -24,6 +25,7 @@ options_log=true
2425
do_tcp=0
2526
checksum=false
2627
filesize=0
28+
connect_per_transfer=1
2729

2830
if [ $tc_loss -eq 100 ];then
2931
tc_loss=1%
@@ -127,6 +129,7 @@ TEST_COUNT=0
127129

128130
cleanup()
129131
{
132+
rm -f "$cin_disconnect" "$cout_disconnect"
130133
rm -f "$cin" "$cout"
131134
rm -f "$sin" "$sout"
132135
rm -f "$capout"
@@ -149,6 +152,8 @@ sout=$(mktemp)
149152
cin=$(mktemp)
150153
cout=$(mktemp)
151154
capout=$(mktemp)
155+
cin_disconnect="$cin".disconnect
156+
cout_disconnect="$cout".disconnect
152157
trap cleanup EXIT
153158

154159
for i in "$ns1" "$ns2" "$ns3" "$ns4";do
@@ -500,8 +505,8 @@ do_transfer()
500505
cookies=${cookies##*=}
501506

502507
if [ ${cl_proto} = "MPTCP" ] && [ ${srv_proto} = "MPTCP" ]; then
503-
expect_synrx=$((stat_synrx_last_l+1))
504-
expect_ackrx=$((stat_ackrx_last_l+1))
508+
expect_synrx=$((stat_synrx_last_l+$connect_per_transfer))
509+
expect_ackrx=$((stat_ackrx_last_l+$connect_per_transfer))
505510
fi
506511

507512
if [ ${stat_synrx_now_l} -lt ${expect_synrx} ]; then
@@ -738,6 +743,33 @@ run_tests_peekmode()
738743
run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-P ${peekmode}"
739744
}
740745

746+
run_tests_disconnect()
747+
{
748+
local peekmode="$1"
749+
local old_cin=$cin
750+
local old_sin=$sin
751+
752+
cat $cin $cin $cin > "$cin".disconnect
753+
754+
# force do_transfer to cope with the multiple tranmissions
755+
sin="$cin.disconnect"
756+
sin_disconnect=$old_sin
757+
cin="$cin.disconnect"
758+
cin_disconnect="$old_cin"
759+
connect_per_transfer=3
760+
761+
echo "INFO: disconnect"
762+
run_tests_lo "$ns1" "$ns1" 10.0.1.1 1 "-I 3 -i $old_cin"
763+
run_tests_lo "$ns1" "$ns1" dead:beef:1::1 1 "-I 3 -i $old_cin"
764+
765+
# restore previous status
766+
cout=$old_cout
767+
cout_disconnect="$cout".disconnect
768+
cin=$old_cin
769+
cin_disconnect="$cin".disconnect
770+
connect_per_transfer=1
771+
}
772+
741773
display_time()
742774
{
743775
time_end=$(date +%s)
@@ -853,6 +885,9 @@ stop_if_error "Tests with peek mode have failed"
853885
# connect to ns4 ip address, ns2 should intercept/proxy
854886
run_test_transparent 10.0.3.1 "tproxy ipv4"
855887
run_test_transparent dead:beef:3::1 "tproxy ipv6"
888+
stop_if_error "Tests with tproxy have failed"
889+
890+
run_tests_disconnect
856891

857892
display_time
858893
exit $ret

0 commit comments

Comments
 (0)