1 // SPDX-License-Identifier: GPL-2.0
2
3 #define _GNU_SOURCE
4
5 #include <errno.h>
6 #include <limits.h>
7 #include <fcntl.h>
8 #include <string.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <strings.h>
14 #include <signal.h>
15 #include <unistd.h>
16
17 #include <sys/poll.h>
18 #include <sys/sendfile.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <sys/mman.h>
23
24 #include <netdb.h>
25 #include <netinet/in.h>
26
27 #include <linux/tcp.h>
28
29 extern int optind;
30
31 #ifndef IPPROTO_MPTCP
32 #define IPPROTO_MPTCP 262
33 #endif
34 #ifndef TCP_ULP
35 #define TCP_ULP 31
36 #endif
37
38 static int poll_timeout = 10 * 1000;
39 static bool listen_mode;
40 static bool quit;
41
42 enum cfg_mode {
43 CFG_MODE_POLL,
44 CFG_MODE_MMAP,
45 CFG_MODE_SENDFILE,
46 };
47
48 static enum cfg_mode cfg_mode = CFG_MODE_POLL;
49 static const char *cfg_host;
50 static const char *cfg_port = "12000";
51 static int cfg_sock_proto = IPPROTO_MPTCP;
52 static bool tcpulp_audit;
53 static int pf = AF_INET;
54 static int cfg_sndbuf;
55 static int cfg_rcvbuf;
56 static bool cfg_join;
57 static bool cfg_remove;
58 static int cfg_wait;
59
die_usage(void)60 static void die_usage(void)
61 {
62 fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
63 "[-l] [-w sec] connect_address\n");
64 fprintf(stderr, "\t-6 use ipv6\n");
65 fprintf(stderr, "\t-t num -- set poll timeout to num\n");
66 fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
67 fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
68 fprintf(stderr, "\t-p num -- use port num\n");
69 fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
70 fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
71 fprintf(stderr, "\t-u -- check mptcp ulp\n");
72 fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
73 exit(1);
74 }
75
handle_signal(int nr)76 static void handle_signal(int nr)
77 {
78 quit = true;
79 }
80
getxinfo_strerr(int err)81 static const char *getxinfo_strerr(int err)
82 {
83 if (err == EAI_SYSTEM)
84 return strerror(errno);
85
86 return gai_strerror(err);
87 }
88
xgetnameinfo(const struct sockaddr * addr,socklen_t addrlen,char * host,socklen_t hostlen,char * serv,socklen_t servlen)89 static void xgetnameinfo(const struct sockaddr *addr, socklen_t addrlen,
90 char *host, socklen_t hostlen,
91 char *serv, socklen_t servlen)
92 {
93 int flags = NI_NUMERICHOST | NI_NUMERICSERV;
94 int err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen,
95 flags);
96
97 if (err) {
98 const char *errstr = getxinfo_strerr(err);
99
100 fprintf(stderr, "Fatal: getnameinfo: %s\n", errstr);
101 exit(1);
102 }
103 }
104
xgetaddrinfo(const char * node,const char * service,const struct addrinfo * hints,struct addrinfo ** res)105 static void xgetaddrinfo(const char *node, const char *service,
106 const struct addrinfo *hints,
107 struct addrinfo **res)
108 {
109 int err = getaddrinfo(node, service, hints, res);
110
111 if (err) {
112 const char *errstr = getxinfo_strerr(err);
113
114 fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
115 node ? node : "", service ? service : "", errstr);
116 exit(1);
117 }
118 }
119
set_rcvbuf(int fd,unsigned int size)120 static void set_rcvbuf(int fd, unsigned int size)
121 {
122 int err;
123
124 err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
125 if (err) {
126 perror("set SO_RCVBUF");
127 exit(1);
128 }
129 }
130
set_sndbuf(int fd,unsigned int size)131 static void set_sndbuf(int fd, unsigned int size)
132 {
133 int err;
134
135 err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
136 if (err) {
137 perror("set SO_SNDBUF");
138 exit(1);
139 }
140 }
141
sock_listen_mptcp(const char * const listenaddr,const char * const port)142 static int sock_listen_mptcp(const char * const listenaddr,
143 const char * const port)
144 {
145 int sock;
146 struct addrinfo hints = {
147 .ai_protocol = IPPROTO_TCP,
148 .ai_socktype = SOCK_STREAM,
149 .ai_flags = AI_PASSIVE | AI_NUMERICHOST
150 };
151
152 hints.ai_family = pf;
153
154 struct addrinfo *a, *addr;
155 int one = 1;
156
157 xgetaddrinfo(listenaddr, port, &hints, &addr);
158 hints.ai_family = pf;
159
160 for (a = addr; a; a = a->ai_next) {
161 sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto);
162 if (sock < 0)
163 continue;
164
165 if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
166 sizeof(one)))
167 perror("setsockopt");
168
169 if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
170 break; /* success */
171
172 perror("bind");
173 close(sock);
174 sock = -1;
175 }
176
177 freeaddrinfo(addr);
178
179 if (sock < 0) {
180 fprintf(stderr, "Could not create listen socket\n");
181 return sock;
182 }
183
184 if (listen(sock, 20)) {
185 perror("listen");
186 close(sock);
187 return -1;
188 }
189
190 return sock;
191 }
192
sock_test_tcpulp(const char * const remoteaddr,const char * const port)193 static bool sock_test_tcpulp(const char * const remoteaddr,
194 const char * const port)
195 {
196 struct addrinfo hints = {
197 .ai_protocol = IPPROTO_TCP,
198 .ai_socktype = SOCK_STREAM,
199 };
200 struct addrinfo *a, *addr;
201 int sock = -1, ret = 0;
202 bool test_pass = false;
203
204 hints.ai_family = AF_INET;
205
206 xgetaddrinfo(remoteaddr, port, &hints, &addr);
207 for (a = addr; a; a = a->ai_next) {
208 sock = socket(a->ai_family, a->ai_socktype, IPPROTO_TCP);
209 if (sock < 0) {
210 perror("socket");
211 continue;
212 }
213 ret = setsockopt(sock, IPPROTO_TCP, TCP_ULP, "mptcp",
214 sizeof("mptcp"));
215 if (ret == -1 && errno == EOPNOTSUPP)
216 test_pass = true;
217 close(sock);
218
219 if (test_pass)
220 break;
221 if (!ret)
222 fprintf(stderr,
223 "setsockopt(TCP_ULP) returned 0\n");
224 else
225 perror("setsockopt(TCP_ULP)");
226 }
227 return test_pass;
228 }
229
sock_connect_mptcp(const char * const remoteaddr,const char * const port,int proto)230 static int sock_connect_mptcp(const char * const remoteaddr,
231 const char * const port, int proto)
232 {
233 struct addrinfo hints = {
234 .ai_protocol = IPPROTO_TCP,
235 .ai_socktype = SOCK_STREAM,
236 };
237 struct addrinfo *a, *addr;
238 int sock = -1;
239
240 hints.ai_family = pf;
241
242 xgetaddrinfo(remoteaddr, port, &hints, &addr);
243 for (a = addr; a; a = a->ai_next) {
244 sock = socket(a->ai_family, a->ai_socktype, proto);
245 if (sock < 0) {
246 perror("socket");
247 continue;
248 }
249
250 if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
251 break; /* success */
252
253 perror("connect()");
254 close(sock);
255 sock = -1;
256 }
257
258 freeaddrinfo(addr);
259 return sock;
260 }
261
do_rnd_write(const int fd,char * buf,const size_t len)262 static size_t do_rnd_write(const int fd, char *buf, const size_t len)
263 {
264 static bool first = true;
265 unsigned int do_w;
266 ssize_t bw;
267
268 do_w = rand() & 0xffff;
269 if (do_w == 0 || do_w > len)
270 do_w = len;
271
272 if (cfg_join && first && do_w > 100)
273 do_w = 100;
274
275 if (cfg_remove && do_w > 50)
276 do_w = 50;
277
278 bw = write(fd, buf, do_w);
279 if (bw < 0)
280 perror("write");
281
282 /* let the join handshake complete, before going on */
283 if (cfg_join && first) {
284 usleep(200000);
285 first = false;
286 }
287
288 if (cfg_remove)
289 usleep(200000);
290
291 return bw;
292 }
293
do_write(const int fd,char * buf,const size_t len)294 static size_t do_write(const int fd, char *buf, const size_t len)
295 {
296 size_t offset = 0;
297
298 while (offset < len) {
299 size_t written;
300 ssize_t bw;
301
302 bw = write(fd, buf + offset, len - offset);
303 if (bw < 0) {
304 perror("write");
305 return 0;
306 }
307
308 written = (size_t)bw;
309 offset += written;
310 }
311
312 return offset;
313 }
314
do_rnd_read(const int fd,char * buf,const size_t len)315 static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
316 {
317 size_t cap = rand();
318
319 cap &= 0xffff;
320
321 if (cap == 0)
322 cap = 1;
323 else if (cap > len)
324 cap = len;
325
326 return read(fd, buf, cap);
327 }
328
set_nonblock(int fd)329 static void set_nonblock(int fd)
330 {
331 int flags = fcntl(fd, F_GETFL);
332
333 if (flags == -1)
334 return;
335
336 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
337 }
338
copyfd_io_poll(int infd,int peerfd,int outfd)339 static int copyfd_io_poll(int infd, int peerfd, int outfd)
340 {
341 struct pollfd fds = {
342 .fd = peerfd,
343 .events = POLLIN | POLLOUT,
344 };
345 unsigned int woff = 0, wlen = 0;
346 char wbuf[8192];
347
348 set_nonblock(peerfd);
349
350 for (;;) {
351 char rbuf[8192];
352 ssize_t len;
353
354 if (fds.events == 0)
355 break;
356
357 switch (poll(&fds, 1, poll_timeout)) {
358 case -1:
359 if (errno == EINTR)
360 continue;
361 perror("poll");
362 return 1;
363 case 0:
364 fprintf(stderr, "%s: poll timed out (events: "
365 "POLLIN %u, POLLOUT %u)\n", __func__,
366 fds.events & POLLIN, fds.events & POLLOUT);
367 return 2;
368 }
369
370 if (fds.revents & POLLIN) {
371 len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
372 if (len == 0) {
373 /* no more data to receive:
374 * peer has closed its write side
375 */
376 fds.events &= ~POLLIN;
377
378 if ((fds.events & POLLOUT) == 0)
379 /* and nothing more to send */
380 break;
381
382 /* Else, still have data to transmit */
383 } else if (len < 0) {
384 perror("read");
385 return 3;
386 }
387
388 do_write(outfd, rbuf, len);
389 }
390
391 if (fds.revents & POLLOUT) {
392 if (wlen == 0) {
393 woff = 0;
394 wlen = read(infd, wbuf, sizeof(wbuf));
395 }
396
397 if (wlen > 0) {
398 ssize_t bw;
399
400 bw = do_rnd_write(peerfd, wbuf + woff, wlen);
401 if (bw < 0)
402 return 111;
403
404 woff += bw;
405 wlen -= bw;
406 } else if (wlen == 0) {
407 /* We have no more data to send. */
408 fds.events &= ~POLLOUT;
409
410 if ((fds.events & POLLIN) == 0)
411 /* ... and peer also closed already */
412 break;
413
414 /* ... but we still receive.
415 * Close our write side, ev. give some time
416 * for address notification and/or checking
417 * the current status
418 */
419 if (cfg_wait)
420 usleep(cfg_wait);
421 shutdown(peerfd, SHUT_WR);
422 } else {
423 if (errno == EINTR)
424 continue;
425 perror("read");
426 return 4;
427 }
428 }
429
430 if (fds.revents & (POLLERR | POLLNVAL)) {
431 fprintf(stderr, "Unexpected revents: "
432 "POLLERR/POLLNVAL(%x)\n", fds.revents);
433 return 5;
434 }
435 }
436
437 /* leave some time for late join/announce */
438 if (cfg_join || cfg_remove)
439 usleep(cfg_wait);
440
441 close(peerfd);
442 return 0;
443 }
444
do_recvfile(int infd,int outfd)445 static int do_recvfile(int infd, int outfd)
446 {
447 ssize_t r;
448
449 do {
450 char buf[16384];
451
452 r = do_rnd_read(infd, buf, sizeof(buf));
453 if (r > 0) {
454 if (write(outfd, buf, r) != r)
455 break;
456 } else if (r < 0) {
457 perror("read");
458 }
459 } while (r > 0);
460
461 return (int)r;
462 }
463
do_mmap(int infd,int outfd,unsigned int size)464 static int do_mmap(int infd, int outfd, unsigned int size)
465 {
466 char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0);
467 ssize_t ret = 0, off = 0;
468 size_t rem;
469
470 if (inbuf == MAP_FAILED) {
471 perror("mmap");
472 return 1;
473 }
474
475 rem = size;
476
477 while (rem > 0) {
478 ret = write(outfd, inbuf + off, rem);
479
480 if (ret < 0) {
481 perror("write");
482 break;
483 }
484
485 off += ret;
486 rem -= ret;
487 }
488
489 munmap(inbuf, size);
490 return rem;
491 }
492
get_infd_size(int fd)493 static int get_infd_size(int fd)
494 {
495 struct stat sb;
496 ssize_t count;
497 int err;
498
499 err = fstat(fd, &sb);
500 if (err < 0) {
501 perror("fstat");
502 return -1;
503 }
504
505 if ((sb.st_mode & S_IFMT) != S_IFREG) {
506 fprintf(stderr, "%s: stdin is not a regular file\n", __func__);
507 return -2;
508 }
509
510 count = sb.st_size;
511 if (count > INT_MAX) {
512 fprintf(stderr, "File too large: %zu\n", count);
513 return -3;
514 }
515
516 return (int)count;
517 }
518
do_sendfile(int infd,int outfd,unsigned int count)519 static int do_sendfile(int infd, int outfd, unsigned int count)
520 {
521 while (count > 0) {
522 ssize_t r;
523
524 r = sendfile(outfd, infd, NULL, count);
525 if (r < 0) {
526 perror("sendfile");
527 return 3;
528 }
529
530 count -= r;
531 }
532
533 return 0;
534 }
535
copyfd_io_mmap(int infd,int peerfd,int outfd,unsigned int size)536 static int copyfd_io_mmap(int infd, int peerfd, int outfd,
537 unsigned int size)
538 {
539 int err;
540
541 if (listen_mode) {
542 err = do_recvfile(peerfd, outfd);
543 if (err)
544 return err;
545
546 err = do_mmap(infd, peerfd, size);
547 } else {
548 err = do_mmap(infd, peerfd, size);
549 if (err)
550 return err;
551
552 shutdown(peerfd, SHUT_WR);
553
554 err = do_recvfile(peerfd, outfd);
555 }
556
557 return err;
558 }
559
copyfd_io_sendfile(int infd,int peerfd,int outfd,unsigned int size)560 static int copyfd_io_sendfile(int infd, int peerfd, int outfd,
561 unsigned int size)
562 {
563 int err;
564
565 if (listen_mode) {
566 err = do_recvfile(peerfd, outfd);
567 if (err)
568 return err;
569
570 err = do_sendfile(infd, peerfd, size);
571 } else {
572 err = do_sendfile(infd, peerfd, size);
573 if (err)
574 return err;
575 err = do_recvfile(peerfd, outfd);
576 }
577
578 return err;
579 }
580
copyfd_io(int infd,int peerfd,int outfd)581 static int copyfd_io(int infd, int peerfd, int outfd)
582 {
583 int file_size;
584
585 switch (cfg_mode) {
586 case CFG_MODE_POLL:
587 return copyfd_io_poll(infd, peerfd, outfd);
588 case CFG_MODE_MMAP:
589 file_size = get_infd_size(infd);
590 if (file_size < 0)
591 return file_size;
592 return copyfd_io_mmap(infd, peerfd, outfd, file_size);
593 case CFG_MODE_SENDFILE:
594 file_size = get_infd_size(infd);
595 if (file_size < 0)
596 return file_size;
597 return copyfd_io_sendfile(infd, peerfd, outfd, file_size);
598 }
599
600 fprintf(stderr, "Invalid mode %d\n", cfg_mode);
601
602 die_usage();
603 return 1;
604 }
605
check_sockaddr(int pf,struct sockaddr_storage * ss,socklen_t salen)606 static void check_sockaddr(int pf, struct sockaddr_storage *ss,
607 socklen_t salen)
608 {
609 struct sockaddr_in6 *sin6;
610 struct sockaddr_in *sin;
611 socklen_t wanted_size = 0;
612
613 switch (pf) {
614 case AF_INET:
615 wanted_size = sizeof(*sin);
616 sin = (void *)ss;
617 if (!sin->sin_port)
618 fprintf(stderr, "accept: something wrong: ip connection from port 0");
619 break;
620 case AF_INET6:
621 wanted_size = sizeof(*sin6);
622 sin6 = (void *)ss;
623 if (!sin6->sin6_port)
624 fprintf(stderr, "accept: something wrong: ipv6 connection from port 0");
625 break;
626 default:
627 fprintf(stderr, "accept: Unknown pf %d, salen %u\n", pf, salen);
628 return;
629 }
630
631 if (salen != wanted_size)
632 fprintf(stderr, "accept: size mismatch, got %d expected %d\n",
633 (int)salen, wanted_size);
634
635 if (ss->ss_family != pf)
636 fprintf(stderr, "accept: pf mismatch, expect %d, ss_family is %d\n",
637 (int)ss->ss_family, pf);
638 }
639
check_getpeername(int fd,struct sockaddr_storage * ss,socklen_t salen)640 static void check_getpeername(int fd, struct sockaddr_storage *ss, socklen_t salen)
641 {
642 struct sockaddr_storage peerss;
643 socklen_t peersalen = sizeof(peerss);
644
645 if (getpeername(fd, (struct sockaddr *)&peerss, &peersalen) < 0) {
646 perror("getpeername");
647 return;
648 }
649
650 if (peersalen != salen) {
651 fprintf(stderr, "%s: %d vs %d\n", __func__, peersalen, salen);
652 return;
653 }
654
655 if (memcmp(ss, &peerss, peersalen)) {
656 char a[INET6_ADDRSTRLEN];
657 char b[INET6_ADDRSTRLEN];
658 char c[INET6_ADDRSTRLEN];
659 char d[INET6_ADDRSTRLEN];
660
661 xgetnameinfo((struct sockaddr *)ss, salen,
662 a, sizeof(a), b, sizeof(b));
663
664 xgetnameinfo((struct sockaddr *)&peerss, peersalen,
665 c, sizeof(c), d, sizeof(d));
666
667 fprintf(stderr, "%s: memcmp failure: accept %s vs peername %s, %s vs %s salen %d vs %d\n",
668 __func__, a, c, b, d, peersalen, salen);
669 }
670 }
671
check_getpeername_connect(int fd)672 static void check_getpeername_connect(int fd)
673 {
674 struct sockaddr_storage ss;
675 socklen_t salen = sizeof(ss);
676 char a[INET6_ADDRSTRLEN];
677 char b[INET6_ADDRSTRLEN];
678
679 if (getpeername(fd, (struct sockaddr *)&ss, &salen) < 0) {
680 perror("getpeername");
681 return;
682 }
683
684 xgetnameinfo((struct sockaddr *)&ss, salen,
685 a, sizeof(a), b, sizeof(b));
686
687 if (strcmp(cfg_host, a) || strcmp(cfg_port, b))
688 fprintf(stderr, "%s: %s vs %s, %s vs %s\n", __func__,
689 cfg_host, a, cfg_port, b);
690 }
691
maybe_close(int fd)692 static void maybe_close(int fd)
693 {
694 unsigned int r = rand();
695
696 if (!(cfg_join || cfg_remove) && (r & 1))
697 close(fd);
698 }
699
main_loop_s(int listensock)700 int main_loop_s(int listensock)
701 {
702 struct sockaddr_storage ss;
703 struct pollfd polls;
704 socklen_t salen;
705 int remotesock;
706
707 polls.fd = listensock;
708 polls.events = POLLIN;
709
710 switch (poll(&polls, 1, poll_timeout)) {
711 case -1:
712 perror("poll");
713 return 1;
714 case 0:
715 fprintf(stderr, "%s: timed out\n", __func__);
716 close(listensock);
717 return 2;
718 }
719
720 salen = sizeof(ss);
721 remotesock = accept(listensock, (struct sockaddr *)&ss, &salen);
722 if (remotesock >= 0) {
723 maybe_close(listensock);
724 check_sockaddr(pf, &ss, salen);
725 check_getpeername(remotesock, &ss, salen);
726
727 return copyfd_io(0, remotesock, 1);
728 }
729
730 perror("accept");
731
732 return 1;
733 }
734
init_rng(void)735 static void init_rng(void)
736 {
737 int fd = open("/dev/urandom", O_RDONLY);
738 unsigned int foo;
739
740 if (fd > 0) {
741 int ret = read(fd, &foo, sizeof(foo));
742
743 if (ret < 0)
744 srand(fd + foo);
745 close(fd);
746 }
747
748 srand(foo);
749 }
750
main_loop(void)751 int main_loop(void)
752 {
753 int fd;
754
755 /* listener is ready. */
756 fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
757 if (fd < 0)
758 return 2;
759
760 check_getpeername_connect(fd);
761
762 if (cfg_rcvbuf)
763 set_rcvbuf(fd, cfg_rcvbuf);
764 if (cfg_sndbuf)
765 set_sndbuf(fd, cfg_sndbuf);
766
767 return copyfd_io(0, fd, 1);
768 }
769
parse_proto(const char * proto)770 int parse_proto(const char *proto)
771 {
772 if (!strcasecmp(proto, "MPTCP"))
773 return IPPROTO_MPTCP;
774 if (!strcasecmp(proto, "TCP"))
775 return IPPROTO_TCP;
776
777 fprintf(stderr, "Unknown protocol: %s\n.", proto);
778 die_usage();
779
780 /* silence compiler warning */
781 return 0;
782 }
783
parse_mode(const char * mode)784 int parse_mode(const char *mode)
785 {
786 if (!strcasecmp(mode, "poll"))
787 return CFG_MODE_POLL;
788 if (!strcasecmp(mode, "mmap"))
789 return CFG_MODE_MMAP;
790 if (!strcasecmp(mode, "sendfile"))
791 return CFG_MODE_SENDFILE;
792
793 fprintf(stderr, "Unknown test mode: %s\n", mode);
794 fprintf(stderr, "Supported modes are:\n");
795 fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
796 fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
797 fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
798
799 die_usage();
800
801 /* silence compiler warning */
802 return 0;
803 }
804
parse_int(const char * size)805 static int parse_int(const char *size)
806 {
807 unsigned long s;
808
809 errno = 0;
810
811 s = strtoul(size, NULL, 0);
812
813 if (errno) {
814 fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
815 size, strerror(errno));
816 die_usage();
817 }
818
819 if (s > INT_MAX) {
820 fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
821 size, strerror(ERANGE));
822 die_usage();
823 }
824
825 return (int)s;
826 }
827
parse_opts(int argc,char ** argv)828 static void parse_opts(int argc, char **argv)
829 {
830 int c;
831
832 while ((c = getopt(argc, argv, "6jrlp:s:hut:m:S:R:w:")) != -1) {
833 switch (c) {
834 case 'j':
835 cfg_join = true;
836 cfg_mode = CFG_MODE_POLL;
837 cfg_wait = 400000;
838 break;
839 case 'r':
840 cfg_remove = true;
841 cfg_mode = CFG_MODE_POLL;
842 cfg_wait = 400000;
843 break;
844 case 'l':
845 listen_mode = true;
846 break;
847 case 'p':
848 cfg_port = optarg;
849 break;
850 case 's':
851 cfg_sock_proto = parse_proto(optarg);
852 break;
853 case 'h':
854 die_usage();
855 break;
856 case 'u':
857 tcpulp_audit = true;
858 break;
859 case '6':
860 pf = AF_INET6;
861 break;
862 case 't':
863 poll_timeout = atoi(optarg) * 1000;
864 if (poll_timeout <= 0)
865 poll_timeout = -1;
866 break;
867 case 'm':
868 cfg_mode = parse_mode(optarg);
869 break;
870 case 'S':
871 cfg_sndbuf = parse_int(optarg);
872 break;
873 case 'R':
874 cfg_rcvbuf = parse_int(optarg);
875 break;
876 case 'w':
877 cfg_wait = atoi(optarg)*1000000;
878 break;
879 }
880 }
881
882 if (optind + 1 != argc)
883 die_usage();
884 cfg_host = argv[optind];
885
886 if (strchr(cfg_host, ':'))
887 pf = AF_INET6;
888 }
889
main(int argc,char * argv[])890 int main(int argc, char *argv[])
891 {
892 init_rng();
893
894 signal(SIGUSR1, handle_signal);
895 parse_opts(argc, argv);
896
897 if (tcpulp_audit)
898 return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1;
899
900 if (listen_mode) {
901 int fd = sock_listen_mptcp(cfg_host, cfg_port);
902
903 if (fd < 0)
904 return 1;
905
906 if (cfg_rcvbuf)
907 set_rcvbuf(fd, cfg_rcvbuf);
908 if (cfg_sndbuf)
909 set_sndbuf(fd, cfg_sndbuf);
910
911 return main_loop_s(fd);
912 }
913
914 return main_loop();
915 }
916