xref: /openbsd-src/regress/sys/netinet/tcpthread/tcpthread.c (revision 939a177eed5e10cc0f2612a5606b4a946539c2b2)
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