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 <time.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <sys/socket.h> 2162306a36Sopenharmony_ci#include <sys/types.h> 2262306a36Sopenharmony_ci#include <sys/wait.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <netdb.h> 2562306a36Sopenharmony_ci#include <netinet/in.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/tcp.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int pf = AF_INET; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifndef IPPROTO_MPTCP 3262306a36Sopenharmony_ci#define IPPROTO_MPTCP 262 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci#ifndef SOL_MPTCP 3562306a36Sopenharmony_ci#define SOL_MPTCP 284 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifndef MPTCP_INFO 3962306a36Sopenharmony_cistruct mptcp_info { 4062306a36Sopenharmony_ci __u8 mptcpi_subflows; 4162306a36Sopenharmony_ci __u8 mptcpi_add_addr_signal; 4262306a36Sopenharmony_ci __u8 mptcpi_add_addr_accepted; 4362306a36Sopenharmony_ci __u8 mptcpi_subflows_max; 4462306a36Sopenharmony_ci __u8 mptcpi_add_addr_signal_max; 4562306a36Sopenharmony_ci __u8 mptcpi_add_addr_accepted_max; 4662306a36Sopenharmony_ci __u32 mptcpi_flags; 4762306a36Sopenharmony_ci __u32 mptcpi_token; 4862306a36Sopenharmony_ci __u64 mptcpi_write_seq; 4962306a36Sopenharmony_ci __u64 mptcpi_snd_una; 5062306a36Sopenharmony_ci __u64 mptcpi_rcv_nxt; 5162306a36Sopenharmony_ci __u8 mptcpi_local_addr_used; 5262306a36Sopenharmony_ci __u8 mptcpi_local_addr_max; 5362306a36Sopenharmony_ci __u8 mptcpi_csum_enabled; 5462306a36Sopenharmony_ci __u32 mptcpi_retransmits; 5562306a36Sopenharmony_ci __u64 mptcpi_bytes_retrans; 5662306a36Sopenharmony_ci __u64 mptcpi_bytes_sent; 5762306a36Sopenharmony_ci __u64 mptcpi_bytes_received; 5862306a36Sopenharmony_ci __u64 mptcpi_bytes_acked; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct mptcp_subflow_data { 6262306a36Sopenharmony_ci __u32 size_subflow_data; /* size of this structure in userspace */ 6362306a36Sopenharmony_ci __u32 num_subflows; /* must be 0, set by kernel */ 6462306a36Sopenharmony_ci __u32 size_kernel; /* must be 0, set by kernel */ 6562306a36Sopenharmony_ci __u32 size_user; /* size of one element in data[] */ 6662306a36Sopenharmony_ci} __attribute__((aligned(8))); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct mptcp_subflow_addrs { 6962306a36Sopenharmony_ci union { 7062306a36Sopenharmony_ci __kernel_sa_family_t sa_family; 7162306a36Sopenharmony_ci struct sockaddr sa_local; 7262306a36Sopenharmony_ci struct sockaddr_in sin_local; 7362306a36Sopenharmony_ci struct sockaddr_in6 sin6_local; 7462306a36Sopenharmony_ci struct __kernel_sockaddr_storage ss_local; 7562306a36Sopenharmony_ci }; 7662306a36Sopenharmony_ci union { 7762306a36Sopenharmony_ci struct sockaddr sa_remote; 7862306a36Sopenharmony_ci struct sockaddr_in sin_remote; 7962306a36Sopenharmony_ci struct sockaddr_in6 sin6_remote; 8062306a36Sopenharmony_ci struct __kernel_sockaddr_storage ss_remote; 8162306a36Sopenharmony_ci }; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define MPTCP_INFO 1 8562306a36Sopenharmony_ci#define MPTCP_TCPINFO 2 8662306a36Sopenharmony_ci#define MPTCP_SUBFLOW_ADDRS 3 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#ifndef MPTCP_FULL_INFO 9062306a36Sopenharmony_cistruct mptcp_subflow_info { 9162306a36Sopenharmony_ci __u32 id; 9262306a36Sopenharmony_ci struct mptcp_subflow_addrs addrs; 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct mptcp_full_info { 9662306a36Sopenharmony_ci __u32 size_tcpinfo_kernel; /* must be 0, set by kernel */ 9762306a36Sopenharmony_ci __u32 size_tcpinfo_user; 9862306a36Sopenharmony_ci __u32 size_sfinfo_kernel; /* must be 0, set by kernel */ 9962306a36Sopenharmony_ci __u32 size_sfinfo_user; 10062306a36Sopenharmony_ci __u32 num_subflows; /* must be 0, set by kernel (real subflow count) */ 10162306a36Sopenharmony_ci __u32 size_arrays_user; /* max subflows that userspace is interested in; 10262306a36Sopenharmony_ci * the buffers at subflow_info/tcp_info 10362306a36Sopenharmony_ci * are respectively at least: 10462306a36Sopenharmony_ci * size_arrays * size_sfinfo_user 10562306a36Sopenharmony_ci * size_arrays * size_tcpinfo_user 10662306a36Sopenharmony_ci * bytes wide 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci __aligned_u64 subflow_info; 10962306a36Sopenharmony_ci __aligned_u64 tcp_info; 11062306a36Sopenharmony_ci struct mptcp_info mptcp_info; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define MPTCP_FULL_INFO 4 11462306a36Sopenharmony_ci#endif 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct so_state { 11762306a36Sopenharmony_ci struct mptcp_info mi; 11862306a36Sopenharmony_ci struct mptcp_info last_sample; 11962306a36Sopenharmony_ci struct tcp_info tcp_info; 12062306a36Sopenharmony_ci struct mptcp_subflow_addrs addrs; 12162306a36Sopenharmony_ci uint64_t mptcpi_rcv_delta; 12262306a36Sopenharmony_ci uint64_t tcpi_rcv_delta; 12362306a36Sopenharmony_ci bool pkt_stats_avail; 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#ifndef MIN 12762306a36Sopenharmony_ci#define MIN(a, b) ((a) < (b) ? (a) : (b)) 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void die_perror(const char *msg) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci perror(msg); 13362306a36Sopenharmony_ci exit(1); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void die_usage(int r) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci fprintf(stderr, "Usage: mptcp_sockopt [-6]\n"); 13962306a36Sopenharmony_ci exit(r); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void xerror(const char *fmt, ...) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci va_list ap; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci va_start(ap, fmt); 14762306a36Sopenharmony_ci vfprintf(stderr, fmt, ap); 14862306a36Sopenharmony_ci va_end(ap); 14962306a36Sopenharmony_ci fputc('\n', stderr); 15062306a36Sopenharmony_ci exit(1); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const char *getxinfo_strerr(int err) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci if (err == EAI_SYSTEM) 15662306a36Sopenharmony_ci return strerror(errno); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return gai_strerror(err); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void xgetaddrinfo(const char *node, const char *service, 16262306a36Sopenharmony_ci const struct addrinfo *hints, 16362306a36Sopenharmony_ci struct addrinfo **res) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int err = getaddrinfo(node, service, hints, res); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (err) { 16862306a36Sopenharmony_ci const char *errstr = getxinfo_strerr(err); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n", 17162306a36Sopenharmony_ci node ? node : "", service ? service : "", errstr); 17262306a36Sopenharmony_ci exit(1); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int sock_listen_mptcp(const char * const listenaddr, 17762306a36Sopenharmony_ci const char * const port) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int sock = -1; 18062306a36Sopenharmony_ci struct addrinfo hints = { 18162306a36Sopenharmony_ci .ai_protocol = IPPROTO_TCP, 18262306a36Sopenharmony_ci .ai_socktype = SOCK_STREAM, 18362306a36Sopenharmony_ci .ai_flags = AI_PASSIVE | AI_NUMERICHOST 18462306a36Sopenharmony_ci }; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci hints.ai_family = pf; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci struct addrinfo *a, *addr; 18962306a36Sopenharmony_ci int one = 1; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci xgetaddrinfo(listenaddr, port, &hints, &addr); 19262306a36Sopenharmony_ci hints.ai_family = pf; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci for (a = addr; a; a = a->ai_next) { 19562306a36Sopenharmony_ci sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP); 19662306a36Sopenharmony_ci if (sock < 0) 19762306a36Sopenharmony_ci continue; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, 20062306a36Sopenharmony_ci sizeof(one))) 20162306a36Sopenharmony_ci perror("setsockopt"); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) 20462306a36Sopenharmony_ci break; /* success */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci perror("bind"); 20762306a36Sopenharmony_ci close(sock); 20862306a36Sopenharmony_ci sock = -1; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci freeaddrinfo(addr); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (sock < 0) 21462306a36Sopenharmony_ci xerror("could not create listen socket"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (listen(sock, 20)) 21762306a36Sopenharmony_ci die_perror("listen"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return sock; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int sock_connect_mptcp(const char * const remoteaddr, 22362306a36Sopenharmony_ci const char * const port, int proto) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct addrinfo hints = { 22662306a36Sopenharmony_ci .ai_protocol = IPPROTO_TCP, 22762306a36Sopenharmony_ci .ai_socktype = SOCK_STREAM, 22862306a36Sopenharmony_ci }; 22962306a36Sopenharmony_ci struct addrinfo *a, *addr; 23062306a36Sopenharmony_ci int sock = -1; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci hints.ai_family = pf; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci xgetaddrinfo(remoteaddr, port, &hints, &addr); 23562306a36Sopenharmony_ci for (a = addr; a; a = a->ai_next) { 23662306a36Sopenharmony_ci sock = socket(a->ai_family, a->ai_socktype, proto); 23762306a36Sopenharmony_ci if (sock < 0) 23862306a36Sopenharmony_ci continue; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) 24162306a36Sopenharmony_ci break; /* success */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci die_perror("connect"); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (sock < 0) 24762306a36Sopenharmony_ci xerror("could not create connect socket"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci freeaddrinfo(addr); 25062306a36Sopenharmony_ci return sock; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void parse_opts(int argc, char **argv) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int c; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci while ((c = getopt(argc, argv, "h6")) != -1) { 25862306a36Sopenharmony_ci switch (c) { 25962306a36Sopenharmony_ci case 'h': 26062306a36Sopenharmony_ci die_usage(0); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case '6': 26362306a36Sopenharmony_ci pf = AF_INET6; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci die_usage(1); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void do_getsockopt_bogus_sf_data(int fd, int optname) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct mptcp_subflow_data good_data; 27562306a36Sopenharmony_ci struct bogus_data { 27662306a36Sopenharmony_ci struct mptcp_subflow_data d; 27762306a36Sopenharmony_ci char buf[2]; 27862306a36Sopenharmony_ci } bd; 27962306a36Sopenharmony_ci socklen_t olen, _olen; 28062306a36Sopenharmony_ci int ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci memset(&bd, 0, sizeof(bd)); 28362306a36Sopenharmony_ci memset(&good_data, 0, sizeof(good_data)); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci olen = sizeof(good_data); 28662306a36Sopenharmony_ci good_data.size_subflow_data = olen; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 28962306a36Sopenharmony_ci assert(ret < 0); /* 0 size_subflow_data */ 29062306a36Sopenharmony_ci assert(olen == sizeof(good_data)); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci bd.d = good_data; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 29562306a36Sopenharmony_ci assert(ret == 0); 29662306a36Sopenharmony_ci assert(olen == sizeof(good_data)); 29762306a36Sopenharmony_ci assert(bd.d.num_subflows == 1); 29862306a36Sopenharmony_ci assert(bd.d.size_kernel > 0); 29962306a36Sopenharmony_ci assert(bd.d.size_user == 0); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci bd.d = good_data; 30262306a36Sopenharmony_ci _olen = rand() % olen; 30362306a36Sopenharmony_ci olen = _olen; 30462306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 30562306a36Sopenharmony_ci assert(ret < 0); /* bogus olen */ 30662306a36Sopenharmony_ci assert(olen == _olen); /* must be unchanged */ 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci bd.d = good_data; 30962306a36Sopenharmony_ci olen = sizeof(good_data); 31062306a36Sopenharmony_ci bd.d.size_kernel = 1; 31162306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 31262306a36Sopenharmony_ci assert(ret < 0); /* size_kernel not 0 */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci bd.d = good_data; 31562306a36Sopenharmony_ci olen = sizeof(good_data); 31662306a36Sopenharmony_ci bd.d.num_subflows = 1; 31762306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 31862306a36Sopenharmony_ci assert(ret < 0); /* num_subflows not 0 */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */ 32162306a36Sopenharmony_ci bd.d = good_data; 32262306a36Sopenharmony_ci olen = sizeof(bd); 32362306a36Sopenharmony_ci bd.d.size_subflow_data = sizeof(bd); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen); 32662306a36Sopenharmony_ci assert(ret == 0); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* olen must be truncated to real data size filled by kernel: */ 32962306a36Sopenharmony_ci assert(olen == sizeof(good_data)); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci assert(bd.d.size_subflow_data == sizeof(bd)); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci bd.d = good_data; 33462306a36Sopenharmony_ci bd.d.size_subflow_data += 1; 33562306a36Sopenharmony_ci bd.d.size_user = 1; 33662306a36Sopenharmony_ci olen = bd.d.size_subflow_data + 1; 33762306a36Sopenharmony_ci _olen = olen; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen); 34062306a36Sopenharmony_ci assert(ret == 0); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */ 34362306a36Sopenharmony_ci assert(olen == _olen); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci assert(bd.d.size_subflow_data == sizeof(good_data) + 1); 34662306a36Sopenharmony_ci assert(bd.buf[0] == 0); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct mptcp_info i; 35262306a36Sopenharmony_ci socklen_t olen; 35362306a36Sopenharmony_ci int ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci olen = sizeof(i); 35662306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (ret < 0) 35962306a36Sopenharmony_ci die_perror("getsockopt MPTCP_INFO"); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci s->pkt_stats_avail = olen >= sizeof(i); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci s->last_sample = i; 36462306a36Sopenharmony_ci if (s->mi.mptcpi_write_seq == 0) 36562306a36Sopenharmony_ci s->mi = i; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct my_tcp_info { 37562306a36Sopenharmony_ci struct mptcp_subflow_data d; 37662306a36Sopenharmony_ci struct tcp_info ti[2]; 37762306a36Sopenharmony_ci } ti; 37862306a36Sopenharmony_ci int ret, tries = 5; 37962306a36Sopenharmony_ci socklen_t olen; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci do { 38262306a36Sopenharmony_ci memset(&ti, 0, sizeof(ti)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data); 38562306a36Sopenharmony_ci ti.d.size_user = sizeof(struct tcp_info); 38662306a36Sopenharmony_ci olen = sizeof(ti); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen); 38962306a36Sopenharmony_ci if (ret < 0) 39062306a36Sopenharmony_ci xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)"); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci assert(olen <= sizeof(ti)); 39362306a36Sopenharmony_ci assert(ti.d.size_kernel > 0); 39462306a36Sopenharmony_ci assert(ti.d.size_user == 39562306a36Sopenharmony_ci MIN(ti.d.size_kernel, sizeof(struct tcp_info))); 39662306a36Sopenharmony_ci assert(ti.d.num_subflows == 1); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); 39962306a36Sopenharmony_ci olen -= sizeof(struct mptcp_subflow_data); 40062306a36Sopenharmony_ci assert(olen == ti.d.size_user); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci s->tcp_info = ti.ti[0]; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (ti.ti[0].tcpi_bytes_sent == w && 40562306a36Sopenharmony_ci ti.ti[0].tcpi_bytes_received == r) 40662306a36Sopenharmony_ci goto done; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (r == 0 && ti.ti[0].tcpi_bytes_sent == w && 40962306a36Sopenharmony_ci ti.ti[0].tcpi_bytes_received) { 41062306a36Sopenharmony_ci s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received; 41162306a36Sopenharmony_ci goto done; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* wait and repeat, might be that tx is still ongoing */ 41562306a36Sopenharmony_ci sleep(1); 41662306a36Sopenharmony_ci } while (tries-- > 0); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu", 41962306a36Sopenharmony_ci ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cidone: 42262306a36Sopenharmony_ci do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void do_getsockopt_subflow_addrs(struct so_state *s, int fd) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct sockaddr_storage remote, local; 42862306a36Sopenharmony_ci socklen_t olen, rlen, llen; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci struct my_addrs { 43162306a36Sopenharmony_ci struct mptcp_subflow_data d; 43262306a36Sopenharmony_ci struct mptcp_subflow_addrs addr[2]; 43362306a36Sopenharmony_ci } addrs; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci memset(&addrs, 0, sizeof(addrs)); 43662306a36Sopenharmony_ci memset(&local, 0, sizeof(local)); 43762306a36Sopenharmony_ci memset(&remote, 0, sizeof(remote)); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data); 44062306a36Sopenharmony_ci addrs.d.size_user = sizeof(struct mptcp_subflow_addrs); 44162306a36Sopenharmony_ci olen = sizeof(addrs); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen); 44462306a36Sopenharmony_ci if (ret < 0) 44562306a36Sopenharmony_ci die_perror("getsockopt MPTCP_SUBFLOW_ADDRS"); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci assert(olen <= sizeof(addrs)); 44862306a36Sopenharmony_ci assert(addrs.d.size_kernel > 0); 44962306a36Sopenharmony_ci assert(addrs.d.size_user == 45062306a36Sopenharmony_ci MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs))); 45162306a36Sopenharmony_ci assert(addrs.d.num_subflows == 1); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); 45462306a36Sopenharmony_ci olen -= sizeof(struct mptcp_subflow_data); 45562306a36Sopenharmony_ci assert(olen == addrs.d.size_user); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci llen = sizeof(local); 45862306a36Sopenharmony_ci ret = getsockname(fd, (struct sockaddr *)&local, &llen); 45962306a36Sopenharmony_ci if (ret < 0) 46062306a36Sopenharmony_ci die_perror("getsockname"); 46162306a36Sopenharmony_ci rlen = sizeof(remote); 46262306a36Sopenharmony_ci ret = getpeername(fd, (struct sockaddr *)&remote, &rlen); 46362306a36Sopenharmony_ci if (ret < 0) 46462306a36Sopenharmony_ci die_perror("getpeername"); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci assert(rlen > 0); 46762306a36Sopenharmony_ci assert(rlen == llen); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci assert(remote.ss_family == local.ss_family); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0); 47262306a36Sopenharmony_ci assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0); 47362306a36Sopenharmony_ci s->addrs = addrs.addr[0]; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci memset(&addrs, 0, sizeof(addrs)); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data); 47862306a36Sopenharmony_ci addrs.d.size_user = sizeof(sa_family_t); 47962306a36Sopenharmony_ci olen = sizeof(addrs.d) + sizeof(sa_family_t); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen); 48262306a36Sopenharmony_ci assert(ret == 0); 48362306a36Sopenharmony_ci assert(olen == sizeof(addrs.d) + sizeof(sa_family_t)); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci assert(addrs.addr[0].sa_family == pf); 48662306a36Sopenharmony_ci assert(addrs.addr[0].sa_family == local.ss_family); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) != 0); 48962306a36Sopenharmony_ci assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) != 0); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void do_getsockopt_mptcp_full_info(struct so_state *s, int fd) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci size_t data_size = sizeof(struct mptcp_full_info); 49762306a36Sopenharmony_ci struct mptcp_subflow_info sfinfo[2]; 49862306a36Sopenharmony_ci struct tcp_info tcp_info[2]; 49962306a36Sopenharmony_ci struct mptcp_full_info mfi; 50062306a36Sopenharmony_ci socklen_t olen; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci memset(&mfi, 0, data_size); 50462306a36Sopenharmony_ci memset(tcp_info, 0, sizeof(tcp_info)); 50562306a36Sopenharmony_ci memset(sfinfo, 0, sizeof(sfinfo)); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci mfi.size_tcpinfo_user = sizeof(struct tcp_info); 50862306a36Sopenharmony_ci mfi.size_sfinfo_user = sizeof(struct mptcp_subflow_info); 50962306a36Sopenharmony_ci mfi.size_arrays_user = 2; 51062306a36Sopenharmony_ci mfi.subflow_info = (unsigned long)&sfinfo[0]; 51162306a36Sopenharmony_ci mfi.tcp_info = (unsigned long)&tcp_info[0]; 51262306a36Sopenharmony_ci olen = data_size; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = getsockopt(fd, SOL_MPTCP, MPTCP_FULL_INFO, &mfi, &olen); 51562306a36Sopenharmony_ci if (ret < 0) { 51662306a36Sopenharmony_ci if (errno == EOPNOTSUPP) { 51762306a36Sopenharmony_ci perror("MPTCP_FULL_INFO test skipped"); 51862306a36Sopenharmony_ci return; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci xerror("getsockopt MPTCP_FULL_INFO"); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci assert(olen <= data_size); 52462306a36Sopenharmony_ci assert(mfi.size_tcpinfo_kernel > 0); 52562306a36Sopenharmony_ci assert(mfi.size_tcpinfo_user == 52662306a36Sopenharmony_ci MIN(mfi.size_tcpinfo_kernel, sizeof(struct tcp_info))); 52762306a36Sopenharmony_ci assert(mfi.size_sfinfo_kernel > 0); 52862306a36Sopenharmony_ci assert(mfi.size_sfinfo_user == 52962306a36Sopenharmony_ci MIN(mfi.size_sfinfo_kernel, sizeof(struct mptcp_subflow_info))); 53062306a36Sopenharmony_ci assert(mfi.num_subflows == 1); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Tolerate future extension to mptcp_info struct and running newer 53362306a36Sopenharmony_ci * test on top of older kernel. 53462306a36Sopenharmony_ci * Anyway any kernel supporting MPTCP_FULL_INFO must at least include 53562306a36Sopenharmony_ci * the following in mptcp_info. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci assert(olen > (socklen_t)__builtin_offsetof(struct mptcp_full_info, tcp_info)); 53862306a36Sopenharmony_ci assert(mfi.mptcp_info.mptcpi_subflows == 0); 53962306a36Sopenharmony_ci assert(mfi.mptcp_info.mptcpi_bytes_sent == s->last_sample.mptcpi_bytes_sent); 54062306a36Sopenharmony_ci assert(mfi.mptcp_info.mptcpi_bytes_received == s->last_sample.mptcpi_bytes_received); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci assert(sfinfo[0].id == 1); 54362306a36Sopenharmony_ci assert(tcp_info[0].tcpi_bytes_sent == s->tcp_info.tcpi_bytes_sent); 54462306a36Sopenharmony_ci assert(tcp_info[0].tcpi_bytes_received == s->tcp_info.tcpi_bytes_received); 54562306a36Sopenharmony_ci assert(!memcmp(&sfinfo->addrs, &s->addrs, sizeof(struct mptcp_subflow_addrs))); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci do_getsockopt_mptcp_info(s, fd, w); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci do_getsockopt_tcp_info(s, fd, r, w); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci do_getsockopt_subflow_addrs(s, fd); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (r) 55762306a36Sopenharmony_ci do_getsockopt_mptcp_full_info(s, fd); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic void connect_one_server(int fd, int pipefd) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci char buf[4096], buf2[4096]; 56362306a36Sopenharmony_ci size_t len, i, total; 56462306a36Sopenharmony_ci struct so_state s; 56562306a36Sopenharmony_ci bool eof = false; 56662306a36Sopenharmony_ci ssize_t ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci memset(&s, 0, sizeof(s)); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci len = rand() % (sizeof(buf) - 1); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (len < 128) 57362306a36Sopenharmony_ci len = 128; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci for (i = 0; i < len ; i++) { 57662306a36Sopenharmony_ci buf[i] = rand() % 26; 57762306a36Sopenharmony_ci buf[i] += 'A'; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci buf[i] = '\n'; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci do_getsockopts(&s, fd, 0, 0); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* un-block server */ 58562306a36Sopenharmony_ci ret = read(pipefd, buf2, 4); 58662306a36Sopenharmony_ci assert(ret == 4); 58762306a36Sopenharmony_ci close(pipefd); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci assert(strncmp(buf2, "xmit", 4) == 0); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ret = write(fd, buf, len); 59262306a36Sopenharmony_ci if (ret < 0) 59362306a36Sopenharmony_ci die_perror("write"); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (ret != (ssize_t)len) 59662306a36Sopenharmony_ci xerror("short write"); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci total = 0; 59962306a36Sopenharmony_ci do { 60062306a36Sopenharmony_ci ret = read(fd, buf2 + total, sizeof(buf2) - total); 60162306a36Sopenharmony_ci if (ret < 0) 60262306a36Sopenharmony_ci die_perror("read"); 60362306a36Sopenharmony_ci if (ret == 0) { 60462306a36Sopenharmony_ci eof = true; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci total += ret; 60962306a36Sopenharmony_ci } while (total < len); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (total != len) 61262306a36Sopenharmony_ci xerror("total %lu, len %lu eof %d\n", total, len, eof); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (memcmp(buf, buf2, len)) 61562306a36Sopenharmony_ci xerror("data corruption"); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (s.tcpi_rcv_delta) 61862306a36Sopenharmony_ci assert(s.tcpi_rcv_delta <= total); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci do_getsockopts(&s, fd, ret, ret); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (eof) 62362306a36Sopenharmony_ci total += 1; /* sequence advances due to FIN */ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci assert(s.mptcpi_rcv_delta == (uint64_t)total); 62662306a36Sopenharmony_ci close(fd); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void process_one_client(int fd, int pipefd) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci ssize_t ret, ret2, ret3; 63262306a36Sopenharmony_ci struct so_state s; 63362306a36Sopenharmony_ci char buf[4096]; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci memset(&s, 0, sizeof(s)); 63662306a36Sopenharmony_ci do_getsockopts(&s, fd, 0, 0); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ret = write(pipefd, "xmit", 4); 63962306a36Sopenharmony_ci assert(ret == 4); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ret = read(fd, buf, sizeof(buf)); 64262306a36Sopenharmony_ci if (ret < 0) 64362306a36Sopenharmony_ci die_perror("read"); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci assert(s.mptcpi_rcv_delta <= (uint64_t)ret); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (s.tcpi_rcv_delta) 64862306a36Sopenharmony_ci assert(s.tcpi_rcv_delta == (uint64_t)ret); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci ret2 = write(fd, buf, ret); 65162306a36Sopenharmony_ci if (ret2 < 0) 65262306a36Sopenharmony_ci die_perror("write"); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* wait for hangup */ 65562306a36Sopenharmony_ci ret3 = read(fd, buf, 1); 65662306a36Sopenharmony_ci if (ret3 != 0) 65762306a36Sopenharmony_ci xerror("expected EOF, got %lu", ret3); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci do_getsockopts(&s, fd, ret, ret2); 66062306a36Sopenharmony_ci if (s.mptcpi_rcv_delta != (uint64_t)ret + 1) 66162306a36Sopenharmony_ci xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64, s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - ret); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* be nice when running on top of older kernel */ 66462306a36Sopenharmony_ci if (s.pkt_stats_avail) { 66562306a36Sopenharmony_ci if (s.last_sample.mptcpi_bytes_sent != ret2) 66662306a36Sopenharmony_ci xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64, 66762306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_sent, ret2, 66862306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_sent - ret2); 66962306a36Sopenharmony_ci if (s.last_sample.mptcpi_bytes_received != ret) 67062306a36Sopenharmony_ci xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64, 67162306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_received, ret, 67262306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_received - ret); 67362306a36Sopenharmony_ci if (s.last_sample.mptcpi_bytes_acked != ret) 67462306a36Sopenharmony_ci xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64, 67562306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_acked, ret2, 67662306a36Sopenharmony_ci s.last_sample.mptcpi_bytes_acked - ret2); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci close(fd); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int xaccept(int s) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int fd = accept(s, NULL, 0); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (fd < 0) 68762306a36Sopenharmony_ci die_perror("accept"); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return fd; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int server(int pipefd) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int fd = -1, r; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci switch (pf) { 69762306a36Sopenharmony_ci case AF_INET: 69862306a36Sopenharmony_ci fd = sock_listen_mptcp("127.0.0.1", "15432"); 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci case AF_INET6: 70162306a36Sopenharmony_ci fd = sock_listen_mptcp("::1", "15432"); 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci default: 70462306a36Sopenharmony_ci xerror("Unknown pf %d\n", pf); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci r = write(pipefd, "conn", 4); 70962306a36Sopenharmony_ci assert(r == 4); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci alarm(15); 71262306a36Sopenharmony_ci r = xaccept(fd); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci process_one_client(r, pipefd); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void test_ip_tos_sockopt(int fd) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci uint8_t tos_in, tos_out; 72262306a36Sopenharmony_ci socklen_t s; 72362306a36Sopenharmony_ci int r; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci tos_in = rand() & 0xfc; 72662306a36Sopenharmony_ci r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out)); 72762306a36Sopenharmony_ci if (r != 0) 72862306a36Sopenharmony_ci die_perror("setsockopt IP_TOS"); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci tos_out = 0; 73162306a36Sopenharmony_ci s = sizeof(tos_out); 73262306a36Sopenharmony_ci r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s); 73362306a36Sopenharmony_ci if (r != 0) 73462306a36Sopenharmony_ci die_perror("getsockopt IP_TOS"); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (tos_in != tos_out) 73762306a36Sopenharmony_ci xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (s != 1) 74062306a36Sopenharmony_ci xerror("tos should be 1 byte"); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci s = 0; 74362306a36Sopenharmony_ci r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s); 74462306a36Sopenharmony_ci if (r != 0) 74562306a36Sopenharmony_ci die_perror("getsockopt IP_TOS 0"); 74662306a36Sopenharmony_ci if (s != 0) 74762306a36Sopenharmony_ci xerror("expect socklen_t == 0"); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci s = -1; 75062306a36Sopenharmony_ci r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s); 75162306a36Sopenharmony_ci if (r != -1 && errno != EINVAL) 75262306a36Sopenharmony_ci die_perror("getsockopt IP_TOS did not indicate -EINVAL"); 75362306a36Sopenharmony_ci if (s != -1) 75462306a36Sopenharmony_ci xerror("expect socklen_t == -1"); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int client(int pipefd) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci int fd = -1; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci alarm(15); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci switch (pf) { 76462306a36Sopenharmony_ci case AF_INET: 76562306a36Sopenharmony_ci fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case AF_INET6: 76862306a36Sopenharmony_ci fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP); 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci default: 77162306a36Sopenharmony_ci xerror("Unknown pf %d\n", pf); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci test_ip_tos_sockopt(fd); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci connect_one_server(fd, pipefd); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic pid_t xfork(void) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci pid_t p = fork(); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (p < 0) 78662306a36Sopenharmony_ci die_perror("fork"); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return p; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int rcheck(int wstatus, const char *what) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci if (WIFEXITED(wstatus)) { 79462306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) == 0) 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus)); 79762306a36Sopenharmony_ci return WEXITSTATUS(wstatus); 79862306a36Sopenharmony_ci } else if (WIFSIGNALED(wstatus)) { 79962306a36Sopenharmony_ci xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus)); 80062306a36Sopenharmony_ci } else if (WIFSTOPPED(wstatus)) { 80162306a36Sopenharmony_ci xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus)); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return 111; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void init_rng(void) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci int fd = open("/dev/urandom", O_RDONLY); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (fd >= 0) { 81262306a36Sopenharmony_ci unsigned int foo; 81362306a36Sopenharmony_ci ssize_t ret; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* can't fail */ 81662306a36Sopenharmony_ci ret = read(fd, &foo, sizeof(foo)); 81762306a36Sopenharmony_ci assert(ret == sizeof(foo)); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci close(fd); 82062306a36Sopenharmony_ci srand(foo); 82162306a36Sopenharmony_ci } else { 82262306a36Sopenharmony_ci srand(time(NULL)); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciint main(int argc, char *argv[]) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci int e1, e2, wstatus; 82962306a36Sopenharmony_ci pid_t s, c, ret; 83062306a36Sopenharmony_ci int pipefds[2]; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci parse_opts(argc, argv); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci init_rng(); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci e1 = pipe(pipefds); 83762306a36Sopenharmony_ci if (e1 < 0) 83862306a36Sopenharmony_ci die_perror("pipe"); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci s = xfork(); 84162306a36Sopenharmony_ci if (s == 0) 84262306a36Sopenharmony_ci return server(pipefds[1]); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci close(pipefds[1]); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* wait until server bound a socket */ 84762306a36Sopenharmony_ci e1 = read(pipefds[0], &e1, 4); 84862306a36Sopenharmony_ci assert(e1 == 4); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci c = xfork(); 85162306a36Sopenharmony_ci if (c == 0) 85262306a36Sopenharmony_ci return client(pipefds[0]); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci close(pipefds[0]); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = waitpid(s, &wstatus, 0); 85762306a36Sopenharmony_ci if (ret == -1) 85862306a36Sopenharmony_ci die_perror("waitpid"); 85962306a36Sopenharmony_ci e1 = rcheck(wstatus, "server"); 86062306a36Sopenharmony_ci ret = waitpid(c, &wstatus, 0); 86162306a36Sopenharmony_ci if (ret == -1) 86262306a36Sopenharmony_ci die_perror("waitpid"); 86362306a36Sopenharmony_ci e2 = rcheck(wstatus, "client"); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return e1 ? e1 : e2; 86662306a36Sopenharmony_ci} 867