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