Annotation of src/usr.bin/tcpbench/tcpbench.c, Revision 1.10
1.1 djm 1: /*
2: * Copyright (c) 2008 Damien Miller <djm@mindrot.org>
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include <sys/types.h>
18: #include <sys/time.h>
19: #include <sys/socket.h>
20: #include <sys/socketvar.h>
21:
22: #include <net/route.h>
23:
24: #include <netinet/in.h>
25: #include <netinet/in_systm.h>
26: #include <netinet/ip.h>
27: #include <netinet/tcp.h>
28: #include <netinet/tcp_timer.h>
29: #include <netinet/tcp_fsm.h>
30: #include <netinet/in_pcb.h>
31: #include <netinet/tcp_var.h>
32:
33: #include <arpa/inet.h>
34:
35: #include <unistd.h>
36: #include <limits.h>
37: #include <stdlib.h>
38: #include <stdio.h>
39: #include <string.h>
40: #include <errno.h>
41: #include <netdb.h>
42: #include <signal.h>
43: #include <err.h>
44: #include <fcntl.h>
45: #include <poll.h>
46:
47: #include <kvm.h>
48: #include <nlist.h>
49:
50: #define DEFAULT_PORT "12345"
51: #define DEFAULT_STATS_INTERVAL 1000 /* ms */
52: #define DEFAULT_BUF 256 * 1024
53:
54: sig_atomic_t done = 0;
55: sig_atomic_t print_stats = 0;
56:
1.9 claudio 57: u_int rdomain;
58:
1.1 djm 59: struct statctx {
60: struct timeval t_start, t_last, t_cur;
61: unsigned long long bytes;
62: pid_t pid;
63: u_long tcbaddr;
64: kvm_t *kh;
65: char **kvars;
66: };
67:
68: /* When adding variables, also add to stats_display() */
69: static const char *allowed_kvars[] = {
70: "inpcb.inp_flags",
71: "sockb.so_rcv.sb_cc",
72: "sockb.so_rcv.sb_hiwat",
73: "sockb.so_snd.sb_cc",
74: "sockb.so_snd.sb_hiwat",
75: "tcpcb.snd_una",
76: "tcpcb.snd_nxt",
77: "tcpcb.snd_wl1",
78: "tcpcb.snd_wl2",
79: "tcpcb.snd_wnd",
80: "tcpcb.rcv_wnd",
81: "tcpcb.rcv_nxt",
82: "tcpcb.rcv_adv",
83: "tcpcb.snd_max",
84: "tcpcb.snd_cwnd",
85: "tcpcb.snd_ssthresh",
86: "tcpcb.t_rcvtime",
87: "tcpcb.t_rtttime",
88: "tcpcb.t_rtseq",
89: "tcpcb.t_srtt",
90: "tcpcb.t_rttvar",
91: "tcpcb.t_rttmin",
92: "tcpcb.max_sndwnd",
93: "tcpcb.snd_scale",
94: "tcpcb.rcv_scale",
95: "tcpcb.last_ack_sent",
96: NULL
97: };
98:
99: static void
100: exitsighand(int signo)
101: {
102: done = signo;
103: }
104:
105: static void
106: alarmhandler(int signo)
107: {
108: print_stats = 1;
109: signal(signo, alarmhandler);
110: }
111:
112: static void __dead
113: usage(void)
114: {
115: fprintf(stderr,
1.6 jmc 116: "usage: tcpbench -l\n"
117: " tcpbench [-v] [-B buf] [-k kvars] [-n connections]"
1.9 claudio 118: " [-p port] [-r rate] [-V rdomain]\n"
1.6 jmc 119: " [-S space] hostname\n"
120: " tcpbench -s [-v] [-B buf] [-k kvars] [-p port] [-r rate]"
1.9 claudio 121: " [-S space] [-V rdomain]\n");
1.1 djm 122: exit(1);
123: }
124:
125: static void
126: saddr_ntop(const struct sockaddr *addr, socklen_t alen, char *buf, size_t len)
127: {
128: char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
129: int herr;
130:
1.3 djm 131: if ((herr = getnameinfo(addr, alen, hbuf, sizeof(hbuf),
132: pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
1.1 djm 133: if (herr == EAI_SYSTEM)
134: err(1, "getnameinfo");
135: else
136: errx(1, "getnameinfo: %s", gai_strerror(herr));
137: }
138: snprintf(buf, len, "[%s]:%s", hbuf, pbuf);
139: }
140:
141: static void
142: kget(kvm_t *kh, u_long addr, void *buf, int size)
143: {
144: if (kvm_read(kh, addr, buf, size) != size)
145: errx(1, "kvm_read: %s", kvm_geterr(kh));
146: }
147:
148: static u_long
149: kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock, int vflag)
150: {
151: struct inpcbtable tcbtab;
152: struct inpcb *head, *next, *prev;
153: struct inpcb inpcb;
154: struct tcpcb tcpcb;
155:
156: struct sockaddr_storage me, them;
157: socklen_t melen, themlen;
158: struct sockaddr_in *in4;
159: struct sockaddr_in6 *in6;
160: char tmp1[64], tmp2[64];
161:
162: melen = themlen = sizeof(struct sockaddr_storage);
163: if (getsockname(sock, (struct sockaddr *)&me, &melen) == -1)
164: err(1, "getsockname");
165: if (getpeername(sock, (struct sockaddr *)&them, &themlen) == -1)
166: err(1, "getpeername");
167: if (me.ss_family != them.ss_family)
168: errx(1, "%s: me.ss_family != them.ss_family", __func__);
169: if (me.ss_family != AF_INET && me.ss_family != AF_INET6)
170: errx(1, "%s: unknown socket family", __func__);
171: if (vflag >= 2) {
172: saddr_ntop((struct sockaddr *)&me, me.ss_len,
173: tmp1, sizeof(tmp1));
174: saddr_ntop((struct sockaddr *)&them, them.ss_len,
175: tmp2, sizeof(tmp2));
176: fprintf(stderr, "Our socket local %s remote %s\n", tmp1, tmp2);
177: }
178: if (vflag >= 2)
179: fprintf(stderr, "Using PCB table at %lu\n", ktcbtab);
180:
181: kget(kh, ktcbtab, &tcbtab, sizeof(tcbtab));
182: prev = head = (struct inpcb *)&CIRCLEQ_FIRST(
183: &((struct inpcbtable *)ktcbtab)->inpt_queue);
184: next = CIRCLEQ_FIRST(&tcbtab.inpt_queue);
185:
186: if (vflag >= 2)
187: fprintf(stderr, "PCB head at %p\n", head);
188: while (next != head) {
189: if (vflag >= 2)
190: fprintf(stderr, "Checking PCB %p\n", next);
191: kget(kh, (u_long)next, &inpcb, sizeof(inpcb));
192: if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev)
193: errx(1, "pcb prev pointer insane");
194: prev = next;
195: next = CIRCLEQ_NEXT(&inpcb, inp_queue);
196:
197: if (me.ss_family == AF_INET) {
198: if ((inpcb.inp_flags & INP_IPV6) != 0) {
199: if (vflag >= 2)
200: fprintf(stderr, "Skip: INP_IPV6");
201: continue;
202: }
203: if (vflag >= 2) {
204: inet_ntop(AF_INET, &inpcb.inp_laddr,
205: tmp1, sizeof(tmp1));
206: inet_ntop(AF_INET, &inpcb.inp_faddr,
207: tmp2, sizeof(tmp2));
208: fprintf(stderr, "PCB %p local: [%s]:%d "
209: "remote: [%s]:%d\n", prev,
210: tmp1, inpcb.inp_lport,
211: tmp2, inpcb.inp_fport);
212: }
213: in4 = (struct sockaddr_in *)&me;
214: if (memcmp(&in4->sin_addr, &inpcb.inp_laddr,
215: sizeof(struct in_addr)) != 0 ||
216: in4->sin_port != inpcb.inp_lport)
217: continue;
218: in4 = (struct sockaddr_in *)&them;
219: if (memcmp(&in4->sin_addr, &inpcb.inp_faddr,
220: sizeof(struct in_addr)) != 0 ||
221: in4->sin_port != inpcb.inp_fport)
222: continue;
223: } else {
224: if ((inpcb.inp_flags & INP_IPV6) == 0)
225: continue;
226: if (vflag >= 2) {
227: inet_ntop(AF_INET6, &inpcb.inp_laddr6,
228: tmp1, sizeof(tmp1));
229: inet_ntop(AF_INET6, &inpcb.inp_faddr6,
230: tmp2, sizeof(tmp2));
231: fprintf(stderr, "PCB %p local: [%s]:%d "
232: "remote: [%s]:%d\n", prev,
233: tmp1, inpcb.inp_lport,
234: tmp2, inpcb.inp_fport);
235: }
236: in6 = (struct sockaddr_in6 *)&me;
237: if (memcmp(&in6->sin6_addr, &inpcb.inp_laddr6,
238: sizeof(struct in6_addr)) != 0 ||
239: in6->sin6_port != inpcb.inp_lport)
240: continue;
241: in6 = (struct sockaddr_in6 *)&them;
242: if (memcmp(&in6->sin6_addr, &inpcb.inp_faddr6,
243: sizeof(struct in6_addr)) != 0 ||
244: in6->sin6_port != inpcb.inp_fport)
245: continue;
246: }
247: kget(kh, (u_long)inpcb.inp_ppcb, &tcpcb, sizeof(tcpcb));
248: if (tcpcb.t_state != TCPS_ESTABLISHED) {
249: if (vflag >= 2)
250: fprintf(stderr, "Not established\n");
251: continue;
252: }
253: if (vflag >= 2)
254: fprintf(stderr, "Found PCB at %p\n", prev);
255: return (u_long)prev;
256: }
257:
258: errx(1, "No matching PCB found");
259: }
260:
261: static void
262: kupdate_stats(kvm_t *kh, u_long tcbaddr,
263: struct inpcb *inpcb, struct tcpcb *tcpcb, struct socket *sockb)
264: {
265: kget(kh, tcbaddr, inpcb, sizeof(*inpcb));
266: kget(kh, (u_long)inpcb->inp_ppcb, tcpcb, sizeof(*tcpcb));
267: kget(kh, (u_long)inpcb->inp_socket, sockb, sizeof(*sockb));
268: }
269:
270: static void
271: check_kvar(const char *var)
272: {
273: size_t i;
274:
275: for (i = 0; allowed_kvars[i] != NULL; i++)
276: if (strcmp(allowed_kvars[i], var) == 0)
277: return;
278: errx(1, "Unrecognised kvar: %s", var);
279: }
280:
281: static void
282: list_kvars(void)
283: {
284: size_t i;
285:
286: fprintf(stderr, "Supported kernel variables:\n");
287: for (i = 0; allowed_kvars[i] != NULL; i++)
288: fprintf(stderr, "\t%s\n", allowed_kvars[i]);
289: }
290:
291: static char **
292: check_prepare_kvars(char *list)
293: {
294: char *item, **ret = NULL;
295: size_t n = 0;
296:
297: while ((item = strsep(&list, ", \t\n")) != NULL) {
298: check_kvar(item);
299: if ((ret = realloc(ret, sizeof(*ret) * (++n + 1))) == NULL)
300: errx(1, "realloc(kvars)");
301: if ((ret[n - 1] = strdup(item)) == NULL)
302: errx(1, "strdup");
303: ret[n] = NULL;
304: }
305: return ret;
306: }
307:
308: static void
309: stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab,
310: int rflag, int vflag, char **kflag)
311: {
312: struct itimerval itv;
313: int i;
314:
315: if (rflag <= 0)
316: return;
317: sc->kh = kh;
318: sc->kvars = kflag;
319: if (kflag)
320: sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd, vflag);
321: gettimeofday(&sc->t_start, NULL);
322: sc->t_last = sc->t_start;
323: signal(SIGALRM, alarmhandler);
324: itv.it_interval.tv_sec = rflag / 1000;
325: itv.it_interval.tv_usec = (rflag % 1000) * 1000;
326: itv.it_value = itv.it_interval;
327: setitimer(ITIMER_REAL, &itv, NULL);
328: sc->bytes = 0;
329: sc->pid = getpid();
330:
331: printf("%8s %12s %14s %12s ", "pid", "elapsed_ms", "bytes", "Mbps");
332: if (sc->kvars != NULL) {
333: for (i = 0; sc->kvars[i] != NULL; i++)
334: printf("%s%s", i > 0 ? "," : "", sc->kvars[i]);
335: }
336: printf("\n");
337: fflush(stdout);
338: }
339:
340: static void
341: stats_update(struct statctx *sc, ssize_t n)
342: {
343: sc->bytes += n;
344: }
345:
346: static void
347: stats_display(struct statctx *sc)
348: {
349: struct timeval t_diff;
350: unsigned long long total_elapsed, since_last;
351: size_t i;
352: struct inpcb inpcb;
353: struct tcpcb tcpcb;
354: struct socket sockb;
355:
356: gettimeofday(&sc->t_cur, NULL);
357: timersub(&sc->t_cur, &sc->t_start, &t_diff);
358: total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
359: timersub(&sc->t_cur, &sc->t_last, &t_diff);
360: since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
361: printf("%8ld %12llu %14llu %12.3Lf ", (long)sc->pid,
362: total_elapsed, sc->bytes,
363: (long double)(sc->bytes * 8) / (since_last * 1000.0));
364: sc->t_last = sc->t_cur;
365: sc->bytes = 0;
366:
367: if (sc->kvars != NULL) {
368: kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb, &sockb);
369: for (i = 0; sc->kvars[i] != NULL; i++) {
370: #define P(v, f) \
371: if (strcmp(sc->kvars[i], #v) == 0) { \
372: printf("%s"f, i > 0 ? "," : "", v); \
373: continue; \
374: }
375: P(inpcb.inp_flags, "0x%08x")
376: P(sockb.so_rcv.sb_cc, "%lu")
377: P(sockb.so_rcv.sb_hiwat, "%lu")
378: P(sockb.so_snd.sb_cc, "%lu")
379: P(sockb.so_snd.sb_hiwat, "%lu")
380: P(tcpcb.snd_una, "%u")
381: P(tcpcb.snd_nxt, "%u")
382: P(tcpcb.snd_wl1, "%u")
383: P(tcpcb.snd_wl2, "%u")
384: P(tcpcb.snd_wnd, "%lu")
385: P(tcpcb.rcv_wnd, "%lu")
386: P(tcpcb.rcv_nxt, "%u")
387: P(tcpcb.rcv_adv, "%u")
388: P(tcpcb.snd_max, "%u")
389: P(tcpcb.snd_cwnd, "%lu")
390: P(tcpcb.snd_ssthresh, "%lu")
391: P(tcpcb.t_rcvtime, "%u")
392: P(tcpcb.t_rtttime, "%u")
393: P(tcpcb.t_rtseq, "%u")
394: P(tcpcb.t_srtt, "%hu")
395: P(tcpcb.t_rttvar, "%hu")
396: P(tcpcb.t_rttmin, "%hu")
397: P(tcpcb.max_sndwnd, "%lu")
398: P(tcpcb.snd_scale, "%u")
399: P(tcpcb.rcv_scale, "%u")
400: P(tcpcb.last_ack_sent, "%u")
401: #undef P
402: }
403: }
404: printf("\n");
405: fflush(stdout);
406: }
407:
408: static void
409: stats_finish(struct statctx *sc)
410: {
411: struct itimerval itv;
412:
413: signal(SIGALRM, SIG_DFL);
414: bzero(&itv, sizeof(itv));
415: setitimer(ITIMER_REAL, &itv, NULL);
416: }
417:
418: static void __dead
419: handle_connection(kvm_t *kvmh, u_long ktcbtab, int sock, int vflag,
420: int rflag, char **kflag, int Bflag)
421: {
422: char *buf;
423: struct pollfd pfd;
424: ssize_t n;
425: int r;
426: struct statctx sc;
427:
428: if ((buf = malloc(Bflag)) == NULL)
429: err(1, "malloc");
430: if ((r = fcntl(sock, F_GETFL, 0)) == -1)
431: err(1, "fcntl(F_GETFL)");
432: r |= O_NONBLOCK;
433: if (fcntl(sock, F_SETFL, r) == -1)
434: err(1, "fcntl(F_SETFL, O_NONBLOCK)");
435:
436: signal(SIGINT, exitsighand);
437: signal(SIGTERM, exitsighand);
438: signal(SIGHUP, exitsighand);
439: signal(SIGPIPE, SIG_IGN);
440:
441: bzero(&pfd, sizeof(pfd));
442: pfd.fd = sock;
443: pfd.events = POLLIN;
444:
445: stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);
446:
447: while (!done) {
448: if (print_stats) {
449: stats_display(&sc);
450: print_stats = 0;
451: }
452: if (poll(&pfd, 1, INFTIM) == -1) {
453: if (errno == EINTR)
454: continue;
455: err(1, "poll");
456: }
457: if ((n = read(pfd.fd, buf, Bflag)) == -1) {
458: if (errno == EINTR || errno == EAGAIN)
459: continue;
460: err(1, "read");
461: }
462: if (n == 0) {
463: fprintf(stderr, "%8ld closed by remote end\n",
464: (long)getpid());
465: done = -1;
466: break;
467: }
468: if (vflag >= 3)
469: fprintf(stderr, "read: %zd bytes\n", n);
470: stats_update(&sc, n);
471: }
472: stats_finish(&sc);
473:
474: free(buf);
475: close(sock);
476: exit(1);
477: }
478:
479: static void __dead
480: serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop,
481: int vflag, int rflag, char **kflag, int Sflag, int Bflag)
482: {
483: char tmp[128];
484: int r, sock, client_id, on = 1;
485: struct addrinfo *ai;
486: struct pollfd *pfd;
487: struct sockaddr_storage ss;
488: socklen_t sslen;
489: size_t nfds, i, j;
490:
491: pfd = NULL;
492: nfds = 0;
493: for (ai = aitop; ai != NULL; ai = ai->ai_next) {
494: saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp));
495: if (vflag)
496: fprintf(stderr, "Try listen on %s\n", tmp);
497: if ((sock = socket(ai->ai_family, ai->ai_socktype,
498: ai->ai_protocol)) == -1) {
499: if (ai->ai_next == NULL)
500: err(1, "socket");
501: if (vflag)
502: warn("socket");
503: continue;
504: }
1.9 claudio 505: if (rdomain && ai->ai_family == AF_INET) {
506: if (setsockopt(sock, IPPROTO_IP, SO_RDOMAIN,
507: &rdomain, sizeof(rdomain)) == -1)
508: err(1, "setsockopt SO_RDOMAIN");
1.10 ! claudio 509: } else if (rdomain)
! 510: warnx("rdomain only supported on AF_INET");
1.1 djm 511: if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
512: &on, sizeof(on)) == -1)
513: warn("reuse port");
514: if (bind(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
515: if (ai->ai_next == NULL)
516: err(1, "bind");
517: if (vflag)
518: warn("bind");
519: close(sock);
520: continue;
521: }
522: if (Sflag) {
523: if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
524: &Sflag, sizeof(Sflag)) == -1)
525: warn("set TCP receive buffer size");
526: }
527: if (listen(sock, 64) == -1) {
528: if (ai->ai_next == NULL)
529: err(1, "listen");
530: if (vflag)
531: warn("listen");
532: close(sock);
533: continue;
534: }
535: if (nfds > 128)
536: break;
537: if ((pfd = realloc(pfd, ++nfds * sizeof(*pfd))) == NULL)
538: errx(1, "realloc(pfd * %zu)", nfds);
539: pfd[nfds - 1].fd = sock;
540: pfd[nfds - 1].events = POLLIN;
541: }
542: freeaddrinfo(aitop);
543: if (nfds == 0)
544: errx(1, "No working listen addresses found");
545:
546: signal(SIGINT, exitsighand);
547: signal(SIGTERM, exitsighand);
548: signal(SIGHUP, exitsighand);
549: signal(SIGPIPE, SIG_IGN);
550: signal(SIGCHLD, SIG_IGN);
551:
552: if (setpgid(0, 0) == -1)
553: err(1, "setpgid");
554:
555: client_id = 0;
556: while (!done) {
557: if ((r = poll(pfd, nfds, INFTIM)) == -1) {
558: if (errno == EINTR)
559: continue;
560: warn("poll");
561: break;
562: }
563: if (vflag >= 3)
564: fprintf(stderr, "poll: %d\n", r);
565: for (i = 0 ; r > 0 && i < nfds; i++) {
566: if ((pfd[i].revents & POLLIN) == 0)
567: continue;
568: if (vflag >= 3)
569: fprintf(stderr, "fd %d active\n", pfd[i].fd);
570: r--;
571: sslen = sizeof(ss);
572: if ((sock = accept(pfd[i].fd, (struct sockaddr *)&ss,
573: &sslen)) == -1) {
574: if (errno == EINTR)
575: continue;
576: warn("accept");
577: break;
578: }
579: saddr_ntop((struct sockaddr *)&ss, sslen,
580: tmp, sizeof(tmp));
581: if (vflag)
582: fprintf(stderr, "Accepted connection %d from "
583: "%s, fd = %d\n", client_id++, tmp, sock);
584: switch (fork()) {
585: case -1:
586: warn("fork");
587: done = -1;
588: break;
589: case 0:
590: for (j = 0; j < nfds; j++)
591: if (j != i)
592: close(pfd[j].fd);
593: handle_connection(kvmh, ktcbtab, sock,
594: vflag, rflag, kflag, Bflag);
595: /* NOTREACHED */
596: _exit(1);
597: default:
598: close(sock);
599: break;
600: }
601: if (done == -1)
602: break;
603: }
604: }
605: for (i = 0; i < nfds; i++)
606: close(pfd[i].fd);
607: if (done > 0)
608: warnx("Terminated by signal %d", done);
609: signal(SIGTERM, SIG_IGN);
610: killpg(0, SIGTERM);
611: exit(1);
612: }
613:
614: static void __dead
1.4 henning 615: clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char *port,
616: int vflag, int rflag, char **kflag, int Sflag, int Bflag, int nconn)
1.1 djm 617: {
618: char tmp[128];
619: char *buf;
1.4 henning 620: int r, sock, herr;
621: struct addrinfo *aitop, *ai, hints;
622: struct pollfd *pfd;
1.1 djm 623: ssize_t n;
624: struct statctx sc;
1.5 henning 625: u_int i, scnt = 0;
1.1 djm 626:
627: if ((buf = malloc(Bflag)) == NULL)
628: err(1, "malloc");
1.4 henning 629:
630: if ((pfd = calloc(nconn, sizeof(struct pollfd))) == NULL)
631: err(1, "clientloop pfd calloc");
632:
633: for (i = 0; i < nconn; i++) {
634: bzero(&hints, sizeof(hints));
635: hints.ai_socktype = SOCK_STREAM;
636: hints.ai_flags = 0;
637: if ((herr = getaddrinfo(host, port, &hints, &aitop)) != 0) {
638: if (herr == EAI_SYSTEM)
639: err(1, "getaddrinfo");
640: else
641: errx(1, "c getaddrinfo: %s", gai_strerror(herr));
642: }
643:
644: for (sock = -1, ai = aitop; ai != NULL; ai = ai->ai_next) {
645: saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp,
646: sizeof(tmp));
1.5 henning 647: if (vflag && scnt == 0)
1.4 henning 648: fprintf(stderr, "Trying %s\n", tmp);
649: if ((sock = socket(ai->ai_family, ai->ai_socktype,
650: ai->ai_protocol)) == -1) {
651: if (ai->ai_next == NULL)
652: err(1, "socket");
653: if (vflag)
654: warn("socket");
655: continue;
656: }
1.9 claudio 657: if (rdomain && ai->ai_family == AF_INET) {
658: if (setsockopt(sock, IPPROTO_IP, SO_RDOMAIN,
659: &rdomain, sizeof(rdomain)) == -1)
660: err(1, "setsockopt SO_RDOMAIN");
1.10 ! claudio 661: } else if (rdomain)
! 662: warnx("rdomain only supported on AF_INET");
1.4 henning 663: if (Sflag) {
664: if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
665: &Sflag, sizeof(Sflag)) == -1)
666: warn("set TCP send buffer size");
667: }
668: if (connect(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
669: if (ai->ai_next == NULL)
670: err(1, "connect");
671: if (vflag)
672: warn("connect");
673: close(sock);
674: sock = -1;
675: continue;
676: }
677: break;
1.1 djm 678: }
1.4 henning 679: freeaddrinfo(aitop);
680: if (sock == -1)
681: errx(1, "No host found");
682:
683: if ((r = fcntl(sock, F_GETFL, 0)) == -1)
684: err(1, "fcntl(F_GETFL)");
685: r |= O_NONBLOCK;
686: if (fcntl(sock, F_SETFL, r) == -1)
687: err(1, "fcntl(F_SETFL, O_NONBLOCK)");
688:
689: pfd[i].fd = sock;
690: pfd[i].events = POLLOUT;
1.5 henning 691: scnt++;
1.1 djm 692: }
693:
1.5 henning 694: if (vflag && scnt > 1)
695: fprintf(stderr, "%u connections established\n", scnt);
1.1 djm 696: arc4random_buf(buf, Bflag);
697:
698: signal(SIGINT, exitsighand);
699: signal(SIGTERM, exitsighand);
700: signal(SIGHUP, exitsighand);
701: signal(SIGPIPE, SIG_IGN);
702:
703: stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);
704:
705: while (!done) {
706: if (print_stats) {
707: stats_display(&sc);
708: print_stats = 0;
709: }
1.4 henning 710: if (poll(pfd, nconn, INFTIM) == -1) {
1.1 djm 711: if (errno == EINTR)
712: continue;
713: err(1, "poll");
714: }
1.4 henning 715: for (i = 0; i < nconn; i++) {
1.7 henning 716: if (pfd[i].revents & POLLOUT) {
1.4 henning 717: if ((n = write(pfd[i].fd, buf, Bflag)) == -1) {
718: if (errno == EINTR || errno == EAGAIN)
719: continue;
720: err(1, "write");
721: }
1.7 henning 722: if (n == 0) {
723: warnx("Remote end closed connection");
724: done = -1;
725: break;
726: }
727: if (vflag >= 3)
728: fprintf(stderr, "write: %zd bytes\n",
729: n);
730: stats_update(&sc, n);
1.4 henning 731: }
1.1 djm 732: }
733: }
734: stats_finish(&sc);
735:
736: if (done > 0)
737: warnx("Terminated by signal %d", done);
738:
739: free(buf);
740: close(sock);
741: exit(0);
742: }
743:
744: static void
745: drop_gid(void)
746: {
747: gid_t gid;
748:
749: gid = getgid();
750: if (setresgid(gid, gid, gid) == -1)
751: err(1, "setresgid");
752: }
753:
754: int
755: main(int argc, char **argv)
756: {
757: extern int optind;
758: extern char *optarg;
759:
760: char kerr[_POSIX2_LINE_MAX], *tmp;
761: const char *errstr;
762: int ch, herr;
763: struct addrinfo *aitop, hints;
1.3 djm 764: kvm_t *kvmh = NULL;
1.1 djm 765:
766: const char *host = NULL, *port = DEFAULT_PORT;
767: char **kflag = NULL;
768: int sflag = 0, vflag = 0, rflag = DEFAULT_STATS_INTERVAL, Sflag = 0;
769: int Bflag = DEFAULT_BUF;
1.4 henning 770: int nconn = 1;
1.1 djm 771:
772: struct nlist nl[] = { { "_tcbtable" }, { "" } };
773:
1.9 claudio 774: while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) {
1.1 djm 775: switch (ch) {
776: case 'l':
777: list_kvars();
778: exit(0);
779: case 'k':
780: if ((tmp = strdup(optarg)) == NULL)
781: errx(1, "strdup");
782: kflag = check_prepare_kvars(tmp);
783: free(tmp);
784: break;
785: case 'r':
786: rflag = strtonum(optarg, 0, 60 * 60 * 24 * 1000,
787: &errstr);
788: if (errstr != NULL)
789: errx(1, "statistics interval is %s: %s",
790: errstr, optarg);
791: break;
792: case 'p':
793: port = optarg;
794: break;
795: case 's':
796: sflag = 1;
797: break;
798: case 'S':
799: Sflag = strtonum(optarg, 0, 1024*1024*1024,
800: &errstr);
801: if (errstr != NULL)
802: errx(1, "receive space interval is %s: %s",
803: errstr, optarg);
804: break;
805: case 'B':
806: Bflag = strtonum(optarg, 0, 1024*1024*1024,
807: &errstr);
808: if (errstr != NULL)
809: errx(1, "read/write buffer size is %s: %s",
810: errstr, optarg);
811: break;
812: case 'v':
1.7 henning 813: vflag++;
1.9 claudio 814: break;
815: case 'V':
816: rdomain = (unsigned int)strtonum(optarg, 0,
817: RT_TABLEID_MAX, &errstr);
818: if (errstr)
819: errx(1, "rdomain value is %s: %s",
820: errstr, optarg);
1.1 djm 821: break;
1.4 henning 822: case 'n':
823: nconn = strtonum(optarg, 0, 65535, &errstr);
824: if (errstr != NULL)
825: errx(1, "number of connections is %s: %s",
826: errstr, optarg);
827: break;
1.1 djm 828: case 'h':
829: default:
830: usage();
831: }
832: }
833:
834: argv += optind;
835: argc -= optind;
836: if (argc != (sflag ? 0 : 1))
837: usage();
1.4 henning 838:
839: if (kflag != NULL && nconn > 1)
840: errx(1, "-k currently only works with a single tcp connection");
841:
1.1 djm 842: if (!sflag)
843: host = argv[0];
844:
1.4 henning 845: if (sflag) {
846: bzero(&hints, sizeof(hints));
847: hints.ai_socktype = SOCK_STREAM;
848: hints.ai_flags = AI_PASSIVE;
849: if ((herr = getaddrinfo(host, port, &hints, &aitop)) != 0) {
850: if (herr == EAI_SYSTEM)
851: err(1, "getaddrinfo");
852: else
853: errx(1, "s getaddrinfo: %s", gai_strerror(herr));
854: }
1.1 djm 855: }
856:
857: if (kflag) {
858: if ((kvmh = kvm_openfiles(NULL, NULL, NULL,
859: O_RDONLY, kerr)) == NULL)
860: errx(1, "kvm_open: %s", kerr);
861: drop_gid();
862: if (kvm_nlist(kvmh, nl) < 0 || nl[0].n_type == 0)
863: errx(1, "kvm: no namelist");
864: } else
865: drop_gid();
866:
867: if (sflag)
868: serverloop(kvmh, nl[0].n_value, aitop, vflag, rflag, kflag,
869: Sflag, Bflag);
870: else
1.4 henning 871: clientloop(kvmh, nl[0].n_value, host, port, vflag, rflag, kflag,
872: Sflag, Bflag, nconn);
1.1 djm 873:
874: return 0;
875: }