brintos

brintos / linux-shallow public Read only

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