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