133657a95SSepherosa Ziehau #include <sys/types.h>
233657a95SSepherosa Ziehau #include <sys/event.h>
3dbaf089dSSepherosa Ziehau #include <sys/ioctl.h>
433657a95SSepherosa Ziehau #include <sys/mman.h>
533657a95SSepherosa Ziehau #include <sys/socket.h>
6a74edad3SSepherosa Ziehau #include <sys/sysctl.h>
733657a95SSepherosa Ziehau #include <sys/time.h>
89a7343b2SSepherosa Ziehau #include <sys/usched.h>
933657a95SSepherosa Ziehau #include <sys/wait.h>
1033657a95SSepherosa Ziehau 
1133657a95SSepherosa Ziehau #include <arpa/inet.h>
1233657a95SSepherosa Ziehau #include <netinet/in.h>
1333657a95SSepherosa Ziehau 
149aebb1f1SSepherosa Ziehau #include <err.h>
1533657a95SSepherosa Ziehau #include <errno.h>
1633657a95SSepherosa Ziehau #include <signal.h>
1733657a95SSepherosa Ziehau #include <stdio.h>
1833657a95SSepherosa Ziehau #include <stdlib.h>
1933657a95SSepherosa Ziehau #include <string.h>
2033657a95SSepherosa Ziehau #include <unistd.h>
2133657a95SSepherosa Ziehau 
22626b6155SSepherosa Ziehau static void	mainloop(const struct sockaddr_in *, int, int, long, u_long *,
239a7343b2SSepherosa Ziehau 		    int, int);
249a7343b2SSepherosa Ziehau 
259a7343b2SSepherosa Ziehau static int	bindcpu;
269a7343b2SSepherosa Ziehau static int	cpucnt;
2733657a95SSepherosa Ziehau 
2833657a95SSepherosa Ziehau static void
usage(const char * cmd)2933657a95SSepherosa Ziehau usage(const char *cmd)
3033657a95SSepherosa Ziehau {
3133657a95SSepherosa Ziehau 	fprintf(stderr, "%s -4 inet4 [-4 inet4_1] -p port "
329a7343b2SSepherosa Ziehau 	    "[-i n_instance] [-c conn_max] [-l duration] [-u] [-B]\n", cmd);
3333657a95SSepherosa Ziehau 	exit(1);
3433657a95SSepherosa Ziehau }
3533657a95SSepherosa Ziehau 
3633657a95SSepherosa Ziehau int
main(int argc,char * argv[])3733657a95SSepherosa Ziehau main(int argc, char *argv[])
3833657a95SSepherosa Ziehau {
3933657a95SSepherosa Ziehau 	struct sockaddr_in *in, *tmp;
40626b6155SSepherosa Ziehau 	int opt, ninst, nconn, i, in_max, in_cnt, do_udp;
4133657a95SSepherosa Ziehau 	long dur;
4233657a95SSepherosa Ziehau 	u_long *result, sum;
4333657a95SSepherosa Ziehau 	u_short port;
44a74edad3SSepherosa Ziehau 	size_t prm_len;
4533657a95SSepherosa Ziehau 
469a7343b2SSepherosa Ziehau 	prm_len = sizeof(cpucnt);
479a7343b2SSepherosa Ziehau 	if (sysctlbyname("hw.ncpu", &cpucnt, &prm_len, NULL, 0) != 0)
489aebb1f1SSepherosa Ziehau 		err(2, "sysctl hw.ncpu failed");
499a7343b2SSepherosa Ziehau 	ninst = cpucnt;
50a74edad3SSepherosa Ziehau 
5133657a95SSepherosa Ziehau 	nconn = 8;
5233657a95SSepherosa Ziehau 	dur = 10;
5333657a95SSepherosa Ziehau 	port = 0;
54626b6155SSepherosa Ziehau 	do_udp = 0;
5533657a95SSepherosa Ziehau 
5633657a95SSepherosa Ziehau 	in_max = 8;
5733657a95SSepherosa Ziehau 	in = calloc(in_max, sizeof(struct sockaddr_in));
589aebb1f1SSepherosa Ziehau 	if (in == NULL)
599aebb1f1SSepherosa Ziehau 		err(1, "calloc failed");
6033657a95SSepherosa Ziehau 	in_cnt = 0;
6133657a95SSepherosa Ziehau 
629a7343b2SSepherosa Ziehau 	while ((opt = getopt(argc, argv, "4:Bc:i:l:p:u")) != -1) {
6333657a95SSepherosa Ziehau 		switch (opt) {
6433657a95SSepherosa Ziehau 		case '4':
6533657a95SSepherosa Ziehau 			if (in_cnt >= in_max) {
6633657a95SSepherosa Ziehau 				struct sockaddr_in *old_in = in;
6733657a95SSepherosa Ziehau 				int old_in_max = in_max;
6833657a95SSepherosa Ziehau 
6933657a95SSepherosa Ziehau 				in_max <<= 1;
7033657a95SSepherosa Ziehau 				in = calloc(in_max, sizeof(struct sockaddr_in));
719aebb1f1SSepherosa Ziehau 				if (in == NULL)
729aebb1f1SSepherosa Ziehau 					err(1, "calloc failed");
7333657a95SSepherosa Ziehau 
7433657a95SSepherosa Ziehau 				memcpy(in, old_in,
7533657a95SSepherosa Ziehau 				    old_in_max * sizeof(struct sockaddr_in));
7633657a95SSepherosa Ziehau 				free(old_in);
7733657a95SSepherosa Ziehau 			}
7833657a95SSepherosa Ziehau 
7933657a95SSepherosa Ziehau 			tmp = &in[in_cnt];
8033657a95SSepherosa Ziehau 			if (inet_pton(AF_INET, optarg, &tmp->sin_addr) <= 0) {
8133657a95SSepherosa Ziehau 				fprintf(stderr, "invalid inet address %s\n",
8233657a95SSepherosa Ziehau 				    optarg);
8333657a95SSepherosa Ziehau 				usage(argv[0]);
8433657a95SSepherosa Ziehau 			}
8533657a95SSepherosa Ziehau 			++in_cnt;
8633657a95SSepherosa Ziehau 			break;
8733657a95SSepherosa Ziehau 
889a7343b2SSepherosa Ziehau 		case 'B':
899a7343b2SSepherosa Ziehau 			bindcpu = 1;
9033657a95SSepherosa Ziehau 			break;
9133657a95SSepherosa Ziehau 
9233657a95SSepherosa Ziehau 		case 'c':
9333657a95SSepherosa Ziehau 			nconn = atoi(optarg);
9433657a95SSepherosa Ziehau 			break;
9533657a95SSepherosa Ziehau 
969a7343b2SSepherosa Ziehau 		case 'i':
979a7343b2SSepherosa Ziehau 			ninst = atoi(optarg);
989a7343b2SSepherosa Ziehau 			break;
999a7343b2SSepherosa Ziehau 
10033657a95SSepherosa Ziehau 		case 'l':
10133657a95SSepherosa Ziehau 			dur = strtol(optarg, NULL, 10);
10233657a95SSepherosa Ziehau 			break;
10333657a95SSepherosa Ziehau 
1049a7343b2SSepherosa Ziehau 		case 'p':
1059a7343b2SSepherosa Ziehau 			port = htons(atoi(optarg));
1069a7343b2SSepherosa Ziehau 			break;
1079a7343b2SSepherosa Ziehau 
108626b6155SSepherosa Ziehau 		case 'u':
109626b6155SSepherosa Ziehau 			do_udp = 1;
110626b6155SSepherosa Ziehau 			break;
111626b6155SSepherosa Ziehau 
11233657a95SSepherosa Ziehau 		default:
11333657a95SSepherosa Ziehau 			usage(argv[0]);
11433657a95SSepherosa Ziehau 		}
11533657a95SSepherosa Ziehau 	}
11633657a95SSepherosa Ziehau 
11733657a95SSepherosa Ziehau 	if (ninst < 1 || dur < 1 || nconn < 1 || port == 0 || in_cnt == 0)
11833657a95SSepherosa Ziehau 		usage(argv[0]);
11933657a95SSepherosa Ziehau 
12033657a95SSepherosa Ziehau 	for (i = 0; i < in_cnt; ++i) {
12133657a95SSepherosa Ziehau 		tmp = &in[i];
12233657a95SSepherosa Ziehau 		tmp->sin_family = AF_INET;
12333657a95SSepherosa Ziehau 		tmp->sin_port = port;
12433657a95SSepherosa Ziehau 	}
12533657a95SSepherosa Ziehau 
12633657a95SSepherosa Ziehau 	result = mmap(NULL, ninst * sizeof(u_long), PROT_READ | PROT_WRITE,
12733657a95SSepherosa Ziehau 	    MAP_ANON | MAP_SHARED, -1, 0);
1289aebb1f1SSepherosa Ziehau 	if (result == MAP_FAILED)
1299aebb1f1SSepherosa Ziehau 		err(1, "mmap failed");
13033657a95SSepherosa Ziehau 	memset(result, 0, ninst * sizeof(u_long));
13133657a95SSepherosa Ziehau 
13233657a95SSepherosa Ziehau 	for (i = 0; i < ninst; ++i) {
13333657a95SSepherosa Ziehau 		pid_t pid;
13433657a95SSepherosa Ziehau 
13533657a95SSepherosa Ziehau 		pid = fork();
13633657a95SSepherosa Ziehau 		if (pid == 0) {
1379a7343b2SSepherosa Ziehau 			mainloop(in, in_cnt, nconn, dur, &result[i], do_udp, i);
13833657a95SSepherosa Ziehau 			exit(0);
13933657a95SSepherosa Ziehau 		} else if (pid < 0) {
1409aebb1f1SSepherosa Ziehau 			err(1, "fork failed");
14133657a95SSepherosa Ziehau 		}
14233657a95SSepherosa Ziehau 	}
14333657a95SSepherosa Ziehau 
14433657a95SSepherosa Ziehau 	for (i = 0; i < ninst; ++i) {
14533657a95SSepherosa Ziehau 		pid_t pid;
14633657a95SSepherosa Ziehau 
14733657a95SSepherosa Ziehau 		pid = waitpid(-1, NULL, 0);
1489aebb1f1SSepherosa Ziehau 		if (pid < 0)
1499aebb1f1SSepherosa Ziehau 			err(1, "waitpid failed");
15033657a95SSepherosa Ziehau 	}
15133657a95SSepherosa Ziehau 
15233657a95SSepherosa Ziehau 	sum = 0;
15333657a95SSepherosa Ziehau 	for (i = 0; i < ninst; ++i)
15433657a95SSepherosa Ziehau 		sum += result[i];
155141605edSSepherosa Ziehau 	printf("%lu\n", sum);
15633657a95SSepherosa Ziehau 
15733657a95SSepherosa Ziehau 	exit(0);
15833657a95SSepherosa Ziehau }
15933657a95SSepherosa Ziehau 
16033657a95SSepherosa Ziehau static void
udp_send(const struct sockaddr_in * in)161626b6155SSepherosa Ziehau udp_send(const struct sockaddr_in *in)
162626b6155SSepherosa Ziehau {
163626b6155SSepherosa Ziehau 	uint8_t d[18];
164626b6155SSepherosa Ziehau 	int s;
165626b6155SSepherosa Ziehau 
166626b6155SSepherosa Ziehau 	s = socket(AF_INET, SOCK_DGRAM, 0);
167626b6155SSepherosa Ziehau 	if (s < 0) {
1689aebb1f1SSepherosa Ziehau 		warn("socket DGRAM failed");
169626b6155SSepherosa Ziehau 		return;
170626b6155SSepherosa Ziehau 	}
171626b6155SSepherosa Ziehau 
172626b6155SSepherosa Ziehau 	if (connect(s, (const struct sockaddr *)in, sizeof(*in)) < 0) {
1739aebb1f1SSepherosa Ziehau 		warn("connect DGRAM failed");
174626b6155SSepherosa Ziehau 		goto done;
175626b6155SSepherosa Ziehau 	}
176626b6155SSepherosa Ziehau 
177626b6155SSepherosa Ziehau 	write(s, d, sizeof(d));
178626b6155SSepherosa Ziehau done:
179626b6155SSepherosa Ziehau 	close(s);
180626b6155SSepherosa Ziehau }
181626b6155SSepherosa Ziehau 
182626b6155SSepherosa Ziehau static void
mainloop(const struct sockaddr_in * in,int in_cnt,int nconn_max,long dur,u_long * res,int do_udp,int inst)18333657a95SSepherosa Ziehau mainloop(const struct sockaddr_in *in, int in_cnt, int nconn_max,
1849a7343b2SSepherosa Ziehau     long dur, u_long *res, int do_udp, int inst)
18533657a95SSepherosa Ziehau {
186141605edSSepherosa Ziehau 	struct timespec start, end;
18733657a95SSepherosa Ziehau 	struct kevent *evt_change0, *evt;
18833657a95SSepherosa Ziehau 	int kq, nchange = 0, nconn = 0, nevt_max;
18933657a95SSepherosa Ziehau 	u_long count = 0;
190141605edSSepherosa Ziehau 	double time_us;
19133657a95SSepherosa Ziehau 	u_int in_idx = 0;
192bb610beaSSepherosa Ziehau #ifndef SOCK_NONBLOCK
193dbaf089dSSepherosa Ziehau 	int nblock = 1;
194bb610beaSSepherosa Ziehau #endif
19533657a95SSepherosa Ziehau 
1969a7343b2SSepherosa Ziehau 	if (bindcpu) {
1979a7343b2SSepherosa Ziehau 		int cpu = inst % cpucnt;
1989a7343b2SSepherosa Ziehau 
1999a7343b2SSepherosa Ziehau 		usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu));
2009a7343b2SSepherosa Ziehau 	}
2019a7343b2SSepherosa Ziehau 
20233657a95SSepherosa Ziehau 	kq = kqueue();
2039aebb1f1SSepherosa Ziehau 	if (kq < 0)
2049aebb1f1SSepherosa Ziehau 		err(1, "kqueue failed");
20533657a95SSepherosa Ziehau 
20633657a95SSepherosa Ziehau 	nevt_max = nconn_max + 1; /* timer */
20733657a95SSepherosa Ziehau 
20833657a95SSepherosa Ziehau 	evt_change0 = malloc(nevt_max * sizeof(struct kevent));
2099aebb1f1SSepherosa Ziehau 	if (evt_change0 == NULL)
2109aebb1f1SSepherosa Ziehau 		err(1, "malloc evt_change failed");
21133657a95SSepherosa Ziehau 
21233657a95SSepherosa Ziehau 	evt = malloc(nevt_max * sizeof(struct kevent));
2139aebb1f1SSepherosa Ziehau 	if (evt == NULL)
2149aebb1f1SSepherosa Ziehau 		err(1, "malloc evt failed");
21533657a95SSepherosa Ziehau 
21633657a95SSepherosa Ziehau 	EV_SET(&evt_change0[0], 0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0,
21733657a95SSepherosa Ziehau 	    dur * 1000L, NULL);
21833657a95SSepherosa Ziehau 	nchange = 1;
21933657a95SSepherosa Ziehau 
220141605edSSepherosa Ziehau 	clock_gettime(CLOCK_MONOTONIC_PRECISE, &start);
22133657a95SSepherosa Ziehau 	for (;;) {
22233657a95SSepherosa Ziehau 		struct kevent *evt_change = NULL;
22333657a95SSepherosa Ziehau 		int n, i, done = 0;
22433657a95SSepherosa Ziehau 
22533657a95SSepherosa Ziehau 		while (nconn < nconn_max) {
22633657a95SSepherosa Ziehau 			const struct sockaddr_in *tmp;
227dbaf089dSSepherosa Ziehau 			int s;
22833657a95SSepherosa Ziehau 
229626b6155SSepherosa Ziehau 			tmp = &in[in_idx % in_cnt];
230626b6155SSepherosa Ziehau 			++in_idx;
231626b6155SSepherosa Ziehau 
232626b6155SSepherosa Ziehau 			if (do_udp)
233626b6155SSepherosa Ziehau 				udp_send(tmp);
234626b6155SSepherosa Ziehau 
235bb610beaSSepherosa Ziehau #ifndef SOCK_NONBLOCK
23633657a95SSepherosa Ziehau 			s = socket(AF_INET, SOCK_STREAM, 0);
2379aebb1f1SSepherosa Ziehau 			if (s < 0)
2389aebb1f1SSepherosa Ziehau 				err(1, "socket failed");
23933657a95SSepherosa Ziehau 
2409aebb1f1SSepherosa Ziehau 			if (ioctl(s, FIONBIO, &nblock, sizeof(nblock)) < 0)
2419aebb1f1SSepherosa Ziehau 				err(1, "ioctl failed");
242bb610beaSSepherosa Ziehau #else
243bb610beaSSepherosa Ziehau 			s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
244bb610beaSSepherosa Ziehau 			if (s < 0)
245bb610beaSSepherosa Ziehau 				err(1, "socket failed");
246bb610beaSSepherosa Ziehau #endif
24733657a95SSepherosa Ziehau 
24833657a95SSepherosa Ziehau 			n = connect(s, (const struct sockaddr *)tmp,
24933657a95SSepherosa Ziehau 			    sizeof(*tmp));
25033657a95SSepherosa Ziehau 			if (n == 0) {
25133657a95SSepherosa Ziehau 				++count;
25233657a95SSepherosa Ziehau 				close(s);
25333657a95SSepherosa Ziehau 				continue;
25433657a95SSepherosa Ziehau 			} else {
25533657a95SSepherosa Ziehau 			    	int error = errno;
25633657a95SSepherosa Ziehau 
2579aebb1f1SSepherosa Ziehau 				if (error != EINPROGRESS)
2589aebb1f1SSepherosa Ziehau 					errc(1, error, "connect failed");
25933657a95SSepherosa Ziehau 			}
26033657a95SSepherosa Ziehau 			++nconn;
26133657a95SSepherosa Ziehau 
26233657a95SSepherosa Ziehau 			if (nchange >= nevt_max) {
26333657a95SSepherosa Ziehau 				fprintf(stderr, "invalid nchange %d, max %d\n",
26433657a95SSepherosa Ziehau 				    nchange, nevt_max);
26533657a95SSepherosa Ziehau 				abort();
26633657a95SSepherosa Ziehau 			}
26733657a95SSepherosa Ziehau 			EV_SET(&evt_change0[nchange], s, EVFILT_WRITE, EV_ADD,
26833657a95SSepherosa Ziehau 			    0, 0, NULL);
26933657a95SSepherosa Ziehau 			++nchange;
27033657a95SSepherosa Ziehau 		}
27133657a95SSepherosa Ziehau 
27233657a95SSepherosa Ziehau 		if (nchange)
27333657a95SSepherosa Ziehau 			evt_change = evt_change0;
27433657a95SSepherosa Ziehau 
27533657a95SSepherosa Ziehau 		n = kevent(kq, evt_change, nchange, evt, nevt_max, NULL);
2769aebb1f1SSepherosa Ziehau 		if (n < 0)
2779aebb1f1SSepherosa Ziehau 			err(1, "kevent failed");
27833657a95SSepherosa Ziehau 		nchange = 0;
27933657a95SSepherosa Ziehau 
28033657a95SSepherosa Ziehau 		for (i = 0; i < n; ++i) {
28133657a95SSepherosa Ziehau 			struct kevent *e = &evt[i];
28233657a95SSepherosa Ziehau 
28333657a95SSepherosa Ziehau 			if (e->filter == EVFILT_TIMER) {
28433657a95SSepherosa Ziehau 				done = 1;
28533657a95SSepherosa Ziehau 				continue;
28633657a95SSepherosa Ziehau 			}
28733657a95SSepherosa Ziehau 
28833657a95SSepherosa Ziehau 			if ((e->flags & EV_EOF) && e->fflags) {
28933657a95SSepherosa Ziehau 				/* Error, don't count */
29033657a95SSepherosa Ziehau 			} else {
29133657a95SSepherosa Ziehau 				++count;
29233657a95SSepherosa Ziehau 			}
29333657a95SSepherosa Ziehau 			close(e->ident);
29433657a95SSepherosa Ziehau 			--nconn;
29533657a95SSepherosa Ziehau 		}
29633657a95SSepherosa Ziehau 		if (done)
29733657a95SSepherosa Ziehau 			break;
29833657a95SSepherosa Ziehau 	}
299141605edSSepherosa Ziehau 	clock_gettime(CLOCK_MONOTONIC_PRECISE, &end);
300141605edSSepherosa Ziehau 
301*944cd60cSSascha Wildner 	timespecsub(&end, &start, &end);
302141605edSSepherosa Ziehau 	time_us = ((double)end.tv_sec * 1000000.0) +
303141605edSSepherosa Ziehau 	    ((double)end.tv_nsec / 1000.0);
304141605edSSepherosa Ziehau 
305141605edSSepherosa Ziehau 	*res = ((double)count * 1000000.0) / time_us;
30633657a95SSepherosa Ziehau }
307