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 <unistd.h>
1862306a36Sopenharmony_ci#include <time.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <sys/ioctl.h>
2162306a36Sopenharmony_ci#include <sys/random.h>
2262306a36Sopenharmony_ci#include <sys/socket.h>
2362306a36Sopenharmony_ci#include <sys/types.h>
2462306a36Sopenharmony_ci#include <sys/wait.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <netdb.h>
2762306a36Sopenharmony_ci#include <netinet/in.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/tcp.h>
3062306a36Sopenharmony_ci#include <linux/sockios.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#ifndef IPPROTO_MPTCP
3362306a36Sopenharmony_ci#define IPPROTO_MPTCP 262
3462306a36Sopenharmony_ci#endif
3562306a36Sopenharmony_ci#ifndef SOL_MPTCP
3662306a36Sopenharmony_ci#define SOL_MPTCP 284
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int pf = AF_INET;
4062306a36Sopenharmony_cistatic int proto_tx = IPPROTO_MPTCP;
4162306a36Sopenharmony_cistatic int proto_rx = IPPROTO_MPTCP;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void die_perror(const char *msg)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	perror(msg);
4662306a36Sopenharmony_ci	exit(1);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void die_usage(int r)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	fprintf(stderr, "Usage: mptcp_inq [-6] [ -t tcp|mptcp ] [ -r tcp|mptcp]\n");
5262306a36Sopenharmony_ci	exit(r);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void xerror(const char *fmt, ...)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	va_list ap;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	va_start(ap, fmt);
6062306a36Sopenharmony_ci	vfprintf(stderr, fmt, ap);
6162306a36Sopenharmony_ci	va_end(ap);
6262306a36Sopenharmony_ci	fputc('\n', stderr);
6362306a36Sopenharmony_ci	exit(1);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic const char *getxinfo_strerr(int err)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (err == EAI_SYSTEM)
6962306a36Sopenharmony_ci		return strerror(errno);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return gai_strerror(err);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void xgetaddrinfo(const char *node, const char *service,
7562306a36Sopenharmony_ci			 const struct addrinfo *hints,
7662306a36Sopenharmony_ci			 struct addrinfo **res)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int err = getaddrinfo(node, service, hints, res);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (err) {
8162306a36Sopenharmony_ci		const char *errstr = getxinfo_strerr(err);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
8462306a36Sopenharmony_ci			node ? node : "", service ? service : "", errstr);
8562306a36Sopenharmony_ci		exit(1);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int sock_listen_mptcp(const char * const listenaddr,
9062306a36Sopenharmony_ci			     const char * const port)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int sock = -1;
9362306a36Sopenharmony_ci	struct addrinfo hints = {
9462306a36Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
9562306a36Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
9662306a36Sopenharmony_ci		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
9762306a36Sopenharmony_ci	};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	hints.ai_family = pf;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	struct addrinfo *a, *addr;
10262306a36Sopenharmony_ci	int one = 1;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	xgetaddrinfo(listenaddr, port, &hints, &addr);
10562306a36Sopenharmony_ci	hints.ai_family = pf;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
10862306a36Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, proto_rx);
10962306a36Sopenharmony_ci		if (sock < 0)
11062306a36Sopenharmony_ci			continue;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
11362306a36Sopenharmony_ci				     sizeof(one)))
11462306a36Sopenharmony_ci			perror("setsockopt");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
11762306a36Sopenharmony_ci			break; /* success */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		perror("bind");
12062306a36Sopenharmony_ci		close(sock);
12162306a36Sopenharmony_ci		sock = -1;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	freeaddrinfo(addr);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (sock < 0)
12762306a36Sopenharmony_ci		xerror("could not create listen socket");
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (listen(sock, 20))
13062306a36Sopenharmony_ci		die_perror("listen");
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return sock;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int sock_connect_mptcp(const char * const remoteaddr,
13662306a36Sopenharmony_ci			      const char * const port, int proto)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct addrinfo hints = {
13962306a36Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
14062306a36Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
14162306a36Sopenharmony_ci	};
14262306a36Sopenharmony_ci	struct addrinfo *a, *addr;
14362306a36Sopenharmony_ci	int sock = -1;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	hints.ai_family = pf;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	xgetaddrinfo(remoteaddr, port, &hints, &addr);
14862306a36Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
14962306a36Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, proto);
15062306a36Sopenharmony_ci		if (sock < 0)
15162306a36Sopenharmony_ci			continue;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
15462306a36Sopenharmony_ci			break; /* success */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		die_perror("connect");
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (sock < 0)
16062306a36Sopenharmony_ci		xerror("could not create connect socket");
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	freeaddrinfo(addr);
16362306a36Sopenharmony_ci	return sock;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int protostr_to_num(const char *s)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	if (strcasecmp(s, "tcp") == 0)
16962306a36Sopenharmony_ci		return IPPROTO_TCP;
17062306a36Sopenharmony_ci	if (strcasecmp(s, "mptcp") == 0)
17162306a36Sopenharmony_ci		return IPPROTO_MPTCP;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	die_usage(1);
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void parse_opts(int argc, char **argv)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int c;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	while ((c = getopt(argc, argv, "h6t:r:")) != -1) {
18262306a36Sopenharmony_ci		switch (c) {
18362306a36Sopenharmony_ci		case 'h':
18462306a36Sopenharmony_ci			die_usage(0);
18562306a36Sopenharmony_ci			break;
18662306a36Sopenharmony_ci		case '6':
18762306a36Sopenharmony_ci			pf = AF_INET6;
18862306a36Sopenharmony_ci			break;
18962306a36Sopenharmony_ci		case 't':
19062306a36Sopenharmony_ci			proto_tx = protostr_to_num(optarg);
19162306a36Sopenharmony_ci			break;
19262306a36Sopenharmony_ci		case 'r':
19362306a36Sopenharmony_ci			proto_rx = protostr_to_num(optarg);
19462306a36Sopenharmony_ci			break;
19562306a36Sopenharmony_ci		default:
19662306a36Sopenharmony_ci			die_usage(1);
19762306a36Sopenharmony_ci			break;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* wait up to timeout milliseconds */
20362306a36Sopenharmony_cistatic void wait_for_ack(int fd, int timeout, size_t total)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int i;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for (i = 0; i < timeout; i++) {
20862306a36Sopenharmony_ci		int nsd, ret, queued = -1;
20962306a36Sopenharmony_ci		struct timespec req;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		ret = ioctl(fd, TIOCOUTQ, &queued);
21262306a36Sopenharmony_ci		if (ret < 0)
21362306a36Sopenharmony_ci			die_perror("TIOCOUTQ");
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		ret = ioctl(fd, SIOCOUTQNSD, &nsd);
21662306a36Sopenharmony_ci		if (ret < 0)
21762306a36Sopenharmony_ci			die_perror("SIOCOUTQNSD");
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if ((size_t)queued > total)
22062306a36Sopenharmony_ci			xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total);
22162306a36Sopenharmony_ci		assert(nsd <= queued);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		if (queued == 0)
22462306a36Sopenharmony_ci			return;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		/* wait for peer to ack rx of all data */
22762306a36Sopenharmony_ci		req.tv_sec = 0;
22862306a36Sopenharmony_ci		req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */
22962306a36Sopenharmony_ci		nanosleep(&req, NULL);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	xerror("still tx data queued after %u ms\n", timeout);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void connect_one_server(int fd, int unixfd)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	size_t len, i, total, sent;
23862306a36Sopenharmony_ci	char buf[4096], buf2[4096];
23962306a36Sopenharmony_ci	ssize_t ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	len = rand() % (sizeof(buf) - 1);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (len < 128)
24462306a36Sopenharmony_ci		len = 128;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (i = 0; i < len ; i++) {
24762306a36Sopenharmony_ci		buf[i] = rand() % 26;
24862306a36Sopenharmony_ci		buf[i] += 'A';
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	buf[i] = '\n';
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* un-block server */
25462306a36Sopenharmony_ci	ret = read(unixfd, buf2, 4);
25562306a36Sopenharmony_ci	assert(ret == 4);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	assert(strncmp(buf2, "xmit", 4) == 0);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	ret = write(unixfd, &len, sizeof(len));
26062306a36Sopenharmony_ci	assert(ret == (ssize_t)sizeof(len));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = write(fd, buf, len);
26362306a36Sopenharmony_ci	if (ret < 0)
26462306a36Sopenharmony_ci		die_perror("write");
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (ret != (ssize_t)len)
26762306a36Sopenharmony_ci		xerror("short write");
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = read(unixfd, buf2, 4);
27062306a36Sopenharmony_ci	assert(strncmp(buf2, "huge", 4) == 0);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	total = rand() % (16 * 1024 * 1024);
27362306a36Sopenharmony_ci	total += (1 * 1024 * 1024);
27462306a36Sopenharmony_ci	sent = total;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	ret = write(unixfd, &total, sizeof(total));
27762306a36Sopenharmony_ci	assert(ret == (ssize_t)sizeof(total));
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	wait_for_ack(fd, 5000, len);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	while (total > 0) {
28262306a36Sopenharmony_ci		if (total > sizeof(buf))
28362306a36Sopenharmony_ci			len = sizeof(buf);
28462306a36Sopenharmony_ci		else
28562306a36Sopenharmony_ci			len = total;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		ret = write(fd, buf, len);
28862306a36Sopenharmony_ci		if (ret < 0)
28962306a36Sopenharmony_ci			die_perror("write");
29062306a36Sopenharmony_ci		total -= ret;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		/* we don't have to care about buf content, only
29362306a36Sopenharmony_ci		 * number of total bytes sent
29462306a36Sopenharmony_ci		 */
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	ret = read(unixfd, buf2, 4);
29862306a36Sopenharmony_ci	assert(ret == 4);
29962306a36Sopenharmony_ci	assert(strncmp(buf2, "shut", 4) == 0);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	wait_for_ack(fd, 5000, sent);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ret = write(fd, buf, 1);
30462306a36Sopenharmony_ci	assert(ret == 1);
30562306a36Sopenharmony_ci	close(fd);
30662306a36Sopenharmony_ci	ret = write(unixfd, "closed", 6);
30762306a36Sopenharmony_ci	assert(ret == 6);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	close(unixfd);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct cmsghdr *cmsg;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
31762306a36Sopenharmony_ci		if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) {
31862306a36Sopenharmony_ci			memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv));
31962306a36Sopenharmony_ci			return;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	xerror("could not find TCP_CM_INQ cmsg type");
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void process_one_client(int fd, int unixfd)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	unsigned int tcp_inq;
32962306a36Sopenharmony_ci	size_t expect_len;
33062306a36Sopenharmony_ci	char msg_buf[4096];
33162306a36Sopenharmony_ci	char buf[4096];
33262306a36Sopenharmony_ci	char tmp[16];
33362306a36Sopenharmony_ci	struct iovec iov = {
33462306a36Sopenharmony_ci		.iov_base = buf,
33562306a36Sopenharmony_ci		.iov_len = 1,
33662306a36Sopenharmony_ci	};
33762306a36Sopenharmony_ci	struct msghdr msg = {
33862306a36Sopenharmony_ci		.msg_iov = &iov,
33962306a36Sopenharmony_ci		.msg_iovlen = 1,
34062306a36Sopenharmony_ci		.msg_control = msg_buf,
34162306a36Sopenharmony_ci		.msg_controllen = sizeof(msg_buf),
34262306a36Sopenharmony_ci	};
34362306a36Sopenharmony_ci	ssize_t ret, tot;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = write(unixfd, "xmit", 4);
34662306a36Sopenharmony_ci	assert(ret == 4);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = read(unixfd, &expect_len, sizeof(expect_len));
34962306a36Sopenharmony_ci	assert(ret == (ssize_t)sizeof(expect_len));
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (expect_len > sizeof(buf))
35262306a36Sopenharmony_ci		xerror("expect len %zu exceeds buffer size", expect_len);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	for (;;) {
35562306a36Sopenharmony_ci		struct timespec req;
35662306a36Sopenharmony_ci		unsigned int queued;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		ret = ioctl(fd, FIONREAD, &queued);
35962306a36Sopenharmony_ci		if (ret < 0)
36062306a36Sopenharmony_ci			die_perror("FIONREAD");
36162306a36Sopenharmony_ci		if (queued > expect_len)
36262306a36Sopenharmony_ci			xerror("FIONREAD returned %u, but only %zu expected\n",
36362306a36Sopenharmony_ci			       queued, expect_len);
36462306a36Sopenharmony_ci		if (queued == expect_len)
36562306a36Sopenharmony_ci			break;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		req.tv_sec = 0;
36862306a36Sopenharmony_ci		req.tv_nsec = 1000 * 1000ul;
36962306a36Sopenharmony_ci		nanosleep(&req, NULL);
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* read one byte, expect cmsg to return expected - 1 */
37362306a36Sopenharmony_ci	ret = recvmsg(fd, &msg, 0);
37462306a36Sopenharmony_ci	if (ret < 0)
37562306a36Sopenharmony_ci		die_perror("recvmsg");
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (msg.msg_controllen == 0)
37862306a36Sopenharmony_ci		xerror("msg_controllen is 0");
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	get_tcp_inq(&msg, &tcp_inq);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	assert((size_t)tcp_inq == (expect_len - 1));
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	iov.iov_len = sizeof(buf);
38562306a36Sopenharmony_ci	ret = recvmsg(fd, &msg, 0);
38662306a36Sopenharmony_ci	if (ret < 0)
38762306a36Sopenharmony_ci		die_perror("recvmsg");
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* should have gotten exact remainder of all pending data */
39062306a36Sopenharmony_ci	assert(ret == (ssize_t)tcp_inq);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* should be 0, all drained */
39362306a36Sopenharmony_ci	get_tcp_inq(&msg, &tcp_inq);
39462306a36Sopenharmony_ci	assert(tcp_inq == 0);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* request a large swath of data. */
39762306a36Sopenharmony_ci	ret = write(unixfd, "huge", 4);
39862306a36Sopenharmony_ci	assert(ret == 4);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = read(unixfd, &expect_len, sizeof(expect_len));
40162306a36Sopenharmony_ci	assert(ret == (ssize_t)sizeof(expect_len));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* peer should send us a few mb of data */
40462306a36Sopenharmony_ci	if (expect_len <= sizeof(buf))
40562306a36Sopenharmony_ci		xerror("expect len %zu too small\n", expect_len);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	tot = 0;
40862306a36Sopenharmony_ci	do {
40962306a36Sopenharmony_ci		iov.iov_len = sizeof(buf);
41062306a36Sopenharmony_ci		ret = recvmsg(fd, &msg, 0);
41162306a36Sopenharmony_ci		if (ret < 0)
41262306a36Sopenharmony_ci			die_perror("recvmsg");
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		tot += ret;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		get_tcp_inq(&msg, &tcp_inq);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		if (tcp_inq > expect_len - tot)
41962306a36Sopenharmony_ci			xerror("inq %d, remaining %d total_len %d\n",
42062306a36Sopenharmony_ci			       tcp_inq, expect_len - tot, (int)expect_len);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		assert(tcp_inq <= expect_len - tot);
42362306a36Sopenharmony_ci	} while ((size_t)tot < expect_len);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	ret = write(unixfd, "shut", 4);
42662306a36Sopenharmony_ci	assert(ret == 4);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* wait for hangup. Should have received one more byte of data. */
42962306a36Sopenharmony_ci	ret = read(unixfd, tmp, sizeof(tmp));
43062306a36Sopenharmony_ci	assert(ret == 6);
43162306a36Sopenharmony_ci	assert(strncmp(tmp, "closed", 6) == 0);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	sleep(1);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	iov.iov_len = 1;
43662306a36Sopenharmony_ci	ret = recvmsg(fd, &msg, 0);
43762306a36Sopenharmony_ci	if (ret < 0)
43862306a36Sopenharmony_ci		die_perror("recvmsg");
43962306a36Sopenharmony_ci	assert(ret == 1);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	get_tcp_inq(&msg, &tcp_inq);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* tcp_inq should be 1 due to received fin. */
44462306a36Sopenharmony_ci	assert(tcp_inq == 1);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	iov.iov_len = 1;
44762306a36Sopenharmony_ci	ret = recvmsg(fd, &msg, 0);
44862306a36Sopenharmony_ci	if (ret < 0)
44962306a36Sopenharmony_ci		die_perror("recvmsg");
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* expect EOF */
45262306a36Sopenharmony_ci	assert(ret == 0);
45362306a36Sopenharmony_ci	get_tcp_inq(&msg, &tcp_inq);
45462306a36Sopenharmony_ci	assert(tcp_inq == 1);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	close(fd);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int xaccept(int s)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	int fd = accept(s, NULL, 0);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (fd < 0)
46462306a36Sopenharmony_ci		die_perror("accept");
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return fd;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int server(int unixfd)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int fd = -1, r, on = 1;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	switch (pf) {
47462306a36Sopenharmony_ci	case AF_INET:
47562306a36Sopenharmony_ci		fd = sock_listen_mptcp("127.0.0.1", "15432");
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	case AF_INET6:
47862306a36Sopenharmony_ci		fd = sock_listen_mptcp("::1", "15432");
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci	default:
48162306a36Sopenharmony_ci		xerror("Unknown pf %d\n", pf);
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	r = write(unixfd, "conn", 4);
48662306a36Sopenharmony_ci	assert(r == 4);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	alarm(15);
48962306a36Sopenharmony_ci	r = xaccept(fd);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (-1 == setsockopt(r, IPPROTO_TCP, TCP_INQ, &on, sizeof(on)))
49262306a36Sopenharmony_ci		die_perror("setsockopt");
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	process_one_client(r, unixfd);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int client(int unixfd)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int fd = -1;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	alarm(15);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	switch (pf) {
50662306a36Sopenharmony_ci	case AF_INET:
50762306a36Sopenharmony_ci		fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx);
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci	case AF_INET6:
51062306a36Sopenharmony_ci		fd = sock_connect_mptcp("::1", "15432", proto_tx);
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	default:
51362306a36Sopenharmony_ci		xerror("Unknown pf %d\n", pf);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	connect_one_server(fd, unixfd);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return 0;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void init_rng(void)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	unsigned int foo;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (getrandom(&foo, sizeof(foo), 0) == -1) {
52662306a36Sopenharmony_ci		perror("getrandom");
52762306a36Sopenharmony_ci		exit(1);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	srand(foo);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic pid_t xfork(void)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	pid_t p = fork();
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (p < 0)
53862306a36Sopenharmony_ci		die_perror("fork");
53962306a36Sopenharmony_ci	else if (p == 0)
54062306a36Sopenharmony_ci		init_rng();
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return p;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic int rcheck(int wstatus, const char *what)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	if (WIFEXITED(wstatus)) {
54862306a36Sopenharmony_ci		if (WEXITSTATUS(wstatus) == 0)
54962306a36Sopenharmony_ci			return 0;
55062306a36Sopenharmony_ci		fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
55162306a36Sopenharmony_ci		return WEXITSTATUS(wstatus);
55262306a36Sopenharmony_ci	} else if (WIFSIGNALED(wstatus)) {
55362306a36Sopenharmony_ci		xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
55462306a36Sopenharmony_ci	} else if (WIFSTOPPED(wstatus)) {
55562306a36Sopenharmony_ci		xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return 111;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ciint main(int argc, char *argv[])
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	int e1, e2, wstatus;
56462306a36Sopenharmony_ci	pid_t s, c, ret;
56562306a36Sopenharmony_ci	int unixfds[2];
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	parse_opts(argc, argv);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	e1 = socketpair(AF_UNIX, SOCK_DGRAM, 0, unixfds);
57062306a36Sopenharmony_ci	if (e1 < 0)
57162306a36Sopenharmony_ci		die_perror("pipe");
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	s = xfork();
57462306a36Sopenharmony_ci	if (s == 0)
57562306a36Sopenharmony_ci		return server(unixfds[1]);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	close(unixfds[1]);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* wait until server bound a socket */
58062306a36Sopenharmony_ci	e1 = read(unixfds[0], &e1, 4);
58162306a36Sopenharmony_ci	assert(e1 == 4);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	c = xfork();
58462306a36Sopenharmony_ci	if (c == 0)
58562306a36Sopenharmony_ci		return client(unixfds[0]);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	close(unixfds[0]);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = waitpid(s, &wstatus, 0);
59062306a36Sopenharmony_ci	if (ret == -1)
59162306a36Sopenharmony_ci		die_perror("waitpid");
59262306a36Sopenharmony_ci	e1 = rcheck(wstatus, "server");
59362306a36Sopenharmony_ci	ret = waitpid(c, &wstatus, 0);
59462306a36Sopenharmony_ci	if (ret == -1)
59562306a36Sopenharmony_ci		die_perror("waitpid");
59662306a36Sopenharmony_ci	e2 = rcheck(wstatus, "client");
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return e1 ? e1 : e2;
59962306a36Sopenharmony_ci}
600