162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#define _GNU_SOURCE
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <assert.h>
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <fcntl.h>
862306a36Sopenharmony_ci#include <limits.h>
962306a36Sopenharmony_ci#include <string.h>
1062306a36Sopenharmony_ci#include <stdarg.h>
1162306a36Sopenharmony_ci#include <stdbool.h>
1262306a36Sopenharmony_ci#include <stdint.h>
1362306a36Sopenharmony_ci#include <inttypes.h>
1462306a36Sopenharmony_ci#include <stdio.h>
1562306a36Sopenharmony_ci#include <stdlib.h>
1662306a36Sopenharmony_ci#include <strings.h>
1762306a36Sopenharmony_ci#include <time.h>
1862306a36Sopenharmony_ci#include <unistd.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sys/socket.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci#include <sys/wait.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <netdb.h>
2562306a36Sopenharmony_ci#include <netinet/in.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/tcp.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int pf = AF_INET;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifndef IPPROTO_MPTCP
3262306a36Sopenharmony_ci#define IPPROTO_MPTCP 262
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci#ifndef SOL_MPTCP
3562306a36Sopenharmony_ci#define SOL_MPTCP 284
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#ifndef MPTCP_INFO
3962306a36Sopenharmony_cistruct mptcp_info {
4062306a36Sopenharmony_ci	__u8	mptcpi_subflows;
4162306a36Sopenharmony_ci	__u8	mptcpi_add_addr_signal;
4262306a36Sopenharmony_ci	__u8	mptcpi_add_addr_accepted;
4362306a36Sopenharmony_ci	__u8	mptcpi_subflows_max;
4462306a36Sopenharmony_ci	__u8	mptcpi_add_addr_signal_max;
4562306a36Sopenharmony_ci	__u8	mptcpi_add_addr_accepted_max;
4662306a36Sopenharmony_ci	__u32	mptcpi_flags;
4762306a36Sopenharmony_ci	__u32	mptcpi_token;
4862306a36Sopenharmony_ci	__u64	mptcpi_write_seq;
4962306a36Sopenharmony_ci	__u64	mptcpi_snd_una;
5062306a36Sopenharmony_ci	__u64	mptcpi_rcv_nxt;
5162306a36Sopenharmony_ci	__u8	mptcpi_local_addr_used;
5262306a36Sopenharmony_ci	__u8	mptcpi_local_addr_max;
5362306a36Sopenharmony_ci	__u8	mptcpi_csum_enabled;
5462306a36Sopenharmony_ci	__u32	mptcpi_retransmits;
5562306a36Sopenharmony_ci	__u64	mptcpi_bytes_retrans;
5662306a36Sopenharmony_ci	__u64	mptcpi_bytes_sent;
5762306a36Sopenharmony_ci	__u64	mptcpi_bytes_received;
5862306a36Sopenharmony_ci	__u64	mptcpi_bytes_acked;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct mptcp_subflow_data {
6262306a36Sopenharmony_ci	__u32		size_subflow_data;		/* size of this structure in userspace */
6362306a36Sopenharmony_ci	__u32		num_subflows;			/* must be 0, set by kernel */
6462306a36Sopenharmony_ci	__u32		size_kernel;			/* must be 0, set by kernel */
6562306a36Sopenharmony_ci	__u32		size_user;			/* size of one element in data[] */
6662306a36Sopenharmony_ci} __attribute__((aligned(8)));
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct mptcp_subflow_addrs {
6962306a36Sopenharmony_ci	union {
7062306a36Sopenharmony_ci		__kernel_sa_family_t sa_family;
7162306a36Sopenharmony_ci		struct sockaddr sa_local;
7262306a36Sopenharmony_ci		struct sockaddr_in sin_local;
7362306a36Sopenharmony_ci		struct sockaddr_in6 sin6_local;
7462306a36Sopenharmony_ci		struct __kernel_sockaddr_storage ss_local;
7562306a36Sopenharmony_ci	};
7662306a36Sopenharmony_ci	union {
7762306a36Sopenharmony_ci		struct sockaddr sa_remote;
7862306a36Sopenharmony_ci		struct sockaddr_in sin_remote;
7962306a36Sopenharmony_ci		struct sockaddr_in6 sin6_remote;
8062306a36Sopenharmony_ci		struct __kernel_sockaddr_storage ss_remote;
8162306a36Sopenharmony_ci	};
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define MPTCP_INFO		1
8562306a36Sopenharmony_ci#define MPTCP_TCPINFO		2
8662306a36Sopenharmony_ci#define MPTCP_SUBFLOW_ADDRS	3
8762306a36Sopenharmony_ci#endif
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#ifndef MPTCP_FULL_INFO
9062306a36Sopenharmony_cistruct mptcp_subflow_info {
9162306a36Sopenharmony_ci	__u32				id;
9262306a36Sopenharmony_ci	struct mptcp_subflow_addrs	addrs;
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct mptcp_full_info {
9662306a36Sopenharmony_ci	__u32		size_tcpinfo_kernel;	/* must be 0, set by kernel */
9762306a36Sopenharmony_ci	__u32		size_tcpinfo_user;
9862306a36Sopenharmony_ci	__u32		size_sfinfo_kernel;	/* must be 0, set by kernel */
9962306a36Sopenharmony_ci	__u32		size_sfinfo_user;
10062306a36Sopenharmony_ci	__u32		num_subflows;		/* must be 0, set by kernel (real subflow count) */
10162306a36Sopenharmony_ci	__u32		size_arrays_user;	/* max subflows that userspace is interested in;
10262306a36Sopenharmony_ci						 * the buffers at subflow_info/tcp_info
10362306a36Sopenharmony_ci						 * are respectively at least:
10462306a36Sopenharmony_ci						 *  size_arrays * size_sfinfo_user
10562306a36Sopenharmony_ci						 *  size_arrays * size_tcpinfo_user
10662306a36Sopenharmony_ci						 * bytes wide
10762306a36Sopenharmony_ci						 */
10862306a36Sopenharmony_ci	__aligned_u64		subflow_info;
10962306a36Sopenharmony_ci	__aligned_u64		tcp_info;
11062306a36Sopenharmony_ci	struct mptcp_info	mptcp_info;
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define MPTCP_FULL_INFO		4
11462306a36Sopenharmony_ci#endif
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistruct so_state {
11762306a36Sopenharmony_ci	struct mptcp_info mi;
11862306a36Sopenharmony_ci	struct mptcp_info last_sample;
11962306a36Sopenharmony_ci	struct tcp_info tcp_info;
12062306a36Sopenharmony_ci	struct mptcp_subflow_addrs addrs;
12162306a36Sopenharmony_ci	uint64_t mptcpi_rcv_delta;
12262306a36Sopenharmony_ci	uint64_t tcpi_rcv_delta;
12362306a36Sopenharmony_ci	bool pkt_stats_avail;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#ifndef MIN
12762306a36Sopenharmony_ci#define MIN(a, b) ((a) < (b) ? (a) : (b))
12862306a36Sopenharmony_ci#endif
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void die_perror(const char *msg)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	perror(msg);
13362306a36Sopenharmony_ci	exit(1);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void die_usage(int r)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
13962306a36Sopenharmony_ci	exit(r);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void xerror(const char *fmt, ...)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	va_list ap;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	va_start(ap, fmt);
14762306a36Sopenharmony_ci	vfprintf(stderr, fmt, ap);
14862306a36Sopenharmony_ci	va_end(ap);
14962306a36Sopenharmony_ci	fputc('\n', stderr);
15062306a36Sopenharmony_ci	exit(1);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const char *getxinfo_strerr(int err)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (err == EAI_SYSTEM)
15662306a36Sopenharmony_ci		return strerror(errno);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return gai_strerror(err);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void xgetaddrinfo(const char *node, const char *service,
16262306a36Sopenharmony_ci			 const struct addrinfo *hints,
16362306a36Sopenharmony_ci			 struct addrinfo **res)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int err = getaddrinfo(node, service, hints, res);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (err) {
16862306a36Sopenharmony_ci		const char *errstr = getxinfo_strerr(err);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
17162306a36Sopenharmony_ci			node ? node : "", service ? service : "", errstr);
17262306a36Sopenharmony_ci		exit(1);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int sock_listen_mptcp(const char * const listenaddr,
17762306a36Sopenharmony_ci			     const char * const port)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int sock = -1;
18062306a36Sopenharmony_ci	struct addrinfo hints = {
18162306a36Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
18262306a36Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
18362306a36Sopenharmony_ci		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
18462306a36Sopenharmony_ci	};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	hints.ai_family = pf;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	struct addrinfo *a, *addr;
18962306a36Sopenharmony_ci	int one = 1;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	xgetaddrinfo(listenaddr, port, &hints, &addr);
19262306a36Sopenharmony_ci	hints.ai_family = pf;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
19562306a36Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
19662306a36Sopenharmony_ci		if (sock < 0)
19762306a36Sopenharmony_ci			continue;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
20062306a36Sopenharmony_ci				     sizeof(one)))
20162306a36Sopenharmony_ci			perror("setsockopt");
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
20462306a36Sopenharmony_ci			break; /* success */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		perror("bind");
20762306a36Sopenharmony_ci		close(sock);
20862306a36Sopenharmony_ci		sock = -1;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	freeaddrinfo(addr);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (sock < 0)
21462306a36Sopenharmony_ci		xerror("could not create listen socket");
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (listen(sock, 20))
21762306a36Sopenharmony_ci		die_perror("listen");
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return sock;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int sock_connect_mptcp(const char * const remoteaddr,
22362306a36Sopenharmony_ci			      const char * const port, int proto)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct addrinfo hints = {
22662306a36Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
22762306a36Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
22862306a36Sopenharmony_ci	};
22962306a36Sopenharmony_ci	struct addrinfo *a, *addr;
23062306a36Sopenharmony_ci	int sock = -1;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	hints.ai_family = pf;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	xgetaddrinfo(remoteaddr, port, &hints, &addr);
23562306a36Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
23662306a36Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, proto);
23762306a36Sopenharmony_ci		if (sock < 0)
23862306a36Sopenharmony_ci			continue;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
24162306a36Sopenharmony_ci			break; /* success */
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		die_perror("connect");
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (sock < 0)
24762306a36Sopenharmony_ci		xerror("could not create connect socket");
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	freeaddrinfo(addr);
25062306a36Sopenharmony_ci	return sock;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void parse_opts(int argc, char **argv)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int c;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	while ((c = getopt(argc, argv, "h6")) != -1) {
25862306a36Sopenharmony_ci		switch (c) {
25962306a36Sopenharmony_ci		case 'h':
26062306a36Sopenharmony_ci			die_usage(0);
26162306a36Sopenharmony_ci			break;
26262306a36Sopenharmony_ci		case '6':
26362306a36Sopenharmony_ci			pf = AF_INET6;
26462306a36Sopenharmony_ci			break;
26562306a36Sopenharmony_ci		default:
26662306a36Sopenharmony_ci			die_usage(1);
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void do_getsockopt_bogus_sf_data(int fd, int optname)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct mptcp_subflow_data good_data;
27562306a36Sopenharmony_ci	struct bogus_data {
27662306a36Sopenharmony_ci		struct mptcp_subflow_data d;
27762306a36Sopenharmony_ci		char buf[2];
27862306a36Sopenharmony_ci	} bd;
27962306a36Sopenharmony_ci	socklen_t olen, _olen;
28062306a36Sopenharmony_ci	int ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	memset(&bd, 0, sizeof(bd));
28362306a36Sopenharmony_ci	memset(&good_data, 0, sizeof(good_data));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	olen = sizeof(good_data);
28662306a36Sopenharmony_ci	good_data.size_subflow_data = olen;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
28962306a36Sopenharmony_ci	assert(ret < 0); /* 0 size_subflow_data */
29062306a36Sopenharmony_ci	assert(olen == sizeof(good_data));
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	bd.d = good_data;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
29562306a36Sopenharmony_ci	assert(ret == 0);
29662306a36Sopenharmony_ci	assert(olen == sizeof(good_data));
29762306a36Sopenharmony_ci	assert(bd.d.num_subflows == 1);
29862306a36Sopenharmony_ci	assert(bd.d.size_kernel > 0);
29962306a36Sopenharmony_ci	assert(bd.d.size_user == 0);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	bd.d = good_data;
30262306a36Sopenharmony_ci	_olen = rand() % olen;
30362306a36Sopenharmony_ci	olen = _olen;
30462306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
30562306a36Sopenharmony_ci	assert(ret < 0);	/* bogus olen */
30662306a36Sopenharmony_ci	assert(olen == _olen);	/* must be unchanged */
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	bd.d = good_data;
30962306a36Sopenharmony_ci	olen = sizeof(good_data);
31062306a36Sopenharmony_ci	bd.d.size_kernel = 1;
31162306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
31262306a36Sopenharmony_ci	assert(ret < 0); /* size_kernel not 0 */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	bd.d = good_data;
31562306a36Sopenharmony_ci	olen = sizeof(good_data);
31662306a36Sopenharmony_ci	bd.d.num_subflows = 1;
31762306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
31862306a36Sopenharmony_ci	assert(ret < 0); /* num_subflows not 0 */
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */
32162306a36Sopenharmony_ci	bd.d = good_data;
32262306a36Sopenharmony_ci	olen = sizeof(bd);
32362306a36Sopenharmony_ci	bd.d.size_subflow_data = sizeof(bd);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
32662306a36Sopenharmony_ci	assert(ret == 0);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* olen must be truncated to real data size filled by kernel: */
32962306a36Sopenharmony_ci	assert(olen == sizeof(good_data));
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	assert(bd.d.size_subflow_data == sizeof(bd));
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	bd.d = good_data;
33462306a36Sopenharmony_ci	bd.d.size_subflow_data += 1;
33562306a36Sopenharmony_ci	bd.d.size_user = 1;
33662306a36Sopenharmony_ci	olen = bd.d.size_subflow_data + 1;
33762306a36Sopenharmony_ci	_olen = olen;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen);
34062306a36Sopenharmony_ci	assert(ret == 0);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */
34362306a36Sopenharmony_ci	assert(olen == _olen);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	assert(bd.d.size_subflow_data == sizeof(good_data) + 1);
34662306a36Sopenharmony_ci	assert(bd.buf[0] == 0);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct mptcp_info i;
35262306a36Sopenharmony_ci	socklen_t olen;
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	olen = sizeof(i);
35662306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (ret < 0)
35962306a36Sopenharmony_ci		die_perror("getsockopt MPTCP_INFO");
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	s->pkt_stats_avail = olen >= sizeof(i);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	s->last_sample = i;
36462306a36Sopenharmony_ci	if (s->mi.mptcpi_write_seq == 0)
36562306a36Sopenharmony_ci		s->mi = i;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct my_tcp_info {
37562306a36Sopenharmony_ci		struct mptcp_subflow_data d;
37662306a36Sopenharmony_ci		struct tcp_info ti[2];
37762306a36Sopenharmony_ci	} ti;
37862306a36Sopenharmony_ci	int ret, tries = 5;
37962306a36Sopenharmony_ci	socklen_t olen;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	do {
38262306a36Sopenharmony_ci		memset(&ti, 0, sizeof(ti));
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
38562306a36Sopenharmony_ci		ti.d.size_user = sizeof(struct tcp_info);
38662306a36Sopenharmony_ci		olen = sizeof(ti);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen);
38962306a36Sopenharmony_ci		if (ret < 0)
39062306a36Sopenharmony_ci			xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		assert(olen <= sizeof(ti));
39362306a36Sopenharmony_ci		assert(ti.d.size_kernel > 0);
39462306a36Sopenharmony_ci		assert(ti.d.size_user ==
39562306a36Sopenharmony_ci		       MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
39662306a36Sopenharmony_ci		assert(ti.d.num_subflows == 1);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
39962306a36Sopenharmony_ci		olen -= sizeof(struct mptcp_subflow_data);
40062306a36Sopenharmony_ci		assert(olen == ti.d.size_user);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		s->tcp_info = ti.ti[0];
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (ti.ti[0].tcpi_bytes_sent == w &&
40562306a36Sopenharmony_ci		    ti.ti[0].tcpi_bytes_received == r)
40662306a36Sopenharmony_ci			goto done;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		if (r == 0 && ti.ti[0].tcpi_bytes_sent == w &&
40962306a36Sopenharmony_ci		    ti.ti[0].tcpi_bytes_received) {
41062306a36Sopenharmony_ci			s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received;
41162306a36Sopenharmony_ci			goto done;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		/* wait and repeat, might be that tx is still ongoing */
41562306a36Sopenharmony_ci		sleep(1);
41662306a36Sopenharmony_ci	} while (tries-- > 0);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu",
41962306a36Sopenharmony_ci		ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cidone:
42262306a36Sopenharmony_ci	do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void do_getsockopt_subflow_addrs(struct so_state *s, int fd)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct sockaddr_storage remote, local;
42862306a36Sopenharmony_ci	socklen_t olen, rlen, llen;
42962306a36Sopenharmony_ci	int ret;
43062306a36Sopenharmony_ci	struct my_addrs {
43162306a36Sopenharmony_ci		struct mptcp_subflow_data d;
43262306a36Sopenharmony_ci		struct mptcp_subflow_addrs addr[2];
43362306a36Sopenharmony_ci	} addrs;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	memset(&addrs, 0, sizeof(addrs));
43662306a36Sopenharmony_ci	memset(&local, 0, sizeof(local));
43762306a36Sopenharmony_ci	memset(&remote, 0, sizeof(remote));
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
44062306a36Sopenharmony_ci	addrs.d.size_user = sizeof(struct mptcp_subflow_addrs);
44162306a36Sopenharmony_ci	olen = sizeof(addrs);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
44462306a36Sopenharmony_ci	if (ret < 0)
44562306a36Sopenharmony_ci		die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	assert(olen <= sizeof(addrs));
44862306a36Sopenharmony_ci	assert(addrs.d.size_kernel > 0);
44962306a36Sopenharmony_ci	assert(addrs.d.size_user ==
45062306a36Sopenharmony_ci	       MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
45162306a36Sopenharmony_ci	assert(addrs.d.num_subflows == 1);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
45462306a36Sopenharmony_ci	olen -= sizeof(struct mptcp_subflow_data);
45562306a36Sopenharmony_ci	assert(olen == addrs.d.size_user);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	llen = sizeof(local);
45862306a36Sopenharmony_ci	ret = getsockname(fd, (struct sockaddr *)&local, &llen);
45962306a36Sopenharmony_ci	if (ret < 0)
46062306a36Sopenharmony_ci		die_perror("getsockname");
46162306a36Sopenharmony_ci	rlen = sizeof(remote);
46262306a36Sopenharmony_ci	ret = getpeername(fd, (struct sockaddr *)&remote, &rlen);
46362306a36Sopenharmony_ci	if (ret < 0)
46462306a36Sopenharmony_ci		die_perror("getpeername");
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	assert(rlen > 0);
46762306a36Sopenharmony_ci	assert(rlen == llen);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	assert(remote.ss_family == local.ss_family);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0);
47262306a36Sopenharmony_ci	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0);
47362306a36Sopenharmony_ci	s->addrs = addrs.addr[0];
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	memset(&addrs, 0, sizeof(addrs));
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
47862306a36Sopenharmony_ci	addrs.d.size_user = sizeof(sa_family_t);
47962306a36Sopenharmony_ci	olen = sizeof(addrs.d) + sizeof(sa_family_t);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
48262306a36Sopenharmony_ci	assert(ret == 0);
48362306a36Sopenharmony_ci	assert(olen == sizeof(addrs.d) + sizeof(sa_family_t));
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	assert(addrs.addr[0].sa_family == pf);
48662306a36Sopenharmony_ci	assert(addrs.addr[0].sa_family == local.ss_family);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) != 0);
48962306a36Sopenharmony_ci	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) != 0);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void do_getsockopt_mptcp_full_info(struct so_state *s, int fd)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	size_t data_size = sizeof(struct mptcp_full_info);
49762306a36Sopenharmony_ci	struct mptcp_subflow_info sfinfo[2];
49862306a36Sopenharmony_ci	struct tcp_info tcp_info[2];
49962306a36Sopenharmony_ci	struct mptcp_full_info mfi;
50062306a36Sopenharmony_ci	socklen_t olen;
50162306a36Sopenharmony_ci	int ret;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	memset(&mfi, 0, data_size);
50462306a36Sopenharmony_ci	memset(tcp_info, 0, sizeof(tcp_info));
50562306a36Sopenharmony_ci	memset(sfinfo, 0, sizeof(sfinfo));
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	mfi.size_tcpinfo_user = sizeof(struct tcp_info);
50862306a36Sopenharmony_ci	mfi.size_sfinfo_user = sizeof(struct mptcp_subflow_info);
50962306a36Sopenharmony_ci	mfi.size_arrays_user = 2;
51062306a36Sopenharmony_ci	mfi.subflow_info = (unsigned long)&sfinfo[0];
51162306a36Sopenharmony_ci	mfi.tcp_info = (unsigned long)&tcp_info[0];
51262306a36Sopenharmony_ci	olen = data_size;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	ret = getsockopt(fd, SOL_MPTCP, MPTCP_FULL_INFO, &mfi, &olen);
51562306a36Sopenharmony_ci	if (ret < 0) {
51662306a36Sopenharmony_ci		if (errno == EOPNOTSUPP) {
51762306a36Sopenharmony_ci			perror("MPTCP_FULL_INFO test skipped");
51862306a36Sopenharmony_ci			return;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci		xerror("getsockopt MPTCP_FULL_INFO");
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	assert(olen <= data_size);
52462306a36Sopenharmony_ci	assert(mfi.size_tcpinfo_kernel > 0);
52562306a36Sopenharmony_ci	assert(mfi.size_tcpinfo_user ==
52662306a36Sopenharmony_ci	       MIN(mfi.size_tcpinfo_kernel, sizeof(struct tcp_info)));
52762306a36Sopenharmony_ci	assert(mfi.size_sfinfo_kernel > 0);
52862306a36Sopenharmony_ci	assert(mfi.size_sfinfo_user ==
52962306a36Sopenharmony_ci	       MIN(mfi.size_sfinfo_kernel, sizeof(struct mptcp_subflow_info)));
53062306a36Sopenharmony_ci	assert(mfi.num_subflows == 1);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Tolerate future extension to mptcp_info struct and running newer
53362306a36Sopenharmony_ci	 * test on top of older kernel.
53462306a36Sopenharmony_ci	 * Anyway any kernel supporting MPTCP_FULL_INFO must at least include
53562306a36Sopenharmony_ci	 * the following in mptcp_info.
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	assert(olen > (socklen_t)__builtin_offsetof(struct mptcp_full_info, tcp_info));
53862306a36Sopenharmony_ci	assert(mfi.mptcp_info.mptcpi_subflows == 0);
53962306a36Sopenharmony_ci	assert(mfi.mptcp_info.mptcpi_bytes_sent == s->last_sample.mptcpi_bytes_sent);
54062306a36Sopenharmony_ci	assert(mfi.mptcp_info.mptcpi_bytes_received == s->last_sample.mptcpi_bytes_received);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	assert(sfinfo[0].id == 1);
54362306a36Sopenharmony_ci	assert(tcp_info[0].tcpi_bytes_sent == s->tcp_info.tcpi_bytes_sent);
54462306a36Sopenharmony_ci	assert(tcp_info[0].tcpi_bytes_received == s->tcp_info.tcpi_bytes_received);
54562306a36Sopenharmony_ci	assert(!memcmp(&sfinfo->addrs, &s->addrs, sizeof(struct mptcp_subflow_addrs)));
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	do_getsockopt_mptcp_info(s, fd, w);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	do_getsockopt_tcp_info(s, fd, r, w);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	do_getsockopt_subflow_addrs(s, fd);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (r)
55762306a36Sopenharmony_ci		do_getsockopt_mptcp_full_info(s, fd);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void connect_one_server(int fd, int pipefd)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	char buf[4096], buf2[4096];
56362306a36Sopenharmony_ci	size_t len, i, total;
56462306a36Sopenharmony_ci	struct so_state s;
56562306a36Sopenharmony_ci	bool eof = false;
56662306a36Sopenharmony_ci	ssize_t ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	memset(&s, 0, sizeof(s));
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	len = rand() % (sizeof(buf) - 1);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (len < 128)
57362306a36Sopenharmony_ci		len = 128;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	for (i = 0; i < len ; i++) {
57662306a36Sopenharmony_ci		buf[i] = rand() % 26;
57762306a36Sopenharmony_ci		buf[i] += 'A';
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	buf[i] = '\n';
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	do_getsockopts(&s, fd, 0, 0);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* un-block server */
58562306a36Sopenharmony_ci	ret = read(pipefd, buf2, 4);
58662306a36Sopenharmony_ci	assert(ret == 4);
58762306a36Sopenharmony_ci	close(pipefd);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	assert(strncmp(buf2, "xmit", 4) == 0);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	ret = write(fd, buf, len);
59262306a36Sopenharmony_ci	if (ret < 0)
59362306a36Sopenharmony_ci		die_perror("write");
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (ret != (ssize_t)len)
59662306a36Sopenharmony_ci		xerror("short write");
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	total = 0;
59962306a36Sopenharmony_ci	do {
60062306a36Sopenharmony_ci		ret = read(fd, buf2 + total, sizeof(buf2) - total);
60162306a36Sopenharmony_ci		if (ret < 0)
60262306a36Sopenharmony_ci			die_perror("read");
60362306a36Sopenharmony_ci		if (ret == 0) {
60462306a36Sopenharmony_ci			eof = true;
60562306a36Sopenharmony_ci			break;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		total += ret;
60962306a36Sopenharmony_ci	} while (total < len);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (total != len)
61262306a36Sopenharmony_ci		xerror("total %lu, len %lu eof %d\n", total, len, eof);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (memcmp(buf, buf2, len))
61562306a36Sopenharmony_ci		xerror("data corruption");
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (s.tcpi_rcv_delta)
61862306a36Sopenharmony_ci		assert(s.tcpi_rcv_delta <= total);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	do_getsockopts(&s, fd, ret, ret);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (eof)
62362306a36Sopenharmony_ci		total += 1; /* sequence advances due to FIN */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	assert(s.mptcpi_rcv_delta == (uint64_t)total);
62662306a36Sopenharmony_ci	close(fd);
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic void process_one_client(int fd, int pipefd)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	ssize_t ret, ret2, ret3;
63262306a36Sopenharmony_ci	struct so_state s;
63362306a36Sopenharmony_ci	char buf[4096];
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	memset(&s, 0, sizeof(s));
63662306a36Sopenharmony_ci	do_getsockopts(&s, fd, 0, 0);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	ret = write(pipefd, "xmit", 4);
63962306a36Sopenharmony_ci	assert(ret == 4);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = read(fd, buf, sizeof(buf));
64262306a36Sopenharmony_ci	if (ret < 0)
64362306a36Sopenharmony_ci		die_perror("read");
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (s.tcpi_rcv_delta)
64862306a36Sopenharmony_ci		assert(s.tcpi_rcv_delta == (uint64_t)ret);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	ret2 = write(fd, buf, ret);
65162306a36Sopenharmony_ci	if (ret2 < 0)
65262306a36Sopenharmony_ci		die_perror("write");
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* wait for hangup */
65562306a36Sopenharmony_ci	ret3 = read(fd, buf, 1);
65662306a36Sopenharmony_ci	if (ret3 != 0)
65762306a36Sopenharmony_ci		xerror("expected EOF, got %lu", ret3);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	do_getsockopts(&s, fd, ret, ret2);
66062306a36Sopenharmony_ci	if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
66162306a36Sopenharmony_ci		xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64, s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - ret);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* be nice when running on top of older kernel */
66462306a36Sopenharmony_ci	if (s.pkt_stats_avail) {
66562306a36Sopenharmony_ci		if (s.last_sample.mptcpi_bytes_sent != ret2)
66662306a36Sopenharmony_ci			xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64,
66762306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_sent, ret2,
66862306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_sent - ret2);
66962306a36Sopenharmony_ci		if (s.last_sample.mptcpi_bytes_received != ret)
67062306a36Sopenharmony_ci			xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64,
67162306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_received, ret,
67262306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_received - ret);
67362306a36Sopenharmony_ci		if (s.last_sample.mptcpi_bytes_acked != ret)
67462306a36Sopenharmony_ci			xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64,
67562306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_acked, ret2,
67662306a36Sopenharmony_ci			       s.last_sample.mptcpi_bytes_acked - ret2);
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	close(fd);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int xaccept(int s)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	int fd = accept(s, NULL, 0);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (fd < 0)
68762306a36Sopenharmony_ci		die_perror("accept");
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return fd;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int server(int pipefd)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	int fd = -1, r;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	switch (pf) {
69762306a36Sopenharmony_ci	case AF_INET:
69862306a36Sopenharmony_ci		fd = sock_listen_mptcp("127.0.0.1", "15432");
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	case AF_INET6:
70162306a36Sopenharmony_ci		fd = sock_listen_mptcp("::1", "15432");
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	default:
70462306a36Sopenharmony_ci		xerror("Unknown pf %d\n", pf);
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	r = write(pipefd, "conn", 4);
70962306a36Sopenharmony_ci	assert(r == 4);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	alarm(15);
71262306a36Sopenharmony_ci	r = xaccept(fd);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	process_one_client(r, pipefd);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void test_ip_tos_sockopt(int fd)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	uint8_t tos_in, tos_out;
72262306a36Sopenharmony_ci	socklen_t s;
72362306a36Sopenharmony_ci	int r;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	tos_in = rand() & 0xfc;
72662306a36Sopenharmony_ci	r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out));
72762306a36Sopenharmony_ci	if (r != 0)
72862306a36Sopenharmony_ci		die_perror("setsockopt IP_TOS");
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	tos_out = 0;
73162306a36Sopenharmony_ci	s = sizeof(tos_out);
73262306a36Sopenharmony_ci	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
73362306a36Sopenharmony_ci	if (r != 0)
73462306a36Sopenharmony_ci		die_perror("getsockopt IP_TOS");
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (tos_in != tos_out)
73762306a36Sopenharmony_ci		xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if (s != 1)
74062306a36Sopenharmony_ci		xerror("tos should be 1 byte");
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	s = 0;
74362306a36Sopenharmony_ci	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
74462306a36Sopenharmony_ci	if (r != 0)
74562306a36Sopenharmony_ci		die_perror("getsockopt IP_TOS 0");
74662306a36Sopenharmony_ci	if (s != 0)
74762306a36Sopenharmony_ci		xerror("expect socklen_t == 0");
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	s = -1;
75062306a36Sopenharmony_ci	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
75162306a36Sopenharmony_ci	if (r != -1 && errno != EINVAL)
75262306a36Sopenharmony_ci		die_perror("getsockopt IP_TOS did not indicate -EINVAL");
75362306a36Sopenharmony_ci	if (s != -1)
75462306a36Sopenharmony_ci		xerror("expect socklen_t == -1");
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int client(int pipefd)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int fd = -1;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	alarm(15);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	switch (pf) {
76462306a36Sopenharmony_ci	case AF_INET:
76562306a36Sopenharmony_ci		fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP);
76662306a36Sopenharmony_ci		break;
76762306a36Sopenharmony_ci	case AF_INET6:
76862306a36Sopenharmony_ci		fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP);
76962306a36Sopenharmony_ci		break;
77062306a36Sopenharmony_ci	default:
77162306a36Sopenharmony_ci		xerror("Unknown pf %d\n", pf);
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	test_ip_tos_sockopt(fd);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	connect_one_server(fd, pipefd);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic pid_t xfork(void)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	pid_t p = fork();
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (p < 0)
78662306a36Sopenharmony_ci		die_perror("fork");
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	return p;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic int rcheck(int wstatus, const char *what)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	if (WIFEXITED(wstatus)) {
79462306a36Sopenharmony_ci		if (WEXITSTATUS(wstatus) == 0)
79562306a36Sopenharmony_ci			return 0;
79662306a36Sopenharmony_ci		fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
79762306a36Sopenharmony_ci		return WEXITSTATUS(wstatus);
79862306a36Sopenharmony_ci	} else if (WIFSIGNALED(wstatus)) {
79962306a36Sopenharmony_ci		xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
80062306a36Sopenharmony_ci	} else if (WIFSTOPPED(wstatus)) {
80162306a36Sopenharmony_ci		xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return 111;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void init_rng(void)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	int fd = open("/dev/urandom", O_RDONLY);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (fd >= 0) {
81262306a36Sopenharmony_ci		unsigned int foo;
81362306a36Sopenharmony_ci		ssize_t ret;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		/* can't fail */
81662306a36Sopenharmony_ci		ret = read(fd, &foo, sizeof(foo));
81762306a36Sopenharmony_ci		assert(ret == sizeof(foo));
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci		close(fd);
82062306a36Sopenharmony_ci		srand(foo);
82162306a36Sopenharmony_ci	} else {
82262306a36Sopenharmony_ci		srand(time(NULL));
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ciint main(int argc, char *argv[])
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	int e1, e2, wstatus;
82962306a36Sopenharmony_ci	pid_t s, c, ret;
83062306a36Sopenharmony_ci	int pipefds[2];
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	parse_opts(argc, argv);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	init_rng();
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	e1 = pipe(pipefds);
83762306a36Sopenharmony_ci	if (e1 < 0)
83862306a36Sopenharmony_ci		die_perror("pipe");
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	s = xfork();
84162306a36Sopenharmony_ci	if (s == 0)
84262306a36Sopenharmony_ci		return server(pipefds[1]);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	close(pipefds[1]);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* wait until server bound a socket */
84762306a36Sopenharmony_ci	e1 = read(pipefds[0], &e1, 4);
84862306a36Sopenharmony_ci	assert(e1 == 4);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	c = xfork();
85162306a36Sopenharmony_ci	if (c == 0)
85262306a36Sopenharmony_ci		return client(pipefds[0]);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	close(pipefds[0]);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ret = waitpid(s, &wstatus, 0);
85762306a36Sopenharmony_ci	if (ret == -1)
85862306a36Sopenharmony_ci		die_perror("waitpid");
85962306a36Sopenharmony_ci	e1 = rcheck(wstatus, "server");
86062306a36Sopenharmony_ci	ret = waitpid(c, &wstatus, 0);
86162306a36Sopenharmony_ci	if (ret == -1)
86262306a36Sopenharmony_ci		die_perror("waitpid");
86362306a36Sopenharmony_ci	e2 = rcheck(wstatus, "client");
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	return e1 ? e1 : e2;
86662306a36Sopenharmony_ci}
867