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