1*234e5132Sbluhm /* $OpenBSD: bindconnect.c,v 1.5 2024/01/04 00:19:17 bluhm Exp $ */
26dd7abcaSbluhm
36dd7abcaSbluhm /*
4*234e5132Sbluhm * Copyright (c) 2023-2024 Alexander Bluhm <bluhm@openbsd.org>
56dd7abcaSbluhm *
66dd7abcaSbluhm * Permission to use, copy, modify, and distribute this software for any
76dd7abcaSbluhm * purpose with or without fee is hereby granted, provided that the above
86dd7abcaSbluhm * copyright notice and this permission notice appear in all copies.
96dd7abcaSbluhm *
106dd7abcaSbluhm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116dd7abcaSbluhm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126dd7abcaSbluhm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136dd7abcaSbluhm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146dd7abcaSbluhm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156dd7abcaSbluhm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166dd7abcaSbluhm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176dd7abcaSbluhm */
186dd7abcaSbluhm
196dd7abcaSbluhm #include <sys/resource.h>
206dd7abcaSbluhm #include <sys/socket.h>
216dd7abcaSbluhm
220d0a919fSbluhm #include <net/route.h>
236dd7abcaSbluhm #include <netinet/in.h>
240d0a919fSbluhm #include <arpa/inet.h>
256dd7abcaSbluhm
266dd7abcaSbluhm #include <err.h>
276dd7abcaSbluhm #include <errno.h>
28ba17659fSbluhm #include <netdb.h>
296dd7abcaSbluhm #include <pthread.h>
306dd7abcaSbluhm #include <stdio.h>
316dd7abcaSbluhm #include <stdlib.h>
326dd7abcaSbluhm #include <string.h>
336dd7abcaSbluhm #include <unistd.h>
346dd7abcaSbluhm
35ba17659fSbluhm #define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
366dd7abcaSbluhm
37dd1c5868Sbluhm #define s6_addr8 __u6_addr.__u6_addr8
38dd1c5868Sbluhm #define s6_addr16 __u6_addr.__u6_addr16
39dd1c5868Sbluhm #define s6_addr32 __u6_addr.__u6_addr32
40dd1c5868Sbluhm
41dd1c5868Sbluhm union sockaddr_union {
42dd1c5868Sbluhm struct sockaddr su_sa;
43dd1c5868Sbluhm struct sockaddr_in su_sin;
44dd1c5868Sbluhm struct sockaddr_in6 su_sin6;
45dd1c5868Sbluhm };
46dd1c5868Sbluhm
47dd1c5868Sbluhm union inaddr_union {
48dd1c5868Sbluhm struct in_addr au_inaddr;
49dd1c5868Sbluhm struct in6_addr au_in6addr;
50dd1c5868Sbluhm };
51dd1c5868Sbluhm
526dd7abcaSbluhm int fd_base;
530d0a919fSbluhm unsigned int fd_num = 128;
546dd7abcaSbluhm unsigned int run_time = 10;
550d0a919fSbluhm unsigned int socket_num = 1, close_num = 1, bind_num = 1, connect_num = 1,
560d0a919fSbluhm delroute_num = 0;
570d0a919fSbluhm int reuse_port = 0;
58dd1c5868Sbluhm union inaddr_union addr, mask;
59ba17659fSbluhm int af = AF_INET, type, proto = IPPROTO_UDP, prefix = -1, route_sock = -1;
606dd7abcaSbluhm
616dd7abcaSbluhm static void __dead
usage(void)626dd7abcaSbluhm usage(void)
636dd7abcaSbluhm {
646dd7abcaSbluhm fprintf(stderr,
65*234e5132Sbluhm "bindconnect [-r] [-b bind] [-c connect] [-d delroute] "
66*234e5132Sbluhm "[-f family] [-N addr/net] [-n num] [-o close] [-p proto] "
67*234e5132Sbluhm "[-s socket] [-t time]\n"
686dd7abcaSbluhm " -b bind threads binding sockets, default %u\n"
696dd7abcaSbluhm " -c connect threads connecting sockets, default %u\n"
700d0a919fSbluhm " -d delroute threads deleting cloned routes, default %u\n"
71ba17659fSbluhm " -f family address family inet or inet6, default inet\n"
720d0a919fSbluhm " -N addr/net connect to any address within network\n"
736dd7abcaSbluhm " -n num number of file descriptors, default %u\n"
746dd7abcaSbluhm " -o close threads closing sockets, default %u\n"
75ba17659fSbluhm " -p proto protocol udp, tcp, name or number, default udp\n"
760d0a919fSbluhm " -r set reuse port socket option\n"
776dd7abcaSbluhm " -s socket threads creating sockets, default %u\n"
786dd7abcaSbluhm " -t time run time in seconds, default %u\n",
79ba17659fSbluhm bind_num, connect_num, delroute_num, fd_num, close_num,
80dd1c5868Sbluhm socket_num, run_time);
816dd7abcaSbluhm exit(2);
826dd7abcaSbluhm }
836dd7abcaSbluhm
840d0a919fSbluhm static void
in_prefixlen2mask(struct in_addr * maskp,int plen)850d0a919fSbluhm in_prefixlen2mask(struct in_addr *maskp, int plen)
860d0a919fSbluhm {
870d0a919fSbluhm if (plen == 0)
880d0a919fSbluhm maskp->s_addr = 0;
890d0a919fSbluhm else
900d0a919fSbluhm maskp->s_addr = htonl(0xffffffff << (32 - plen));
910d0a919fSbluhm }
920d0a919fSbluhm
93dd1c5868Sbluhm static void
in6_prefixlen2mask(struct in6_addr * maskp,int len)94dd1c5868Sbluhm in6_prefixlen2mask(struct in6_addr *maskp, int len)
95dd1c5868Sbluhm {
96dd1c5868Sbluhm u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
97dd1c5868Sbluhm int bytelen, bitlen, i;
98dd1c5868Sbluhm
99dd1c5868Sbluhm bzero(maskp, sizeof(*maskp));
100dd1c5868Sbluhm bytelen = len / 8;
101dd1c5868Sbluhm bitlen = len % 8;
102dd1c5868Sbluhm for (i = 0; i < bytelen; i++)
103dd1c5868Sbluhm maskp->s6_addr[i] = 0xff;
104dd1c5868Sbluhm /* len == 128 is ok because bitlen == 0 then */
105dd1c5868Sbluhm if (bitlen)
106dd1c5868Sbluhm maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
107dd1c5868Sbluhm }
108dd1c5868Sbluhm
109dd1c5868Sbluhm static void
fill_sockaddr(union sockaddr_union * su)110dd1c5868Sbluhm fill_sockaddr(union sockaddr_union *su)
111dd1c5868Sbluhm {
112dd1c5868Sbluhm memset(su, 0, sizeof(*su));
113dd1c5868Sbluhm su->su_sa.sa_family = af;
114dd1c5868Sbluhm if (af == AF_INET) {
115dd1c5868Sbluhm su->su_sin.sin_len = sizeof(su->su_sin);
116dd1c5868Sbluhm if (prefix >= 0)
117dd1c5868Sbluhm su->su_sin.sin_addr = addr.au_inaddr;
118dd1c5868Sbluhm else
119dd1c5868Sbluhm su->su_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
120dd1c5868Sbluhm }
121dd1c5868Sbluhm if (af == AF_INET6) {
122dd1c5868Sbluhm su->su_sin6.sin6_len = sizeof(su->su_sin6);
123dd1c5868Sbluhm if (prefix >= 0)
124dd1c5868Sbluhm su->su_sin6.sin6_addr = addr.au_in6addr;
125dd1c5868Sbluhm else
126dd1c5868Sbluhm su->su_sin6.sin6_addr = in6addr_loopback;
127dd1c5868Sbluhm }
128dd1c5868Sbluhm }
129dd1c5868Sbluhm
130dd1c5868Sbluhm static void
mask_sockaddr(union sockaddr_union * su)131dd1c5868Sbluhm mask_sockaddr(union sockaddr_union *su)
132dd1c5868Sbluhm {
133dd1c5868Sbluhm if (af == AF_INET) {
134dd1c5868Sbluhm if (prefix >=0 && prefix != 32) {
135dd1c5868Sbluhm su->su_sin.sin_addr.s_addr &=
136dd1c5868Sbluhm mask.au_inaddr.s_addr;
137dd1c5868Sbluhm /* do only 8 bits variation, routes should be reused */
138dd1c5868Sbluhm su->su_sin.sin_addr.s_addr |= htonl(255) &
139dd1c5868Sbluhm ~mask.au_inaddr.s_addr & arc4random();
140dd1c5868Sbluhm }
141dd1c5868Sbluhm }
142dd1c5868Sbluhm if (af == AF_INET6) {
143dd1c5868Sbluhm if (prefix >=0 && prefix != 128) {
144dd1c5868Sbluhm su->su_sin6.sin6_addr.s6_addr32[0] &=
145dd1c5868Sbluhm mask.au_in6addr.s6_addr32[0];
146dd1c5868Sbluhm su->su_sin6.sin6_addr.s6_addr32[1] &=
147dd1c5868Sbluhm mask.au_in6addr.s6_addr32[1];
148dd1c5868Sbluhm su->su_sin6.sin6_addr.s6_addr32[2] &=
149dd1c5868Sbluhm mask.au_in6addr.s6_addr32[2];
150dd1c5868Sbluhm su->su_sin6.sin6_addr.s6_addr32[3] &=
151dd1c5868Sbluhm mask.au_in6addr.s6_addr32[3];
152dd1c5868Sbluhm /* do only 8 bits variation, routes should be reused */
153dd1c5868Sbluhm su->su_sin6.sin6_addr.s6_addr32[3] |= htonl(255) &
154dd1c5868Sbluhm ~mask.au_in6addr.s6_addr32[3] & arc4random();
155dd1c5868Sbluhm }
156dd1c5868Sbluhm }
157dd1c5868Sbluhm }
158dd1c5868Sbluhm
1596dd7abcaSbluhm static void *
thread_socket(void * arg)1606dd7abcaSbluhm thread_socket(void *arg)
1616dd7abcaSbluhm {
1626dd7abcaSbluhm volatile int *run = arg;
1636dd7abcaSbluhm unsigned long count;
1640d0a919fSbluhm int fd;
1656dd7abcaSbluhm
1666dd7abcaSbluhm for (count = 0; *run; count++) {
1670d0a919fSbluhm int opt;
1680d0a919fSbluhm
169ba17659fSbluhm fd = socket(af, type | SOCK_NONBLOCK, proto);
1700d0a919fSbluhm if (fd < 0 || !reuse_port)
1710d0a919fSbluhm continue;
1720d0a919fSbluhm opt = 1;
1730d0a919fSbluhm setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
1746dd7abcaSbluhm }
1756dd7abcaSbluhm
1766dd7abcaSbluhm return (void *)count;
1776dd7abcaSbluhm }
1786dd7abcaSbluhm
1796dd7abcaSbluhm static void *
thread_close(void * arg)1806dd7abcaSbluhm thread_close(void *arg)
1816dd7abcaSbluhm {
1826dd7abcaSbluhm volatile int *run = arg;
1836dd7abcaSbluhm unsigned long count;
1846dd7abcaSbluhm int fd;
1856dd7abcaSbluhm
1866dd7abcaSbluhm for (count = 0; *run; count++) {
1876dd7abcaSbluhm fd = fd_base + arc4random_uniform(fd_num);
1886dd7abcaSbluhm close(fd);
1896dd7abcaSbluhm }
1906dd7abcaSbluhm
1916dd7abcaSbluhm return (void *)count;
1926dd7abcaSbluhm }
1936dd7abcaSbluhm
1946dd7abcaSbluhm static void *
thread_bind(void * arg)1956dd7abcaSbluhm thread_bind(void *arg)
1966dd7abcaSbluhm {
1976dd7abcaSbluhm volatile int *run = arg;
1986dd7abcaSbluhm unsigned long count;
1996dd7abcaSbluhm int fd;
200dd1c5868Sbluhm union sockaddr_union su;
2016dd7abcaSbluhm
202dd1c5868Sbluhm fill_sockaddr(&su);
2030d0a919fSbluhm
2046dd7abcaSbluhm for (count = 0; *run; count++) {
2056dd7abcaSbluhm fd = fd_base + arc4random_uniform(fd_num);
206dd1c5868Sbluhm bind(fd, &su.su_sa, su.su_sa.sa_len);
2076dd7abcaSbluhm }
2086dd7abcaSbluhm
2096dd7abcaSbluhm return (void *)count;
2106dd7abcaSbluhm }
2116dd7abcaSbluhm
2126dd7abcaSbluhm static void *
thread_connect(void * arg)2136dd7abcaSbluhm thread_connect(void *arg)
2146dd7abcaSbluhm {
2156dd7abcaSbluhm volatile int *run = arg;
2166dd7abcaSbluhm unsigned long count;
2176dd7abcaSbluhm int fd;
218dd1c5868Sbluhm union sockaddr_union su;
2196dd7abcaSbluhm
220dd1c5868Sbluhm fill_sockaddr(&su);
2216dd7abcaSbluhm
2226dd7abcaSbluhm for (count = 0; *run; count++) {
2236dd7abcaSbluhm fd = fd_base + arc4random_uniform(fd_num);
224dd1c5868Sbluhm mask_sockaddr(&su);
225dd1c5868Sbluhm if (af == AF_INET)
226dd1c5868Sbluhm su.su_sin.sin_port = arc4random();
227dd1c5868Sbluhm if (af == AF_INET6)
228dd1c5868Sbluhm su.su_sin6.sin6_port = arc4random();
229dd1c5868Sbluhm connect(fd, &su.su_sa, su.su_sa.sa_len);
2306dd7abcaSbluhm }
2316dd7abcaSbluhm
2326dd7abcaSbluhm return (void *)count;
2336dd7abcaSbluhm }
2346dd7abcaSbluhm
2350d0a919fSbluhm static void *
thread_delroute(void * arg)2360d0a919fSbluhm thread_delroute(void *arg)
2370d0a919fSbluhm {
2380d0a919fSbluhm volatile int *run = arg;
2390d0a919fSbluhm unsigned long count;
2400d0a919fSbluhm int seq = 0;
2410d0a919fSbluhm struct {
2420d0a919fSbluhm struct rt_msghdr m_rtm;
2430d0a919fSbluhm char m_space[512];
2440d0a919fSbluhm } m_rtmsg;
245dd1c5868Sbluhm union sockaddr_union su;
2460d0a919fSbluhm
2470d0a919fSbluhm #define rtm \
2480d0a919fSbluhm m_rtmsg.m_rtm
2490d0a919fSbluhm #define ROUNDUP(a) \
2500d0a919fSbluhm ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
2510d0a919fSbluhm #define ADVANCE(x, n) \
2520d0a919fSbluhm (x += ROUNDUP((n)->sa_len))
2530d0a919fSbluhm #define NEXTADDR(w, sa) \
2540d0a919fSbluhm if (rtm.rtm_addrs & (w)) { \
255dd1c5868Sbluhm int l = ROUNDUP((sa)->sa_len); \
256dd1c5868Sbluhm memcpy(cp, (sa), l); \
2570d0a919fSbluhm cp += l; \
2580d0a919fSbluhm }
2590d0a919fSbluhm
2600d0a919fSbluhm memset(&m_rtmsg, 0, sizeof(m_rtmsg));
2610d0a919fSbluhm rtm.rtm_type = RTM_DELETE;
2620d0a919fSbluhm rtm.rtm_flags = RTF_HOST;
2630d0a919fSbluhm rtm.rtm_version = RTM_VERSION;
2640d0a919fSbluhm rtm.rtm_addrs = RTA_DST;
2650d0a919fSbluhm rtm.rtm_hdrlen = sizeof(rtm);
2660d0a919fSbluhm
267dd1c5868Sbluhm fill_sockaddr(&su);
2680d0a919fSbluhm
2690d0a919fSbluhm for (count = 0; *run; count++) {
2700d0a919fSbluhm char *cp = m_rtmsg.m_space;
2710d0a919fSbluhm
2720d0a919fSbluhm rtm.rtm_seq = ++seq;
273dd1c5868Sbluhm mask_sockaddr(&su);
274dd1c5868Sbluhm NEXTADDR(RTA_DST, &su.su_sa);
2750d0a919fSbluhm rtm.rtm_msglen = cp - (char *)&m_rtmsg;
2760d0a919fSbluhm write(route_sock, &m_rtmsg, rtm.rtm_msglen);
2770d0a919fSbluhm }
2780d0a919fSbluhm
2790d0a919fSbluhm #undef rtm
2800d0a919fSbluhm #undef ROUNDUP
2810d0a919fSbluhm #undef ADVANCE
2820d0a919fSbluhm #undef NEXTADDR
2830d0a919fSbluhm
2840d0a919fSbluhm return (void *)count;
2850d0a919fSbluhm }
2860d0a919fSbluhm
2876dd7abcaSbluhm int
main(int argc,char * argv[])2886dd7abcaSbluhm main(int argc, char *argv[])
2896dd7abcaSbluhm {
2906dd7abcaSbluhm struct rlimit rlim;
291ba17659fSbluhm struct protoent *pent;
2920d0a919fSbluhm pthread_t *tsocket, *tclose, *tbind, *tconnect, *tdelroute;
2930d0a919fSbluhm const char *errstr, *addr_net = NULL;
294dd1c5868Sbluhm char buf[128], *p;
2956dd7abcaSbluhm int ch, run;
2966dd7abcaSbluhm unsigned int n;
2970d0a919fSbluhm unsigned long socket_count, close_count, bind_count, connect_count,
2980d0a919fSbluhm delroute_count;
299dd1c5868Sbluhm union sockaddr_union su;
3006dd7abcaSbluhm
301ba17659fSbluhm while ((ch = getopt(argc, argv, "b:c:d:f:N:n:o:p:rs:t:")) != -1) {
3026dd7abcaSbluhm switch (ch) {
3036dd7abcaSbluhm case 'b':
3046dd7abcaSbluhm bind_num = strtonum(optarg, 0, UINT_MAX, &errstr);
3056dd7abcaSbluhm if (errstr != NULL)
3066dd7abcaSbluhm errx(1, "bind is %s: %s", errstr, optarg);
3076dd7abcaSbluhm break;
3086dd7abcaSbluhm case 'c':
3096dd7abcaSbluhm connect_num = strtonum(optarg, 0, UINT_MAX, &errstr);
3106dd7abcaSbluhm if (errstr != NULL)
3116dd7abcaSbluhm errx(1, "connect is %s: %s", errstr, optarg);
3126dd7abcaSbluhm break;
3130d0a919fSbluhm case 'd':
3140d0a919fSbluhm delroute_num = strtonum(optarg, 0, UINT_MAX, &errstr);
3150d0a919fSbluhm if (errstr != NULL)
3160d0a919fSbluhm errx(1, "delroute is %s: %s", errstr, optarg);
3170d0a919fSbluhm break;
318dd1c5868Sbluhm case 'f':
319ba17659fSbluhm if (strcmp(optarg, "inet") == 0)
320ba17659fSbluhm af = AF_INET;
321ba17659fSbluhm else if (strcmp(optarg, "inet6") == 0)
322ba17659fSbluhm af = AF_INET6;
323ba17659fSbluhm else
324ba17659fSbluhm errx(1, "bad address family %s", optarg);
325dd1c5868Sbluhm break;
3260d0a919fSbluhm case 'N':
3270d0a919fSbluhm addr_net = optarg;
3280d0a919fSbluhm break;
3296dd7abcaSbluhm case 'n':
3300d0a919fSbluhm fd_num = strtonum(optarg, 1, INT_MAX, &errstr);
3316dd7abcaSbluhm if (errstr != NULL)
3326dd7abcaSbluhm errx(1, "num is %s: %s", errstr, optarg);
3336dd7abcaSbluhm break;
3346dd7abcaSbluhm case 'o':
3356dd7abcaSbluhm close_num = strtonum(optarg, 0, UINT_MAX, &errstr);
3366dd7abcaSbluhm if (errstr != NULL)
3376dd7abcaSbluhm errx(1, "close is %s: %s", errstr, optarg);
3386dd7abcaSbluhm break;
339ba17659fSbluhm case 'p':
340ba17659fSbluhm pent = getprotobyname(optarg);
341ba17659fSbluhm if (pent != NULL) {
342ba17659fSbluhm proto = pent->p_proto;
343ba17659fSbluhm break;
344ba17659fSbluhm }
345ba17659fSbluhm proto = strtonum(optarg, 0, IPPROTO_MAX -1 , &errstr);
346ba17659fSbluhm if (errstr != NULL)
347ba17659fSbluhm errx(1, "proto is %s: %s", errstr, optarg);
348ba17659fSbluhm break;
3490d0a919fSbluhm case 'r':
3500d0a919fSbluhm reuse_port = 1;
3510d0a919fSbluhm break;
3526dd7abcaSbluhm case 's':
3536dd7abcaSbluhm socket_num = strtonum(optarg, 0, UINT_MAX, &errstr);
3546dd7abcaSbluhm if (errstr != NULL)
3556dd7abcaSbluhm errx(1, "socket is %s: %s", errstr, optarg);
3566dd7abcaSbluhm break;
3576dd7abcaSbluhm case 't':
3586dd7abcaSbluhm run_time = strtonum(optarg, 0, UINT_MAX, &errstr);
3596dd7abcaSbluhm if (errstr != NULL)
3606dd7abcaSbluhm errx(1, "time is %s: %s", errstr, optarg);
3616dd7abcaSbluhm break;
3626dd7abcaSbluhm default:
3636dd7abcaSbluhm usage();
3646dd7abcaSbluhm }
3656dd7abcaSbluhm }
3666dd7abcaSbluhm argc -= optind;
3676dd7abcaSbluhm argv += optind;
3686dd7abcaSbluhm if (argc > 0)
3696dd7abcaSbluhm usage();
3706dd7abcaSbluhm
371dd1c5868Sbluhm /* split addr/net into addr, mask, prefix */
3720d0a919fSbluhm if (addr_net != NULL) {
373dd1c5868Sbluhm prefix = inet_net_pton(af, addr_net, &addr, sizeof(addr));
3740d0a919fSbluhm if (prefix < 0)
3750d0a919fSbluhm err(1, "inet_net_pton %s", addr_net);
376dd1c5868Sbluhm if (af == AF_INET6) {
377dd1c5868Sbluhm /*
378dd1c5868Sbluhm * Man page says inet_net_pton() preserves lower
379dd1c5868Sbluhm * bits. That is not true, call inet_pton() again.
380dd1c5868Sbluhm */
381dd1c5868Sbluhm if (strlcpy(buf, addr_net, sizeof(buf)) >= sizeof(buf))
382dd1c5868Sbluhm err(1, "strlcpy %s", addr_net);
383dd1c5868Sbluhm p = strchr(buf, '/');
384dd1c5868Sbluhm if (p != NULL ) {
385dd1c5868Sbluhm *p = '\0';
386dd1c5868Sbluhm if (inet_pton(af, buf, &addr) < 0)
387dd1c5868Sbluhm err(1, "inet_pton %s", buf);
3880d0a919fSbluhm }
389dd1c5868Sbluhm }
390dd1c5868Sbluhm if (af == AF_INET)
391dd1c5868Sbluhm in_prefixlen2mask(&mask.au_inaddr, prefix);
392dd1c5868Sbluhm if (af == AF_INET6)
393dd1c5868Sbluhm in6_prefixlen2mask(&mask.au_in6addr, prefix);
394dd1c5868Sbluhm }
395dd1c5868Sbluhm
396dd1c5868Sbluhm /* preopen route socket before file descriptor limits are set */
3970d0a919fSbluhm if (delroute_num > 0) {
3980d0a919fSbluhm if (prefix < 0 || prefix == 32)
3990d0a919fSbluhm errx(1, "delroute %u needs addr/net", delroute_num);
400dd1c5868Sbluhm route_sock = socket(AF_ROUTE, SOCK_RAW, af);
4010d0a919fSbluhm if (route_sock < 0)
4020d0a919fSbluhm err(1, "socket route");
4030d0a919fSbluhm if (shutdown(route_sock, SHUT_RD) < 0)
4040d0a919fSbluhm err(1, "shutdown read route");
4050d0a919fSbluhm }
4060d0a919fSbluhm
407dd1c5868Sbluhm /* detect lowest file desciptor, test bind, close everything above */
408ba17659fSbluhm switch (proto) {
409ba17659fSbluhm case IPPROTO_TCP:
410ba17659fSbluhm type = SOCK_STREAM;
411ba17659fSbluhm break;
412ba17659fSbluhm case IPPROTO_UDP:
413ba17659fSbluhm type = SOCK_DGRAM;
414ba17659fSbluhm break;
415ba17659fSbluhm default:
416ba17659fSbluhm type = SOCK_RAW;
417ba17659fSbluhm break;
418ba17659fSbluhm }
419ba17659fSbluhm fd_base = socket(af, type, proto);
4200d0a919fSbluhm if (fd_base < 0)
4210d0a919fSbluhm err(1, "socket fd_base");
4220d0a919fSbluhm if (fd_base > INT_MAX - (int)fd_num)
4230d0a919fSbluhm err(1, "fd base %d and num %u overflow", fd_base, fd_num);
424dd1c5868Sbluhm fill_sockaddr(&su);
425dd1c5868Sbluhm if (bind(fd_base, &su.su_sa, su.su_sa.sa_len) < 0)
426dd1c5868Sbluhm err(1, "bind %s", inet_ntop(af, &addr, buf, sizeof(buf)));
4276dd7abcaSbluhm if (closefrom(fd_base) < 0)
4286dd7abcaSbluhm err(1, "closefrom %d", fd_base);
4296dd7abcaSbluhm
4306dd7abcaSbluhm if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
4316dd7abcaSbluhm err(1, "getrlimit");
432ba17659fSbluhm rlim.rlim_max = MAXIMUM(rlim.rlim_max, fd_base + fd_num);
4336dd7abcaSbluhm rlim.rlim_cur = fd_base + fd_num;
4346dd7abcaSbluhm if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
4356dd7abcaSbluhm err(1, "setrlimit %llu", rlim.rlim_cur);
4366dd7abcaSbluhm
4376dd7abcaSbluhm run = 1;
4380d0a919fSbluhm
4396dd7abcaSbluhm tsocket = calloc(socket_num, sizeof(pthread_t));
4406dd7abcaSbluhm if (tsocket == NULL)
4416dd7abcaSbluhm err(1, "tsocket");
4426dd7abcaSbluhm for (n = 0; n < socket_num; n++) {
4436dd7abcaSbluhm errno = pthread_create(&tsocket[n], NULL, thread_socket, &run);
4446dd7abcaSbluhm if (errno)
4456dd7abcaSbluhm err(1, "pthread_create socket %u", n);
4466dd7abcaSbluhm }
4470d0a919fSbluhm
4486dd7abcaSbluhm tclose = calloc(close_num, sizeof(pthread_t));
4496dd7abcaSbluhm if (tclose == NULL)
4506dd7abcaSbluhm err(1, "tclose");
4516dd7abcaSbluhm for (n = 0; n < close_num; n++) {
4526dd7abcaSbluhm errno = pthread_create(&tclose[n], NULL, thread_close, &run);
4536dd7abcaSbluhm if (errno)
4546dd7abcaSbluhm err(1, "pthread_create close %u", n);
4556dd7abcaSbluhm }
4560d0a919fSbluhm
4576dd7abcaSbluhm tbind = calloc(bind_num, sizeof(pthread_t));
4586dd7abcaSbluhm if (tbind == NULL)
4596dd7abcaSbluhm err(1, "tbind");
4606dd7abcaSbluhm for (n = 0; n < bind_num; n++) {
4616dd7abcaSbluhm errno = pthread_create(&tbind[n], NULL, thread_bind, &run);
4626dd7abcaSbluhm if (errno)
4636dd7abcaSbluhm err(1, "pthread_create bind %u", n);
4646dd7abcaSbluhm }
4650d0a919fSbluhm
4666dd7abcaSbluhm tconnect = calloc(connect_num, sizeof(pthread_t));
4676dd7abcaSbluhm if (tconnect == NULL)
4686dd7abcaSbluhm err(1, "tconnect");
4696dd7abcaSbluhm for (n = 0; n < connect_num; n++) {
4706dd7abcaSbluhm errno = pthread_create(&tconnect[n], NULL, thread_connect,
4716dd7abcaSbluhm &run);
4726dd7abcaSbluhm if (errno)
4736dd7abcaSbluhm err(1, "pthread_create connect %u", n);
4746dd7abcaSbluhm }
4756dd7abcaSbluhm
4760d0a919fSbluhm tdelroute = calloc(delroute_num, sizeof(pthread_t));
4770d0a919fSbluhm if (tdelroute == NULL)
4780d0a919fSbluhm err(1, "tdelroute");
4790d0a919fSbluhm for (n = 0; n < delroute_num; n++) {
4800d0a919fSbluhm errno = pthread_create(&tdelroute[n], NULL, thread_delroute,
4810d0a919fSbluhm &run);
4820d0a919fSbluhm if (errno)
4830d0a919fSbluhm err(1, "pthread_create delroute %u", n);
4840d0a919fSbluhm }
4850d0a919fSbluhm
4866dd7abcaSbluhm if (run_time > 0) {
4876dd7abcaSbluhm if (sleep(run_time) < 0)
4886dd7abcaSbluhm err(1, "sleep %u", run_time);
4896dd7abcaSbluhm }
4906dd7abcaSbluhm
4916dd7abcaSbluhm run = 0;
4926dd7abcaSbluhm socket_count = 0;
4936dd7abcaSbluhm for (n = 0; n < socket_num; n++) {
4946dd7abcaSbluhm unsigned long count;
4956dd7abcaSbluhm
4966dd7abcaSbluhm errno = pthread_join(tsocket[n], (void **)&count);
4976dd7abcaSbluhm if (errno)
4986dd7abcaSbluhm err(1, "pthread_join socket %u", n);
4996dd7abcaSbluhm socket_count += count;
5006dd7abcaSbluhm }
5010d0a919fSbluhm free(tsocket);
5020d0a919fSbluhm
5036dd7abcaSbluhm close_count = 0;
5046dd7abcaSbluhm for (n = 0; n < close_num; n++) {
5056dd7abcaSbluhm unsigned long count;
5066dd7abcaSbluhm
5076dd7abcaSbluhm errno = pthread_join(tclose[n], (void **)&count);
5086dd7abcaSbluhm if (errno)
5096dd7abcaSbluhm err(1, "pthread_join close %u", n);
5106dd7abcaSbluhm close_count += count;
5116dd7abcaSbluhm }
5120d0a919fSbluhm free(tclose);
5130d0a919fSbluhm
5146dd7abcaSbluhm bind_count = 0;
5156dd7abcaSbluhm for (n = 0; n < bind_num; n++) {
5166dd7abcaSbluhm unsigned long count;
5176dd7abcaSbluhm
5186dd7abcaSbluhm errno = pthread_join(tbind[n], (void **)&count);
5196dd7abcaSbluhm if (errno)
5206dd7abcaSbluhm err(1, "pthread_join bind %u", n);
5216dd7abcaSbluhm bind_count += count;
5226dd7abcaSbluhm }
5230d0a919fSbluhm free(tbind);
5240d0a919fSbluhm
5256dd7abcaSbluhm connect_count = 0;
5266dd7abcaSbluhm for (n = 0; n < connect_num; n++) {
5276dd7abcaSbluhm unsigned long count;
5286dd7abcaSbluhm
5296dd7abcaSbluhm errno = pthread_join(tconnect[n], (void **)&count);
5306dd7abcaSbluhm if (errno)
5316dd7abcaSbluhm err(1, "pthread_join connect %u", n);
5326dd7abcaSbluhm connect_count += count;
5336dd7abcaSbluhm }
5340d0a919fSbluhm free(tconnect);
5350d0a919fSbluhm
5360d0a919fSbluhm delroute_count = 0;
5370d0a919fSbluhm for (n = 0; n < delroute_num; n++) {
5380d0a919fSbluhm unsigned long count;
5390d0a919fSbluhm
5400d0a919fSbluhm errno = pthread_join(tdelroute[n], (void **)&count);
5410d0a919fSbluhm if (errno)
5420d0a919fSbluhm err(1, "pthread_join delroute %u", n);
5430d0a919fSbluhm delroute_count += count;
5440d0a919fSbluhm }
5450d0a919fSbluhm free(tdelroute);
5460d0a919fSbluhm
5470d0a919fSbluhm printf("count: socket %lu, close %lu, bind %lu, connect %lu, "
5480d0a919fSbluhm "delroute %lu\n",
5490d0a919fSbluhm socket_count, close_count, bind_count, connect_count,
5500d0a919fSbluhm delroute_count);
5516dd7abcaSbluhm
5526dd7abcaSbluhm return 0;
5536dd7abcaSbluhm }
554