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>
2829
2930#include <linux/tcp.h>
3031#include <linux/time_types.h>
32+ #include <linux/sockios.h>
3133
3234extern int optind ;
3335
@@ -68,6 +70,8 @@ static unsigned int cfg_time;
6870static unsigned int cfg_do_w ;
6971static int cfg_wait ;
7072static uint32_t cfg_mark ;
73+ static char * cfg_input ;
74+ static int cfg_repeat = 1 ;
7175
7276struct cfg_cmsg_types {
7377 unsigned int cmsg_enabled :1 ;
@@ -91,22 +95,31 @@ static struct cfg_sockopt_types cfg_sockopt_types;
9195
9296static 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
312325static 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
537556static 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
9741010static 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+
10601126int 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
10831175int 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 ;
0 commit comments