• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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