xref: /openbmc/linux/tools/testing/selftests/net/mptcp/mptcp_connect.c (revision 8b0adbe3e38dbe5aae9edf6f5159ffdca7cfbdf1)
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 unsigned int cfg_do_w;
59 static int cfg_wait;
60 
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 
77 static void handle_signal(int nr)
78 {
79 	quit = true;
80 }
81 
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 
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 
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 
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 
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 
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 
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 
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 
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 > cfg_do_w)
277 		do_w = cfg_do_w;
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
736 static void init_rng(void)
737 {
738 	int fd = open("/dev/urandom", O_RDONLY);
739 	unsigned int foo;
740 
741 	if (fd > 0) {
742 		int ret = read(fd, &foo, sizeof(foo));
743 
744 		if (ret < 0)
745 			srand(fd + foo);
746 		close(fd);
747 	}
748 
749 	srand(foo);
750 }
751 
752 int main_loop(void)
753 {
754 	int fd;
755 
756 	/* listener is ready. */
757 	fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
758 	if (fd < 0)
759 		return 2;
760 
761 	check_getpeername_connect(fd);
762 
763 	if (cfg_rcvbuf)
764 		set_rcvbuf(fd, cfg_rcvbuf);
765 	if (cfg_sndbuf)
766 		set_sndbuf(fd, cfg_sndbuf);
767 
768 	return copyfd_io(0, fd, 1);
769 }
770 
771 int parse_proto(const char *proto)
772 {
773 	if (!strcasecmp(proto, "MPTCP"))
774 		return IPPROTO_MPTCP;
775 	if (!strcasecmp(proto, "TCP"))
776 		return IPPROTO_TCP;
777 
778 	fprintf(stderr, "Unknown protocol: %s\n.", proto);
779 	die_usage();
780 
781 	/* silence compiler warning */
782 	return 0;
783 }
784 
785 int parse_mode(const char *mode)
786 {
787 	if (!strcasecmp(mode, "poll"))
788 		return CFG_MODE_POLL;
789 	if (!strcasecmp(mode, "mmap"))
790 		return CFG_MODE_MMAP;
791 	if (!strcasecmp(mode, "sendfile"))
792 		return CFG_MODE_SENDFILE;
793 
794 	fprintf(stderr, "Unknown test mode: %s\n", mode);
795 	fprintf(stderr, "Supported modes are:\n");
796 	fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
797 	fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
798 	fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
799 
800 	die_usage();
801 
802 	/* silence compiler warning */
803 	return 0;
804 }
805 
806 static int parse_int(const char *size)
807 {
808 	unsigned long s;
809 
810 	errno = 0;
811 
812 	s = strtoul(size, NULL, 0);
813 
814 	if (errno) {
815 		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
816 			size, strerror(errno));
817 		die_usage();
818 	}
819 
820 	if (s > INT_MAX) {
821 		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
822 			size, strerror(ERANGE));
823 		die_usage();
824 	}
825 
826 	return (int)s;
827 }
828 
829 static void parse_opts(int argc, char **argv)
830 {
831 	int c;
832 
833 	while ((c = getopt(argc, argv, "6jr:lp:s:hut:m:S:R:w:")) != -1) {
834 		switch (c) {
835 		case 'j':
836 			cfg_join = true;
837 			cfg_mode = CFG_MODE_POLL;
838 			cfg_wait = 400000;
839 			break;
840 		case 'r':
841 			cfg_remove = true;
842 			cfg_mode = CFG_MODE_POLL;
843 			cfg_wait = 400000;
844 			cfg_do_w = atoi(optarg);
845 			if (cfg_do_w <= 0)
846 				cfg_do_w = 50;
847 			break;
848 		case 'l':
849 			listen_mode = true;
850 			break;
851 		case 'p':
852 			cfg_port = optarg;
853 			break;
854 		case 's':
855 			cfg_sock_proto = parse_proto(optarg);
856 			break;
857 		case 'h':
858 			die_usage();
859 			break;
860 		case 'u':
861 			tcpulp_audit = true;
862 			break;
863 		case '6':
864 			pf = AF_INET6;
865 			break;
866 		case 't':
867 			poll_timeout = atoi(optarg) * 1000;
868 			if (poll_timeout <= 0)
869 				poll_timeout = -1;
870 			break;
871 		case 'm':
872 			cfg_mode = parse_mode(optarg);
873 			break;
874 		case 'S':
875 			cfg_sndbuf = parse_int(optarg);
876 			break;
877 		case 'R':
878 			cfg_rcvbuf = parse_int(optarg);
879 			break;
880 		case 'w':
881 			cfg_wait = atoi(optarg)*1000000;
882 			break;
883 		}
884 	}
885 
886 	if (optind + 1 != argc)
887 		die_usage();
888 	cfg_host = argv[optind];
889 
890 	if (strchr(cfg_host, ':'))
891 		pf = AF_INET6;
892 }
893 
894 int main(int argc, char *argv[])
895 {
896 	init_rng();
897 
898 	signal(SIGUSR1, handle_signal);
899 	parse_opts(argc, argv);
900 
901 	if (tcpulp_audit)
902 		return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1;
903 
904 	if (listen_mode) {
905 		int fd = sock_listen_mptcp(cfg_host, cfg_port);
906 
907 		if (fd < 0)
908 			return 1;
909 
910 		if (cfg_rcvbuf)
911 			set_rcvbuf(fd, cfg_rcvbuf);
912 		if (cfg_sndbuf)
913 			set_sndbuf(fd, cfg_sndbuf);
914 
915 		return main_loop_s(fd);
916 	}
917 
918 	return main_loop();
919 }
920