Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #define _GNU_SOURCE
0004 
0005 #include <assert.h>
0006 #include <errno.h>
0007 #include <fcntl.h>
0008 #include <limits.h>
0009 #include <string.h>
0010 #include <stdarg.h>
0011 #include <stdbool.h>
0012 #include <stdint.h>
0013 #include <inttypes.h>
0014 #include <stdio.h>
0015 #include <stdlib.h>
0016 #include <strings.h>
0017 #include <time.h>
0018 #include <unistd.h>
0019 
0020 #include <sys/socket.h>
0021 #include <sys/types.h>
0022 #include <sys/wait.h>
0023 
0024 #include <netdb.h>
0025 #include <netinet/in.h>
0026 
0027 #include <linux/tcp.h>
0028 
0029 static int pf = AF_INET;
0030 
0031 #ifndef IPPROTO_MPTCP
0032 #define IPPROTO_MPTCP 262
0033 #endif
0034 #ifndef SOL_MPTCP
0035 #define SOL_MPTCP 284
0036 #endif
0037 
0038 #ifndef MPTCP_INFO
0039 struct mptcp_info {
0040     __u8    mptcpi_subflows;
0041     __u8    mptcpi_add_addr_signal;
0042     __u8    mptcpi_add_addr_accepted;
0043     __u8    mptcpi_subflows_max;
0044     __u8    mptcpi_add_addr_signal_max;
0045     __u8    mptcpi_add_addr_accepted_max;
0046     __u32   mptcpi_flags;
0047     __u32   mptcpi_token;
0048     __u64   mptcpi_write_seq;
0049     __u64   mptcpi_snd_una;
0050     __u64   mptcpi_rcv_nxt;
0051     __u8    mptcpi_local_addr_used;
0052     __u8    mptcpi_local_addr_max;
0053     __u8    mptcpi_csum_enabled;
0054 };
0055 
0056 struct mptcp_subflow_data {
0057     __u32       size_subflow_data;      /* size of this structure in userspace */
0058     __u32       num_subflows;           /* must be 0, set by kernel */
0059     __u32       size_kernel;            /* must be 0, set by kernel */
0060     __u32       size_user;          /* size of one element in data[] */
0061 } __attribute__((aligned(8)));
0062 
0063 struct mptcp_subflow_addrs {
0064     union {
0065         __kernel_sa_family_t sa_family;
0066         struct sockaddr sa_local;
0067         struct sockaddr_in sin_local;
0068         struct sockaddr_in6 sin6_local;
0069         struct __kernel_sockaddr_storage ss_local;
0070     };
0071     union {
0072         struct sockaddr sa_remote;
0073         struct sockaddr_in sin_remote;
0074         struct sockaddr_in6 sin6_remote;
0075         struct __kernel_sockaddr_storage ss_remote;
0076     };
0077 };
0078 
0079 #define MPTCP_INFO      1
0080 #define MPTCP_TCPINFO       2
0081 #define MPTCP_SUBFLOW_ADDRS 3
0082 #endif
0083 
0084 struct so_state {
0085     struct mptcp_info mi;
0086     uint64_t mptcpi_rcv_delta;
0087     uint64_t tcpi_rcv_delta;
0088 };
0089 
0090 static void die_perror(const char *msg)
0091 {
0092     perror(msg);
0093     exit(1);
0094 }
0095 
0096 static void die_usage(int r)
0097 {
0098     fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
0099     exit(r);
0100 }
0101 
0102 static void xerror(const char *fmt, ...)
0103 {
0104     va_list ap;
0105 
0106     va_start(ap, fmt);
0107     vfprintf(stderr, fmt, ap);
0108     va_end(ap);
0109     fputc('\n', stderr);
0110     exit(1);
0111 }
0112 
0113 static const char *getxinfo_strerr(int err)
0114 {
0115     if (err == EAI_SYSTEM)
0116         return strerror(errno);
0117 
0118     return gai_strerror(err);
0119 }
0120 
0121 static void xgetaddrinfo(const char *node, const char *service,
0122              const struct addrinfo *hints,
0123              struct addrinfo **res)
0124 {
0125     int err = getaddrinfo(node, service, hints, res);
0126 
0127     if (err) {
0128         const char *errstr = getxinfo_strerr(err);
0129 
0130         fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
0131             node ? node : "", service ? service : "", errstr);
0132         exit(1);
0133     }
0134 }
0135 
0136 static int sock_listen_mptcp(const char * const listenaddr,
0137                  const char * const port)
0138 {
0139     int sock = -1;
0140     struct addrinfo hints = {
0141         .ai_protocol = IPPROTO_TCP,
0142         .ai_socktype = SOCK_STREAM,
0143         .ai_flags = AI_PASSIVE | AI_NUMERICHOST
0144     };
0145 
0146     hints.ai_family = pf;
0147 
0148     struct addrinfo *a, *addr;
0149     int one = 1;
0150 
0151     xgetaddrinfo(listenaddr, port, &hints, &addr);
0152     hints.ai_family = pf;
0153 
0154     for (a = addr; a; a = a->ai_next) {
0155         sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
0156         if (sock < 0)
0157             continue;
0158 
0159         if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
0160                      sizeof(one)))
0161             perror("setsockopt");
0162 
0163         if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
0164             break; /* success */
0165 
0166         perror("bind");
0167         close(sock);
0168         sock = -1;
0169     }
0170 
0171     freeaddrinfo(addr);
0172 
0173     if (sock < 0)
0174         xerror("could not create listen socket");
0175 
0176     if (listen(sock, 20))
0177         die_perror("listen");
0178 
0179     return sock;
0180 }
0181 
0182 static int sock_connect_mptcp(const char * const remoteaddr,
0183                   const char * const port, int proto)
0184 {
0185     struct addrinfo hints = {
0186         .ai_protocol = IPPROTO_TCP,
0187         .ai_socktype = SOCK_STREAM,
0188     };
0189     struct addrinfo *a, *addr;
0190     int sock = -1;
0191 
0192     hints.ai_family = pf;
0193 
0194     xgetaddrinfo(remoteaddr, port, &hints, &addr);
0195     for (a = addr; a; a = a->ai_next) {
0196         sock = socket(a->ai_family, a->ai_socktype, proto);
0197         if (sock < 0)
0198             continue;
0199 
0200         if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
0201             break; /* success */
0202 
0203         die_perror("connect");
0204     }
0205 
0206     if (sock < 0)
0207         xerror("could not create connect socket");
0208 
0209     freeaddrinfo(addr);
0210     return sock;
0211 }
0212 
0213 static void parse_opts(int argc, char **argv)
0214 {
0215     int c;
0216 
0217     while ((c = getopt(argc, argv, "h6")) != -1) {
0218         switch (c) {
0219         case 'h':
0220             die_usage(0);
0221             break;
0222         case '6':
0223             pf = AF_INET6;
0224             break;
0225         default:
0226             die_usage(1);
0227             break;
0228         }
0229     }
0230 }
0231 
0232 static void do_getsockopt_bogus_sf_data(int fd, int optname)
0233 {
0234     struct mptcp_subflow_data good_data;
0235     struct bogus_data {
0236         struct mptcp_subflow_data d;
0237         char buf[2];
0238     } bd;
0239     socklen_t olen, _olen;
0240     int ret;
0241 
0242     memset(&bd, 0, sizeof(bd));
0243     memset(&good_data, 0, sizeof(good_data));
0244 
0245     olen = sizeof(good_data);
0246     good_data.size_subflow_data = olen;
0247 
0248     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0249     assert(ret < 0); /* 0 size_subflow_data */
0250     assert(olen == sizeof(good_data));
0251 
0252     bd.d = good_data;
0253 
0254     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0255     assert(ret == 0);
0256     assert(olen == sizeof(good_data));
0257     assert(bd.d.num_subflows == 1);
0258     assert(bd.d.size_kernel > 0);
0259     assert(bd.d.size_user == 0);
0260 
0261     bd.d = good_data;
0262     _olen = rand() % olen;
0263     olen = _olen;
0264     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0265     assert(ret < 0);    /* bogus olen */
0266     assert(olen == _olen);  /* must be unchanged */
0267 
0268     bd.d = good_data;
0269     olen = sizeof(good_data);
0270     bd.d.size_kernel = 1;
0271     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0272     assert(ret < 0); /* size_kernel not 0 */
0273 
0274     bd.d = good_data;
0275     olen = sizeof(good_data);
0276     bd.d.num_subflows = 1;
0277     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0278     assert(ret < 0); /* num_subflows not 0 */
0279 
0280     /* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */
0281     bd.d = good_data;
0282     olen = sizeof(bd);
0283     bd.d.size_subflow_data = sizeof(bd);
0284 
0285     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
0286     assert(ret == 0);
0287 
0288     /* olen must be truncated to real data size filled by kernel: */
0289     assert(olen == sizeof(good_data));
0290 
0291     assert(bd.d.size_subflow_data == sizeof(bd));
0292 
0293     bd.d = good_data;
0294     bd.d.size_subflow_data += 1;
0295     bd.d.size_user = 1;
0296     olen = bd.d.size_subflow_data + 1;
0297     _olen = olen;
0298 
0299     ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen);
0300     assert(ret == 0);
0301 
0302     /* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */
0303     assert(olen == _olen);
0304 
0305     assert(bd.d.size_subflow_data == sizeof(good_data) + 1);
0306     assert(bd.buf[0] == 0);
0307 }
0308 
0309 static void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w)
0310 {
0311     struct mptcp_info i;
0312     socklen_t olen;
0313     int ret;
0314 
0315     olen = sizeof(i);
0316     ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen);
0317 
0318     if (ret < 0)
0319         die_perror("getsockopt MPTCP_INFO");
0320 
0321     assert(olen == sizeof(i));
0322 
0323     if (s->mi.mptcpi_write_seq == 0)
0324         s->mi = i;
0325 
0326     assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq);
0327 
0328     s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt;
0329 }
0330 
0331 static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w)
0332 {
0333     struct my_tcp_info {
0334         struct mptcp_subflow_data d;
0335         struct tcp_info ti[2];
0336     } ti;
0337     int ret, tries = 5;
0338     socklen_t olen;
0339 
0340     do {
0341         memset(&ti, 0, sizeof(ti));
0342 
0343         ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
0344         ti.d.size_user = sizeof(struct tcp_info);
0345         olen = sizeof(ti);
0346 
0347         ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen);
0348         if (ret < 0)
0349             xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
0350 
0351         assert(olen <= sizeof(ti));
0352         assert(ti.d.size_user == ti.d.size_kernel);
0353         assert(ti.d.size_user == sizeof(struct tcp_info));
0354         assert(ti.d.num_subflows == 1);
0355 
0356         assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
0357         olen -= sizeof(struct mptcp_subflow_data);
0358         assert(olen == sizeof(struct tcp_info));
0359 
0360         if (ti.ti[0].tcpi_bytes_sent == w &&
0361             ti.ti[0].tcpi_bytes_received == r)
0362             goto done;
0363 
0364         if (r == 0 && ti.ti[0].tcpi_bytes_sent == w &&
0365             ti.ti[0].tcpi_bytes_received) {
0366             s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received;
0367             goto done;
0368         }
0369 
0370         /* wait and repeat, might be that tx is still ongoing */
0371         sleep(1);
0372     } while (tries-- > 0);
0373 
0374     xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu",
0375         ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r);
0376 
0377 done:
0378     do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
0379 }
0380 
0381 static void do_getsockopt_subflow_addrs(int fd)
0382 {
0383     struct sockaddr_storage remote, local;
0384     socklen_t olen, rlen, llen;
0385     int ret;
0386     struct my_addrs {
0387         struct mptcp_subflow_data d;
0388         struct mptcp_subflow_addrs addr[2];
0389     } addrs;
0390 
0391     memset(&addrs, 0, sizeof(addrs));
0392     memset(&local, 0, sizeof(local));
0393     memset(&remote, 0, sizeof(remote));
0394 
0395     addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
0396     addrs.d.size_user = sizeof(struct mptcp_subflow_addrs);
0397     olen = sizeof(addrs);
0398 
0399     ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
0400     if (ret < 0)
0401         die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
0402 
0403     assert(olen <= sizeof(addrs));
0404     assert(addrs.d.size_user == addrs.d.size_kernel);
0405     assert(addrs.d.size_user == sizeof(struct mptcp_subflow_addrs));
0406     assert(addrs.d.num_subflows == 1);
0407 
0408     assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
0409     olen -= sizeof(struct mptcp_subflow_data);
0410     assert(olen == sizeof(struct mptcp_subflow_addrs));
0411 
0412     llen = sizeof(local);
0413     ret = getsockname(fd, (struct sockaddr *)&local, &llen);
0414     if (ret < 0)
0415         die_perror("getsockname");
0416     rlen = sizeof(remote);
0417     ret = getpeername(fd, (struct sockaddr *)&remote, &rlen);
0418     if (ret < 0)
0419         die_perror("getpeername");
0420 
0421     assert(rlen > 0);
0422     assert(rlen == llen);
0423 
0424     assert(remote.ss_family == local.ss_family);
0425 
0426     assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0);
0427     assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0);
0428 
0429     memset(&addrs, 0, sizeof(addrs));
0430 
0431     addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
0432     addrs.d.size_user = sizeof(sa_family_t);
0433     olen = sizeof(addrs.d) + sizeof(sa_family_t);
0434 
0435     ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
0436     assert(ret == 0);
0437     assert(olen == sizeof(addrs.d) + sizeof(sa_family_t));
0438 
0439     assert(addrs.addr[0].sa_family == pf);
0440     assert(addrs.addr[0].sa_family == local.ss_family);
0441 
0442     assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) != 0);
0443     assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) != 0);
0444 
0445     do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
0446 }
0447 
0448 static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
0449 {
0450     do_getsockopt_mptcp_info(s, fd, w);
0451 
0452     do_getsockopt_tcp_info(s, fd, r, w);
0453 
0454     do_getsockopt_subflow_addrs(fd);
0455 }
0456 
0457 static void connect_one_server(int fd, int pipefd)
0458 {
0459     char buf[4096], buf2[4096];
0460     size_t len, i, total;
0461     struct so_state s;
0462     bool eof = false;
0463     ssize_t ret;
0464 
0465     memset(&s, 0, sizeof(s));
0466 
0467     len = rand() % (sizeof(buf) - 1);
0468 
0469     if (len < 128)
0470         len = 128;
0471 
0472     for (i = 0; i < len ; i++) {
0473         buf[i] = rand() % 26;
0474         buf[i] += 'A';
0475     }
0476 
0477     buf[i] = '\n';
0478 
0479     do_getsockopts(&s, fd, 0, 0);
0480 
0481     /* un-block server */
0482     ret = read(pipefd, buf2, 4);
0483     assert(ret == 4);
0484     close(pipefd);
0485 
0486     assert(strncmp(buf2, "xmit", 4) == 0);
0487 
0488     ret = write(fd, buf, len);
0489     if (ret < 0)
0490         die_perror("write");
0491 
0492     if (ret != (ssize_t)len)
0493         xerror("short write");
0494 
0495     total = 0;
0496     do {
0497         ret = read(fd, buf2 + total, sizeof(buf2) - total);
0498         if (ret < 0)
0499             die_perror("read");
0500         if (ret == 0) {
0501             eof = true;
0502             break;
0503         }
0504 
0505         total += ret;
0506     } while (total < len);
0507 
0508     if (total != len)
0509         xerror("total %lu, len %lu eof %d\n", total, len, eof);
0510 
0511     if (memcmp(buf, buf2, len))
0512         xerror("data corruption");
0513 
0514     if (s.tcpi_rcv_delta)
0515         assert(s.tcpi_rcv_delta <= total);
0516 
0517     do_getsockopts(&s, fd, ret, ret);
0518 
0519     if (eof)
0520         total += 1; /* sequence advances due to FIN */
0521 
0522     assert(s.mptcpi_rcv_delta == (uint64_t)total);
0523     close(fd);
0524 }
0525 
0526 static void process_one_client(int fd, int pipefd)
0527 {
0528     ssize_t ret, ret2, ret3;
0529     struct so_state s;
0530     char buf[4096];
0531 
0532     memset(&s, 0, sizeof(s));
0533     do_getsockopts(&s, fd, 0, 0);
0534 
0535     ret = write(pipefd, "xmit", 4);
0536     assert(ret == 4);
0537 
0538     ret = read(fd, buf, sizeof(buf));
0539     if (ret < 0)
0540         die_perror("read");
0541 
0542     assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
0543 
0544     if (s.tcpi_rcv_delta)
0545         assert(s.tcpi_rcv_delta == (uint64_t)ret);
0546 
0547     ret2 = write(fd, buf, ret);
0548     if (ret2 < 0)
0549         die_perror("write");
0550 
0551     /* wait for hangup */
0552     ret3 = read(fd, buf, 1);
0553     if (ret3 != 0)
0554         xerror("expected EOF, got %lu", ret3);
0555 
0556     do_getsockopts(&s, fd, ret, ret2);
0557     if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
0558         xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64, s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - ret);
0559     close(fd);
0560 }
0561 
0562 static int xaccept(int s)
0563 {
0564     int fd = accept(s, NULL, 0);
0565 
0566     if (fd < 0)
0567         die_perror("accept");
0568 
0569     return fd;
0570 }
0571 
0572 static int server(int pipefd)
0573 {
0574     int fd = -1, r;
0575 
0576     switch (pf) {
0577     case AF_INET:
0578         fd = sock_listen_mptcp("127.0.0.1", "15432");
0579         break;
0580     case AF_INET6:
0581         fd = sock_listen_mptcp("::1", "15432");
0582         break;
0583     default:
0584         xerror("Unknown pf %d\n", pf);
0585         break;
0586     }
0587 
0588     r = write(pipefd, "conn", 4);
0589     assert(r == 4);
0590 
0591     alarm(15);
0592     r = xaccept(fd);
0593 
0594     process_one_client(r, pipefd);
0595 
0596     return 0;
0597 }
0598 
0599 static void test_ip_tos_sockopt(int fd)
0600 {
0601     uint8_t tos_in, tos_out;
0602     socklen_t s;
0603     int r;
0604 
0605     tos_in = rand() & 0xfc;
0606     r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out));
0607     if (r != 0)
0608         die_perror("setsockopt IP_TOS");
0609 
0610     tos_out = 0;
0611     s = sizeof(tos_out);
0612     r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
0613     if (r != 0)
0614         die_perror("getsockopt IP_TOS");
0615 
0616     if (tos_in != tos_out)
0617         xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s);
0618 
0619     if (s != 1)
0620         xerror("tos should be 1 byte");
0621 
0622     s = 0;
0623     r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
0624     if (r != 0)
0625         die_perror("getsockopt IP_TOS 0");
0626     if (s != 0)
0627         xerror("expect socklen_t == 0");
0628 
0629     s = -1;
0630     r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
0631     if (r != -1 && errno != EINVAL)
0632         die_perror("getsockopt IP_TOS did not indicate -EINVAL");
0633     if (s != -1)
0634         xerror("expect socklen_t == -1");
0635 }
0636 
0637 static int client(int pipefd)
0638 {
0639     int fd = -1;
0640 
0641     alarm(15);
0642 
0643     switch (pf) {
0644     case AF_INET:
0645         fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP);
0646         break;
0647     case AF_INET6:
0648         fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP);
0649         break;
0650     default:
0651         xerror("Unknown pf %d\n", pf);
0652     }
0653 
0654     test_ip_tos_sockopt(fd);
0655 
0656     connect_one_server(fd, pipefd);
0657 
0658     return 0;
0659 }
0660 
0661 static pid_t xfork(void)
0662 {
0663     pid_t p = fork();
0664 
0665     if (p < 0)
0666         die_perror("fork");
0667 
0668     return p;
0669 }
0670 
0671 static int rcheck(int wstatus, const char *what)
0672 {
0673     if (WIFEXITED(wstatus)) {
0674         if (WEXITSTATUS(wstatus) == 0)
0675             return 0;
0676         fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
0677         return WEXITSTATUS(wstatus);
0678     } else if (WIFSIGNALED(wstatus)) {
0679         xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
0680     } else if (WIFSTOPPED(wstatus)) {
0681         xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
0682     }
0683 
0684     return 111;
0685 }
0686 
0687 static void init_rng(void)
0688 {
0689     int fd = open("/dev/urandom", O_RDONLY);
0690 
0691     if (fd >= 0) {
0692         unsigned int foo;
0693         ssize_t ret;
0694 
0695         /* can't fail */
0696         ret = read(fd, &foo, sizeof(foo));
0697         assert(ret == sizeof(foo));
0698 
0699         close(fd);
0700         srand(foo);
0701     } else {
0702         srand(time(NULL));
0703     }
0704 }
0705 
0706 int main(int argc, char *argv[])
0707 {
0708     int e1, e2, wstatus;
0709     pid_t s, c, ret;
0710     int pipefds[2];
0711 
0712     parse_opts(argc, argv);
0713 
0714     init_rng();
0715 
0716     e1 = pipe(pipefds);
0717     if (e1 < 0)
0718         die_perror("pipe");
0719 
0720     s = xfork();
0721     if (s == 0)
0722         return server(pipefds[1]);
0723 
0724     close(pipefds[1]);
0725 
0726     /* wait until server bound a socket */
0727     e1 = read(pipefds[0], &e1, 4);
0728     assert(e1 == 4);
0729 
0730     c = xfork();
0731     if (c == 0)
0732         return client(pipefds[0]);
0733 
0734     close(pipefds[0]);
0735 
0736     ret = waitpid(s, &wstatus, 0);
0737     if (ret == -1)
0738         die_perror("waitpid");
0739     e1 = rcheck(wstatus, "server");
0740     ret = waitpid(c, &wstatus, 0);
0741     if (ret == -1)
0742         die_perror("waitpid");
0743     e2 = rcheck(wstatus, "client");
0744 
0745     return e1 ? e1 : e2;
0746 }