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