Annotation of src/usr.bin/tcpbench/tcpbench.c, Revision 1.9
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");
! 509: }
1.1 djm 510: if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
511: &on, sizeof(on)) == -1)
512: warn("reuse port");
513: if (bind(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
514: if (ai->ai_next == NULL)
515: err(1, "bind");
516: if (vflag)
517: warn("bind");
518: close(sock);
519: continue;
520: }
521: if (Sflag) {
522: if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
523: &Sflag, sizeof(Sflag)) == -1)
524: warn("set TCP receive buffer size");
525: }
526: if (listen(sock, 64) == -1) {
527: if (ai->ai_next == NULL)
528: err(1, "listen");
529: if (vflag)
530: warn("listen");
531: close(sock);
532: continue;
533: }
534: if (nfds > 128)
535: break;
536: if ((pfd = realloc(pfd, ++nfds * sizeof(*pfd))) == NULL)
537: errx(1, "realloc(pfd * %zu)", nfds);
538: pfd[nfds - 1].fd = sock;
539: pfd[nfds - 1].events = POLLIN;
540: }
541: freeaddrinfo(aitop);
542: if (nfds == 0)
543: errx(1, "No working listen addresses found");
544:
545: signal(SIGINT, exitsighand);
546: signal(SIGTERM, exitsighand);
547: signal(SIGHUP, exitsighand);
548: signal(SIGPIPE, SIG_IGN);
549: signal(SIGCHLD, SIG_IGN);
550:
551: if (setpgid(0, 0) == -1)
552: err(1, "setpgid");
553:
554: client_id = 0;
555: while (!done) {
556: if ((r = poll(pfd, nfds, INFTIM)) == -1) {
557: if (errno == EINTR)
558: continue;
559: warn("poll");
560: break;
561: }
562: if (vflag >= 3)
563: fprintf(stderr, "poll: %d\n", r);
564: for (i = 0 ; r > 0 && i < nfds; i++) {
565: if ((pfd[i].revents & POLLIN) == 0)
566: continue;
567: if (vflag >= 3)
568: fprintf(stderr, "fd %d active\n", pfd[i].fd);
569: r--;
570: sslen = sizeof(ss);
571: if ((sock = accept(pfd[i].fd, (struct sockaddr *)&ss,
572: &sslen)) == -1) {
573: if (errno == EINTR)
574: continue;
575: warn("accept");
576: break;
577: }
578: saddr_ntop((struct sockaddr *)&ss, sslen,
579: tmp, sizeof(tmp));
580: if (vflag)
581: fprintf(stderr, "Accepted connection %d from "
582: "%s, fd = %d\n", client_id++, tmp, sock);
583: switch (fork()) {
584: case -1:
585: warn("fork");
586: done = -1;
587: break;
588: case 0:
589: for (j = 0; j < nfds; j++)
590: if (j != i)
591: close(pfd[j].fd);
592: handle_connection(kvmh, ktcbtab, sock,
593: vflag, rflag, kflag, Bflag);
594: /* NOTREACHED */
595: _exit(1);
596: default:
597: close(sock);
598: break;
599: }
600: if (done == -1)
601: break;
602: }
603: }
604: for (i = 0; i < nfds; i++)
605: close(pfd[i].fd);
606: if (done > 0)
607: warnx("Terminated by signal %d", done);
608: signal(SIGTERM, SIG_IGN);
609: killpg(0, SIGTERM);
610: exit(1);
611: }
612:
613: static void __dead
1.4 henning 614: clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char *port,
615: int vflag, int rflag, char **kflag, int Sflag, int Bflag, int nconn)
1.1 djm 616: {
617: char tmp[128];
618: char *buf;
1.4 henning 619: int r, sock, herr;
620: struct addrinfo *aitop, *ai, hints;
621: struct pollfd *pfd;
1.1 djm 622: ssize_t n;
623: struct statctx sc;
1.5 henning 624: u_int i, scnt = 0;
1.1 djm 625:
626: if ((buf = malloc(Bflag)) == NULL)
627: err(1, "malloc");
1.4 henning 628:
629: if ((pfd = calloc(nconn, sizeof(struct pollfd))) == NULL)
630: err(1, "clientloop pfd calloc");
631:
632: for (i = 0; i < nconn; i++) {
633: bzero(&hints, sizeof(hints));
634: hints.ai_socktype = SOCK_STREAM;
635: hints.ai_flags = 0;
636: if ((herr = getaddrinfo(host, port, &hints, &aitop)) != 0) {
637: if (herr == EAI_SYSTEM)
638: err(1, "getaddrinfo");
639: else
640: errx(1, "c getaddrinfo: %s", gai_strerror(herr));
641: }
642:
643: for (sock = -1, ai = aitop; ai != NULL; ai = ai->ai_next) {
644: saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp,
645: sizeof(tmp));
1.5 henning 646: if (vflag && scnt == 0)
1.4 henning 647: fprintf(stderr, "Trying %s\n", tmp);
648: if ((sock = socket(ai->ai_family, ai->ai_socktype,
649: ai->ai_protocol)) == -1) {
650: if (ai->ai_next == NULL)
651: err(1, "socket");
652: if (vflag)
653: warn("socket");
654: continue;
655: }
1.9 ! claudio 656: if (rdomain && ai->ai_family == AF_INET) {
! 657: if (setsockopt(sock, IPPROTO_IP, SO_RDOMAIN,
! 658: &rdomain, sizeof(rdomain)) == -1)
! 659: err(1, "setsockopt SO_RDOMAIN");
! 660: }
1.4 henning 661: if (Sflag) {
662: if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
663: &Sflag, sizeof(Sflag)) == -1)
664: warn("set TCP send buffer size");
665: }
666: if (connect(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
667: if (ai->ai_next == NULL)
668: err(1, "connect");
669: if (vflag)
670: warn("connect");
671: close(sock);
672: sock = -1;
673: continue;
674: }
675: break;
1.1 djm 676: }
1.4 henning 677: freeaddrinfo(aitop);
678: if (sock == -1)
679: errx(1, "No host found");
680:
681: if ((r = fcntl(sock, F_GETFL, 0)) == -1)
682: err(1, "fcntl(F_GETFL)");
683: r |= O_NONBLOCK;
684: if (fcntl(sock, F_SETFL, r) == -1)
685: err(1, "fcntl(F_SETFL, O_NONBLOCK)");
686:
687: pfd[i].fd = sock;
688: pfd[i].events = POLLOUT;
1.5 henning 689: scnt++;
1.1 djm 690: }
691:
1.5 henning 692: if (vflag && scnt > 1)
693: fprintf(stderr, "%u connections established\n", scnt);
1.1 djm 694: arc4random_buf(buf, Bflag);
695:
696: signal(SIGINT, exitsighand);
697: signal(SIGTERM, exitsighand);
698: signal(SIGHUP, exitsighand);
699: signal(SIGPIPE, SIG_IGN);
700:
701: stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);
702:
703: while (!done) {
704: if (print_stats) {
705: stats_display(&sc);
706: print_stats = 0;
707: }
1.4 henning 708: if (poll(pfd, nconn, INFTIM) == -1) {
1.1 djm 709: if (errno == EINTR)
710: continue;
711: err(1, "poll");
712: }
1.4 henning 713: for (i = 0; i < nconn; i++) {
1.7 henning 714: if (pfd[i].revents & POLLOUT) {
1.4 henning 715: if ((n = write(pfd[i].fd, buf, Bflag)) == -1) {
716: if (errno == EINTR || errno == EAGAIN)
717: continue;
718: err(1, "write");
719: }
1.7 henning 720: if (n == 0) {
721: warnx("Remote end closed connection");
722: done = -1;
723: break;
724: }
725: if (vflag >= 3)
726: fprintf(stderr, "write: %zd bytes\n",
727: n);
728: stats_update(&sc, n);
1.4 henning 729: }
1.1 djm 730: }
731: }
732: stats_finish(&sc);
733:
734: if (done > 0)
735: warnx("Terminated by signal %d", done);
736:
737: free(buf);
738: close(sock);
739: exit(0);
740: }
741:
742: static void
743: drop_gid(void)
744: {
745: gid_t gid;
746:
747: gid = getgid();
748: if (setresgid(gid, gid, gid) == -1)
749: err(1, "setresgid");
750: }
751:
752: int
753: main(int argc, char **argv)
754: {
755: extern int optind;
756: extern char *optarg;
757:
758: char kerr[_POSIX2_LINE_MAX], *tmp;
759: const char *errstr;
760: int ch, herr;
761: struct addrinfo *aitop, hints;
1.3 djm 762: kvm_t *kvmh = NULL;
1.1 djm 763:
764: const char *host = NULL, *port = DEFAULT_PORT;
765: char **kflag = NULL;
766: int sflag = 0, vflag = 0, rflag = DEFAULT_STATS_INTERVAL, Sflag = 0;
767: int Bflag = DEFAULT_BUF;
1.4 henning 768: int nconn = 1;
1.1 djm 769:
770: struct nlist nl[] = { { "_tcbtable" }, { "" } };
771:
1.9 ! claudio 772: while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) {
1.1 djm 773: switch (ch) {
774: case 'l':
775: list_kvars();
776: exit(0);
777: case 'k':
778: if ((tmp = strdup(optarg)) == NULL)
779: errx(1, "strdup");
780: kflag = check_prepare_kvars(tmp);
781: free(tmp);
782: break;
783: case 'r':
784: rflag = strtonum(optarg, 0, 60 * 60 * 24 * 1000,
785: &errstr);
786: if (errstr != NULL)
787: errx(1, "statistics interval is %s: %s",
788: errstr, optarg);
789: break;
790: case 'p':
791: port = optarg;
792: break;
793: case 's':
794: sflag = 1;
795: break;
796: case 'S':
797: Sflag = strtonum(optarg, 0, 1024*1024*1024,
798: &errstr);
799: if (errstr != NULL)
800: errx(1, "receive space interval is %s: %s",
801: errstr, optarg);
802: break;
803: case 'B':
804: Bflag = strtonum(optarg, 0, 1024*1024*1024,
805: &errstr);
806: if (errstr != NULL)
807: errx(1, "read/write buffer size is %s: %s",
808: errstr, optarg);
809: break;
810: case 'v':
1.7 henning 811: vflag++;
1.9 ! claudio 812: break;
! 813: case 'V':
! 814: rdomain = (unsigned int)strtonum(optarg, 0,
! 815: RT_TABLEID_MAX, &errstr);
! 816: if (errstr)
! 817: errx(1, "rdomain value is %s: %s",
! 818: errstr, optarg);
1.1 djm 819: break;
1.4 henning 820: case 'n':
821: nconn = strtonum(optarg, 0, 65535, &errstr);
822: if (errstr != NULL)
823: errx(1, "number of connections is %s: %s",
824: errstr, optarg);
825: break;
1.1 djm 826: case 'h':
827: default:
828: usage();
829: }
830: }
831:
832: argv += optind;
833: argc -= optind;
834: if (argc != (sflag ? 0 : 1))
835: usage();
1.4 henning 836:
837: if (kflag != NULL && nconn > 1)
838: errx(1, "-k currently only works with a single tcp connection");
839:
1.1 djm 840: if (!sflag)
841: host = argv[0];
842:
1.4 henning 843: if (sflag) {
844: bzero(&hints, sizeof(hints));
845: hints.ai_socktype = SOCK_STREAM;
846: hints.ai_flags = AI_PASSIVE;
847: if ((herr = getaddrinfo(host, port, &hints, &aitop)) != 0) {
848: if (herr == EAI_SYSTEM)
849: err(1, "getaddrinfo");
850: else
851: errx(1, "s getaddrinfo: %s", gai_strerror(herr));
852: }
1.1 djm 853: }
854:
855: if (kflag) {
856: if ((kvmh = kvm_openfiles(NULL, NULL, NULL,
857: O_RDONLY, kerr)) == NULL)
858: errx(1, "kvm_open: %s", kerr);
859: drop_gid();
860: if (kvm_nlist(kvmh, nl) < 0 || nl[0].n_type == 0)
861: errx(1, "kvm: no namelist");
862: } else
863: drop_gid();
864:
865: if (sflag)
866: serverloop(kvmh, nl[0].n_value, aitop, vflag, rflag, kflag,
867: Sflag, Bflag);
868: else
1.4 henning 869: clientloop(kvmh, nl[0].n_value, host, port, vflag, rflag, kflag,
870: Sflag, Bflag, nconn);
1.1 djm 871:
872: return 0;
873: }