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