xref: /openbmc/linux/tools/testing/selftests/net/mptcp/mptcp_sockopt.c (revision 5dcff89e14555a4ee6cfa132b82f7d13dcb1e80a)
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 
29 static int pf = AF_INET;
30 
31 #ifndef IPPROTO_MPTCP
32 #define IPPROTO_MPTCP 262
33 #endif
34 #ifndef SOL_MPTCP
35 #define SOL_MPTCP 284
36 #endif
37 
38 #ifndef MPTCP_INFO
39 struct 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 
61 struct 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 
68 struct 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		1
85 #define MPTCP_TCPINFO		2
86 #define MPTCP_SUBFLOW_ADDRS	3
87 #endif
88 
89 struct so_state {
90 	struct mptcp_info mi;
91 	struct mptcp_info last_sample;
92 	uint64_t mptcpi_rcv_delta;
93 	uint64_t tcpi_rcv_delta;
94 	bool pkt_stats_avail;
95 };
96 
97 #ifndef MIN
98 #define MIN(a, b) ((a) < (b) ? (a) : (b))
99 #endif
100 
101 static void die_perror(const char *msg)
102 {
103 	perror(msg);
104 	exit(1);
105 }
106 
107 static void die_usage(int r)
108 {
109 	fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
110 	exit(r);
111 }
112 
113 static void xerror(const char *fmt, ...)
114 {
115 	va_list ap;
116 
117 	va_start(ap, fmt);
118 	vfprintf(stderr, fmt, ap);
119 	va_end(ap);
120 	fputc('\n', stderr);
121 	exit(1);
122 }
123 
124 static const char *getxinfo_strerr(int err)
125 {
126 	if (err == EAI_SYSTEM)
127 		return strerror(errno);
128 
129 	return gai_strerror(err);
130 }
131 
132 static void xgetaddrinfo(const char *node, const char *service,
133 			 const struct addrinfo *hints,
134 			 struct addrinfo **res)
135 {
136 	int err = getaddrinfo(node, service, hints, res);
137 
138 	if (err) {
139 		const char *errstr = getxinfo_strerr(err);
140 
141 		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
142 			node ? node : "", service ? service : "", errstr);
143 		exit(1);
144 	}
145 }
146 
147 static int sock_listen_mptcp(const char * const listenaddr,
148 			     const char * const port)
149 {
150 	int sock = -1;
151 	struct addrinfo hints = {
152 		.ai_protocol = IPPROTO_TCP,
153 		.ai_socktype = SOCK_STREAM,
154 		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
155 	};
156 
157 	hints.ai_family = pf;
158 
159 	struct addrinfo *a, *addr;
160 	int one = 1;
161 
162 	xgetaddrinfo(listenaddr, port, &hints, &addr);
163 	hints.ai_family = pf;
164 
165 	for (a = addr; a; a = a->ai_next) {
166 		sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
167 		if (sock < 0)
168 			continue;
169 
170 		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
171 				     sizeof(one)))
172 			perror("setsockopt");
173 
174 		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
175 			break; /* success */
176 
177 		perror("bind");
178 		close(sock);
179 		sock = -1;
180 	}
181 
182 	freeaddrinfo(addr);
183 
184 	if (sock < 0)
185 		xerror("could not create listen socket");
186 
187 	if (listen(sock, 20))
188 		die_perror("listen");
189 
190 	return sock;
191 }
192 
193 static int sock_connect_mptcp(const char * const remoteaddr,
194 			      const char * const port, int proto)
195 {
196 	struct addrinfo hints = {
197 		.ai_protocol = IPPROTO_TCP,
198 		.ai_socktype = SOCK_STREAM,
199 	};
200 	struct addrinfo *a, *addr;
201 	int sock = -1;
202 
203 	hints.ai_family = pf;
204 
205 	xgetaddrinfo(remoteaddr, port, &hints, &addr);
206 	for (a = addr; a; a = a->ai_next) {
207 		sock = socket(a->ai_family, a->ai_socktype, proto);
208 		if (sock < 0)
209 			continue;
210 
211 		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
212 			break; /* success */
213 
214 		die_perror("connect");
215 	}
216 
217 	if (sock < 0)
218 		xerror("could not create connect socket");
219 
220 	freeaddrinfo(addr);
221 	return sock;
222 }
223 
224 static void parse_opts(int argc, char **argv)
225 {
226 	int c;
227 
228 	while ((c = getopt(argc, argv, "h6")) != -1) {
229 		switch (c) {
230 		case 'h':
231 			die_usage(0);
232 			break;
233 		case '6':
234 			pf = AF_INET6;
235 			break;
236 		default:
237 			die_usage(1);
238 			break;
239 		}
240 	}
241 }
242 
243 static void do_getsockopt_bogus_sf_data(int fd, int optname)
244 {
245 	struct mptcp_subflow_data good_data;
246 	struct bogus_data {
247 		struct mptcp_subflow_data d;
248 		char buf[2];
249 	} bd;
250 	socklen_t olen, _olen;
251 	int ret;
252 
253 	memset(&bd, 0, sizeof(bd));
254 	memset(&good_data, 0, sizeof(good_data));
255 
256 	olen = sizeof(good_data);
257 	good_data.size_subflow_data = olen;
258 
259 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
260 	assert(ret < 0); /* 0 size_subflow_data */
261 	assert(olen == sizeof(good_data));
262 
263 	bd.d = good_data;
264 
265 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
266 	assert(ret == 0);
267 	assert(olen == sizeof(good_data));
268 	assert(bd.d.num_subflows == 1);
269 	assert(bd.d.size_kernel > 0);
270 	assert(bd.d.size_user == 0);
271 
272 	bd.d = good_data;
273 	_olen = rand() % olen;
274 	olen = _olen;
275 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
276 	assert(ret < 0);	/* bogus olen */
277 	assert(olen == _olen);	/* must be unchanged */
278 
279 	bd.d = good_data;
280 	olen = sizeof(good_data);
281 	bd.d.size_kernel = 1;
282 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
283 	assert(ret < 0); /* size_kernel not 0 */
284 
285 	bd.d = good_data;
286 	olen = sizeof(good_data);
287 	bd.d.num_subflows = 1;
288 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
289 	assert(ret < 0); /* num_subflows not 0 */
290 
291 	/* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */
292 	bd.d = good_data;
293 	olen = sizeof(bd);
294 	bd.d.size_subflow_data = sizeof(bd);
295 
296 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
297 	assert(ret == 0);
298 
299 	/* olen must be truncated to real data size filled by kernel: */
300 	assert(olen == sizeof(good_data));
301 
302 	assert(bd.d.size_subflow_data == sizeof(bd));
303 
304 	bd.d = good_data;
305 	bd.d.size_subflow_data += 1;
306 	bd.d.size_user = 1;
307 	olen = bd.d.size_subflow_data + 1;
308 	_olen = olen;
309 
310 	ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen);
311 	assert(ret == 0);
312 
313 	/* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */
314 	assert(olen == _olen);
315 
316 	assert(bd.d.size_subflow_data == sizeof(good_data) + 1);
317 	assert(bd.buf[0] == 0);
318 }
319 
320 static void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w)
321 {
322 	struct mptcp_info i;
323 	socklen_t olen;
324 	int ret;
325 
326 	olen = sizeof(i);
327 	ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen);
328 
329 	if (ret < 0)
330 		die_perror("getsockopt MPTCP_INFO");
331 
332 	s->pkt_stats_avail = olen >= sizeof(i);
333 
334 	s->last_sample = i;
335 	if (s->mi.mptcpi_write_seq == 0)
336 		s->mi = i;
337 
338 	assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq);
339 
340 	s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt;
341 }
342 
343 static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w)
344 {
345 	struct my_tcp_info {
346 		struct mptcp_subflow_data d;
347 		struct tcp_info ti[2];
348 	} ti;
349 	int ret, tries = 5;
350 	socklen_t olen;
351 
352 	do {
353 		memset(&ti, 0, sizeof(ti));
354 
355 		ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
356 		ti.d.size_user = sizeof(struct tcp_info);
357 		olen = sizeof(ti);
358 
359 		ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen);
360 		if (ret < 0)
361 			xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
362 
363 		assert(olen <= sizeof(ti));
364 		assert(ti.d.size_kernel > 0);
365 		assert(ti.d.size_user ==
366 		       MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
367 		assert(ti.d.num_subflows == 1);
368 
369 		assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
370 		olen -= sizeof(struct mptcp_subflow_data);
371 		assert(olen == ti.d.size_user);
372 
373 		if (ti.ti[0].tcpi_bytes_sent == w &&
374 		    ti.ti[0].tcpi_bytes_received == r)
375 			goto done;
376 
377 		if (r == 0 && ti.ti[0].tcpi_bytes_sent == w &&
378 		    ti.ti[0].tcpi_bytes_received) {
379 			s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received;
380 			goto done;
381 		}
382 
383 		/* wait and repeat, might be that tx is still ongoing */
384 		sleep(1);
385 	} while (tries-- > 0);
386 
387 	xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu",
388 		ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r);
389 
390 done:
391 	do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
392 }
393 
394 static void do_getsockopt_subflow_addrs(int fd)
395 {
396 	struct sockaddr_storage remote, local;
397 	socklen_t olen, rlen, llen;
398 	int ret;
399 	struct my_addrs {
400 		struct mptcp_subflow_data d;
401 		struct mptcp_subflow_addrs addr[2];
402 	} addrs;
403 
404 	memset(&addrs, 0, sizeof(addrs));
405 	memset(&local, 0, sizeof(local));
406 	memset(&remote, 0, sizeof(remote));
407 
408 	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
409 	addrs.d.size_user = sizeof(struct mptcp_subflow_addrs);
410 	olen = sizeof(addrs);
411 
412 	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
413 	if (ret < 0)
414 		die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
415 
416 	assert(olen <= sizeof(addrs));
417 	assert(addrs.d.size_kernel > 0);
418 	assert(addrs.d.size_user ==
419 	       MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
420 	assert(addrs.d.num_subflows == 1);
421 
422 	assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
423 	olen -= sizeof(struct mptcp_subflow_data);
424 	assert(olen == addrs.d.size_user);
425 
426 	llen = sizeof(local);
427 	ret = getsockname(fd, (struct sockaddr *)&local, &llen);
428 	if (ret < 0)
429 		die_perror("getsockname");
430 	rlen = sizeof(remote);
431 	ret = getpeername(fd, (struct sockaddr *)&remote, &rlen);
432 	if (ret < 0)
433 		die_perror("getpeername");
434 
435 	assert(rlen > 0);
436 	assert(rlen == llen);
437 
438 	assert(remote.ss_family == local.ss_family);
439 
440 	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0);
441 	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0);
442 
443 	memset(&addrs, 0, sizeof(addrs));
444 
445 	addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
446 	addrs.d.size_user = sizeof(sa_family_t);
447 	olen = sizeof(addrs.d) + sizeof(sa_family_t);
448 
449 	ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
450 	assert(ret == 0);
451 	assert(olen == sizeof(addrs.d) + sizeof(sa_family_t));
452 
453 	assert(addrs.addr[0].sa_family == pf);
454 	assert(addrs.addr[0].sa_family == local.ss_family);
455 
456 	assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) != 0);
457 	assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) != 0);
458 
459 	do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
460 }
461 
462 static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
463 {
464 	do_getsockopt_mptcp_info(s, fd, w);
465 
466 	do_getsockopt_tcp_info(s, fd, r, w);
467 
468 	do_getsockopt_subflow_addrs(fd);
469 }
470 
471 static void connect_one_server(int fd, int pipefd)
472 {
473 	char buf[4096], buf2[4096];
474 	size_t len, i, total;
475 	struct so_state s;
476 	bool eof = false;
477 	ssize_t ret;
478 
479 	memset(&s, 0, sizeof(s));
480 
481 	len = rand() % (sizeof(buf) - 1);
482 
483 	if (len < 128)
484 		len = 128;
485 
486 	for (i = 0; i < len ; i++) {
487 		buf[i] = rand() % 26;
488 		buf[i] += 'A';
489 	}
490 
491 	buf[i] = '\n';
492 
493 	do_getsockopts(&s, fd, 0, 0);
494 
495 	/* un-block server */
496 	ret = read(pipefd, buf2, 4);
497 	assert(ret == 4);
498 	close(pipefd);
499 
500 	assert(strncmp(buf2, "xmit", 4) == 0);
501 
502 	ret = write(fd, buf, len);
503 	if (ret < 0)
504 		die_perror("write");
505 
506 	if (ret != (ssize_t)len)
507 		xerror("short write");
508 
509 	total = 0;
510 	do {
511 		ret = read(fd, buf2 + total, sizeof(buf2) - total);
512 		if (ret < 0)
513 			die_perror("read");
514 		if (ret == 0) {
515 			eof = true;
516 			break;
517 		}
518 
519 		total += ret;
520 	} while (total < len);
521 
522 	if (total != len)
523 		xerror("total %lu, len %lu eof %d\n", total, len, eof);
524 
525 	if (memcmp(buf, buf2, len))
526 		xerror("data corruption");
527 
528 	if (s.tcpi_rcv_delta)
529 		assert(s.tcpi_rcv_delta <= total);
530 
531 	do_getsockopts(&s, fd, ret, ret);
532 
533 	if (eof)
534 		total += 1; /* sequence advances due to FIN */
535 
536 	assert(s.mptcpi_rcv_delta == (uint64_t)total);
537 	close(fd);
538 }
539 
540 static void process_one_client(int fd, int pipefd)
541 {
542 	ssize_t ret, ret2, ret3;
543 	struct so_state s;
544 	char buf[4096];
545 
546 	memset(&s, 0, sizeof(s));
547 	do_getsockopts(&s, fd, 0, 0);
548 
549 	ret = write(pipefd, "xmit", 4);
550 	assert(ret == 4);
551 
552 	ret = read(fd, buf, sizeof(buf));
553 	if (ret < 0)
554 		die_perror("read");
555 
556 	assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
557 
558 	if (s.tcpi_rcv_delta)
559 		assert(s.tcpi_rcv_delta == (uint64_t)ret);
560 
561 	ret2 = write(fd, buf, ret);
562 	if (ret2 < 0)
563 		die_perror("write");
564 
565 	/* wait for hangup */
566 	ret3 = read(fd, buf, 1);
567 	if (ret3 != 0)
568 		xerror("expected EOF, got %lu", ret3);
569 
570 	do_getsockopts(&s, fd, ret, ret2);
571 	if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
572 		xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64, s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - ret);
573 
574 	/* be nice when running on top of older kernel */
575 	if (s.pkt_stats_avail) {
576 		if (s.last_sample.mptcpi_bytes_sent != ret2)
577 			xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64,
578 			       s.last_sample.mptcpi_bytes_sent, ret2,
579 			       s.last_sample.mptcpi_bytes_sent - ret2);
580 		if (s.last_sample.mptcpi_bytes_received != ret)
581 			xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64,
582 			       s.last_sample.mptcpi_bytes_received, ret,
583 			       s.last_sample.mptcpi_bytes_received - ret);
584 		if (s.last_sample.mptcpi_bytes_acked != ret)
585 			xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64,
586 			       s.last_sample.mptcpi_bytes_acked, ret2,
587 			       s.last_sample.mptcpi_bytes_acked - ret2);
588 	}
589 
590 	close(fd);
591 }
592 
593 static int xaccept(int s)
594 {
595 	int fd = accept(s, NULL, 0);
596 
597 	if (fd < 0)
598 		die_perror("accept");
599 
600 	return fd;
601 }
602 
603 static int server(int pipefd)
604 {
605 	int fd = -1, r;
606 
607 	switch (pf) {
608 	case AF_INET:
609 		fd = sock_listen_mptcp("127.0.0.1", "15432");
610 		break;
611 	case AF_INET6:
612 		fd = sock_listen_mptcp("::1", "15432");
613 		break;
614 	default:
615 		xerror("Unknown pf %d\n", pf);
616 		break;
617 	}
618 
619 	r = write(pipefd, "conn", 4);
620 	assert(r == 4);
621 
622 	alarm(15);
623 	r = xaccept(fd);
624 
625 	process_one_client(r, pipefd);
626 
627 	return 0;
628 }
629 
630 static void test_ip_tos_sockopt(int fd)
631 {
632 	uint8_t tos_in, tos_out;
633 	socklen_t s;
634 	int r;
635 
636 	tos_in = rand() & 0xfc;
637 	r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out));
638 	if (r != 0)
639 		die_perror("setsockopt IP_TOS");
640 
641 	tos_out = 0;
642 	s = sizeof(tos_out);
643 	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
644 	if (r != 0)
645 		die_perror("getsockopt IP_TOS");
646 
647 	if (tos_in != tos_out)
648 		xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s);
649 
650 	if (s != 1)
651 		xerror("tos should be 1 byte");
652 
653 	s = 0;
654 	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
655 	if (r != 0)
656 		die_perror("getsockopt IP_TOS 0");
657 	if (s != 0)
658 		xerror("expect socklen_t == 0");
659 
660 	s = -1;
661 	r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
662 	if (r != -1 && errno != EINVAL)
663 		die_perror("getsockopt IP_TOS did not indicate -EINVAL");
664 	if (s != -1)
665 		xerror("expect socklen_t == -1");
666 }
667 
668 static int client(int pipefd)
669 {
670 	int fd = -1;
671 
672 	alarm(15);
673 
674 	switch (pf) {
675 	case AF_INET:
676 		fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP);
677 		break;
678 	case AF_INET6:
679 		fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP);
680 		break;
681 	default:
682 		xerror("Unknown pf %d\n", pf);
683 	}
684 
685 	test_ip_tos_sockopt(fd);
686 
687 	connect_one_server(fd, pipefd);
688 
689 	return 0;
690 }
691 
692 static pid_t xfork(void)
693 {
694 	pid_t p = fork();
695 
696 	if (p < 0)
697 		die_perror("fork");
698 
699 	return p;
700 }
701 
702 static int rcheck(int wstatus, const char *what)
703 {
704 	if (WIFEXITED(wstatus)) {
705 		if (WEXITSTATUS(wstatus) == 0)
706 			return 0;
707 		fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
708 		return WEXITSTATUS(wstatus);
709 	} else if (WIFSIGNALED(wstatus)) {
710 		xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
711 	} else if (WIFSTOPPED(wstatus)) {
712 		xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
713 	}
714 
715 	return 111;
716 }
717 
718 static void init_rng(void)
719 {
720 	int fd = open("/dev/urandom", O_RDONLY);
721 
722 	if (fd >= 0) {
723 		unsigned int foo;
724 		ssize_t ret;
725 
726 		/* can't fail */
727 		ret = read(fd, &foo, sizeof(foo));
728 		assert(ret == sizeof(foo));
729 
730 		close(fd);
731 		srand(foo);
732 	} else {
733 		srand(time(NULL));
734 	}
735 }
736 
737 int main(int argc, char *argv[])
738 {
739 	int e1, e2, wstatus;
740 	pid_t s, c, ret;
741 	int pipefds[2];
742 
743 	parse_opts(argc, argv);
744 
745 	init_rng();
746 
747 	e1 = pipe(pipefds);
748 	if (e1 < 0)
749 		die_perror("pipe");
750 
751 	s = xfork();
752 	if (s == 0)
753 		return server(pipefds[1]);
754 
755 	close(pipefds[1]);
756 
757 	/* wait until server bound a socket */
758 	e1 = read(pipefds[0], &e1, 4);
759 	assert(e1 == 4);
760 
761 	c = xfork();
762 	if (c == 0)
763 		return client(pipefds[0]);
764 
765 	close(pipefds[0]);
766 
767 	ret = waitpid(s, &wstatus, 0);
768 	if (ret == -1)
769 		die_perror("waitpid");
770 	e1 = rcheck(wstatus, "server");
771 	ret = waitpid(c, &wstatus, 0);
772 	if (ret == -1)
773 		die_perror("waitpid");
774 	e2 = rcheck(wstatus, "client");
775 
776 	return e1 ? e1 : e2;
777 }
778