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