File Index
Files and directories
Sign in
FILE

mptcp_sockopt.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/mptcp/mptcp_sockopt.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#define _GNU_SOURCE
  4
  5#include <assert.h>
  6#include <errno.h>
  7#include <fcntl.h>
  8#include <limits.h>
  9#include <string.h>
 10#include <stdarg.h>
 11#include <stdbool.h>
 12#include <stdint.h>
 13#include <inttypes.h>
 14#include <stdio.h>
 15#include <stdlib.h>
 16#include <strings.h>
 17#include <time.h>
 18#include <unistd.h>
 19
 20#include <sys/socket.h>
 21#include <sys/types.h>
 22#include <sys/wait.h>
 23
 24#include <netdb.h>
 25#include <netinet/in.h>
 26
 27#include <linux/tcp.h>
 28#include <linux/compiler.h>
 29
 30static int pf = AF_INET;
 31
 32#ifndef IPPROTO_MPTCP
 33#define IPPROTO_MPTCP 262
 34#endif
 35#ifndef SOL_MPTCP
 36#define SOL_MPTCP 284
 37#endif
 38
 39#ifndef MPTCP_INFO
 40struct mptcp_info {
 41	__u8	mptcpi_subflows;
 42	__u8	mptcpi_add_addr_signal;
 43	__u8	mptcpi_add_addr_accepted;
 44	__u8	mptcpi_subflows_max;
 45	__u8	mptcpi_add_addr_signal_max;
 46	__u8	mptcpi_add_addr_accepted_max;
 47	__u32	mptcpi_flags;
 48	__u32	mptcpi_token;
 49	__u64	mptcpi_write_seq;
 50	__u64	mptcpi_snd_una;
 51	__u64	mptcpi_rcv_nxt;
 52	__u8	mptcpi_local_addr_used;
 53	__u8	mptcpi_local_addr_max;
 54	__u8	mptcpi_csum_enabled;
 55	__u32	mptcpi_retransmits;
 56	__u64	mptcpi_bytes_retrans;
 57	__u64	mptcpi_bytes_sent;
 58	__u64	mptcpi_bytes_received;
 59	__u64	mptcpi_bytes_acked;
 60};
 61
 62struct mptcp_subflow_data {
 63	__u32		size_subflow_data;		/* size of this structure in userspace */
 64	__u32		num_subflows;			/* must be 0, set by kernel */
 65	__u32		size_kernel;			/* must be 0, set by kernel */
 66	__u32		size_user;			/* size of one element in data[] */
 67} __attribute__((aligned(8)));
 68
 69struct mptcp_subflow_addrs {
 70	union {
 71		__kernel_sa_family_t sa_family;
 72		struct sockaddr sa_local;
 73		struct sockaddr_in sin_local;
 74		struct sockaddr_in6 sin6_local;
 75		struct __kernel_sockaddr_storage ss_local;
 76	};
 77	union {
 78		struct sockaddr sa_remote;
 79		struct sockaddr_in sin_remote;
 80		struct sockaddr_in6 sin6_remote;
 81		struct __kernel_sockaddr_storage ss_remote;
 82	};
 83};
 84
 85#define MPTCP_INFO		1
 86#define MPTCP_TCPINFO		2
 87#define MPTCP_SUBFLOW_ADDRS	3
 88#endif
 89
 90#ifndef MPTCP_FULL_INFO
 91struct mptcp_subflow_info {
 92	__u32				id;
 93	struct mptcp_subflow_addrs	addrs;
 94};
 95
 96struct mptcp_full_info {
 97	__u32		size_tcpinfo_kernel;	/* must be 0, set by kernel */
 98	__u32		size_tcpinfo_user;
 99	__u32		size_sfinfo_kernel;	/* must be 0, set by kernel */
100	__u32		size_sfinfo_user;
101	__u32		num_subflows;		/* must be 0, set by kernel (real subflow count) */
102	__u32		size_arrays_user;	/* max subflows that userspace is interested in;
103						 * the buffers at subflow_info/tcp_info
104						 * are respectively at least:
105						 *  size_arrays * size_sfinfo_user
106						 *  size_arrays * size_tcpinfo_user
107						 * bytes wide
108						 */
109	__aligned_u64		subflow_info;
110	__aligned_u64		tcp_info;
111	struct mptcp_info	mptcp_info;
112};
113
114#define MPTCP_FULL_INFO		4
115#endif
116
117struct so_state {
118	struct mptcp_info mi;
119	struct mptcp_info last_sample;
120	struct tcp_info tcp_info;
121	struct mptcp_subflow_addrs addrs;
122	uint64_t mptcpi_rcv_delta;
123	uint64_t tcpi_rcv_delta;
124	bool pkt_stats_avail;
125};
126
127#ifndef MIN
128#define MIN(a, b) ((a) < (b) ? (a) : (b))
129#endif
130
131static void __noreturn die_perror(const char *msg)
132{
133	perror(msg);
134	exit(1);
135}
136
137static void die_usage(int r)
138{
139	fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
140	exit(r);
141}
142
143static void __noreturn xerror(const char *fmt, ...)
144{
145	va_list ap;
146
147	va_start(ap, fmt);
148	vfprintf(stderr, fmt, ap);
149	va_end(ap);
150	fputc('\n', stderr);
151	exit(1);
152}
153
154static const char *getxinfo_strerr(int err)
155{
156	if (err == EAI_SYSTEM)
157		return strerror(errno);
158
159	return gai_strerror(err);
160}
161
162static void xgetaddrinfo(const char *node, const char *service,
163			 struct addrinfo *hints,
164			 struct addrinfo **res)
165{
166	int err;
167
168again:
169	err = getaddrinfo(node, service, hints, res);
170	if (err) {
171		const char *errstr;
172
173		if (err == EAI_SOCKTYPE) {
174			hints->ai_protocol = IPPROTO_TCP;
175			goto again;
176		}
177
178		errstr = getxinfo_strerr(err);
179
180		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
181			node ? node : "", service ? service : "", errstr);
182		exit(1);
183	}
184}
185
186static int sock_listen_mptcp(const char * const listenaddr,
187			     const char * const port)
188{
189	int sock = -1;
190	struct addrinfo hints = {
191		.ai_protocol = IPPROTO_MPTCP,
192		.ai_socktype = SOCK_STREAM,
193		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
194	};
195
196	hints.ai_family = pf;
197
198	struct addrinfo *a, *addr;
199	int one = 1;
200
201	xgetaddrinfo(listenaddr, port, &hints, &addr);
202	hints.ai_family = pf;
203
204	for (a = addr; a; a = a->ai_next) {
205		sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
206		if (sock < 0)
207			continue;
208
209		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
210				     sizeof(one)))
211			perror("setsockopt");
212
213		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
214			break; /* success */
215
216		perror("bind");
217		close(sock);
218		sock = -1;
219	}
220
221	freeaddrinfo(addr);
222
223	if (sock < 0)
224		xerror("could not create listen socket");
225
226	if (listen(sock, 20))
227		die_perror("listen");
228
229	return sock;
230}
231
232static int sock_connect_mptcp(const char * const remoteaddr,
233			      const char * const port, int proto)
234{
235	struct addrinfo hints = {
236		.ai_protocol = IPPROTO_MPTCP,
237		.ai_socktype = SOCK_STREAM,
238	};
239	struct addrinfo *a, *addr;
240	int sock = -1;
241
242	hints.ai_family = pf;
243
244	xgetaddrinfo(remoteaddr, port, &hints, &addr);
245	for (a = addr; a; a = a->ai_next) {
246		sock = socket(a->ai_family, a->ai_socktype, proto);
247		if (sock < 0)
248			continue;
249
250		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
251			break; /* success */
252
253		die_perror("connect");
254	}
255
256	if (sock < 0)
257		xerror("could not create connect socket");
258
259	freeaddrinfo(addr);
260	return sock;
261}
262
263static void parse_opts(int argc, char **argv)
264{
265	int c;
266
267	while ((c = getopt(argc, argv, "h6")) != -1) {
268		switch (c) {
269		case 'h':
270			die_usage(0);
271			break;
272		case '6':
273			pf = AF_INET6;
274			break;
275		default:
276			die_usage(1);
277			break;
278		}
279	}
280}
281
282static void do_getsockopt_bogus_sf_data(int fd, int optname)
283{
284	struct mptcp_subflow_data good_data;
285	struct bogus_data {
286		struct mptcp_subflow_data d;
287		char buf[2];
288	} bd;
289	socklen_t olen, _olen;
290	int ret;
291
292	memset(&bd, 0, sizeof(bd));
293	memset(&good_data, 0, sizeof(good_data));
294
295	olen = sizeof(good_data);
296	good_data.size_subflow_data = olen;
297
298	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
299	assert(ret < 0); /* 0 size_subflow_data */
300	assert(olen == sizeof(good_data));
301
302	bd.d = good_data;
303
304	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
305	assert(ret == 0);
306	assert(olen == sizeof(good_data));
307	assert(bd.d.num_subflows == 1);
308	assert(bd.d.size_kernel > 0);
309	assert(bd.d.size_user == 0);
310
311	bd.d = good_data;
312	_olen = rand() % olen;
313	olen = _olen;
314	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
315	assert(ret < 0);	/* bogus olen */
316	assert(olen == _olen);	/* must be unchanged */
317
318	bd.d = good_data;
319	olen = sizeof(good_data);
320	bd.d.size_kernel = 1;
321	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
322	assert(ret < 0); /* size_kernel not 0 */
323
324	bd.d = good_data;
325	olen = sizeof(good_data);
326	bd.d.num_subflows = 1;
327	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
328	assert(ret < 0); /* num_subflows not 0 */
329
330	/* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */
331	bd.d = good_data;
332	olen = sizeof(bd);
333	bd.d.size_subflow_data = sizeof(bd);
334
335	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
336	assert(ret == 0);
337
338	/* olen must be truncated to real data size filled by kernel: */
339	assert(olen == sizeof(good_data));
340
341	assert(bd.d.size_subflow_data == sizeof(bd));
342
343	bd.d = good_data;
344	bd.d.size_subflow_data += 1;
345	bd.d.size_user = 1;
346	olen = bd.d.size_subflow_data + 1;
347	_olen = olen;
348
349	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen);
350	assert(ret == 0);
351
352	/* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */
353	assert(olen == _olen);
354
355	assert(bd.d.size_subflow_data == sizeof(good_data) + 1);
356	assert(bd.buf[0] == 0);
357}
358
359static void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w)
360{
361	struct mptcp_info i;
362	socklen_t olen;
363	int ret;
364
365	olen = sizeof(i);
366	ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen);
367
368	if (ret < 0)
369		die_perror("getsockopt MPTCP_INFO");
370
371	s->pkt_stats_avail = olen >= sizeof(i);
372
373	s->last_sample = i;
374	if (s->mi.mptcpi_write_seq == 0)
375		s->mi = i;
376
377	assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq);
378
379	s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt;
380}
381
382static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w)
383{
384	struct my_tcp_info {
385		struct mptcp_subflow_data d;
386		struct tcp_info ti[2];
387	} ti;
388	int ret, tries = 5;
389	socklen_t olen;
390
391	do {
392		memset(&ti, 0, sizeof(ti));
393
394		ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
395		ti.d.size_user = sizeof(struct tcp_info);
396		olen = sizeof(ti);
397
398		ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen);
399		if (ret < 0)
400			xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
401
402		assert(olen <= sizeof(ti));
403		assert(ti.d.size_kernel > 0);
404		assert(ti.d.size_user ==
405		       MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
406		assert(ti.d.num_subflows == 1);
407
408		assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
409		olen -= sizeof(struct mptcp_subflow_data);
410		assert(olen == ti.d.size_user);
411
412		s->tcp_info = ti.ti[0];
413
414		if (ti.ti[0].tcpi_bytes_sent == w &&
415		    ti.ti[0].tcpi_bytes_received == r)
416			goto done;
417
418		if (r == 0 && ti.ti[0].tcpi_bytes_sent == w &&
419		    ti.ti[0].tcpi_bytes_received) {
420			s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received;
421			goto done;
422		}
423
424		/* wait and repeat, might be that tx is still ongoing */
425		sleep(1);
426	} while (tries-- > 0);
427
428	xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu",
429		ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r);
430
431done:
432	do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
433}
434
435static void do_getsockopt_subflow_addrs(struct so_state *s, int fd)
436{
437	struct sockaddr_storage remote, local;
438	socklen_t olen, rlen, llen;
439	int ret;
440	struct my_addrs {
441		struct mptcp_subflow_data d;
442		struct mptcp_subflow_addrs addr[2];
443	} addrs;
444
445	memset(&addrs, 0, sizeof(addrs));
446	memset(&local, 0, sizeof(local));
447	memset(&remote, 0, sizeof(remote));
448
449	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
450	addrs.d.size_user = sizeof(struct mptcp_subflow_addrs);
451	olen = sizeof(addrs);
452
453	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
454	if (ret < 0)
455		die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
456
457	assert(olen <= sizeof(addrs));
458	assert(addrs.d.size_kernel > 0);
459	assert(addrs.d.size_user ==
460	       MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
461	assert(addrs.d.num_subflows == 1);
462
463	assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
464	olen -= sizeof(struct mptcp_subflow_data);
465	assert(olen == addrs.d.size_user);
466
467	llen = sizeof(local);
468	ret = getsockname(fd, (struct sockaddr *)&local, &llen);
469	if (ret < 0)
470		die_perror("getsockname");
471	rlen = sizeof(remote);
472	ret = getpeername(fd, (struct sockaddr *)&remote, &rlen);
473	if (ret < 0)
474		die_perror("getpeername");
475
476	assert(rlen > 0);
477	assert(rlen == llen);
478
479	assert(remote.ss_family == local.ss_family);
480
481	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0);
482	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0);
483	s->addrs = addrs.addr[0];
484
485	memset(&addrs, 0, sizeof(addrs));
486
487	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
488	addrs.d.size_user = sizeof(sa_family_t);
489	olen = sizeof(addrs.d) + sizeof(sa_family_t);
490
491	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
492	assert(ret == 0);
493	assert(olen == sizeof(addrs.d) + sizeof(sa_family_t));
494
495	assert(addrs.addr[0].sa_family == pf);
496	assert(addrs.addr[0].sa_family == local.ss_family);
497
498	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) != 0);
499	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) != 0);
500
501	do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
502}
503
504static void do_getsockopt_mptcp_full_info(struct so_state *s, int fd)
505{
506	size_t data_size = sizeof(struct mptcp_full_info);
507	struct mptcp_subflow_info sfinfo[2];
508	struct tcp_info tcp_info[2];
509	struct mptcp_full_info mfi;
510	socklen_t olen;
511	int ret;
512
513	memset(&mfi, 0, data_size);
514	memset(tcp_info, 0, sizeof(tcp_info));
515	memset(sfinfo, 0, sizeof(sfinfo));
516
517	mfi.size_tcpinfo_user = sizeof(struct tcp_info);
518	mfi.size_sfinfo_user = sizeof(struct mptcp_subflow_info);
519	mfi.size_arrays_user = 2;
520	mfi.subflow_info = (unsigned long)&sfinfo[0];
521	mfi.tcp_info = (unsigned long)&tcp_info[0];
522	olen = data_size;
523
524	ret = getsockopt(fd, SOL_MPTCP, MPTCP_FULL_INFO, &mfi, &olen);
525	if (ret < 0) {
526		if (errno == EOPNOTSUPP) {
527			perror("MPTCP_FULL_INFO test skipped");
528			return;
529		}
530		xerror("getsockopt MPTCP_FULL_INFO");
531	}
532
533	assert(olen <= data_size);
534	assert(mfi.size_tcpinfo_kernel > 0);
535	assert(mfi.size_tcpinfo_user ==
536	       MIN(mfi.size_tcpinfo_kernel, sizeof(struct tcp_info)));
537	assert(mfi.size_sfinfo_kernel > 0);
538	assert(mfi.size_sfinfo_user ==
539	       MIN(mfi.size_sfinfo_kernel, sizeof(struct mptcp_subflow_info)));
540	assert(mfi.num_subflows == 1);
541
542	/* Tolerate future extension to mptcp_info struct and running newer
543	 * test on top of older kernel.
544	 * Anyway any kernel supporting MPTCP_FULL_INFO must at least include
545	 * the following in mptcp_info.
546	 */
547	assert(olen > (socklen_t)__builtin_offsetof(struct mptcp_full_info, tcp_info));
548	assert(mfi.mptcp_info.mptcpi_subflows == 0);
549	assert(mfi.mptcp_info.mptcpi_bytes_sent == s->last_sample.mptcpi_bytes_sent);
550	assert(mfi.mptcp_info.mptcpi_bytes_received == s->last_sample.mptcpi_bytes_received);
551
552	assert(sfinfo[0].id == 1);
553	assert(tcp_info[0].tcpi_bytes_sent == s->tcp_info.tcpi_bytes_sent);
554	assert(tcp_info[0].tcpi_bytes_received == s->tcp_info.tcpi_bytes_received);
555	assert(!memcmp(&sfinfo->addrs, &s->addrs, sizeof(struct mptcp_subflow_addrs)));
556}
557
558static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
559{
560	do_getsockopt_mptcp_info(s, fd, w);
561
562	do_getsockopt_tcp_info(s, fd, r, w);
563
564	do_getsockopt_subflow_addrs(s, fd);
565
566	if (r)
567		do_getsockopt_mptcp_full_info(s, fd);
568}
569
570static void connect_one_server(int fd, int pipefd)
571{
572	char buf[4096], buf2[4096];
573	size_t len, i, total;
574	struct so_state s;
575	bool eof = false;
576	ssize_t ret;
577
578	memset(&s, 0, sizeof(s));
579
580	len = rand() % (sizeof(buf) - 1);
581
582	if (len < 128)
583		len = 128;
584
585	for (i = 0; i < len ; i++) {
586		buf[i] = rand() % 26;
587		buf[i] += 'A';
588	}
589
590	buf[i] = '\n';
591
592	do_getsockopts(&s, fd, 0, 0);
593
594	/* un-block server */
595	ret = read(pipefd, buf2, 4);
596	assert(ret == 4);
597	close(pipefd);
598
599	assert(strncmp(buf2, "xmit", 4) == 0);
600
601	ret = write(fd, buf, len);
602	if (ret < 0)
603		die_perror("write");
604
605	if (ret != (ssize_t)len)
606		xerror("short write");
607
608	total = 0;
609	do {
610		ret = read(fd, buf2 + total, sizeof(buf2) - total);
611		if (ret < 0)
612			die_perror("read");
613		if (ret == 0) {
614			eof = true;
615			break;
616		}
617
618		total += ret;
619	} while (total < len);
620
621	if (total != len)
622		xerror("total %lu, len %lu eof %d\n", total, len, eof);
623
624	if (memcmp(buf, buf2, len))
625		xerror("data corruption");
626
627	if (s.tcpi_rcv_delta)
628		assert(s.tcpi_rcv_delta <= total);
629
630	do_getsockopts(&s, fd, ret, ret);
631
632	if (eof)
633		total += 1; /* sequence advances due to FIN */
634
635	assert(s.mptcpi_rcv_delta == (uint64_t)total);
636	close(fd);
637}
638
639static void process_one_client(int fd, int pipefd)
640{
641	ssize_t ret, ret2, ret3;
642	struct so_state s;
643	char buf[4096];
644
645	memset(&s, 0, sizeof(s));
646	do_getsockopts(&s, fd, 0, 0);
647
648	ret = write(pipefd, "xmit", 4);
649	assert(ret == 4);
650
651	ret = read(fd, buf, sizeof(buf));
652	if (ret < 0)
653		die_perror("read");
654
655	assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
656
657	if (s.tcpi_rcv_delta)
658		assert(s.tcpi_rcv_delta == (uint64_t)ret);
659
660	ret2 = write(fd, buf, ret);
661	if (ret2 < 0)
662		die_perror("write");
663
664	/* wait for hangup */
665	ret3 = read(fd, buf, 1);
666	if (ret3 != 0)
667		xerror("expected EOF, got %lu", ret3);
668
669	do_getsockopts(&s, fd, ret, ret2);
670	if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
671		xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64,
672		       s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - (ret + 1));
673
674	/* be nice when running on top of older kernel */
675	if (s.pkt_stats_avail) {
676		if (s.last_sample.mptcpi_bytes_sent != ret2)
677			xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64
678			       ", diff %" PRId64,
679			       s.last_sample.mptcpi_bytes_sent, ret2,
680			       s.last_sample.mptcpi_bytes_sent - ret2);
681		if (s.last_sample.mptcpi_bytes_received != ret)
682			xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64
683			       ", diff %" PRId64,
684			       s.last_sample.mptcpi_bytes_received, ret,
685			       s.last_sample.mptcpi_bytes_received - ret);
686		if (s.last_sample.mptcpi_bytes_acked != ret)
687			xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64
688			       ", diff %" PRId64,
689			       s.last_sample.mptcpi_bytes_acked, ret,
690			       s.last_sample.mptcpi_bytes_acked - ret);
691	}
692
693	close(fd);
694}
695
696static int xaccept(int s)
697{
698	int fd = accept(s, NULL, 0);
699
700	if (fd < 0)
701		die_perror("accept");
702
703	return fd;
704}
705
706static int server(int pipefd)
707{
708	int fd = -1, r;
709
710	switch (pf) {
711	case AF_INET:
712		fd = sock_listen_mptcp("127.0.0.1", "15432");
713		break;
714	case AF_INET6:
715		fd = sock_listen_mptcp("::1", "15432");
716		break;
717	default:
718		xerror("Unknown pf %d\n", pf);
719		break;
720	}
721
722	r = write(pipefd, "conn", 4);
723	assert(r == 4);
724
725	alarm(15);
726	r = xaccept(fd);
727
728	process_one_client(r, pipefd);
729
730	close(fd);
731	return 0;
732}
733
734static void test_ip_tos_sockopt(int fd)
735{
736	uint8_t tos_in, tos_out;
737	socklen_t s;
738	int r;
739
740	tos_in = rand() & 0xfc;
741	r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out));
742	if (r != 0)
743		die_perror("setsockopt IP_TOS");
744
745	tos_out = 0;
746	s = sizeof(tos_out);
747	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
748	if (r != 0)
749		die_perror("getsockopt IP_TOS");
750
751	if (tos_in != tos_out)
752		xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s);
753
754	if (s != 1)
755		xerror("tos should be 1 byte");
756
757	s = 0;
758	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
759	if (r != 0)
760		die_perror("getsockopt IP_TOS 0");
761	if (s != 0)
762		xerror("expect socklen_t == 0");
763
764	s = -1;
765	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
766	if (r != -1 && errno != EINVAL)
767		die_perror("getsockopt IP_TOS did not indicate -EINVAL");
768	if (s != -1)
769		xerror("expect socklen_t == -1");
770}
771
772static int client(int pipefd)
773{
774	int fd = -1;
775
776	alarm(15);
777
778	switch (pf) {
779	case AF_INET:
780		fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP);
781		break;
782	case AF_INET6:
783		fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP);
784		break;
785	default:
786		xerror("Unknown pf %d\n", pf);
787	}
788
789	test_ip_tos_sockopt(fd);
790
791	connect_one_server(fd, pipefd);
792
793	return 0;
794}
795
796static pid_t xfork(void)
797{
798	pid_t p = fork();
799
800	if (p < 0)
801		die_perror("fork");
802
803	return p;
804}
805
806static int rcheck(int wstatus, const char *what)
807{
808	if (WIFEXITED(wstatus)) {
809		if (WEXITSTATUS(wstatus) == 0)
810			return 0;
811		fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
812		return WEXITSTATUS(wstatus);
813	} else if (WIFSIGNALED(wstatus)) {
814		xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
815	} else if (WIFSTOPPED(wstatus)) {
816		xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
817	}
818
819	return 111;
820}
821
822static void init_rng(void)
823{
824	int fd = open("/dev/urandom", O_RDONLY);
825
826	if (fd >= 0) {
827		unsigned int foo;
828		ssize_t ret;
829
830		/* can't fail */
831		ret = read(fd, &foo, sizeof(foo));
832		assert(ret == sizeof(foo));
833
834		close(fd);
835		srand(foo);
836	} else {
837		srand(time(NULL));
838	}
839}
840
841int main(int argc, char *argv[])
842{
843	int e1, e2, wstatus;
844	pid_t s, c, ret;
845	int pipefds[2];
846
847	parse_opts(argc, argv);
848
849	init_rng();
850
851	e1 = pipe(pipefds);
852	if (e1 < 0)
853		die_perror("pipe");
854
855	s = xfork();
856	if (s == 0) {
857		close(pipefds[0]);
858		ret = server(pipefds[1]);
859		close(pipefds[1]);
860		return ret;
861	}
862
863	close(pipefds[1]);
864
865	/* wait until server bound a socket */
866	e1 = read(pipefds[0], &e1, 4);
867	assert(e1 == 4);
868
869	c = xfork();
870	if (c == 0)
871		return client(pipefds[0]);
872
873	close(pipefds[0]);
874
875	ret = waitpid(s, &wstatus, 0);
876	if (ret == -1)
877		die_perror("waitpid");
878	e1 = rcheck(wstatus, "server");
879	ret = waitpid(c, &wstatus, 0);
880	if (ret == -1)
881		die_perror("waitpid");
882	e2 = rcheck(wstatus, "client");
883
884	return e1 ? e1 : e2;
885}
~/ Read-only file preview.