1*939a177eSbluhm /* $OpenBSD: tcpthread.c,v 1.3 2025/01/13 12:55:13 bluhm Exp $ */ 27364c003Sbluhm 37364c003Sbluhm /* 47364c003Sbluhm * Copyright (c) 2025 Alexander Bluhm <bluhm@openbsd.org> 57364c003Sbluhm * 67364c003Sbluhm * Permission to use, copy, modify, and distribute this software for any 77364c003Sbluhm * purpose with or without fee is hereby granted, provided that the above 87364c003Sbluhm * copyright notice and this permission notice appear in all copies. 97364c003Sbluhm * 107364c003Sbluhm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 117364c003Sbluhm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 127364c003Sbluhm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 137364c003Sbluhm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 147364c003Sbluhm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 157364c003Sbluhm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 167364c003Sbluhm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 177364c003Sbluhm */ 187364c003Sbluhm 197364c003Sbluhm #include <sys/types.h> 207364c003Sbluhm #include <sys/atomic.h> 215a6c4815Sbluhm #include <sys/queue.h> 227364c003Sbluhm #include <sys/socket.h> 235a6c4815Sbluhm #include <sys/sysctl.h> 247364c003Sbluhm 257364c003Sbluhm #include <netinet/in.h> 265a6c4815Sbluhm #include <netinet/tcp.h> 275a6c4815Sbluhm #include <netinet/tcp_timer.h> 285a6c4815Sbluhm #include <netinet/tcp_var.h> 297364c003Sbluhm 307364c003Sbluhm #include <err.h> 317364c003Sbluhm #include <errno.h> 327364c003Sbluhm #include <pthread.h> 337364c003Sbluhm #include <signal.h> 347364c003Sbluhm #include <stdio.h> 357364c003Sbluhm #include <stdlib.h> 367364c003Sbluhm #include <string.h> 377364c003Sbluhm #include <unistd.h> 387364c003Sbluhm 397364c003Sbluhm static const struct timespec time_1000ns = { 0, 1000 }; 407364c003Sbluhm static const struct timeval time_1us = { 0, 1 }; 417364c003Sbluhm 427364c003Sbluhm union sockaddr_union { 437364c003Sbluhm struct sockaddr su_sa; 447364c003Sbluhm struct sockaddr_in su_sin; 457364c003Sbluhm struct sockaddr_in6 su_sin6; 467364c003Sbluhm }; 477364c003Sbluhm 487364c003Sbluhm unsigned int run_time = 10; 497364c003Sbluhm unsigned int sock_num = 1; 507364c003Sbluhm unsigned int connect_num = 1, accept_num = 1, send_num = 1, recv_num = 1, 515a6c4815Sbluhm close_num = 1, splice_num = 0, unsplice_num = 0, drop_num = 0; 527364c003Sbluhm int max_percent = 0, idle_percent = 0; 537364c003Sbluhm volatile unsigned long max_count = 0, idle_count = 0; 545a6c4815Sbluhm int *listen_socks, *splice_listen_socks; 555a6c4815Sbluhm volatile int *connect_socks, *accept_socks, 565a6c4815Sbluhm *splice_accept_socks, *splice_connect_socks; 575a6c4815Sbluhm union sockaddr_union *listen_addrs, *splice_listen_addrs; 585a6c4815Sbluhm struct tcp_ident_mapping *accept_tims, *splice_accept_tims; 597364c003Sbluhm struct sockaddr_in sin_loopback; 607364c003Sbluhm struct sockaddr_in6 sin6_loopback; 617364c003Sbluhm 627364c003Sbluhm static void __dead 637364c003Sbluhm usage(void) 647364c003Sbluhm { 657364c003Sbluhm fprintf(stderr, 665a6c4815Sbluhm "tcpthread [-a accept] [-c connect] [-D drop] [-I idle] [-M max] " 675a6c4815Sbluhm "[-n num] [-o close] [-r recv] [-S splice] [-s send] [-t time] " 687364c003Sbluhm "[-U unsplice]\n" 697364c003Sbluhm " -a accept threads accepting sockets, default %u\n" 707364c003Sbluhm " -c connect threads connecting sockets, default %u\n" 715a6c4815Sbluhm " -D drop threads dropping TCP connections, default %u\n" 727364c003Sbluhm " -I idle percent with splice idle time, default %u\n" 737364c003Sbluhm " -M max percent with splice max lenght, default %d\n" 747364c003Sbluhm " -n num number of file descriptors, default %d\n" 757364c003Sbluhm " -o close threads closing sockets, default %u\n" 767364c003Sbluhm " -r recv threads receiving data, default %u\n" 777364c003Sbluhm " -S splice threads splicing sockets, default %u\n" 787364c003Sbluhm " -s send threads sending data, default %u\n" 797364c003Sbluhm " -t time run time in seconds, default %u\n" 807364c003Sbluhm " -U unsplice threads running unsplice, default %u\n", 815a6c4815Sbluhm accept_num, connect_num, drop_num, idle_percent, max_percent, 825a6c4815Sbluhm sock_num, close_num, recv_num, splice_num, send_num, run_time, 835a6c4815Sbluhm unsplice_num); 847364c003Sbluhm exit(2); 857364c003Sbluhm } 867364c003Sbluhm 877364c003Sbluhm static volatile int * 887364c003Sbluhm random_socket(unsigned int *seed) 897364c003Sbluhm { 907364c003Sbluhm static volatile int **sockets[] = { 917364c003Sbluhm &connect_socks, 927364c003Sbluhm &accept_socks, 937364c003Sbluhm &splice_accept_socks, 947364c003Sbluhm &splice_connect_socks, 957364c003Sbluhm }; 967364c003Sbluhm volatile int **socksp; 977364c003Sbluhm 987364c003Sbluhm socksp = sockets[(rand_r(seed) % ((splice_num > 0) ? 4 : 2))]; 997364c003Sbluhm 1007364c003Sbluhm return &(*socksp)[rand_r(seed) % sock_num]; 1017364c003Sbluhm } 1027364c003Sbluhm 1037364c003Sbluhm static int 1047364c003Sbluhm connect_socket(volatile int *connectp, struct sockaddr *addr) 1057364c003Sbluhm { 1067364c003Sbluhm int sock; 1077364c003Sbluhm 1087364c003Sbluhm if (*connectp != -1) { 1097364c003Sbluhm /* still connected, not closed */ 1107364c003Sbluhm return 0; 1117364c003Sbluhm } 1127364c003Sbluhm /* connect to random listen socket */ 1137364c003Sbluhm sock = socket(addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 1147364c003Sbluhm IPPROTO_TCP); 1157364c003Sbluhm if (sock < 0) 1167364c003Sbluhm err(1, "%s: socket", __func__); 1177364c003Sbluhm if (connect(sock, addr, addr->sa_len) < 0 && 1187364c003Sbluhm errno != EINPROGRESS) { 1197364c003Sbluhm err(1, "%s: connect %d", __func__, sock); 1207364c003Sbluhm } 1217364c003Sbluhm if ((int)atomic_cas_uint(connectp, -1, sock) != -1) { 1227364c003Sbluhm /* another thread has connect slot n */ 1237364c003Sbluhm if (close(sock) < 0) 1247364c003Sbluhm err(1, "%s: close %d", __func__, sock); 1257364c003Sbluhm return 0; 1267364c003Sbluhm } 1277364c003Sbluhm return 1; 1287364c003Sbluhm } 1297364c003Sbluhm 1307364c003Sbluhm static void * 1317364c003Sbluhm connect_routine(void *arg) 1327364c003Sbluhm { 1337364c003Sbluhm volatile int *run = arg; 1347364c003Sbluhm unsigned long count = 0; 1357364c003Sbluhm struct sockaddr *addr; 1367364c003Sbluhm unsigned int seed; 1377364c003Sbluhm unsigned int n; 1387364c003Sbluhm int connected; 1397364c003Sbluhm 1407364c003Sbluhm seed = arc4random(); 1417364c003Sbluhm 1427364c003Sbluhm while (*run) { 1437364c003Sbluhm connected = 0; 1447364c003Sbluhm for (n = 0; n < sock_num; n++) { 1455a6c4815Sbluhm addr = &((splice_num > 0) ? 1465a6c4815Sbluhm splice_listen_addrs : listen_addrs) 1477364c003Sbluhm [rand_r(&seed) % sock_num].su_sa; 1487364c003Sbluhm if (!connect_socket(&connect_socks[n], addr)) 1497364c003Sbluhm continue; 1507364c003Sbluhm connected = 1; 1517364c003Sbluhm count++; 1527364c003Sbluhm } 1537364c003Sbluhm if (!connected) { 1547364c003Sbluhm /* all sockets were connected, wait a bit */ 1557364c003Sbluhm if (nanosleep(&time_1000ns, NULL) < 0) 1567364c003Sbluhm err(1, "%s: nanosleep", __func__); 1577364c003Sbluhm } 1587364c003Sbluhm } 1597364c003Sbluhm 1607364c003Sbluhm return (void *)count; 1617364c003Sbluhm } 1627364c003Sbluhm 1637364c003Sbluhm static int 1645a6c4815Sbluhm accept_socket(volatile int *acceptp, int *listens, 1655a6c4815Sbluhm struct tcp_ident_mapping *tim, union sockaddr_union *addrs) 1667364c003Sbluhm { 1677364c003Sbluhm unsigned int i; 1687364c003Sbluhm int sock; 1695a6c4815Sbluhm struct sockaddr *sa; 1705a6c4815Sbluhm socklen_t len; 1717364c003Sbluhm 1727364c003Sbluhm if (*acceptp != -1) { 1737364c003Sbluhm /* still accepted, not closed */ 1747364c003Sbluhm return 0; 1757364c003Sbluhm } 1767364c003Sbluhm sock = -1; 1777364c003Sbluhm for (i = 0; i < sock_num; i++) { 1785a6c4815Sbluhm sa = (struct sockaddr *)&tim->faddr; 1795a6c4815Sbluhm len = sizeof(tim->faddr); 1805a6c4815Sbluhm sock = accept4(listens[i], sa, &len, SOCK_NONBLOCK); 1817364c003Sbluhm if (sock < 0) { 1827364c003Sbluhm if (errno == EWOULDBLOCK) { 1837364c003Sbluhm /* no connection to accept */ 1847364c003Sbluhm continue; 1857364c003Sbluhm } 186*939a177eSbluhm if (errno == ECONNABORTED) { 187*939a177eSbluhm /* accepted socket was disconnected */ 188*939a177eSbluhm continue; 189*939a177eSbluhm } 1907364c003Sbluhm err(1, "%s: accept %d", __func__, listens[i]); 1917364c003Sbluhm } 1925a6c4815Sbluhm sa = &addrs[i].su_sa; 1935a6c4815Sbluhm memcpy(&tim->laddr, sa, sa->sa_len); 1947364c003Sbluhm break; 1957364c003Sbluhm } 1967364c003Sbluhm if (sock == -1) { 1977364c003Sbluhm /* all listen sockets block, wait a bit */ 1987364c003Sbluhm if (nanosleep(&time_1000ns, NULL) < 0) 1997364c003Sbluhm err(1, "%s: nanosleep", __func__); 2007364c003Sbluhm return 0; 2017364c003Sbluhm } 2025a6c4815Sbluhm membar_producer(); 2037364c003Sbluhm if ((int)atomic_cas_uint(acceptp, -1, sock) != -1) { 2047364c003Sbluhm /* another thread has accepted slot n */ 2057364c003Sbluhm if (close(sock) < 0) 2067364c003Sbluhm err(1, "%s: close %d", __func__, sock); 2077364c003Sbluhm return 0; 2087364c003Sbluhm } 2097364c003Sbluhm return 1; 2107364c003Sbluhm } 2117364c003Sbluhm 2127364c003Sbluhm static void * 2137364c003Sbluhm accept_routine(void *arg) 2147364c003Sbluhm { 2157364c003Sbluhm volatile int *run = arg; 2167364c003Sbluhm unsigned long count = 0; 2177364c003Sbluhm unsigned int n; 2187364c003Sbluhm int accepted; 2197364c003Sbluhm 2207364c003Sbluhm while (*run) { 2217364c003Sbluhm accepted = 0; 2227364c003Sbluhm for (n = 0; n < sock_num; n++) { 2235a6c4815Sbluhm if (!accept_socket(&accept_socks[n], listen_socks, 2245a6c4815Sbluhm &accept_tims[n], listen_addrs)) 2257364c003Sbluhm continue; 2267364c003Sbluhm accepted = 1; 2277364c003Sbluhm count++; 2287364c003Sbluhm } 2297364c003Sbluhm if (!accepted) { 2307364c003Sbluhm /* all sockets were accepted, wait a bit */ 2317364c003Sbluhm if (nanosleep(&time_1000ns, NULL) < 0) 2327364c003Sbluhm err(1, "%s: nanosleep", __func__); 2337364c003Sbluhm } 2347364c003Sbluhm } 2357364c003Sbluhm 2367364c003Sbluhm return (void *)count; 2377364c003Sbluhm } 2387364c003Sbluhm 2397364c003Sbluhm static void * 2407364c003Sbluhm send_routine(void *arg) 2417364c003Sbluhm { 2427364c003Sbluhm volatile int *run = arg; 2437364c003Sbluhm unsigned long count = 0; 2447364c003Sbluhm unsigned int seed; 2457364c003Sbluhm char buf[1024]; /* 1 KB */ 2467364c003Sbluhm volatile int *sockp; 2477364c003Sbluhm int sock; 2487364c003Sbluhm 2497364c003Sbluhm seed = arc4random(); 2507364c003Sbluhm 2517364c003Sbluhm while (*run) { 2527364c003Sbluhm sockp = random_socket(&seed); 2537364c003Sbluhm sock = *sockp; 2547364c003Sbluhm if (sock == -1) 2557364c003Sbluhm continue; 2567364c003Sbluhm if (send(sock, buf, sizeof(buf), 0) < 0) { 2577364c003Sbluhm if (errno == EWOULDBLOCK) 2587364c003Sbluhm continue; 2597364c003Sbluhm if (errno == EFBIG) 2607364c003Sbluhm atomic_inc_long(&max_count); 2617364c003Sbluhm if (errno == ETIMEDOUT) 2627364c003Sbluhm atomic_inc_long(&idle_count); 2637364c003Sbluhm if ((int)atomic_cas_uint(sockp, sock, -1) != sock) { 2647364c003Sbluhm /* another thread has closed sockp */ 2657364c003Sbluhm continue; 2667364c003Sbluhm } 2677364c003Sbluhm if (close(sock) < 0) 2687364c003Sbluhm err(1, "%s: close %d", __func__, sock); 2697364c003Sbluhm } 2707364c003Sbluhm count++; 2717364c003Sbluhm } 2727364c003Sbluhm 2737364c003Sbluhm return (void *)count; 2747364c003Sbluhm } 2757364c003Sbluhm 2767364c003Sbluhm static void * 2777364c003Sbluhm recv_routine(void *arg) 2787364c003Sbluhm { 2797364c003Sbluhm volatile int *run = arg; 2807364c003Sbluhm unsigned long count = 0; 2817364c003Sbluhm unsigned int seed; 2827364c003Sbluhm char buf[10*1024]; /* 10 KB */ 2837364c003Sbluhm volatile int *sockp; 2847364c003Sbluhm int sock; 2857364c003Sbluhm 2867364c003Sbluhm seed = arc4random(); 2877364c003Sbluhm 2887364c003Sbluhm while (*run) { 2897364c003Sbluhm sockp = &((rand_r(&seed) % 2) ? connect_socks : accept_socks) 2907364c003Sbluhm [rand_r(&seed) % sock_num]; 2917364c003Sbluhm sock = *sockp; 2927364c003Sbluhm if (sock == -1) 2937364c003Sbluhm continue; 2947364c003Sbluhm errno = 0; 2957364c003Sbluhm if (recv(sock, buf, sizeof(buf), 0) <= 0) { 2967364c003Sbluhm if (errno == EWOULDBLOCK) 2977364c003Sbluhm continue; 2987364c003Sbluhm if (errno == EFBIG) 2997364c003Sbluhm atomic_inc_long(&max_count); 3007364c003Sbluhm if (errno == ETIMEDOUT) 3017364c003Sbluhm atomic_inc_long(&idle_count); 3027364c003Sbluhm if ((int)atomic_cas_uint(sockp, sock, -1) != sock) { 3037364c003Sbluhm /* another thread has closed sockp */ 3047364c003Sbluhm continue; 3057364c003Sbluhm } 3067364c003Sbluhm if (close(sock) < 0) 3077364c003Sbluhm err(1, "%s: close %d", __func__, sock); 3087364c003Sbluhm } 3097364c003Sbluhm count++; 3107364c003Sbluhm } 3117364c003Sbluhm 3127364c003Sbluhm return (void *)count; 3137364c003Sbluhm } 3147364c003Sbluhm 3157364c003Sbluhm static void * 3167364c003Sbluhm close_routine(void *arg) 3177364c003Sbluhm { 3187364c003Sbluhm volatile int *run = arg; 3197364c003Sbluhm unsigned long count = 0; 3207364c003Sbluhm unsigned int seed; 3217364c003Sbluhm volatile int *sockp; 3227364c003Sbluhm int sock; 3237364c003Sbluhm 3247364c003Sbluhm seed = arc4random(); 3257364c003Sbluhm 3267364c003Sbluhm while (*run) { 3277364c003Sbluhm sockp = random_socket(&seed); 3287364c003Sbluhm if (*sockp == -1) 3297364c003Sbluhm continue; 3307364c003Sbluhm sock = atomic_swap_uint(sockp, -1); 3317364c003Sbluhm if (sock == -1) { 3327364c003Sbluhm /* another thead has closed the socket, wait a bit */ 3337364c003Sbluhm if (nanosleep(&time_1000ns, NULL) < 0) 3347364c003Sbluhm err(1, "%s: nanosleep", __func__); 3357364c003Sbluhm continue; 3367364c003Sbluhm } 3377364c003Sbluhm if (close(sock) < 0) 3387364c003Sbluhm err(1, "%s: close %d", __func__, sock); 3397364c003Sbluhm count++; 3407364c003Sbluhm } 3417364c003Sbluhm 3427364c003Sbluhm return (void *)count; 3437364c003Sbluhm } 3447364c003Sbluhm 3457364c003Sbluhm static void * 3467364c003Sbluhm splice_routine(void *arg) 3477364c003Sbluhm { 3487364c003Sbluhm volatile int *run = arg; 3497364c003Sbluhm unsigned long count = 0; 3507364c003Sbluhm struct sockaddr *addr; 3517364c003Sbluhm unsigned int seed; 3527364c003Sbluhm unsigned int n; 3537364c003Sbluhm int sock; 3547364c003Sbluhm struct splice accept_splice, connect_splice; 3557364c003Sbluhm int spliced; 3567364c003Sbluhm 3577364c003Sbluhm seed = arc4random(); 3587364c003Sbluhm 3597364c003Sbluhm while (*run) { 3607364c003Sbluhm spliced = 0; 3617364c003Sbluhm for (n = 0; n < sock_num; n++) { 3627364c003Sbluhm if (!accept_socket(&splice_accept_socks[n], 3635a6c4815Sbluhm splice_listen_socks, 3645a6c4815Sbluhm &splice_accept_tims[n], splice_listen_addrs)) 3657364c003Sbluhm continue; 3667364c003Sbluhm /* free the matching connect slot */ 3677364c003Sbluhm sock = atomic_swap_uint(&splice_connect_socks[n], -1); 3687364c003Sbluhm if (sock != -1) { 3697364c003Sbluhm if (close(sock) < 0) 3707364c003Sbluhm err(1, "%s: close %d", __func__, sock); 3717364c003Sbluhm } 3727364c003Sbluhm addr = &listen_addrs[rand_r(&seed) % sock_num].su_sa; 3737364c003Sbluhm if (!connect_socket(&splice_connect_socks[n], addr)) { 3747364c003Sbluhm /* close the accepted socket */ 3757364c003Sbluhm sock = atomic_swap_uint( 3767364c003Sbluhm &splice_accept_socks[n], -1); 3777364c003Sbluhm if (sock != -1) { 3787364c003Sbluhm if (close(sock) < 0) { 3797364c003Sbluhm err(1, "%s: close %d", 3807364c003Sbluhm __func__, sock); 3817364c003Sbluhm } 3827364c003Sbluhm } 3837364c003Sbluhm continue; 3847364c003Sbluhm } 3857364c003Sbluhm memset(&accept_splice, 0, sizeof(accept_splice)); 3867364c003Sbluhm memset(&connect_splice, 0, sizeof(connect_splice)); 3877364c003Sbluhm accept_splice.sp_fd = splice_accept_socks[n]; 3887364c003Sbluhm connect_splice.sp_fd = splice_connect_socks[n]; 3897364c003Sbluhm if ((rand_r(&seed) % 100) < max_percent) { 3907364c003Sbluhm accept_splice.sp_max = 1; 3917364c003Sbluhm connect_splice.sp_max = 1; 3927364c003Sbluhm } 3937364c003Sbluhm if ((rand_r(&seed) % 100) < idle_percent) { 3947364c003Sbluhm accept_splice.sp_idle = time_1us; 3957364c003Sbluhm connect_splice.sp_idle = time_1us; 3967364c003Sbluhm } 3977364c003Sbluhm if (accept_splice.sp_fd == -1 || 3987364c003Sbluhm connect_splice.sp_fd == -1 || 3997364c003Sbluhm setsockopt(accept_splice.sp_fd, 4007364c003Sbluhm SOL_SOCKET, SO_SPLICE, 4017364c003Sbluhm &connect_splice, sizeof(connect_splice)) < 0 || 4027364c003Sbluhm setsockopt(connect_splice.sp_fd, 4037364c003Sbluhm SOL_SOCKET, SO_SPLICE, 4047364c003Sbluhm &accept_splice, sizeof(accept_splice)) < 0) { 4057364c003Sbluhm /* close the accepted and connected socket */ 4067364c003Sbluhm sock = atomic_swap_uint( 4077364c003Sbluhm &splice_accept_socks[n], -1); 4087364c003Sbluhm if (sock != -1) { 4097364c003Sbluhm if (close(sock) < 0) { 4107364c003Sbluhm err(1, "%s: close %d", 4117364c003Sbluhm __func__, sock); 4127364c003Sbluhm } 4137364c003Sbluhm } 4147364c003Sbluhm sock = atomic_swap_uint( 4157364c003Sbluhm &splice_connect_socks[n], -1); 4167364c003Sbluhm if (sock != -1) { 4177364c003Sbluhm if (close(sock) < 0) { 4187364c003Sbluhm err(1, "%s: close %d", 4197364c003Sbluhm __func__, sock); 4207364c003Sbluhm } 4217364c003Sbluhm } 4227364c003Sbluhm continue; 4237364c003Sbluhm } 4247364c003Sbluhm spliced = 1; 4257364c003Sbluhm count++; 4267364c003Sbluhm } 4277364c003Sbluhm if (!spliced) { 4287364c003Sbluhm /* splicing for all sockets failed, wait a bit */ 4297364c003Sbluhm if (nanosleep(&time_1000ns, NULL) < 0) 4307364c003Sbluhm err(1, "%s: nanosleep", __func__); 4317364c003Sbluhm } 4327364c003Sbluhm } 4337364c003Sbluhm 4347364c003Sbluhm return (void *)count; 4357364c003Sbluhm } 4367364c003Sbluhm 4377364c003Sbluhm static void * 4387364c003Sbluhm unsplice_routine(void *arg) 4397364c003Sbluhm { 4407364c003Sbluhm volatile int *run = arg; 4417364c003Sbluhm unsigned long count = 0; 4427364c003Sbluhm unsigned int seed; 4437364c003Sbluhm volatile int *sockp; 4447364c003Sbluhm int sock; 4457364c003Sbluhm 4467364c003Sbluhm seed = arc4random(); 4477364c003Sbluhm 4487364c003Sbluhm while (*run) { 4497364c003Sbluhm sockp = &((rand_r(&seed) % 2) ? 4507364c003Sbluhm splice_accept_socks : splice_connect_socks) 4517364c003Sbluhm [rand_r(&seed) % sock_num]; 4527364c003Sbluhm sock = *sockp; 4537364c003Sbluhm if (sock == -1) 4547364c003Sbluhm continue; 4557364c003Sbluhm if (setsockopt(sock, SOL_SOCKET, SO_SPLICE, NULL, 0) < 0) { 4567364c003Sbluhm if ((int)atomic_cas_uint(sockp, sock, -1) != sock) { 4577364c003Sbluhm /* another thread has closed sockp */ 4587364c003Sbluhm continue; 4597364c003Sbluhm } 4607364c003Sbluhm if (close(sock) < 0) 4617364c003Sbluhm err(1, "%s: close %d", __func__, sock); 4627364c003Sbluhm } 4637364c003Sbluhm count++; 4647364c003Sbluhm } 4657364c003Sbluhm 4667364c003Sbluhm return (void *)count; 4677364c003Sbluhm } 4687364c003Sbluhm 4695a6c4815Sbluhm static void * 4705a6c4815Sbluhm drop_routine(void *arg) 4715a6c4815Sbluhm { 4725a6c4815Sbluhm static const int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_DROP }; 4735a6c4815Sbluhm volatile int *run = arg; 4745a6c4815Sbluhm unsigned long count = 0; 4755a6c4815Sbluhm unsigned int seed, n; 4765a6c4815Sbluhm volatile int *socks; 4775a6c4815Sbluhm struct tcp_ident_mapping *tims; 4785a6c4815Sbluhm 4795a6c4815Sbluhm seed = arc4random(); 4805a6c4815Sbluhm 4815a6c4815Sbluhm while (*run) { 4825a6c4815Sbluhm if (splice_num > 0 && (rand_r(&seed) % 2)) { 4835a6c4815Sbluhm socks = splice_accept_socks; 4845a6c4815Sbluhm tims = splice_accept_tims; 4855a6c4815Sbluhm } else { 4865a6c4815Sbluhm socks = accept_socks; 4875a6c4815Sbluhm tims = accept_tims; 4885a6c4815Sbluhm } 4895a6c4815Sbluhm n = rand_r(&seed) % sock_num; 4905a6c4815Sbluhm if (socks[n] == -1) 4915a6c4815Sbluhm continue; 4925a6c4815Sbluhm membar_consumer(); 4935a6c4815Sbluhm /* accept_tims is not MP safe, but only ESRCH may happen */ 4945a6c4815Sbluhm if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, 4955a6c4815Sbluhm &tims[n], sizeof(tims[0])) < 0) { 4965a6c4815Sbluhm if (errno == ESRCH) 4975a6c4815Sbluhm continue; 4985a6c4815Sbluhm err(1, "sysctl TCPCTL_DROP"); 4995a6c4815Sbluhm } 5005a6c4815Sbluhm count++; 5015a6c4815Sbluhm } 5025a6c4815Sbluhm 5035a6c4815Sbluhm return (void *)count; 5045a6c4815Sbluhm } 5055a6c4815Sbluhm 5067364c003Sbluhm int 5077364c003Sbluhm main(int argc, char *argv[]) 5087364c003Sbluhm { 5097364c003Sbluhm pthread_t *connect_thread, *accept_thread, *send_thread, *recv_thread, 5105a6c4815Sbluhm *close_thread, *splice_thread, *unsplice_thread, *drop_thread; 5117364c003Sbluhm struct sockaddr *sa; 5127364c003Sbluhm const char *errstr; 5137364c003Sbluhm unsigned int seed; 5147364c003Sbluhm int ch, run; 5157364c003Sbluhm unsigned int n; 5167364c003Sbluhm unsigned long connect_count, accept_count, send_count, recv_count, 5175a6c4815Sbluhm close_count, splice_count, unsplice_count, drop_count; 5187364c003Sbluhm socklen_t len; 5197364c003Sbluhm 5207364c003Sbluhm seed = arc4random(); 5217364c003Sbluhm 5225a6c4815Sbluhm while ((ch = getopt(argc, argv, "a:c:D:I:M:n:o:r:S:s:t:U:")) != -1) { 5237364c003Sbluhm switch (ch) { 5247364c003Sbluhm case 'a': 5257364c003Sbluhm accept_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5267364c003Sbluhm if (errstr != NULL) 5277364c003Sbluhm errx(1, "accept is %s: %s", errstr, optarg); 5287364c003Sbluhm break; 5297364c003Sbluhm case 'c': 5307364c003Sbluhm connect_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5317364c003Sbluhm if (errstr != NULL) 5327364c003Sbluhm errx(1, "connect is %s: %s", errstr, optarg); 5337364c003Sbluhm break; 5345a6c4815Sbluhm case 'D': 5355a6c4815Sbluhm drop_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5365a6c4815Sbluhm if (errstr != NULL) 5375a6c4815Sbluhm errx(1, "drop is %s: %s", errstr, optarg); 5385a6c4815Sbluhm break; 5397364c003Sbluhm case 'I': 5407364c003Sbluhm idle_percent = strtonum(optarg, 0, 100, &errstr); 5417364c003Sbluhm if (errstr != NULL) 5427364c003Sbluhm errx(1, "idle is %s: %s", errstr, optarg); 5437364c003Sbluhm break; 5447364c003Sbluhm case 'M': 5457364c003Sbluhm max_percent = strtonum(optarg, 0, 100, &errstr); 5467364c003Sbluhm if (errstr != NULL) 5477364c003Sbluhm errx(1, "max is %s: %s", errstr, optarg); 5487364c003Sbluhm break; 5497364c003Sbluhm case 'n': 5507364c003Sbluhm sock_num = strtonum(optarg, 1, INT_MAX, &errstr); 5517364c003Sbluhm if (errstr != NULL) 5527364c003Sbluhm errx(1, "num is %s: %s", errstr, optarg); 5537364c003Sbluhm break; 5547364c003Sbluhm case 'o': 5557364c003Sbluhm close_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5567364c003Sbluhm if (errstr != NULL) 5577364c003Sbluhm errx(1, "close is %s: %s", errstr, optarg); 5587364c003Sbluhm break; 5597364c003Sbluhm case 'r': 5607364c003Sbluhm recv_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5617364c003Sbluhm if (errstr != NULL) 5627364c003Sbluhm errx(1, "recv is %s: %s", errstr, optarg); 5637364c003Sbluhm break; 5647364c003Sbluhm case 'S': 5657364c003Sbluhm splice_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5667364c003Sbluhm if (errstr != NULL) 5677364c003Sbluhm errx(1, "splice is %s: %s", errstr, optarg); 5687364c003Sbluhm break; 5697364c003Sbluhm case 's': 5707364c003Sbluhm send_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5717364c003Sbluhm if (errstr != NULL) 5727364c003Sbluhm errx(1, "send is %s: %s", errstr, optarg); 5737364c003Sbluhm break; 5747364c003Sbluhm case 't': 5757364c003Sbluhm run_time = strtonum(optarg, 0, UINT_MAX, &errstr); 5767364c003Sbluhm if (errstr != NULL) 5777364c003Sbluhm errx(1, "time is %s: %s", errstr, optarg); 5787364c003Sbluhm break; 5797364c003Sbluhm case 'U': 5807364c003Sbluhm unsplice_num = strtonum(optarg, 0, UINT_MAX, &errstr); 5817364c003Sbluhm if (errstr != NULL) 5827364c003Sbluhm errx(1, "unsplice is %s: %s", errstr, optarg); 5837364c003Sbluhm break; 5847364c003Sbluhm default: 5857364c003Sbluhm usage(); 5867364c003Sbluhm } 5877364c003Sbluhm } 5887364c003Sbluhm argc -= optind; 5897364c003Sbluhm argv += optind; 5907364c003Sbluhm if (argc > 0) 5917364c003Sbluhm usage(); 5927364c003Sbluhm 5937364c003Sbluhm if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 5947364c003Sbluhm err(1, "signal"); 5957364c003Sbluhm 5967364c003Sbluhm sin_loopback.sin_family = AF_INET; 5977364c003Sbluhm sin_loopback.sin_len = sizeof(sin_loopback); 5987364c003Sbluhm sin_loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 5997364c003Sbluhm 6007364c003Sbluhm sin6_loopback.sin6_family = AF_INET6; 6017364c003Sbluhm sin6_loopback.sin6_len = sizeof(sin6_loopback); 6027364c003Sbluhm sin6_loopback.sin6_addr = in6addr_loopback; 6037364c003Sbluhm 6047364c003Sbluhm listen_socks = reallocarray(NULL, sock_num, sizeof(int)); 6057364c003Sbluhm if (listen_socks == NULL) 6067364c003Sbluhm err(1, "listen_socks"); 6077364c003Sbluhm connect_socks = reallocarray(NULL, sock_num, sizeof(int)); 6087364c003Sbluhm if (connect_socks == NULL) 6097364c003Sbluhm err(1, "connect_socks"); 6107364c003Sbluhm accept_socks = reallocarray(NULL, sock_num, sizeof(int)); 6117364c003Sbluhm if (accept_socks == NULL) 6127364c003Sbluhm err(1, "accept_socks"); 6137364c003Sbluhm for (n = 0; n < sock_num; n++) 6147364c003Sbluhm listen_socks[n] = connect_socks[n] = accept_socks[n] = -1; 6157364c003Sbluhm listen_addrs = calloc(sock_num, sizeof(listen_addrs[0])); 6167364c003Sbluhm if (listen_addrs == NULL) 6177364c003Sbluhm err(1, "listen_addrs"); 6185a6c4815Sbluhm accept_tims = calloc(sock_num, sizeof(accept_tims[0])); 6195a6c4815Sbluhm if (accept_tims == NULL) 6205a6c4815Sbluhm err(1, "accept_tims"); 6217364c003Sbluhm if (splice_num > 0) { 6227364c003Sbluhm splice_listen_socks = reallocarray(NULL, sock_num, sizeof(int)); 6237364c003Sbluhm if (splice_listen_socks == NULL) 6247364c003Sbluhm err(1, "splice_listen_socks"); 6257364c003Sbluhm splice_accept_socks = reallocarray(NULL, sock_num, sizeof(int)); 6267364c003Sbluhm if (splice_accept_socks == NULL) 6277364c003Sbluhm err(1, "splice_accept_socks"); 6287364c003Sbluhm splice_connect_socks = 6297364c003Sbluhm reallocarray(NULL, sock_num, sizeof(int)); 6307364c003Sbluhm if (splice_connect_socks == NULL) 6317364c003Sbluhm err(1, "splice_connect_socks"); 6327364c003Sbluhm for (n = 0; n < sock_num; n++) { 6337364c003Sbluhm splice_listen_socks[n] = splice_accept_socks[n] = 6347364c003Sbluhm splice_connect_socks[n] = -1; 6357364c003Sbluhm } 6365a6c4815Sbluhm splice_listen_addrs = calloc(sock_num, 6375a6c4815Sbluhm sizeof(splice_listen_addrs[0])); 6385a6c4815Sbluhm if (splice_listen_addrs == NULL) 6395a6c4815Sbluhm err(1, "splice_listen_addrs"); 6405a6c4815Sbluhm splice_accept_tims = calloc(sock_num, 6415a6c4815Sbluhm sizeof(splice_accept_tims[0])); 6425a6c4815Sbluhm if (splice_accept_tims == NULL) 6435a6c4815Sbluhm err(1, "splice_accept_tims"); 6447364c003Sbluhm } 6457364c003Sbluhm 6467364c003Sbluhm for (n = 0; n < sock_num; n++) { 6477364c003Sbluhm int af; 6487364c003Sbluhm 6497364c003Sbluhm af = (rand_r(&seed) % 2) ? AF_INET : AF_INET6; 6507364c003Sbluhm listen_socks[n] = socket(af, SOCK_STREAM | SOCK_NONBLOCK, 6517364c003Sbluhm IPPROTO_TCP); 6527364c003Sbluhm if (listen_socks[n] < 0) 6537364c003Sbluhm err(1, "socket"); 6547364c003Sbluhm if (af == AF_INET) 6557364c003Sbluhm sa = (struct sockaddr *)&sin_loopback; 6567364c003Sbluhm if (af == AF_INET6) 6577364c003Sbluhm sa = (struct sockaddr *)&sin6_loopback; 6587364c003Sbluhm if (bind(listen_socks[n], sa, sa->sa_len) < 0) 6597364c003Sbluhm err(1, "bind"); 6607364c003Sbluhm len = sizeof(listen_addrs[n]); 6617364c003Sbluhm if (getsockname(listen_socks[n], &listen_addrs[n].su_sa, &len) 6627364c003Sbluhm < 0) 6637364c003Sbluhm err(1, "getsockname"); 6647364c003Sbluhm if (listen(listen_socks[n], 128) < 0) 6657364c003Sbluhm err(1, "listen"); 6667364c003Sbluhm 6677364c003Sbluhm if (splice_num > 0) { 6687364c003Sbluhm af = (rand_r(&seed) % 2) ? AF_INET : AF_INET6; 6697364c003Sbluhm splice_listen_socks[n] = socket(af, 6707364c003Sbluhm SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); 6717364c003Sbluhm if (splice_listen_socks[n] < 0) 6727364c003Sbluhm err(1, "socket"); 6737364c003Sbluhm if (af == AF_INET) 6747364c003Sbluhm sa = (struct sockaddr *)&sin_loopback; 6757364c003Sbluhm if (af == AF_INET6) 6767364c003Sbluhm sa = (struct sockaddr *)&sin6_loopback; 6777364c003Sbluhm if (bind(splice_listen_socks[n], sa, sa->sa_len) < 0) 6787364c003Sbluhm err(1, "bind"); 6795a6c4815Sbluhm len = sizeof(splice_listen_addrs[n]); 6807364c003Sbluhm if (getsockname(splice_listen_socks[n], 6815a6c4815Sbluhm &splice_listen_addrs[n].su_sa, &len) < 0) 6827364c003Sbluhm err(1, "getsockname"); 6837364c003Sbluhm if (listen(splice_listen_socks[n], 128) < 0) 6847364c003Sbluhm err(1, "listen"); 6857364c003Sbluhm } 6867364c003Sbluhm } 6877364c003Sbluhm 6887364c003Sbluhm run = 1; 6897364c003Sbluhm 6907364c003Sbluhm connect_thread = calloc(connect_num, sizeof(pthread_t)); 6917364c003Sbluhm if (connect_thread == NULL) 6927364c003Sbluhm err(1, "connect_thread"); 6937364c003Sbluhm for (n = 0; n < connect_num; n++) { 6947364c003Sbluhm errno = pthread_create(&connect_thread[n], NULL, 6957364c003Sbluhm connect_routine, &run); 6967364c003Sbluhm if (errno) 6977364c003Sbluhm err(1, "pthread_create connect %u", n); 6987364c003Sbluhm } 6997364c003Sbluhm 7007364c003Sbluhm accept_thread = calloc(accept_num, sizeof(pthread_t)); 7017364c003Sbluhm if (accept_thread == NULL) 7027364c003Sbluhm err(1, "accept_thread"); 7037364c003Sbluhm for (n = 0; n < accept_num; n++) { 7047364c003Sbluhm errno = pthread_create(&accept_thread[n], NULL, 7057364c003Sbluhm accept_routine, &run); 7067364c003Sbluhm if (errno) 7077364c003Sbluhm err(1, "pthread_create accept %u", n); 7087364c003Sbluhm } 7097364c003Sbluhm 7107364c003Sbluhm send_thread = calloc(send_num, sizeof(pthread_t)); 7117364c003Sbluhm if (send_thread == NULL) 7127364c003Sbluhm err(1, "send_thread"); 7137364c003Sbluhm for (n = 0; n < send_num; n++) { 7147364c003Sbluhm errno = pthread_create(&send_thread[n], NULL, 7157364c003Sbluhm send_routine, &run); 7167364c003Sbluhm if (errno) 7177364c003Sbluhm err(1, "pthread_create send %u", n); 7187364c003Sbluhm } 7197364c003Sbluhm 7207364c003Sbluhm recv_thread = calloc(recv_num, sizeof(pthread_t)); 7217364c003Sbluhm if (recv_thread == NULL) 7227364c003Sbluhm err(1, "recv_thread"); 7237364c003Sbluhm for (n = 0; n < recv_num; n++) { 7247364c003Sbluhm errno = pthread_create(&recv_thread[n], NULL, 7257364c003Sbluhm recv_routine, &run); 7267364c003Sbluhm if (errno) 7277364c003Sbluhm err(1, "pthread_create recv %u", n); 7287364c003Sbluhm } 7297364c003Sbluhm 7307364c003Sbluhm close_thread = calloc(close_num, sizeof(pthread_t)); 7317364c003Sbluhm if (close_thread == NULL) 7327364c003Sbluhm err(1, "close_thread"); 7337364c003Sbluhm for (n = 0; n < close_num; n++) { 7347364c003Sbluhm errno = pthread_create(&close_thread[n], NULL, 7357364c003Sbluhm close_routine, &run); 7367364c003Sbluhm if (errno) 7377364c003Sbluhm err(1, "pthread_create close %u", n); 7387364c003Sbluhm } 7397364c003Sbluhm 7407364c003Sbluhm if (splice_num > 0) { 7417364c003Sbluhm splice_thread = calloc(splice_num, sizeof(pthread_t)); 7427364c003Sbluhm if (splice_thread == NULL) 7437364c003Sbluhm err(1, "splice_thread"); 7447364c003Sbluhm for (n = 0; n < splice_num; n++) { 7457364c003Sbluhm errno = pthread_create(&splice_thread[n], NULL, 7467364c003Sbluhm splice_routine, &run); 7477364c003Sbluhm if (errno) 7487364c003Sbluhm err(1, "pthread_create splice %u", n); 7497364c003Sbluhm } 7507364c003Sbluhm 7517364c003Sbluhm unsplice_thread = calloc(unsplice_num, sizeof(pthread_t)); 7527364c003Sbluhm if (unsplice_thread == NULL) 7537364c003Sbluhm err(1, "unsplice_thread"); 7547364c003Sbluhm for (n = 0; n < unsplice_num; n++) { 7557364c003Sbluhm errno = pthread_create(&unsplice_thread[n], NULL, 7567364c003Sbluhm unsplice_routine, &run); 7577364c003Sbluhm if (errno) 7587364c003Sbluhm err(1, "pthread_create unsplice %u", n); 7597364c003Sbluhm } 7607364c003Sbluhm } 7615a6c4815Sbluhm drop_thread = calloc(drop_num, sizeof(pthread_t)); 7625a6c4815Sbluhm if (drop_thread == NULL) 7635a6c4815Sbluhm err(1, "drop_thread"); 7645a6c4815Sbluhm for (n = 0; n < drop_num; n++) { 7655a6c4815Sbluhm errno = pthread_create(&drop_thread[n], NULL, 7665a6c4815Sbluhm drop_routine, &run); 7675a6c4815Sbluhm if (errno) 7685a6c4815Sbluhm err(1, "pthread_create drop %u", n); 7695a6c4815Sbluhm } 7707364c003Sbluhm 7717364c003Sbluhm if (run_time > 0) { 7727364c003Sbluhm if (sleep(run_time) < 0) 7737364c003Sbluhm err(1, "sleep %u", run_time); 7747364c003Sbluhm } 7757364c003Sbluhm run = 0; 7767364c003Sbluhm 7777364c003Sbluhm connect_count = 0; 7787364c003Sbluhm for (n = 0; n < connect_num; n++) { 7797364c003Sbluhm unsigned long count; 7807364c003Sbluhm 7817364c003Sbluhm errno = pthread_join(connect_thread[n], (void **)&count); 7827364c003Sbluhm if (errno) 7837364c003Sbluhm err(1, "pthread_join connect %u", n); 7847364c003Sbluhm connect_count += count; 7857364c003Sbluhm } 7867364c003Sbluhm free(connect_thread); 7877364c003Sbluhm 7887364c003Sbluhm accept_count = 0; 7897364c003Sbluhm for (n = 0; n < accept_num; n++) { 7907364c003Sbluhm unsigned long count; 7917364c003Sbluhm 7927364c003Sbluhm errno = pthread_join(accept_thread[n], (void **)&count); 7937364c003Sbluhm if (errno) 7947364c003Sbluhm err(1, "pthread_join accept %u", n); 7957364c003Sbluhm accept_count += count; 7967364c003Sbluhm } 7977364c003Sbluhm free(accept_thread); 7987364c003Sbluhm 7997364c003Sbluhm send_count = 0; 8007364c003Sbluhm for (n = 0; n < send_num; n++) { 8017364c003Sbluhm unsigned long count; 8027364c003Sbluhm 8037364c003Sbluhm errno = pthread_join(send_thread[n], (void **)&count); 8047364c003Sbluhm if (errno) 8057364c003Sbluhm err(1, "pthread_join send %u", n); 8067364c003Sbluhm send_count += count; 8077364c003Sbluhm } 8087364c003Sbluhm free(send_thread); 8097364c003Sbluhm 8107364c003Sbluhm recv_count = 0; 8117364c003Sbluhm for (n = 0; n < recv_num; n++) { 8127364c003Sbluhm unsigned long count; 8137364c003Sbluhm 8147364c003Sbluhm errno = pthread_join(recv_thread[n], (void **)&count); 8157364c003Sbluhm if (errno) 8167364c003Sbluhm err(1, "pthread_join recv %u", n); 8177364c003Sbluhm recv_count += count; 8187364c003Sbluhm } 8197364c003Sbluhm free(recv_thread); 8207364c003Sbluhm 8217364c003Sbluhm close_count = 0; 8227364c003Sbluhm for (n = 0; n < close_num; n++) { 8237364c003Sbluhm unsigned long count; 8247364c003Sbluhm 8257364c003Sbluhm errno = pthread_join(close_thread[n], (void **)&count); 8267364c003Sbluhm if (errno) 8277364c003Sbluhm err(1, "pthread_join close %u", n); 8287364c003Sbluhm close_count += count; 8297364c003Sbluhm } 8307364c003Sbluhm free(close_thread); 8317364c003Sbluhm 8327364c003Sbluhm if (splice_num > 0) { 8337364c003Sbluhm splice_count = 0; 8347364c003Sbluhm for (n = 0; n < splice_num; n++) { 8357364c003Sbluhm unsigned long count; 8367364c003Sbluhm 8377364c003Sbluhm errno = pthread_join(splice_thread[n], (void **)&count); 8387364c003Sbluhm if (errno) 8397364c003Sbluhm err(1, "pthread_join splice %u", n); 8407364c003Sbluhm splice_count += count; 8417364c003Sbluhm } 8427364c003Sbluhm free(splice_thread); 8437364c003Sbluhm 8447364c003Sbluhm unsplice_count = 0; 8457364c003Sbluhm for (n = 0; n < unsplice_num; n++) { 8467364c003Sbluhm unsigned long count; 8477364c003Sbluhm 8487364c003Sbluhm errno = pthread_join(unsplice_thread[n], 8497364c003Sbluhm (void **)&count); 8507364c003Sbluhm if (errno) 8517364c003Sbluhm err(1, "pthread_join unsplice %u", n); 8527364c003Sbluhm unsplice_count += count; 8537364c003Sbluhm } 8547364c003Sbluhm free(unsplice_thread); 8557364c003Sbluhm } 8565a6c4815Sbluhm drop_count = 0; 8575a6c4815Sbluhm for (n = 0; n < drop_num; n++) { 8585a6c4815Sbluhm unsigned long count; 8595a6c4815Sbluhm 8605a6c4815Sbluhm errno = pthread_join(drop_thread[n], (void **)&count); 8615a6c4815Sbluhm if (errno) 8625a6c4815Sbluhm err(1, "pthread_join drop %u", n); 8635a6c4815Sbluhm drop_count += count; 8645a6c4815Sbluhm } 8655a6c4815Sbluhm free(drop_thread); 8667364c003Sbluhm 8677364c003Sbluhm free((int *)listen_socks); 8687364c003Sbluhm free((int *)connect_socks); 8697364c003Sbluhm free((int *)accept_socks); 8707364c003Sbluhm free(listen_addrs); 8715a6c4815Sbluhm free(accept_tims); 8727364c003Sbluhm if (splice_num > 0) { 8737364c003Sbluhm free((int *)splice_listen_socks); 8747364c003Sbluhm free((int *)splice_accept_socks); 8757364c003Sbluhm free((int *)splice_connect_socks); 8765a6c4815Sbluhm free(splice_listen_addrs); 8775a6c4815Sbluhm free(splice_accept_tims); 8787364c003Sbluhm } 8797364c003Sbluhm 8807364c003Sbluhm printf("count: connect %lu, ", connect_count); 8817364c003Sbluhm if (splice_num > 0) { 8827364c003Sbluhm printf("splice %lu, unsplice %lu, max %lu, idle %lu, ", 8837364c003Sbluhm splice_count, unsplice_count, max_count, idle_count); 8847364c003Sbluhm } 8855a6c4815Sbluhm printf("accept %lu, send %lu, recv %lu, close %lu, drop %lu\n", 8865a6c4815Sbluhm accept_count, send_count, recv_count, close_count, drop_count); 8877364c003Sbluhm 8887364c003Sbluhm return 0; 8897364c003Sbluhm } 890