1048d19d4SFlorian Westphal // SPDX-License-Identifier: GPL-2.0
2048d19d4SFlorian Westphal 
3048d19d4SFlorian Westphal #define _GNU_SOURCE
4048d19d4SFlorian Westphal 
5048d19d4SFlorian Westphal #include <errno.h>
6048d19d4SFlorian Westphal #include <limits.h>
7048d19d4SFlorian Westphal #include <fcntl.h>
8048d19d4SFlorian Westphal #include <string.h>
9048d19d4SFlorian Westphal #include <stdbool.h>
10048d19d4SFlorian Westphal #include <stdint.h>
11048d19d4SFlorian Westphal #include <stdio.h>
12048d19d4SFlorian Westphal #include <stdlib.h>
13048d19d4SFlorian Westphal #include <strings.h>
14df62f2ecSPaolo Abeni #include <signal.h>
15048d19d4SFlorian Westphal #include <unistd.h>
16048d19d4SFlorian Westphal 
17048d19d4SFlorian Westphal #include <sys/poll.h>
18048d19d4SFlorian Westphal #include <sys/sendfile.h>
19048d19d4SFlorian Westphal #include <sys/stat.h>
20048d19d4SFlorian Westphal #include <sys/socket.h>
21048d19d4SFlorian Westphal #include <sys/types.h>
22048d19d4SFlorian Westphal #include <sys/mman.h>
23048d19d4SFlorian Westphal 
24048d19d4SFlorian Westphal #include <netdb.h>
25048d19d4SFlorian Westphal #include <netinet/in.h>
26048d19d4SFlorian Westphal 
27048d19d4SFlorian Westphal #include <linux/tcp.h>
28048d19d4SFlorian Westphal 
29048d19d4SFlorian Westphal extern int optind;
30048d19d4SFlorian Westphal 
31048d19d4SFlorian Westphal #ifndef IPPROTO_MPTCP
32048d19d4SFlorian Westphal #define IPPROTO_MPTCP 262
33048d19d4SFlorian Westphal #endif
34048d19d4SFlorian Westphal #ifndef TCP_ULP
35048d19d4SFlorian Westphal #define TCP_ULP 31
36048d19d4SFlorian Westphal #endif
37048d19d4SFlorian Westphal 
388a4b910dSFlorian Westphal static int  poll_timeout = 10 * 1000;
39048d19d4SFlorian Westphal static bool listen_mode;
40df62f2ecSPaolo Abeni static bool quit;
41048d19d4SFlorian Westphal 
42048d19d4SFlorian Westphal enum cfg_mode {
43048d19d4SFlorian Westphal 	CFG_MODE_POLL,
44048d19d4SFlorian Westphal 	CFG_MODE_MMAP,
45048d19d4SFlorian Westphal 	CFG_MODE_SENDFILE,
46048d19d4SFlorian Westphal };
47048d19d4SFlorian Westphal 
48048d19d4SFlorian Westphal static enum cfg_mode cfg_mode = CFG_MODE_POLL;
49048d19d4SFlorian Westphal static const char *cfg_host;
50048d19d4SFlorian Westphal static const char *cfg_port	= "12000";
51048d19d4SFlorian Westphal static int cfg_sock_proto	= IPPROTO_MPTCP;
52048d19d4SFlorian Westphal static bool tcpulp_audit;
53048d19d4SFlorian Westphal static int pf = AF_INET;
54048d19d4SFlorian Westphal static int cfg_sndbuf;
558a4b910dSFlorian Westphal static int cfg_rcvbuf;
56b08fbf24SPaolo Abeni static bool cfg_join;
57df62f2ecSPaolo Abeni static int cfg_wait;
58048d19d4SFlorian Westphal 
59048d19d4SFlorian Westphal static void die_usage(void)
60048d19d4SFlorian Westphal {
618a4b910dSFlorian Westphal 	fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
62df62f2ecSPaolo Abeni 		"[-l] [-w sec] connect_address\n");
638a4b910dSFlorian Westphal 	fprintf(stderr, "\t-6 use ipv6\n");
648a4b910dSFlorian Westphal 	fprintf(stderr, "\t-t num -- set poll timeout to num\n");
658a4b910dSFlorian Westphal 	fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
668a4b910dSFlorian Westphal 	fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
678a4b910dSFlorian Westphal 	fprintf(stderr, "\t-p num -- use port num\n");
68*c6f4c2b0SDavide Caratti 	fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
69*c6f4c2b0SDavide Caratti 	fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
708a4b910dSFlorian Westphal 	fprintf(stderr, "\t-u -- check mptcp ulp\n");
71df62f2ecSPaolo Abeni 	fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
72048d19d4SFlorian Westphal 	exit(1);
73048d19d4SFlorian Westphal }
74048d19d4SFlorian Westphal 
75df62f2ecSPaolo Abeni static void handle_signal(int nr)
76df62f2ecSPaolo Abeni {
77df62f2ecSPaolo Abeni 	quit = true;
78df62f2ecSPaolo Abeni }
79df62f2ecSPaolo Abeni 
80048d19d4SFlorian Westphal static const char *getxinfo_strerr(int err)
81048d19d4SFlorian Westphal {
82048d19d4SFlorian Westphal 	if (err == EAI_SYSTEM)
83048d19d4SFlorian Westphal 		return strerror(errno);
84048d19d4SFlorian Westphal 
85048d19d4SFlorian Westphal 	return gai_strerror(err);
86048d19d4SFlorian Westphal }
87048d19d4SFlorian Westphal 
88048d19d4SFlorian Westphal static void xgetnameinfo(const struct sockaddr *addr, socklen_t addrlen,
89048d19d4SFlorian Westphal 			 char *host, socklen_t hostlen,
90048d19d4SFlorian Westphal 			 char *serv, socklen_t servlen)
91048d19d4SFlorian Westphal {
92048d19d4SFlorian Westphal 	int flags = NI_NUMERICHOST | NI_NUMERICSERV;
93048d19d4SFlorian Westphal 	int err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen,
94048d19d4SFlorian Westphal 			      flags);
95048d19d4SFlorian Westphal 
96048d19d4SFlorian Westphal 	if (err) {
97048d19d4SFlorian Westphal 		const char *errstr = getxinfo_strerr(err);
98048d19d4SFlorian Westphal 
99048d19d4SFlorian Westphal 		fprintf(stderr, "Fatal: getnameinfo: %s\n", errstr);
100048d19d4SFlorian Westphal 		exit(1);
101048d19d4SFlorian Westphal 	}
102048d19d4SFlorian Westphal }
103048d19d4SFlorian Westphal 
104048d19d4SFlorian Westphal static void xgetaddrinfo(const char *node, const char *service,
105048d19d4SFlorian Westphal 			 const struct addrinfo *hints,
106048d19d4SFlorian Westphal 			 struct addrinfo **res)
107048d19d4SFlorian Westphal {
108048d19d4SFlorian Westphal 	int err = getaddrinfo(node, service, hints, res);
109048d19d4SFlorian Westphal 
110048d19d4SFlorian Westphal 	if (err) {
111048d19d4SFlorian Westphal 		const char *errstr = getxinfo_strerr(err);
112048d19d4SFlorian Westphal 
113048d19d4SFlorian Westphal 		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
114048d19d4SFlorian Westphal 			node ? node : "", service ? service : "", errstr);
115048d19d4SFlorian Westphal 		exit(1);
116048d19d4SFlorian Westphal 	}
117048d19d4SFlorian Westphal }
118048d19d4SFlorian Westphal 
1198a4b910dSFlorian Westphal static void set_rcvbuf(int fd, unsigned int size)
1208a4b910dSFlorian Westphal {
1218a4b910dSFlorian Westphal 	int err;
1228a4b910dSFlorian Westphal 
1238a4b910dSFlorian Westphal 	err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
1248a4b910dSFlorian Westphal 	if (err) {
1258a4b910dSFlorian Westphal 		perror("set SO_RCVBUF");
1268a4b910dSFlorian Westphal 		exit(1);
1278a4b910dSFlorian Westphal 	}
1288a4b910dSFlorian Westphal }
1298a4b910dSFlorian Westphal 
130048d19d4SFlorian Westphal static void set_sndbuf(int fd, unsigned int size)
131048d19d4SFlorian Westphal {
132048d19d4SFlorian Westphal 	int err;
133048d19d4SFlorian Westphal 
134048d19d4SFlorian Westphal 	err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
135048d19d4SFlorian Westphal 	if (err) {
136048d19d4SFlorian Westphal 		perror("set SO_SNDBUF");
137048d19d4SFlorian Westphal 		exit(1);
138048d19d4SFlorian Westphal 	}
139048d19d4SFlorian Westphal }
140048d19d4SFlorian Westphal 
141048d19d4SFlorian Westphal static int sock_listen_mptcp(const char * const listenaddr,
142048d19d4SFlorian Westphal 			     const char * const port)
143048d19d4SFlorian Westphal {
144048d19d4SFlorian Westphal 	int sock;
145048d19d4SFlorian Westphal 	struct addrinfo hints = {
146048d19d4SFlorian Westphal 		.ai_protocol = IPPROTO_TCP,
147048d19d4SFlorian Westphal 		.ai_socktype = SOCK_STREAM,
148048d19d4SFlorian Westphal 		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
149048d19d4SFlorian Westphal 	};
150048d19d4SFlorian Westphal 
151048d19d4SFlorian Westphal 	hints.ai_family = pf;
152048d19d4SFlorian Westphal 
153048d19d4SFlorian Westphal 	struct addrinfo *a, *addr;
154048d19d4SFlorian Westphal 	int one = 1;
155048d19d4SFlorian Westphal 
156048d19d4SFlorian Westphal 	xgetaddrinfo(listenaddr, port, &hints, &addr);
157048d19d4SFlorian Westphal 	hints.ai_family = pf;
158048d19d4SFlorian Westphal 
159048d19d4SFlorian Westphal 	for (a = addr; a; a = a->ai_next) {
160048d19d4SFlorian Westphal 		sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto);
161048d19d4SFlorian Westphal 		if (sock < 0)
162048d19d4SFlorian Westphal 			continue;
163048d19d4SFlorian Westphal 
164048d19d4SFlorian Westphal 		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
165048d19d4SFlorian Westphal 				     sizeof(one)))
166048d19d4SFlorian Westphal 			perror("setsockopt");
167048d19d4SFlorian Westphal 
168048d19d4SFlorian Westphal 		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
169048d19d4SFlorian Westphal 			break; /* success */
170048d19d4SFlorian Westphal 
171048d19d4SFlorian Westphal 		perror("bind");
172048d19d4SFlorian Westphal 		close(sock);
173048d19d4SFlorian Westphal 		sock = -1;
174048d19d4SFlorian Westphal 	}
175048d19d4SFlorian Westphal 
176048d19d4SFlorian Westphal 	freeaddrinfo(addr);
177048d19d4SFlorian Westphal 
178048d19d4SFlorian Westphal 	if (sock < 0) {
179048d19d4SFlorian Westphal 		fprintf(stderr, "Could not create listen socket\n");
180048d19d4SFlorian Westphal 		return sock;
181048d19d4SFlorian Westphal 	}
182048d19d4SFlorian Westphal 
183048d19d4SFlorian Westphal 	if (listen(sock, 20)) {
184048d19d4SFlorian Westphal 		perror("listen");
185048d19d4SFlorian Westphal 		close(sock);
186048d19d4SFlorian Westphal 		return -1;
187048d19d4SFlorian Westphal 	}
188048d19d4SFlorian Westphal 
189048d19d4SFlorian Westphal 	return sock;
190048d19d4SFlorian Westphal }
191048d19d4SFlorian Westphal 
192048d19d4SFlorian Westphal static bool sock_test_tcpulp(const char * const remoteaddr,
193048d19d4SFlorian Westphal 			     const char * const port)
194048d19d4SFlorian Westphal {
195048d19d4SFlorian Westphal 	struct addrinfo hints = {
196048d19d4SFlorian Westphal 		.ai_protocol = IPPROTO_TCP,
197048d19d4SFlorian Westphal 		.ai_socktype = SOCK_STREAM,
198048d19d4SFlorian Westphal 	};
199048d19d4SFlorian Westphal 	struct addrinfo *a, *addr;
200048d19d4SFlorian Westphal 	int sock = -1, ret = 0;
201048d19d4SFlorian Westphal 	bool test_pass = false;
202048d19d4SFlorian Westphal 
203048d19d4SFlorian Westphal 	hints.ai_family = AF_INET;
204048d19d4SFlorian Westphal 
205048d19d4SFlorian Westphal 	xgetaddrinfo(remoteaddr, port, &hints, &addr);
206048d19d4SFlorian Westphal 	for (a = addr; a; a = a->ai_next) {
207048d19d4SFlorian Westphal 		sock = socket(a->ai_family, a->ai_socktype, IPPROTO_TCP);
208048d19d4SFlorian Westphal 		if (sock < 0) {
209048d19d4SFlorian Westphal 			perror("socket");
210048d19d4SFlorian Westphal 			continue;
211048d19d4SFlorian Westphal 		}
212048d19d4SFlorian Westphal 		ret = setsockopt(sock, IPPROTO_TCP, TCP_ULP, "mptcp",
213048d19d4SFlorian Westphal 				 sizeof("mptcp"));
214048d19d4SFlorian Westphal 		if (ret == -1 && errno == EOPNOTSUPP)
215048d19d4SFlorian Westphal 			test_pass = true;
216048d19d4SFlorian Westphal 		close(sock);
217048d19d4SFlorian Westphal 
218048d19d4SFlorian Westphal 		if (test_pass)
219048d19d4SFlorian Westphal 			break;
220048d19d4SFlorian Westphal 		if (!ret)
221048d19d4SFlorian Westphal 			fprintf(stderr,
222048d19d4SFlorian Westphal 				"setsockopt(TCP_ULP) returned 0\n");
223048d19d4SFlorian Westphal 		else
224048d19d4SFlorian Westphal 			perror("setsockopt(TCP_ULP)");
225048d19d4SFlorian Westphal 	}
226048d19d4SFlorian Westphal 	return test_pass;
227048d19d4SFlorian Westphal }
228048d19d4SFlorian Westphal 
229048d19d4SFlorian Westphal static int sock_connect_mptcp(const char * const remoteaddr,
230048d19d4SFlorian Westphal 			      const char * const port, int proto)
231048d19d4SFlorian Westphal {
232048d19d4SFlorian Westphal 	struct addrinfo hints = {
233048d19d4SFlorian Westphal 		.ai_protocol = IPPROTO_TCP,
234048d19d4SFlorian Westphal 		.ai_socktype = SOCK_STREAM,
235048d19d4SFlorian Westphal 	};
236048d19d4SFlorian Westphal 	struct addrinfo *a, *addr;
237048d19d4SFlorian Westphal 	int sock = -1;
238048d19d4SFlorian Westphal 
239048d19d4SFlorian Westphal 	hints.ai_family = pf;
240048d19d4SFlorian Westphal 
241048d19d4SFlorian Westphal 	xgetaddrinfo(remoteaddr, port, &hints, &addr);
242048d19d4SFlorian Westphal 	for (a = addr; a; a = a->ai_next) {
243048d19d4SFlorian Westphal 		sock = socket(a->ai_family, a->ai_socktype, proto);
244048d19d4SFlorian Westphal 		if (sock < 0) {
245048d19d4SFlorian Westphal 			perror("socket");
246048d19d4SFlorian Westphal 			continue;
247048d19d4SFlorian Westphal 		}
248048d19d4SFlorian Westphal 
249048d19d4SFlorian Westphal 		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
250048d19d4SFlorian Westphal 			break; /* success */
251048d19d4SFlorian Westphal 
252048d19d4SFlorian Westphal 		perror("connect()");
253048d19d4SFlorian Westphal 		close(sock);
254048d19d4SFlorian Westphal 		sock = -1;
255048d19d4SFlorian Westphal 	}
256048d19d4SFlorian Westphal 
257048d19d4SFlorian Westphal 	freeaddrinfo(addr);
258048d19d4SFlorian Westphal 	return sock;
259048d19d4SFlorian Westphal }
260048d19d4SFlorian Westphal 
261048d19d4SFlorian Westphal static size_t do_rnd_write(const int fd, char *buf, const size_t len)
262048d19d4SFlorian Westphal {
263b08fbf24SPaolo Abeni 	static bool first = true;
264048d19d4SFlorian Westphal 	unsigned int do_w;
265048d19d4SFlorian Westphal 	ssize_t bw;
266048d19d4SFlorian Westphal 
267048d19d4SFlorian Westphal 	do_w = rand() & 0xffff;
268048d19d4SFlorian Westphal 	if (do_w == 0 || do_w > len)
269048d19d4SFlorian Westphal 		do_w = len;
270048d19d4SFlorian Westphal 
271b08fbf24SPaolo Abeni 	if (cfg_join && first && do_w > 100)
272b08fbf24SPaolo Abeni 		do_w = 100;
273b08fbf24SPaolo Abeni 
274048d19d4SFlorian Westphal 	bw = write(fd, buf, do_w);
275048d19d4SFlorian Westphal 	if (bw < 0)
276048d19d4SFlorian Westphal 		perror("write");
277048d19d4SFlorian Westphal 
278b08fbf24SPaolo Abeni 	/* let the join handshake complete, before going on */
279b08fbf24SPaolo Abeni 	if (cfg_join && first) {
280b08fbf24SPaolo Abeni 		usleep(200000);
281b08fbf24SPaolo Abeni 		first = false;
282b08fbf24SPaolo Abeni 	}
283b08fbf24SPaolo Abeni 
284048d19d4SFlorian Westphal 	return bw;
285048d19d4SFlorian Westphal }
286048d19d4SFlorian Westphal 
287048d19d4SFlorian Westphal static size_t do_write(const int fd, char *buf, const size_t len)
288048d19d4SFlorian Westphal {
289048d19d4SFlorian Westphal 	size_t offset = 0;
290048d19d4SFlorian Westphal 
291048d19d4SFlorian Westphal 	while (offset < len) {
292048d19d4SFlorian Westphal 		size_t written;
293048d19d4SFlorian Westphal 		ssize_t bw;
294048d19d4SFlorian Westphal 
295048d19d4SFlorian Westphal 		bw = write(fd, buf + offset, len - offset);
296048d19d4SFlorian Westphal 		if (bw < 0) {
297048d19d4SFlorian Westphal 			perror("write");
298048d19d4SFlorian Westphal 			return 0;
299048d19d4SFlorian Westphal 		}
300048d19d4SFlorian Westphal 
301048d19d4SFlorian Westphal 		written = (size_t)bw;
302048d19d4SFlorian Westphal 		offset += written;
303048d19d4SFlorian Westphal 	}
304048d19d4SFlorian Westphal 
305048d19d4SFlorian Westphal 	return offset;
306048d19d4SFlorian Westphal }
307048d19d4SFlorian Westphal 
308048d19d4SFlorian Westphal static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
309048d19d4SFlorian Westphal {
310048d19d4SFlorian Westphal 	size_t cap = rand();
311048d19d4SFlorian Westphal 
312048d19d4SFlorian Westphal 	cap &= 0xffff;
313048d19d4SFlorian Westphal 
314048d19d4SFlorian Westphal 	if (cap == 0)
315048d19d4SFlorian Westphal 		cap = 1;
316048d19d4SFlorian Westphal 	else if (cap > len)
317048d19d4SFlorian Westphal 		cap = len;
318048d19d4SFlorian Westphal 
319048d19d4SFlorian Westphal 	return read(fd, buf, cap);
320048d19d4SFlorian Westphal }
321048d19d4SFlorian Westphal 
322048d19d4SFlorian Westphal static void set_nonblock(int fd)
323048d19d4SFlorian Westphal {
324048d19d4SFlorian Westphal 	int flags = fcntl(fd, F_GETFL);
325048d19d4SFlorian Westphal 
326048d19d4SFlorian Westphal 	if (flags == -1)
327048d19d4SFlorian Westphal 		return;
328048d19d4SFlorian Westphal 
329048d19d4SFlorian Westphal 	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
330048d19d4SFlorian Westphal }
331048d19d4SFlorian Westphal 
332048d19d4SFlorian Westphal static int copyfd_io_poll(int infd, int peerfd, int outfd)
333048d19d4SFlorian Westphal {
334048d19d4SFlorian Westphal 	struct pollfd fds = {
335048d19d4SFlorian Westphal 		.fd = peerfd,
336048d19d4SFlorian Westphal 		.events = POLLIN | POLLOUT,
337048d19d4SFlorian Westphal 	};
338048d19d4SFlorian Westphal 	unsigned int woff = 0, wlen = 0;
339048d19d4SFlorian Westphal 	char wbuf[8192];
340048d19d4SFlorian Westphal 
341048d19d4SFlorian Westphal 	set_nonblock(peerfd);
342048d19d4SFlorian Westphal 
343048d19d4SFlorian Westphal 	for (;;) {
344048d19d4SFlorian Westphal 		char rbuf[8192];
345048d19d4SFlorian Westphal 		ssize_t len;
346048d19d4SFlorian Westphal 
347048d19d4SFlorian Westphal 		if (fds.events == 0)
348048d19d4SFlorian Westphal 			break;
349048d19d4SFlorian Westphal 
350048d19d4SFlorian Westphal 		switch (poll(&fds, 1, poll_timeout)) {
351048d19d4SFlorian Westphal 		case -1:
352048d19d4SFlorian Westphal 			if (errno == EINTR)
353048d19d4SFlorian Westphal 				continue;
354048d19d4SFlorian Westphal 			perror("poll");
355048d19d4SFlorian Westphal 			return 1;
356048d19d4SFlorian Westphal 		case 0:
357048d19d4SFlorian Westphal 			fprintf(stderr, "%s: poll timed out (events: "
358048d19d4SFlorian Westphal 				"POLLIN %u, POLLOUT %u)\n", __func__,
359048d19d4SFlorian Westphal 				fds.events & POLLIN, fds.events & POLLOUT);
360048d19d4SFlorian Westphal 			return 2;
361048d19d4SFlorian Westphal 		}
362048d19d4SFlorian Westphal 
363048d19d4SFlorian Westphal 		if (fds.revents & POLLIN) {
364048d19d4SFlorian Westphal 			len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
365048d19d4SFlorian Westphal 			if (len == 0) {
366048d19d4SFlorian Westphal 				/* no more data to receive:
367048d19d4SFlorian Westphal 				 * peer has closed its write side
368048d19d4SFlorian Westphal 				 */
369048d19d4SFlorian Westphal 				fds.events &= ~POLLIN;
370048d19d4SFlorian Westphal 
371048d19d4SFlorian Westphal 				if ((fds.events & POLLOUT) == 0)
372048d19d4SFlorian Westphal 					/* and nothing more to send */
373048d19d4SFlorian Westphal 					break;
374048d19d4SFlorian Westphal 
375048d19d4SFlorian Westphal 			/* Else, still have data to transmit */
376048d19d4SFlorian Westphal 			} else if (len < 0) {
377048d19d4SFlorian Westphal 				perror("read");
378048d19d4SFlorian Westphal 				return 3;
379048d19d4SFlorian Westphal 			}
380048d19d4SFlorian Westphal 
381048d19d4SFlorian Westphal 			do_write(outfd, rbuf, len);
382048d19d4SFlorian Westphal 		}
383048d19d4SFlorian Westphal 
384048d19d4SFlorian Westphal 		if (fds.revents & POLLOUT) {
385048d19d4SFlorian Westphal 			if (wlen == 0) {
386048d19d4SFlorian Westphal 				woff = 0;
387048d19d4SFlorian Westphal 				wlen = read(infd, wbuf, sizeof(wbuf));
388048d19d4SFlorian Westphal 			}
389048d19d4SFlorian Westphal 
390048d19d4SFlorian Westphal 			if (wlen > 0) {
391048d19d4SFlorian Westphal 				ssize_t bw;
392048d19d4SFlorian Westphal 
393048d19d4SFlorian Westphal 				bw = do_rnd_write(peerfd, wbuf + woff, wlen);
394048d19d4SFlorian Westphal 				if (bw < 0)
395048d19d4SFlorian Westphal 					return 111;
396048d19d4SFlorian Westphal 
397048d19d4SFlorian Westphal 				woff += bw;
398048d19d4SFlorian Westphal 				wlen -= bw;
399048d19d4SFlorian Westphal 			} else if (wlen == 0) {
400048d19d4SFlorian Westphal 				/* We have no more data to send. */
401048d19d4SFlorian Westphal 				fds.events &= ~POLLOUT;
402048d19d4SFlorian Westphal 
403048d19d4SFlorian Westphal 				if ((fds.events & POLLIN) == 0)
404048d19d4SFlorian Westphal 					/* ... and peer also closed already */
405048d19d4SFlorian Westphal 					break;
406048d19d4SFlorian Westphal 
407048d19d4SFlorian Westphal 				/* ... but we still receive.
408b08fbf24SPaolo Abeni 				 * Close our write side, ev. give some time
4096bdb6211SPaolo Abeni 				 * for address notification and/or checking
4106bdb6211SPaolo Abeni 				 * the current status
411048d19d4SFlorian Westphal 				 */
4126bdb6211SPaolo Abeni 				if (cfg_wait)
4136bdb6211SPaolo Abeni 					usleep(cfg_wait);
414048d19d4SFlorian Westphal 				shutdown(peerfd, SHUT_WR);
415048d19d4SFlorian Westphal 			} else {
416048d19d4SFlorian Westphal 				if (errno == EINTR)
417048d19d4SFlorian Westphal 					continue;
418048d19d4SFlorian Westphal 				perror("read");
419048d19d4SFlorian Westphal 				return 4;
420048d19d4SFlorian Westphal 			}
421048d19d4SFlorian Westphal 		}
422048d19d4SFlorian Westphal 
423048d19d4SFlorian Westphal 		if (fds.revents & (POLLERR | POLLNVAL)) {
424048d19d4SFlorian Westphal 			fprintf(stderr, "Unexpected revents: "
425048d19d4SFlorian Westphal 				"POLLERR/POLLNVAL(%x)\n", fds.revents);
426048d19d4SFlorian Westphal 			return 5;
427048d19d4SFlorian Westphal 		}
428048d19d4SFlorian Westphal 	}
429048d19d4SFlorian Westphal 
430b08fbf24SPaolo Abeni 	/* leave some time for late join/announce */
4316bdb6211SPaolo Abeni 	if (cfg_join)
432df62f2ecSPaolo Abeni 		usleep(cfg_wait);
433b08fbf24SPaolo Abeni 
434048d19d4SFlorian Westphal 	close(peerfd);
435048d19d4SFlorian Westphal 	return 0;
436048d19d4SFlorian Westphal }
437048d19d4SFlorian Westphal 
438048d19d4SFlorian Westphal static int do_recvfile(int infd, int outfd)
439048d19d4SFlorian Westphal {
440048d19d4SFlorian Westphal 	ssize_t r;
441048d19d4SFlorian Westphal 
442048d19d4SFlorian Westphal 	do {
443048d19d4SFlorian Westphal 		char buf[16384];
444048d19d4SFlorian Westphal 
445048d19d4SFlorian Westphal 		r = do_rnd_read(infd, buf, sizeof(buf));
446048d19d4SFlorian Westphal 		if (r > 0) {
447048d19d4SFlorian Westphal 			if (write(outfd, buf, r) != r)
448048d19d4SFlorian Westphal 				break;
449048d19d4SFlorian Westphal 		} else if (r < 0) {
450048d19d4SFlorian Westphal 			perror("read");
451048d19d4SFlorian Westphal 		}
452048d19d4SFlorian Westphal 	} while (r > 0);
453048d19d4SFlorian Westphal 
454048d19d4SFlorian Westphal 	return (int)r;
455048d19d4SFlorian Westphal }
456048d19d4SFlorian Westphal 
457048d19d4SFlorian Westphal static int do_mmap(int infd, int outfd, unsigned int size)
458048d19d4SFlorian Westphal {
459048d19d4SFlorian Westphal 	char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0);
460048d19d4SFlorian Westphal 	ssize_t ret = 0, off = 0;
461048d19d4SFlorian Westphal 	size_t rem;
462048d19d4SFlorian Westphal 
463048d19d4SFlorian Westphal 	if (inbuf == MAP_FAILED) {
464048d19d4SFlorian Westphal 		perror("mmap");
465048d19d4SFlorian Westphal 		return 1;
466048d19d4SFlorian Westphal 	}
467048d19d4SFlorian Westphal 
468048d19d4SFlorian Westphal 	rem = size;
469048d19d4SFlorian Westphal 
470048d19d4SFlorian Westphal 	while (rem > 0) {
471048d19d4SFlorian Westphal 		ret = write(outfd, inbuf + off, rem);
472048d19d4SFlorian Westphal 
473048d19d4SFlorian Westphal 		if (ret < 0) {
474048d19d4SFlorian Westphal 			perror("write");
475048d19d4SFlorian Westphal 			break;
476048d19d4SFlorian Westphal 		}
477048d19d4SFlorian Westphal 
478048d19d4SFlorian Westphal 		off += ret;
479048d19d4SFlorian Westphal 		rem -= ret;
480048d19d4SFlorian Westphal 	}
481048d19d4SFlorian Westphal 
482048d19d4SFlorian Westphal 	munmap(inbuf, size);
483048d19d4SFlorian Westphal 	return rem;
484048d19d4SFlorian Westphal }
485048d19d4SFlorian Westphal 
486048d19d4SFlorian Westphal static int get_infd_size(int fd)
487048d19d4SFlorian Westphal {
488048d19d4SFlorian Westphal 	struct stat sb;
489048d19d4SFlorian Westphal 	ssize_t count;
490048d19d4SFlorian Westphal 	int err;
491048d19d4SFlorian Westphal 
492048d19d4SFlorian Westphal 	err = fstat(fd, &sb);
493048d19d4SFlorian Westphal 	if (err < 0) {
494048d19d4SFlorian Westphal 		perror("fstat");
495048d19d4SFlorian Westphal 		return -1;
496048d19d4SFlorian Westphal 	}
497048d19d4SFlorian Westphal 
498048d19d4SFlorian Westphal 	if ((sb.st_mode & S_IFMT) != S_IFREG) {
499048d19d4SFlorian Westphal 		fprintf(stderr, "%s: stdin is not a regular file\n", __func__);
500048d19d4SFlorian Westphal 		return -2;
501048d19d4SFlorian Westphal 	}
502048d19d4SFlorian Westphal 
503048d19d4SFlorian Westphal 	count = sb.st_size;
504048d19d4SFlorian Westphal 	if (count > INT_MAX) {
505048d19d4SFlorian Westphal 		fprintf(stderr, "File too large: %zu\n", count);
506048d19d4SFlorian Westphal 		return -3;
507048d19d4SFlorian Westphal 	}
508048d19d4SFlorian Westphal 
509048d19d4SFlorian Westphal 	return (int)count;
510048d19d4SFlorian Westphal }
511048d19d4SFlorian Westphal 
512048d19d4SFlorian Westphal static int do_sendfile(int infd, int outfd, unsigned int count)
513048d19d4SFlorian Westphal {
514048d19d4SFlorian Westphal 	while (count > 0) {
515048d19d4SFlorian Westphal 		ssize_t r;
516048d19d4SFlorian Westphal 
517048d19d4SFlorian Westphal 		r = sendfile(outfd, infd, NULL, count);
518048d19d4SFlorian Westphal 		if (r < 0) {
519048d19d4SFlorian Westphal 			perror("sendfile");
520048d19d4SFlorian Westphal 			return 3;
521048d19d4SFlorian Westphal 		}
522048d19d4SFlorian Westphal 
523048d19d4SFlorian Westphal 		count -= r;
524048d19d4SFlorian Westphal 	}
525048d19d4SFlorian Westphal 
526048d19d4SFlorian Westphal 	return 0;
527048d19d4SFlorian Westphal }
528048d19d4SFlorian Westphal 
529048d19d4SFlorian Westphal static int copyfd_io_mmap(int infd, int peerfd, int outfd,
530048d19d4SFlorian Westphal 			  unsigned int size)
531048d19d4SFlorian Westphal {
532048d19d4SFlorian Westphal 	int err;
533048d19d4SFlorian Westphal 
534048d19d4SFlorian Westphal 	if (listen_mode) {
535048d19d4SFlorian Westphal 		err = do_recvfile(peerfd, outfd);
536048d19d4SFlorian Westphal 		if (err)
537048d19d4SFlorian Westphal 			return err;
538048d19d4SFlorian Westphal 
539048d19d4SFlorian Westphal 		err = do_mmap(infd, peerfd, size);
540048d19d4SFlorian Westphal 	} else {
541048d19d4SFlorian Westphal 		err = do_mmap(infd, peerfd, size);
542048d19d4SFlorian Westphal 		if (err)
543048d19d4SFlorian Westphal 			return err;
544048d19d4SFlorian Westphal 
545048d19d4SFlorian Westphal 		shutdown(peerfd, SHUT_WR);
546048d19d4SFlorian Westphal 
547048d19d4SFlorian Westphal 		err = do_recvfile(peerfd, outfd);
548048d19d4SFlorian Westphal 	}
549048d19d4SFlorian Westphal 
550048d19d4SFlorian Westphal 	return err;
551048d19d4SFlorian Westphal }
552048d19d4SFlorian Westphal 
553048d19d4SFlorian Westphal static int copyfd_io_sendfile(int infd, int peerfd, int outfd,
554048d19d4SFlorian Westphal 			      unsigned int size)
555048d19d4SFlorian Westphal {
556048d19d4SFlorian Westphal 	int err;
557048d19d4SFlorian Westphal 
558048d19d4SFlorian Westphal 	if (listen_mode) {
559048d19d4SFlorian Westphal 		err = do_recvfile(peerfd, outfd);
560048d19d4SFlorian Westphal 		if (err)
561048d19d4SFlorian Westphal 			return err;
562048d19d4SFlorian Westphal 
563048d19d4SFlorian Westphal 		err = do_sendfile(infd, peerfd, size);
564048d19d4SFlorian Westphal 	} else {
565048d19d4SFlorian Westphal 		err = do_sendfile(infd, peerfd, size);
566048d19d4SFlorian Westphal 		if (err)
567048d19d4SFlorian Westphal 			return err;
568048d19d4SFlorian Westphal 		err = do_recvfile(peerfd, outfd);
569048d19d4SFlorian Westphal 	}
570048d19d4SFlorian Westphal 
571048d19d4SFlorian Westphal 	return err;
572048d19d4SFlorian Westphal }
573048d19d4SFlorian Westphal 
574048d19d4SFlorian Westphal static int copyfd_io(int infd, int peerfd, int outfd)
575048d19d4SFlorian Westphal {
576048d19d4SFlorian Westphal 	int file_size;
577048d19d4SFlorian Westphal 
578048d19d4SFlorian Westphal 	switch (cfg_mode) {
579048d19d4SFlorian Westphal 	case CFG_MODE_POLL:
580048d19d4SFlorian Westphal 		return copyfd_io_poll(infd, peerfd, outfd);
581048d19d4SFlorian Westphal 	case CFG_MODE_MMAP:
582048d19d4SFlorian Westphal 		file_size = get_infd_size(infd);
583048d19d4SFlorian Westphal 		if (file_size < 0)
584048d19d4SFlorian Westphal 			return file_size;
585048d19d4SFlorian Westphal 		return copyfd_io_mmap(infd, peerfd, outfd, file_size);
586048d19d4SFlorian Westphal 	case CFG_MODE_SENDFILE:
587048d19d4SFlorian Westphal 		file_size = get_infd_size(infd);
588048d19d4SFlorian Westphal 		if (file_size < 0)
589048d19d4SFlorian Westphal 			return file_size;
590048d19d4SFlorian Westphal 		return copyfd_io_sendfile(infd, peerfd, outfd, file_size);
591048d19d4SFlorian Westphal 	}
592048d19d4SFlorian Westphal 
593048d19d4SFlorian Westphal 	fprintf(stderr, "Invalid mode %d\n", cfg_mode);
594048d19d4SFlorian Westphal 
595048d19d4SFlorian Westphal 	die_usage();
596048d19d4SFlorian Westphal 	return 1;
597048d19d4SFlorian Westphal }
598048d19d4SFlorian Westphal 
599048d19d4SFlorian Westphal static void check_sockaddr(int pf, struct sockaddr_storage *ss,
600048d19d4SFlorian Westphal 			   socklen_t salen)
601048d19d4SFlorian Westphal {
602048d19d4SFlorian Westphal 	struct sockaddr_in6 *sin6;
603048d19d4SFlorian Westphal 	struct sockaddr_in *sin;
604048d19d4SFlorian Westphal 	socklen_t wanted_size = 0;
605048d19d4SFlorian Westphal 
606048d19d4SFlorian Westphal 	switch (pf) {
607048d19d4SFlorian Westphal 	case AF_INET:
608048d19d4SFlorian Westphal 		wanted_size = sizeof(*sin);
609048d19d4SFlorian Westphal 		sin = (void *)ss;
610048d19d4SFlorian Westphal 		if (!sin->sin_port)
611048d19d4SFlorian Westphal 			fprintf(stderr, "accept: something wrong: ip connection from port 0");
612048d19d4SFlorian Westphal 		break;
613048d19d4SFlorian Westphal 	case AF_INET6:
614048d19d4SFlorian Westphal 		wanted_size = sizeof(*sin6);
615048d19d4SFlorian Westphal 		sin6 = (void *)ss;
616048d19d4SFlorian Westphal 		if (!sin6->sin6_port)
617048d19d4SFlorian Westphal 			fprintf(stderr, "accept: something wrong: ipv6 connection from port 0");
618048d19d4SFlorian Westphal 		break;
619048d19d4SFlorian Westphal 	default:
620048d19d4SFlorian Westphal 		fprintf(stderr, "accept: Unknown pf %d, salen %u\n", pf, salen);
621048d19d4SFlorian Westphal 		return;
622048d19d4SFlorian Westphal 	}
623048d19d4SFlorian Westphal 
624048d19d4SFlorian Westphal 	if (salen != wanted_size)
625048d19d4SFlorian Westphal 		fprintf(stderr, "accept: size mismatch, got %d expected %d\n",
626048d19d4SFlorian Westphal 			(int)salen, wanted_size);
627048d19d4SFlorian Westphal 
628048d19d4SFlorian Westphal 	if (ss->ss_family != pf)
629048d19d4SFlorian Westphal 		fprintf(stderr, "accept: pf mismatch, expect %d, ss_family is %d\n",
630048d19d4SFlorian Westphal 			(int)ss->ss_family, pf);
631048d19d4SFlorian Westphal }
632048d19d4SFlorian Westphal 
633048d19d4SFlorian Westphal static void check_getpeername(int fd, struct sockaddr_storage *ss, socklen_t salen)
634048d19d4SFlorian Westphal {
635048d19d4SFlorian Westphal 	struct sockaddr_storage peerss;
636048d19d4SFlorian Westphal 	socklen_t peersalen = sizeof(peerss);
637048d19d4SFlorian Westphal 
638048d19d4SFlorian Westphal 	if (getpeername(fd, (struct sockaddr *)&peerss, &peersalen) < 0) {
639048d19d4SFlorian Westphal 		perror("getpeername");
640048d19d4SFlorian Westphal 		return;
641048d19d4SFlorian Westphal 	}
642048d19d4SFlorian Westphal 
643048d19d4SFlorian Westphal 	if (peersalen != salen) {
644048d19d4SFlorian Westphal 		fprintf(stderr, "%s: %d vs %d\n", __func__, peersalen, salen);
645048d19d4SFlorian Westphal 		return;
646048d19d4SFlorian Westphal 	}
647048d19d4SFlorian Westphal 
648048d19d4SFlorian Westphal 	if (memcmp(ss, &peerss, peersalen)) {
649048d19d4SFlorian Westphal 		char a[INET6_ADDRSTRLEN];
650048d19d4SFlorian Westphal 		char b[INET6_ADDRSTRLEN];
651048d19d4SFlorian Westphal 		char c[INET6_ADDRSTRLEN];
652048d19d4SFlorian Westphal 		char d[INET6_ADDRSTRLEN];
653048d19d4SFlorian Westphal 
654048d19d4SFlorian Westphal 		xgetnameinfo((struct sockaddr *)ss, salen,
655048d19d4SFlorian Westphal 			     a, sizeof(a), b, sizeof(b));
656048d19d4SFlorian Westphal 
657048d19d4SFlorian Westphal 		xgetnameinfo((struct sockaddr *)&peerss, peersalen,
658048d19d4SFlorian Westphal 			     c, sizeof(c), d, sizeof(d));
659048d19d4SFlorian Westphal 
660048d19d4SFlorian Westphal 		fprintf(stderr, "%s: memcmp failure: accept %s vs peername %s, %s vs %s salen %d vs %d\n",
661048d19d4SFlorian Westphal 			__func__, a, c, b, d, peersalen, salen);
662048d19d4SFlorian Westphal 	}
663048d19d4SFlorian Westphal }
664048d19d4SFlorian Westphal 
665048d19d4SFlorian Westphal static void check_getpeername_connect(int fd)
666048d19d4SFlorian Westphal {
667048d19d4SFlorian Westphal 	struct sockaddr_storage ss;
668048d19d4SFlorian Westphal 	socklen_t salen = sizeof(ss);
669048d19d4SFlorian Westphal 	char a[INET6_ADDRSTRLEN];
670048d19d4SFlorian Westphal 	char b[INET6_ADDRSTRLEN];
671048d19d4SFlorian Westphal 
672048d19d4SFlorian Westphal 	if (getpeername(fd, (struct sockaddr *)&ss, &salen) < 0) {
673048d19d4SFlorian Westphal 		perror("getpeername");
674048d19d4SFlorian Westphal 		return;
675048d19d4SFlorian Westphal 	}
676048d19d4SFlorian Westphal 
677048d19d4SFlorian Westphal 	xgetnameinfo((struct sockaddr *)&ss, salen,
678048d19d4SFlorian Westphal 		     a, sizeof(a), b, sizeof(b));
679048d19d4SFlorian Westphal 
680048d19d4SFlorian Westphal 	if (strcmp(cfg_host, a) || strcmp(cfg_port, b))
681048d19d4SFlorian Westphal 		fprintf(stderr, "%s: %s vs %s, %s vs %s\n", __func__,
682048d19d4SFlorian Westphal 			cfg_host, a, cfg_port, b);
683048d19d4SFlorian Westphal }
684048d19d4SFlorian Westphal 
685b0519de8SFlorian Westphal static void maybe_close(int fd)
686b0519de8SFlorian Westphal {
687b0519de8SFlorian Westphal 	unsigned int r = rand();
688b0519de8SFlorian Westphal 
689b08fbf24SPaolo Abeni 	if (!cfg_join && (r & 1))
690b0519de8SFlorian Westphal 		close(fd);
691b0519de8SFlorian Westphal }
692b0519de8SFlorian Westphal 
693048d19d4SFlorian Westphal int main_loop_s(int listensock)
694048d19d4SFlorian Westphal {
695048d19d4SFlorian Westphal 	struct sockaddr_storage ss;
696048d19d4SFlorian Westphal 	struct pollfd polls;
697048d19d4SFlorian Westphal 	socklen_t salen;
698048d19d4SFlorian Westphal 	int remotesock;
699048d19d4SFlorian Westphal 
700048d19d4SFlorian Westphal 	polls.fd = listensock;
701048d19d4SFlorian Westphal 	polls.events = POLLIN;
702048d19d4SFlorian Westphal 
703048d19d4SFlorian Westphal 	switch (poll(&polls, 1, poll_timeout)) {
704048d19d4SFlorian Westphal 	case -1:
705048d19d4SFlorian Westphal 		perror("poll");
706048d19d4SFlorian Westphal 		return 1;
707048d19d4SFlorian Westphal 	case 0:
708048d19d4SFlorian Westphal 		fprintf(stderr, "%s: timed out\n", __func__);
709048d19d4SFlorian Westphal 		close(listensock);
710048d19d4SFlorian Westphal 		return 2;
711048d19d4SFlorian Westphal 	}
712048d19d4SFlorian Westphal 
713048d19d4SFlorian Westphal 	salen = sizeof(ss);
714048d19d4SFlorian Westphal 	remotesock = accept(listensock, (struct sockaddr *)&ss, &salen);
715048d19d4SFlorian Westphal 	if (remotesock >= 0) {
716b0519de8SFlorian Westphal 		maybe_close(listensock);
717048d19d4SFlorian Westphal 		check_sockaddr(pf, &ss, salen);
718048d19d4SFlorian Westphal 		check_getpeername(remotesock, &ss, salen);
719048d19d4SFlorian Westphal 
720048d19d4SFlorian Westphal 		return copyfd_io(0, remotesock, 1);
721048d19d4SFlorian Westphal 	}
722048d19d4SFlorian Westphal 
723048d19d4SFlorian Westphal 	perror("accept");
724048d19d4SFlorian Westphal 
725048d19d4SFlorian Westphal 	return 1;
726048d19d4SFlorian Westphal }
727048d19d4SFlorian Westphal 
728048d19d4SFlorian Westphal static void init_rng(void)
729048d19d4SFlorian Westphal {
730048d19d4SFlorian Westphal 	int fd = open("/dev/urandom", O_RDONLY);
731048d19d4SFlorian Westphal 	unsigned int foo;
732048d19d4SFlorian Westphal 
733048d19d4SFlorian Westphal 	if (fd > 0) {
734048d19d4SFlorian Westphal 		int ret = read(fd, &foo, sizeof(foo));
735048d19d4SFlorian Westphal 
736048d19d4SFlorian Westphal 		if (ret < 0)
737048d19d4SFlorian Westphal 			srand(fd + foo);
738048d19d4SFlorian Westphal 		close(fd);
739048d19d4SFlorian Westphal 	}
740048d19d4SFlorian Westphal 
741048d19d4SFlorian Westphal 	srand(foo);
742048d19d4SFlorian Westphal }
743048d19d4SFlorian Westphal 
744048d19d4SFlorian Westphal int main_loop(void)
745048d19d4SFlorian Westphal {
746048d19d4SFlorian Westphal 	int fd;
747048d19d4SFlorian Westphal 
748048d19d4SFlorian Westphal 	/* listener is ready. */
749048d19d4SFlorian Westphal 	fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
750048d19d4SFlorian Westphal 	if (fd < 0)
751048d19d4SFlorian Westphal 		return 2;
752048d19d4SFlorian Westphal 
753048d19d4SFlorian Westphal 	check_getpeername_connect(fd);
754048d19d4SFlorian Westphal 
7558a4b910dSFlorian Westphal 	if (cfg_rcvbuf)
7568a4b910dSFlorian Westphal 		set_rcvbuf(fd, cfg_rcvbuf);
757048d19d4SFlorian Westphal 	if (cfg_sndbuf)
758048d19d4SFlorian Westphal 		set_sndbuf(fd, cfg_sndbuf);
759048d19d4SFlorian Westphal 
760048d19d4SFlorian Westphal 	return copyfd_io(0, fd, 1);
761048d19d4SFlorian Westphal }
762048d19d4SFlorian Westphal 
763048d19d4SFlorian Westphal int parse_proto(const char *proto)
764048d19d4SFlorian Westphal {
765048d19d4SFlorian Westphal 	if (!strcasecmp(proto, "MPTCP"))
766048d19d4SFlorian Westphal 		return IPPROTO_MPTCP;
767048d19d4SFlorian Westphal 	if (!strcasecmp(proto, "TCP"))
768048d19d4SFlorian Westphal 		return IPPROTO_TCP;
769048d19d4SFlorian Westphal 
770048d19d4SFlorian Westphal 	fprintf(stderr, "Unknown protocol: %s\n.", proto);
771048d19d4SFlorian Westphal 	die_usage();
772048d19d4SFlorian Westphal 
773048d19d4SFlorian Westphal 	/* silence compiler warning */
774048d19d4SFlorian Westphal 	return 0;
775048d19d4SFlorian Westphal }
776048d19d4SFlorian Westphal 
777048d19d4SFlorian Westphal int parse_mode(const char *mode)
778048d19d4SFlorian Westphal {
779048d19d4SFlorian Westphal 	if (!strcasecmp(mode, "poll"))
780048d19d4SFlorian Westphal 		return CFG_MODE_POLL;
781048d19d4SFlorian Westphal 	if (!strcasecmp(mode, "mmap"))
782048d19d4SFlorian Westphal 		return CFG_MODE_MMAP;
783048d19d4SFlorian Westphal 	if (!strcasecmp(mode, "sendfile"))
784048d19d4SFlorian Westphal 		return CFG_MODE_SENDFILE;
785048d19d4SFlorian Westphal 
786048d19d4SFlorian Westphal 	fprintf(stderr, "Unknown test mode: %s\n", mode);
787048d19d4SFlorian Westphal 	fprintf(stderr, "Supported modes are:\n");
788048d19d4SFlorian Westphal 	fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
789048d19d4SFlorian Westphal 	fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
790048d19d4SFlorian Westphal 	fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
791048d19d4SFlorian Westphal 
792048d19d4SFlorian Westphal 	die_usage();
793048d19d4SFlorian Westphal 
794048d19d4SFlorian Westphal 	/* silence compiler warning */
795048d19d4SFlorian Westphal 	return 0;
796048d19d4SFlorian Westphal }
797048d19d4SFlorian Westphal 
7988a4b910dSFlorian Westphal static int parse_int(const char *size)
799048d19d4SFlorian Westphal {
800048d19d4SFlorian Westphal 	unsigned long s;
801048d19d4SFlorian Westphal 
802048d19d4SFlorian Westphal 	errno = 0;
803048d19d4SFlorian Westphal 
804048d19d4SFlorian Westphal 	s = strtoul(size, NULL, 0);
805048d19d4SFlorian Westphal 
806048d19d4SFlorian Westphal 	if (errno) {
807048d19d4SFlorian Westphal 		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
808048d19d4SFlorian Westphal 			size, strerror(errno));
809048d19d4SFlorian Westphal 		die_usage();
810048d19d4SFlorian Westphal 	}
811048d19d4SFlorian Westphal 
812048d19d4SFlorian Westphal 	if (s > INT_MAX) {
813048d19d4SFlorian Westphal 		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
814048d19d4SFlorian Westphal 			size, strerror(ERANGE));
815048d19d4SFlorian Westphal 		die_usage();
816048d19d4SFlorian Westphal 	}
817048d19d4SFlorian Westphal 
8188a4b910dSFlorian Westphal 	return (int)s;
819048d19d4SFlorian Westphal }
820048d19d4SFlorian Westphal 
821048d19d4SFlorian Westphal static void parse_opts(int argc, char **argv)
822048d19d4SFlorian Westphal {
823048d19d4SFlorian Westphal 	int c;
824048d19d4SFlorian Westphal 
825df62f2ecSPaolo Abeni 	while ((c = getopt(argc, argv, "6jlp:s:hut:m:S:R:w:")) != -1) {
826048d19d4SFlorian Westphal 		switch (c) {
827b08fbf24SPaolo Abeni 		case 'j':
828b08fbf24SPaolo Abeni 			cfg_join = true;
829b08fbf24SPaolo Abeni 			cfg_mode = CFG_MODE_POLL;
830df62f2ecSPaolo Abeni 			cfg_wait = 400000;
831b08fbf24SPaolo Abeni 			break;
832048d19d4SFlorian Westphal 		case 'l':
833048d19d4SFlorian Westphal 			listen_mode = true;
834048d19d4SFlorian Westphal 			break;
835048d19d4SFlorian Westphal 		case 'p':
836048d19d4SFlorian Westphal 			cfg_port = optarg;
837048d19d4SFlorian Westphal 			break;
838048d19d4SFlorian Westphal 		case 's':
839048d19d4SFlorian Westphal 			cfg_sock_proto = parse_proto(optarg);
840048d19d4SFlorian Westphal 			break;
841048d19d4SFlorian Westphal 		case 'h':
842048d19d4SFlorian Westphal 			die_usage();
843048d19d4SFlorian Westphal 			break;
844048d19d4SFlorian Westphal 		case 'u':
845048d19d4SFlorian Westphal 			tcpulp_audit = true;
846048d19d4SFlorian Westphal 			break;
847048d19d4SFlorian Westphal 		case '6':
848048d19d4SFlorian Westphal 			pf = AF_INET6;
849048d19d4SFlorian Westphal 			break;
850048d19d4SFlorian Westphal 		case 't':
851048d19d4SFlorian Westphal 			poll_timeout = atoi(optarg) * 1000;
852048d19d4SFlorian Westphal 			if (poll_timeout <= 0)
853048d19d4SFlorian Westphal 				poll_timeout = -1;
854048d19d4SFlorian Westphal 			break;
855048d19d4SFlorian Westphal 		case 'm':
856048d19d4SFlorian Westphal 			cfg_mode = parse_mode(optarg);
857048d19d4SFlorian Westphal 			break;
8588a4b910dSFlorian Westphal 		case 'S':
8598a4b910dSFlorian Westphal 			cfg_sndbuf = parse_int(optarg);
8608a4b910dSFlorian Westphal 			break;
8618a4b910dSFlorian Westphal 		case 'R':
8628a4b910dSFlorian Westphal 			cfg_rcvbuf = parse_int(optarg);
863048d19d4SFlorian Westphal 			break;
864df62f2ecSPaolo Abeni 		case 'w':
865df62f2ecSPaolo Abeni 			cfg_wait = atoi(optarg)*1000000;
866df62f2ecSPaolo Abeni 			break;
867048d19d4SFlorian Westphal 		}
868048d19d4SFlorian Westphal 	}
869048d19d4SFlorian Westphal 
870048d19d4SFlorian Westphal 	if (optind + 1 != argc)
871048d19d4SFlorian Westphal 		die_usage();
872048d19d4SFlorian Westphal 	cfg_host = argv[optind];
873048d19d4SFlorian Westphal 
874048d19d4SFlorian Westphal 	if (strchr(cfg_host, ':'))
875048d19d4SFlorian Westphal 		pf = AF_INET6;
876048d19d4SFlorian Westphal }
877048d19d4SFlorian Westphal 
878048d19d4SFlorian Westphal int main(int argc, char *argv[])
879048d19d4SFlorian Westphal {
880048d19d4SFlorian Westphal 	init_rng();
881048d19d4SFlorian Westphal 
882df62f2ecSPaolo Abeni 	signal(SIGUSR1, handle_signal);
883048d19d4SFlorian Westphal 	parse_opts(argc, argv);
884048d19d4SFlorian Westphal 
885048d19d4SFlorian Westphal 	if (tcpulp_audit)
886048d19d4SFlorian Westphal 		return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1;
887048d19d4SFlorian Westphal 
888048d19d4SFlorian Westphal 	if (listen_mode) {
889048d19d4SFlorian Westphal 		int fd = sock_listen_mptcp(cfg_host, cfg_port);
890048d19d4SFlorian Westphal 
891048d19d4SFlorian Westphal 		if (fd < 0)
892048d19d4SFlorian Westphal 			return 1;
893048d19d4SFlorian Westphal 
8948a4b910dSFlorian Westphal 		if (cfg_rcvbuf)
8958a4b910dSFlorian Westphal 			set_rcvbuf(fd, cfg_rcvbuf);
896048d19d4SFlorian Westphal 		if (cfg_sndbuf)
897048d19d4SFlorian Westphal 			set_sndbuf(fd, cfg_sndbuf);
898048d19d4SFlorian Westphal 
899048d19d4SFlorian Westphal 		return main_loop_s(fd);
900048d19d4SFlorian Westphal 	}
901048d19d4SFlorian Westphal 
902048d19d4SFlorian Westphal 	return main_loop();
903048d19d4SFlorian Westphal }
904