162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <assert.h> 662306a36Sopenharmony_ci#include <errno.h> 762306a36Sopenharmony_ci#include <fcntl.h> 862306a36Sopenharmony_ci#include <limits.h> 962306a36Sopenharmony_ci#include <string.h> 1062306a36Sopenharmony_ci#include <stdarg.h> 1162306a36Sopenharmony_ci#include <stdbool.h> 1262306a36Sopenharmony_ci#include <stdint.h> 1362306a36Sopenharmony_ci#include <inttypes.h> 1462306a36Sopenharmony_ci#include <stdio.h> 1562306a36Sopenharmony_ci#include <stdlib.h> 1662306a36Sopenharmony_ci#include <strings.h> 1762306a36Sopenharmony_ci#include <unistd.h> 1862306a36Sopenharmony_ci#include <time.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <sys/ioctl.h> 2162306a36Sopenharmony_ci#include <sys/random.h> 2262306a36Sopenharmony_ci#include <sys/socket.h> 2362306a36Sopenharmony_ci#include <sys/types.h> 2462306a36Sopenharmony_ci#include <sys/wait.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <netdb.h> 2762306a36Sopenharmony_ci#include <netinet/in.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/tcp.h> 3062306a36Sopenharmony_ci#include <linux/sockios.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#ifndef IPPROTO_MPTCP 3362306a36Sopenharmony_ci#define IPPROTO_MPTCP 262 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci#ifndef SOL_MPTCP 3662306a36Sopenharmony_ci#define SOL_MPTCP 284 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int pf = AF_INET; 4062306a36Sopenharmony_cistatic int proto_tx = IPPROTO_MPTCP; 4162306a36Sopenharmony_cistatic int proto_rx = IPPROTO_MPTCP; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void die_perror(const char *msg) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci perror(msg); 4662306a36Sopenharmony_ci exit(1); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void die_usage(int r) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci fprintf(stderr, "Usage: mptcp_inq [-6] [ -t tcp|mptcp ] [ -r tcp|mptcp]\n"); 5262306a36Sopenharmony_ci exit(r); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void xerror(const char *fmt, ...) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci va_list ap; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci va_start(ap, fmt); 6062306a36Sopenharmony_ci vfprintf(stderr, fmt, ap); 6162306a36Sopenharmony_ci va_end(ap); 6262306a36Sopenharmony_ci fputc('\n', stderr); 6362306a36Sopenharmony_ci exit(1); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const char *getxinfo_strerr(int err) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci if (err == EAI_SYSTEM) 6962306a36Sopenharmony_ci return strerror(errno); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return gai_strerror(err); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void xgetaddrinfo(const char *node, const char *service, 7562306a36Sopenharmony_ci const struct addrinfo *hints, 7662306a36Sopenharmony_ci struct addrinfo **res) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int err = getaddrinfo(node, service, hints, res); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (err) { 8162306a36Sopenharmony_ci const char *errstr = getxinfo_strerr(err); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n", 8462306a36Sopenharmony_ci node ? node : "", service ? service : "", errstr); 8562306a36Sopenharmony_ci exit(1); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int sock_listen_mptcp(const char * const listenaddr, 9062306a36Sopenharmony_ci const char * const port) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci int sock = -1; 9362306a36Sopenharmony_ci struct addrinfo hints = { 9462306a36Sopenharmony_ci .ai_protocol = IPPROTO_TCP, 9562306a36Sopenharmony_ci .ai_socktype = SOCK_STREAM, 9662306a36Sopenharmony_ci .ai_flags = AI_PASSIVE | AI_NUMERICHOST 9762306a36Sopenharmony_ci }; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci hints.ai_family = pf; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci struct addrinfo *a, *addr; 10262306a36Sopenharmony_ci int one = 1; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci xgetaddrinfo(listenaddr, port, &hints, &addr); 10562306a36Sopenharmony_ci hints.ai_family = pf; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (a = addr; a; a = a->ai_next) { 10862306a36Sopenharmony_ci sock = socket(a->ai_family, a->ai_socktype, proto_rx); 10962306a36Sopenharmony_ci if (sock < 0) 11062306a36Sopenharmony_ci continue; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, 11362306a36Sopenharmony_ci sizeof(one))) 11462306a36Sopenharmony_ci perror("setsockopt"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) 11762306a36Sopenharmony_ci break; /* success */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci perror("bind"); 12062306a36Sopenharmony_ci close(sock); 12162306a36Sopenharmony_ci sock = -1; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci freeaddrinfo(addr); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (sock < 0) 12762306a36Sopenharmony_ci xerror("could not create listen socket"); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (listen(sock, 20)) 13062306a36Sopenharmony_ci die_perror("listen"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return sock; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int sock_connect_mptcp(const char * const remoteaddr, 13662306a36Sopenharmony_ci const char * const port, int proto) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct addrinfo hints = { 13962306a36Sopenharmony_ci .ai_protocol = IPPROTO_TCP, 14062306a36Sopenharmony_ci .ai_socktype = SOCK_STREAM, 14162306a36Sopenharmony_ci }; 14262306a36Sopenharmony_ci struct addrinfo *a, *addr; 14362306a36Sopenharmony_ci int sock = -1; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci hints.ai_family = pf; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci xgetaddrinfo(remoteaddr, port, &hints, &addr); 14862306a36Sopenharmony_ci for (a = addr; a; a = a->ai_next) { 14962306a36Sopenharmony_ci sock = socket(a->ai_family, a->ai_socktype, proto); 15062306a36Sopenharmony_ci if (sock < 0) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) 15462306a36Sopenharmony_ci break; /* success */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci die_perror("connect"); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (sock < 0) 16062306a36Sopenharmony_ci xerror("could not create connect socket"); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci freeaddrinfo(addr); 16362306a36Sopenharmony_ci return sock; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int protostr_to_num(const char *s) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci if (strcasecmp(s, "tcp") == 0) 16962306a36Sopenharmony_ci return IPPROTO_TCP; 17062306a36Sopenharmony_ci if (strcasecmp(s, "mptcp") == 0) 17162306a36Sopenharmony_ci return IPPROTO_MPTCP; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci die_usage(1); 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void parse_opts(int argc, char **argv) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int c; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci while ((c = getopt(argc, argv, "h6t:r:")) != -1) { 18262306a36Sopenharmony_ci switch (c) { 18362306a36Sopenharmony_ci case 'h': 18462306a36Sopenharmony_ci die_usage(0); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case '6': 18762306a36Sopenharmony_ci pf = AF_INET6; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case 't': 19062306a36Sopenharmony_ci proto_tx = protostr_to_num(optarg); 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci case 'r': 19362306a36Sopenharmony_ci proto_rx = protostr_to_num(optarg); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci die_usage(1); 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* wait up to timeout milliseconds */ 20362306a36Sopenharmony_cistatic void wait_for_ack(int fd, int timeout, size_t total) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < timeout; i++) { 20862306a36Sopenharmony_ci int nsd, ret, queued = -1; 20962306a36Sopenharmony_ci struct timespec req; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = ioctl(fd, TIOCOUTQ, &queued); 21262306a36Sopenharmony_ci if (ret < 0) 21362306a36Sopenharmony_ci die_perror("TIOCOUTQ"); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = ioctl(fd, SIOCOUTQNSD, &nsd); 21662306a36Sopenharmony_ci if (ret < 0) 21762306a36Sopenharmony_ci die_perror("SIOCOUTQNSD"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if ((size_t)queued > total) 22062306a36Sopenharmony_ci xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total); 22162306a36Sopenharmony_ci assert(nsd <= queued); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (queued == 0) 22462306a36Sopenharmony_ci return; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* wait for peer to ack rx of all data */ 22762306a36Sopenharmony_ci req.tv_sec = 0; 22862306a36Sopenharmony_ci req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */ 22962306a36Sopenharmony_ci nanosleep(&req, NULL); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci xerror("still tx data queued after %u ms\n", timeout); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void connect_one_server(int fd, int unixfd) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci size_t len, i, total, sent; 23862306a36Sopenharmony_ci char buf[4096], buf2[4096]; 23962306a36Sopenharmony_ci ssize_t ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci len = rand() % (sizeof(buf) - 1); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (len < 128) 24462306a36Sopenharmony_ci len = 128; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci for (i = 0; i < len ; i++) { 24762306a36Sopenharmony_ci buf[i] = rand() % 26; 24862306a36Sopenharmony_ci buf[i] += 'A'; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci buf[i] = '\n'; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* un-block server */ 25462306a36Sopenharmony_ci ret = read(unixfd, buf2, 4); 25562306a36Sopenharmony_ci assert(ret == 4); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci assert(strncmp(buf2, "xmit", 4) == 0); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = write(unixfd, &len, sizeof(len)); 26062306a36Sopenharmony_ci assert(ret == (ssize_t)sizeof(len)); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = write(fd, buf, len); 26362306a36Sopenharmony_ci if (ret < 0) 26462306a36Sopenharmony_ci die_perror("write"); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (ret != (ssize_t)len) 26762306a36Sopenharmony_ci xerror("short write"); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ret = read(unixfd, buf2, 4); 27062306a36Sopenharmony_ci assert(strncmp(buf2, "huge", 4) == 0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci total = rand() % (16 * 1024 * 1024); 27362306a36Sopenharmony_ci total += (1 * 1024 * 1024); 27462306a36Sopenharmony_ci sent = total; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ret = write(unixfd, &total, sizeof(total)); 27762306a36Sopenharmony_ci assert(ret == (ssize_t)sizeof(total)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci wait_for_ack(fd, 5000, len); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci while (total > 0) { 28262306a36Sopenharmony_ci if (total > sizeof(buf)) 28362306a36Sopenharmony_ci len = sizeof(buf); 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci len = total; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = write(fd, buf, len); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci die_perror("write"); 29062306a36Sopenharmony_ci total -= ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* we don't have to care about buf content, only 29362306a36Sopenharmony_ci * number of total bytes sent 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = read(unixfd, buf2, 4); 29862306a36Sopenharmony_ci assert(ret == 4); 29962306a36Sopenharmony_ci assert(strncmp(buf2, "shut", 4) == 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci wait_for_ack(fd, 5000, sent); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = write(fd, buf, 1); 30462306a36Sopenharmony_ci assert(ret == 1); 30562306a36Sopenharmony_ci close(fd); 30662306a36Sopenharmony_ci ret = write(unixfd, "closed", 6); 30762306a36Sopenharmony_ci assert(ret == 6); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci close(unixfd); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct cmsghdr *cmsg; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) { 31762306a36Sopenharmony_ci if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) { 31862306a36Sopenharmony_ci memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv)); 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci xerror("could not find TCP_CM_INQ cmsg type"); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void process_one_client(int fd, int unixfd) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci unsigned int tcp_inq; 32962306a36Sopenharmony_ci size_t expect_len; 33062306a36Sopenharmony_ci char msg_buf[4096]; 33162306a36Sopenharmony_ci char buf[4096]; 33262306a36Sopenharmony_ci char tmp[16]; 33362306a36Sopenharmony_ci struct iovec iov = { 33462306a36Sopenharmony_ci .iov_base = buf, 33562306a36Sopenharmony_ci .iov_len = 1, 33662306a36Sopenharmony_ci }; 33762306a36Sopenharmony_ci struct msghdr msg = { 33862306a36Sopenharmony_ci .msg_iov = &iov, 33962306a36Sopenharmony_ci .msg_iovlen = 1, 34062306a36Sopenharmony_ci .msg_control = msg_buf, 34162306a36Sopenharmony_ci .msg_controllen = sizeof(msg_buf), 34262306a36Sopenharmony_ci }; 34362306a36Sopenharmony_ci ssize_t ret, tot; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = write(unixfd, "xmit", 4); 34662306a36Sopenharmony_ci assert(ret == 4); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = read(unixfd, &expect_len, sizeof(expect_len)); 34962306a36Sopenharmony_ci assert(ret == (ssize_t)sizeof(expect_len)); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (expect_len > sizeof(buf)) 35262306a36Sopenharmony_ci xerror("expect len %zu exceeds buffer size", expect_len); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci for (;;) { 35562306a36Sopenharmony_ci struct timespec req; 35662306a36Sopenharmony_ci unsigned int queued; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = ioctl(fd, FIONREAD, &queued); 35962306a36Sopenharmony_ci if (ret < 0) 36062306a36Sopenharmony_ci die_perror("FIONREAD"); 36162306a36Sopenharmony_ci if (queued > expect_len) 36262306a36Sopenharmony_ci xerror("FIONREAD returned %u, but only %zu expected\n", 36362306a36Sopenharmony_ci queued, expect_len); 36462306a36Sopenharmony_ci if (queued == expect_len) 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci req.tv_sec = 0; 36862306a36Sopenharmony_ci req.tv_nsec = 1000 * 1000ul; 36962306a36Sopenharmony_ci nanosleep(&req, NULL); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* read one byte, expect cmsg to return expected - 1 */ 37362306a36Sopenharmony_ci ret = recvmsg(fd, &msg, 0); 37462306a36Sopenharmony_ci if (ret < 0) 37562306a36Sopenharmony_ci die_perror("recvmsg"); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (msg.msg_controllen == 0) 37862306a36Sopenharmony_ci xerror("msg_controllen is 0"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci get_tcp_inq(&msg, &tcp_inq); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci assert((size_t)tcp_inq == (expect_len - 1)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci iov.iov_len = sizeof(buf); 38562306a36Sopenharmony_ci ret = recvmsg(fd, &msg, 0); 38662306a36Sopenharmony_ci if (ret < 0) 38762306a36Sopenharmony_ci die_perror("recvmsg"); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* should have gotten exact remainder of all pending data */ 39062306a36Sopenharmony_ci assert(ret == (ssize_t)tcp_inq); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* should be 0, all drained */ 39362306a36Sopenharmony_ci get_tcp_inq(&msg, &tcp_inq); 39462306a36Sopenharmony_ci assert(tcp_inq == 0); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* request a large swath of data. */ 39762306a36Sopenharmony_ci ret = write(unixfd, "huge", 4); 39862306a36Sopenharmony_ci assert(ret == 4); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = read(unixfd, &expect_len, sizeof(expect_len)); 40162306a36Sopenharmony_ci assert(ret == (ssize_t)sizeof(expect_len)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* peer should send us a few mb of data */ 40462306a36Sopenharmony_ci if (expect_len <= sizeof(buf)) 40562306a36Sopenharmony_ci xerror("expect len %zu too small\n", expect_len); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci tot = 0; 40862306a36Sopenharmony_ci do { 40962306a36Sopenharmony_ci iov.iov_len = sizeof(buf); 41062306a36Sopenharmony_ci ret = recvmsg(fd, &msg, 0); 41162306a36Sopenharmony_ci if (ret < 0) 41262306a36Sopenharmony_ci die_perror("recvmsg"); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci tot += ret; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci get_tcp_inq(&msg, &tcp_inq); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (tcp_inq > expect_len - tot) 41962306a36Sopenharmony_ci xerror("inq %d, remaining %d total_len %d\n", 42062306a36Sopenharmony_ci tcp_inq, expect_len - tot, (int)expect_len); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci assert(tcp_inq <= expect_len - tot); 42362306a36Sopenharmony_ci } while ((size_t)tot < expect_len); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = write(unixfd, "shut", 4); 42662306a36Sopenharmony_ci assert(ret == 4); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* wait for hangup. Should have received one more byte of data. */ 42962306a36Sopenharmony_ci ret = read(unixfd, tmp, sizeof(tmp)); 43062306a36Sopenharmony_ci assert(ret == 6); 43162306a36Sopenharmony_ci assert(strncmp(tmp, "closed", 6) == 0); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci sleep(1); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci iov.iov_len = 1; 43662306a36Sopenharmony_ci ret = recvmsg(fd, &msg, 0); 43762306a36Sopenharmony_ci if (ret < 0) 43862306a36Sopenharmony_ci die_perror("recvmsg"); 43962306a36Sopenharmony_ci assert(ret == 1); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci get_tcp_inq(&msg, &tcp_inq); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* tcp_inq should be 1 due to received fin. */ 44462306a36Sopenharmony_ci assert(tcp_inq == 1); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci iov.iov_len = 1; 44762306a36Sopenharmony_ci ret = recvmsg(fd, &msg, 0); 44862306a36Sopenharmony_ci if (ret < 0) 44962306a36Sopenharmony_ci die_perror("recvmsg"); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* expect EOF */ 45262306a36Sopenharmony_ci assert(ret == 0); 45362306a36Sopenharmony_ci get_tcp_inq(&msg, &tcp_inq); 45462306a36Sopenharmony_ci assert(tcp_inq == 1); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci close(fd); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int xaccept(int s) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci int fd = accept(s, NULL, 0); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (fd < 0) 46462306a36Sopenharmony_ci die_perror("accept"); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return fd; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int server(int unixfd) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int fd = -1, r, on = 1; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci switch (pf) { 47462306a36Sopenharmony_ci case AF_INET: 47562306a36Sopenharmony_ci fd = sock_listen_mptcp("127.0.0.1", "15432"); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case AF_INET6: 47862306a36Sopenharmony_ci fd = sock_listen_mptcp("::1", "15432"); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci default: 48162306a36Sopenharmony_ci xerror("Unknown pf %d\n", pf); 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci r = write(unixfd, "conn", 4); 48662306a36Sopenharmony_ci assert(r == 4); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci alarm(15); 48962306a36Sopenharmony_ci r = xaccept(fd); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (-1 == setsockopt(r, IPPROTO_TCP, TCP_INQ, &on, sizeof(on))) 49262306a36Sopenharmony_ci die_perror("setsockopt"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci process_one_client(r, unixfd); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int client(int unixfd) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int fd = -1; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci alarm(15); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci switch (pf) { 50662306a36Sopenharmony_ci case AF_INET: 50762306a36Sopenharmony_ci fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx); 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci case AF_INET6: 51062306a36Sopenharmony_ci fd = sock_connect_mptcp("::1", "15432", proto_tx); 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci default: 51362306a36Sopenharmony_ci xerror("Unknown pf %d\n", pf); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci connect_one_server(fd, unixfd); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic void init_rng(void) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci unsigned int foo; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (getrandom(&foo, sizeof(foo), 0) == -1) { 52662306a36Sopenharmony_ci perror("getrandom"); 52762306a36Sopenharmony_ci exit(1); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci srand(foo); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic pid_t xfork(void) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci pid_t p = fork(); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (p < 0) 53862306a36Sopenharmony_ci die_perror("fork"); 53962306a36Sopenharmony_ci else if (p == 0) 54062306a36Sopenharmony_ci init_rng(); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return p; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int rcheck(int wstatus, const char *what) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci if (WIFEXITED(wstatus)) { 54862306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) == 0) 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus)); 55162306a36Sopenharmony_ci return WEXITSTATUS(wstatus); 55262306a36Sopenharmony_ci } else if (WIFSIGNALED(wstatus)) { 55362306a36Sopenharmony_ci xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus)); 55462306a36Sopenharmony_ci } else if (WIFSTOPPED(wstatus)) { 55562306a36Sopenharmony_ci xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus)); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return 111; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciint main(int argc, char *argv[]) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int e1, e2, wstatus; 56462306a36Sopenharmony_ci pid_t s, c, ret; 56562306a36Sopenharmony_ci int unixfds[2]; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci parse_opts(argc, argv); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci e1 = socketpair(AF_UNIX, SOCK_DGRAM, 0, unixfds); 57062306a36Sopenharmony_ci if (e1 < 0) 57162306a36Sopenharmony_ci die_perror("pipe"); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci s = xfork(); 57462306a36Sopenharmony_ci if (s == 0) 57562306a36Sopenharmony_ci return server(unixfds[1]); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci close(unixfds[1]); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* wait until server bound a socket */ 58062306a36Sopenharmony_ci e1 = read(unixfds[0], &e1, 4); 58162306a36Sopenharmony_ci assert(e1 == 4); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci c = xfork(); 58462306a36Sopenharmony_ci if (c == 0) 58562306a36Sopenharmony_ci return client(unixfds[0]); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci close(unixfds[0]); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = waitpid(s, &wstatus, 0); 59062306a36Sopenharmony_ci if (ret == -1) 59162306a36Sopenharmony_ci die_perror("waitpid"); 59262306a36Sopenharmony_ci e1 = rcheck(wstatus, "server"); 59362306a36Sopenharmony_ci ret = waitpid(c, &wstatus, 0); 59462306a36Sopenharmony_ci if (ret == -1) 59562306a36Sopenharmony_ci die_perror("waitpid"); 59662306a36Sopenharmony_ci e2 = rcheck(wstatus, "client"); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return e1 ? e1 : e2; 59962306a36Sopenharmony_ci} 600