Annotation of src/usr.bin/tcpbench/tcpbench.c, Revision 1.19
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>
1.11 claudio 21: #include <sys/resource.h>
1.1 djm 22:
23: #include <net/route.h>
24:
25: #include <netinet/in.h>
26: #include <netinet/in_systm.h>
27: #include <netinet/ip.h>
28: #include <netinet/tcp.h>
29: #include <netinet/tcp_timer.h>
30: #include <netinet/tcp_fsm.h>
31: #include <netinet/in_pcb.h>
32: #include <netinet/tcp_var.h>
33:
34: #include <arpa/inet.h>
35:
36: #include <unistd.h>
37: #include <limits.h>
38: #include <stdlib.h>
39: #include <stdio.h>
40: #include <string.h>
41: #include <errno.h>
42: #include <netdb.h>
43: #include <signal.h>
44: #include <err.h>
45: #include <fcntl.h>
46: #include <poll.h>
47:
48: #include <kvm.h>
49: #include <nlist.h>
50:
1.11 claudio 51: #define DEFAULT_PORT "12345"
52: #define DEFAULT_STATS_INTERVAL 1000 /* ms */
53: #define DEFAULT_BUF 256 * 1024
54: #define MAX_FD 1024
1.1 djm 55:
56: sig_atomic_t done = 0;
1.11 claudio 57: sig_atomic_t proc_slice = 0;
1.1 djm 58:
1.17 guenther 59: static u_int rtableid;
1.11 claudio 60: static char **kflag;
61: static size_t Bflag;
62: static int Sflag;
63: static int rflag;
64: static int sflag;
65: static int vflag;
1.9 claudio 66:
1.11 claudio 67: /* stats for a single connection */
1.1 djm 68: struct statctx {
1.11 claudio 69: struct timeval t_start, t_last;
1.1 djm 70: unsigned long long bytes;
71: u_long tcbaddr;
1.11 claudio 72: char **kvars;
1.1 djm 73: kvm_t *kh;
74: };
75:
1.11 claudio 76: /*
77: * We account the mainstats here, that is the stats
78: * for all connections, all variables starting with slice
79: * are used to account information for the timeslice
80: * between each output. Peak variables record the highest
81: * between all slices so far.
82: */
83: static struct {
84: unsigned long long slice_bytes; /* bytes for last slice */
85: struct timeval t_start; /* when we started counting */
86: long double peak_mbps; /* peak mbps so far */
87: int nconns; /* connected clients */
88: } mainstats;
89:
1.1 djm 90: /* When adding variables, also add to stats_display() */
91: static const char *allowed_kvars[] = {
92: "inpcb.inp_flags",
93: "sockb.so_rcv.sb_cc",
1.19 ! claudio 94: "sockb.so_rcv.sb_wat",
1.1 djm 95: "sockb.so_rcv.sb_hiwat",
96: "sockb.so_snd.sb_cc",
1.19 ! claudio 97: "sockb.so_snd.sb_wat",
1.1 djm 98: "sockb.so_snd.sb_hiwat",
99: "tcpcb.snd_una",
100: "tcpcb.snd_nxt",
101: "tcpcb.snd_wl1",
102: "tcpcb.snd_wl2",
103: "tcpcb.snd_wnd",
104: "tcpcb.rcv_wnd",
105: "tcpcb.rcv_nxt",
106: "tcpcb.rcv_adv",
107: "tcpcb.snd_max",
108: "tcpcb.snd_cwnd",
109: "tcpcb.snd_ssthresh",
110: "tcpcb.t_rcvtime",
111: "tcpcb.t_rtttime",
112: "tcpcb.t_rtseq",
113: "tcpcb.t_srtt",
114: "tcpcb.t_rttvar",
115: "tcpcb.t_rttmin",
116: "tcpcb.max_sndwnd",
117: "tcpcb.snd_scale",
118: "tcpcb.rcv_scale",
119: "tcpcb.last_ack_sent",
1.19 ! claudio 120: "tcpcb.rfbuf_cnt",
! 121: "tcpcb.rfbuf_ts",
! 122: "tcpcb.ts_recent_age",
! 123: "tcpcb.ts_recent",
1.1 djm 124: NULL
125: };
126:
127: static void
128: exitsighand(int signo)
129: {
130: done = signo;
131: }
132:
133: static void
134: alarmhandler(int signo)
135: {
1.11 claudio 136: proc_slice = 1;
1.1 djm 137: signal(signo, alarmhandler);
138: }
139:
140: static void __dead
141: usage(void)
142: {
143: fprintf(stderr,
1.6 jmc 144: "usage: tcpbench -l\n"
1.15 sthen 145: " tcpbench [-v] [-B buf] [-k kvars] [-n connections] [-p port]\n"
1.17 guenther 146: " [-r rate] [-S space] [-V rtable] hostname\n"
1.15 sthen 147: " tcpbench -s [-v] [-B buf] [-k kvars] [-p port]\n"
1.17 guenther 148: " [-r rate] [-S space] [-V rtable]\n");
1.1 djm 149: exit(1);
150: }
151:
152: static void
153: saddr_ntop(const struct sockaddr *addr, socklen_t alen, char *buf, size_t len)
154: {
155: char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
156: int herr;
157:
1.3 djm 158: if ((herr = getnameinfo(addr, alen, hbuf, sizeof(hbuf),
159: pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
1.1 djm 160: if (herr == EAI_SYSTEM)
161: err(1, "getnameinfo");
162: else
163: errx(1, "getnameinfo: %s", gai_strerror(herr));
164: }
165: snprintf(buf, len, "[%s]:%s", hbuf, pbuf);
166: }
167:
168: static void
1.11 claudio 169: set_timer(int toggle)
170: {
171: struct itimerval itv;
172:
173: if (rflag <= 0)
174: return;
175:
176: if (toggle) {
177: itv.it_interval.tv_sec = rflag / 1000;
178: itv.it_interval.tv_usec = (rflag % 1000) * 1000;
179: itv.it_value = itv.it_interval;
180: }
181: else
182: bzero(&itv, sizeof(itv));
183:
184: setitimer(ITIMER_REAL, &itv, NULL);
185: }
186:
187: static void
188: print_header(void)
189: {
190: char **kv;
191:
192: printf("%12s %14s %12s %8s ", "elapsed_ms", "bytes", "mbps",
193: "bwidth");
194:
195: for (kv = kflag; kflag != NULL && *kv != NULL; kv++)
196: printf("%s%s", kv != kflag ? "," : "", *kv);
197:
198: printf("\n");
199: }
200:
201: static void
1.16 deraadt 202: kget(kvm_t *kh, u_long addr, void *buf, size_t size)
1.1 djm 203: {
1.16 deraadt 204: if (kvm_read(kh, addr, buf, size) != (ssize_t)size)
1.1 djm 205: errx(1, "kvm_read: %s", kvm_geterr(kh));
206: }
207:
208: static u_long
1.11 claudio 209: kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock)
1.1 djm 210: {
211: struct inpcbtable tcbtab;
212: struct inpcb *head, *next, *prev;
213: struct inpcb inpcb;
214: struct tcpcb tcpcb;
215:
216: struct sockaddr_storage me, them;
217: socklen_t melen, themlen;
218: struct sockaddr_in *in4;
219: struct sockaddr_in6 *in6;
220: char tmp1[64], tmp2[64];
1.11 claudio 221: int nretry;
1.1 djm 222:
1.11 claudio 223: nretry = 10;
1.1 djm 224: melen = themlen = sizeof(struct sockaddr_storage);
225: if (getsockname(sock, (struct sockaddr *)&me, &melen) == -1)
226: err(1, "getsockname");
227: if (getpeername(sock, (struct sockaddr *)&them, &themlen) == -1)
228: err(1, "getpeername");
229: if (me.ss_family != them.ss_family)
230: errx(1, "%s: me.ss_family != them.ss_family", __func__);
231: if (me.ss_family != AF_INET && me.ss_family != AF_INET6)
232: errx(1, "%s: unknown socket family", __func__);
233: if (vflag >= 2) {
234: saddr_ntop((struct sockaddr *)&me, me.ss_len,
235: tmp1, sizeof(tmp1));
236: saddr_ntop((struct sockaddr *)&them, them.ss_len,
237: tmp2, sizeof(tmp2));
238: fprintf(stderr, "Our socket local %s remote %s\n", tmp1, tmp2);
239: }
240: if (vflag >= 2)
241: fprintf(stderr, "Using PCB table at %lu\n", ktcbtab);
1.11 claudio 242: retry:
1.1 djm 243: kget(kh, ktcbtab, &tcbtab, sizeof(tcbtab));
244: prev = head = (struct inpcb *)&CIRCLEQ_FIRST(
245: &((struct inpcbtable *)ktcbtab)->inpt_queue);
246: next = CIRCLEQ_FIRST(&tcbtab.inpt_queue);
247:
248: if (vflag >= 2)
249: fprintf(stderr, "PCB head at %p\n", head);
250: while (next != head) {
251: if (vflag >= 2)
252: fprintf(stderr, "Checking PCB %p\n", next);
253: kget(kh, (u_long)next, &inpcb, sizeof(inpcb));
1.11 claudio 254: if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
255: if (nretry--) {
256: warnx("pcb prev pointer insane");
257: goto retry;
258: }
259: else
260: errx(1, "pcb prev pointer insane,"
261: " all attempts exausted");
262: }
1.1 djm 263: prev = next;
264: next = CIRCLEQ_NEXT(&inpcb, inp_queue);
265:
266: if (me.ss_family == AF_INET) {
267: if ((inpcb.inp_flags & INP_IPV6) != 0) {
268: if (vflag >= 2)
269: fprintf(stderr, "Skip: INP_IPV6");
270: continue;
271: }
272: if (vflag >= 2) {
273: inet_ntop(AF_INET, &inpcb.inp_laddr,
274: tmp1, sizeof(tmp1));
275: inet_ntop(AF_INET, &inpcb.inp_faddr,
276: tmp2, sizeof(tmp2));
277: fprintf(stderr, "PCB %p local: [%s]:%d "
278: "remote: [%s]:%d\n", prev,
279: tmp1, inpcb.inp_lport,
280: tmp2, inpcb.inp_fport);
281: }
282: in4 = (struct sockaddr_in *)&me;
283: if (memcmp(&in4->sin_addr, &inpcb.inp_laddr,
284: sizeof(struct in_addr)) != 0 ||
285: in4->sin_port != inpcb.inp_lport)
286: continue;
287: in4 = (struct sockaddr_in *)&them;
288: if (memcmp(&in4->sin_addr, &inpcb.inp_faddr,
289: sizeof(struct in_addr)) != 0 ||
290: in4->sin_port != inpcb.inp_fport)
291: continue;
292: } else {
293: if ((inpcb.inp_flags & INP_IPV6) == 0)
294: continue;
295: if (vflag >= 2) {
296: inet_ntop(AF_INET6, &inpcb.inp_laddr6,
297: tmp1, sizeof(tmp1));
298: inet_ntop(AF_INET6, &inpcb.inp_faddr6,
299: tmp2, sizeof(tmp2));
300: fprintf(stderr, "PCB %p local: [%s]:%d "
301: "remote: [%s]:%d\n", prev,
302: tmp1, inpcb.inp_lport,
303: tmp2, inpcb.inp_fport);
304: }
305: in6 = (struct sockaddr_in6 *)&me;
306: if (memcmp(&in6->sin6_addr, &inpcb.inp_laddr6,
307: sizeof(struct in6_addr)) != 0 ||
308: in6->sin6_port != inpcb.inp_lport)
309: continue;
310: in6 = (struct sockaddr_in6 *)&them;
311: if (memcmp(&in6->sin6_addr, &inpcb.inp_faddr6,
312: sizeof(struct in6_addr)) != 0 ||
313: in6->sin6_port != inpcb.inp_fport)
314: continue;
315: }
316: kget(kh, (u_long)inpcb.inp_ppcb, &tcpcb, sizeof(tcpcb));
317: if (tcpcb.t_state != TCPS_ESTABLISHED) {
318: if (vflag >= 2)
319: fprintf(stderr, "Not established\n");
320: continue;
321: }
322: if (vflag >= 2)
323: fprintf(stderr, "Found PCB at %p\n", prev);
324: return (u_long)prev;
325: }
326:
327: errx(1, "No matching PCB found");
328: }
329:
330: static void
331: kupdate_stats(kvm_t *kh, u_long tcbaddr,
332: struct inpcb *inpcb, struct tcpcb *tcpcb, struct socket *sockb)
333: {
334: kget(kh, tcbaddr, inpcb, sizeof(*inpcb));
335: kget(kh, (u_long)inpcb->inp_ppcb, tcpcb, sizeof(*tcpcb));
336: kget(kh, (u_long)inpcb->inp_socket, sockb, sizeof(*sockb));
337: }
338:
339: static void
340: check_kvar(const char *var)
341: {
1.16 deraadt 342: u_int i;
1.1 djm 343:
344: for (i = 0; allowed_kvars[i] != NULL; i++)
345: if (strcmp(allowed_kvars[i], var) == 0)
346: return;
347: errx(1, "Unrecognised kvar: %s", var);
348: }
349:
350: static void
351: list_kvars(void)
352: {
1.16 deraadt 353: u_int i;
1.1 djm 354:
355: fprintf(stderr, "Supported kernel variables:\n");
356: for (i = 0; allowed_kvars[i] != NULL; i++)
357: fprintf(stderr, "\t%s\n", allowed_kvars[i]);
358: }
359:
360: static char **
361: check_prepare_kvars(char *list)
362: {
363: char *item, **ret = NULL;
1.16 deraadt 364: u_int n = 0;
1.1 djm 365:
366: while ((item = strsep(&list, ", \t\n")) != NULL) {
367: check_kvar(item);
368: if ((ret = realloc(ret, sizeof(*ret) * (++n + 1))) == NULL)
369: errx(1, "realloc(kvars)");
370: if ((ret[n - 1] = strdup(item)) == NULL)
371: errx(1, "strdup");
372: ret[n] = NULL;
373: }
374: return ret;
375: }
376:
377: static void
1.11 claudio 378: stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab)
1.1 djm 379: {
380: if (rflag <= 0)
381: return;
382: sc->kh = kh;
383: sc->kvars = kflag;
384: if (kflag)
1.11 claudio 385: sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd);
386: if (gettimeofday(&sc->t_start, NULL) == -1)
387: err(1, "gettimeofday");
1.1 djm 388: sc->t_last = sc->t_start;
389: sc->bytes = 0;
390: }
391:
392: static void
393: stats_update(struct statctx *sc, ssize_t n)
394: {
395: sc->bytes += n;
1.11 claudio 396: mainstats.slice_bytes += n;
1.1 djm 397: }
398:
399: static void
1.11 claudio 400: stats_cleanslice(void)
1.1 djm 401: {
1.11 claudio 402: mainstats.slice_bytes = 0;
403: }
1.1 djm 404:
1.11 claudio 405: static void
406: stats_display(unsigned long long total_elapsed, long double mbps,
407: float bwperc, struct statctx *sc, struct inpcb *inpcb,
408: struct tcpcb *tcpcb, struct socket *sockb)
409: {
410: int j;
411:
412: printf("%12llu %14llu %12.3Lf %7.2f%% ", total_elapsed, sc->bytes,
413: mbps, bwperc);
414:
415: if (sc->kvars != NULL) {
416: kupdate_stats(sc->kh, sc->tcbaddr, inpcb, tcpcb,
417: sockb);
1.1 djm 418:
1.11 claudio 419: for (j = 0; sc->kvars[j] != NULL; j++) {
420: #define S(a) #a
421: #define P(b, v, f) \
422: if (strcmp(sc->kvars[j], S(b.v)) == 0) { \
423: printf("%s"f, j > 0 ? "," : "", b->v); \
424: continue; \
425: }
426: P(inpcb, inp_flags, "0x%08x")
427: P(sockb, so_rcv.sb_cc, "%lu")
1.19 ! claudio 428: P(sockb, so_rcv.sb_wat, "%lu")
1.11 claudio 429: P(sockb, so_rcv.sb_hiwat, "%lu")
430: P(sockb, so_snd.sb_cc, "%lu")
1.19 ! claudio 431: P(sockb, so_snd.sb_wat, "%lu")
1.11 claudio 432: P(sockb, so_snd.sb_hiwat, "%lu")
433: P(tcpcb, snd_una, "%u")
434: P(tcpcb, snd_nxt, "%u")
435: P(tcpcb, snd_wl1, "%u")
436: P(tcpcb, snd_wl2, "%u")
437: P(tcpcb, snd_wnd, "%lu")
438: P(tcpcb, rcv_wnd, "%lu")
439: P(tcpcb, rcv_nxt, "%u")
440: P(tcpcb, rcv_adv, "%u")
441: P(tcpcb, snd_max, "%u")
442: P(tcpcb, snd_cwnd, "%lu")
443: P(tcpcb, snd_ssthresh, "%lu")
444: P(tcpcb, t_rcvtime, "%u")
445: P(tcpcb, t_rtttime, "%u")
446: P(tcpcb, t_rtseq, "%u")
447: P(tcpcb, t_srtt, "%hu")
448: P(tcpcb, t_rttvar, "%hu")
449: P(tcpcb, t_rttmin, "%hu")
450: P(tcpcb, max_sndwnd, "%lu")
451: P(tcpcb, snd_scale, "%u")
452: P(tcpcb, rcv_scale, "%u")
453: P(tcpcb, last_ack_sent, "%u")
1.19 ! claudio 454: P(tcpcb, rfbuf_cnt, "%u")
! 455: P(tcpcb, rfbuf_ts, "%u")
! 456: P(tcpcb, ts_recent_age, "%u")
! 457: P(tcpcb, ts_recent, "%u")
1.11 claudio 458: #undef S
1.1 djm 459: #undef P
460: }
461: }
462: printf("\n");
463: }
464:
465: static void
1.11 claudio 466: mainstats_display(long double slice_mbps, long double avg_mbps)
1.1 djm 467: {
1.11 claudio 468: printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n",
469: mainstats.nconns, slice_mbps, mainstats.peak_mbps, avg_mbps);
1.1 djm 470: }
471:
1.11 claudio 472: static void
473: process_slice(struct statctx *sc, size_t nsc)
1.1 djm 474: {
1.11 claudio 475: unsigned long long total_elapsed, since_last;
476: long double mbps, slice_mbps = 0;
477: float bwperc;
478: nfds_t i;
479: struct timeval t_cur, t_diff;
480: struct inpcb inpcb;
481: struct tcpcb tcpcb;
482: struct socket sockb;
483:
484: for (i = 0; i < nsc; i++, sc++) {
485: if (gettimeofday(&t_cur, NULL) == -1)
486: err(1, "gettimeofday");
487: if (sc->kvars != NULL) /* process kernel stats */
488: kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb,
489: &sockb);
490: timersub(&t_cur, &sc->t_start, &t_diff);
491: total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
492: timersub(&t_cur, &sc->t_last, &t_diff);
493: since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
494: bwperc = (sc->bytes * 100.0) / mainstats.slice_bytes;
495: mbps = (sc->bytes * 8) / (since_last * 1000.0);
496: slice_mbps += mbps;
497:
498: stats_display(total_elapsed, mbps, bwperc, sc,
499: &inpcb, &tcpcb, &sockb);
500:
501: sc->t_last = t_cur;
502: sc->bytes = 0;
1.1 djm 503:
1.11 claudio 504: }
1.1 djm 505:
1.11 claudio 506: /* process stats for this slice */
507: if (slice_mbps > mainstats.peak_mbps)
508: mainstats.peak_mbps = slice_mbps;
509: mainstats_display(slice_mbps, slice_mbps / mainstats.nconns);
510: }
1.1 djm 511:
1.11 claudio 512: static int
513: handle_connection(struct statctx *sc, int fd, char *buf, size_t buflen)
514: {
515: ssize_t n;
1.1 djm 516:
1.11 claudio 517: again:
518: n = read(fd, buf, buflen);
519: if (n == -1) {
520: if (errno == EINTR)
521: goto again;
522: else if (errno == EWOULDBLOCK)
523: return 0;
524: warn("fd %d read error", fd);
525:
526: return -1;
1.1 djm 527: }
1.11 claudio 528: else if (n == 0) {
529: if (vflag)
530: fprintf(stderr, "%8d closed by remote end\n", fd);
531: close(fd);
532: return -1;
533: }
534: if (vflag >= 3)
535: fprintf(stderr, "read: %zd bytes\n", n);
536:
537: stats_update(sc, n);
538: return 0;
1.1 djm 539: }
540:
1.11 claudio 541: static nfds_t
542: serverbind(struct pollfd *pfd, nfds_t max_nfds, struct addrinfo *aitop)
1.1 djm 543: {
544: char tmp[128];
1.11 claudio 545: int sock, on = 1;
1.1 djm 546: struct addrinfo *ai;
1.11 claudio 547: nfds_t lnfds;
1.1 djm 548:
1.11 claudio 549: lnfds = 0;
1.1 djm 550: for (ai = aitop; ai != NULL; ai = ai->ai_next) {
1.11 claudio 551: if (lnfds == max_nfds) {
552: fprintf(stderr,
553: "maximum number of listening fds reached\n");
554: break;
555: }
1.1 djm 556: saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp));
557: if (vflag)
1.11 claudio 558: fprintf(stderr, "Try to listen on %s\n", tmp);
1.1 djm 559: if ((sock = socket(ai->ai_family, ai->ai_socktype,
560: ai->ai_protocol)) == -1) {
561: if (ai->ai_next == NULL)
562: err(1, "socket");
563: if (vflag)
564: warn("socket");
565: continue;
566: }
1.17 guenther 567: if (rtableid && ai->ai_family == AF_INET) {
568: if (setsockopt(sock, IPPROTO_IP, SO_RTABLE,
569: &rtableid, sizeof(rtableid)) == -1)
570: err(1, "setsockopt SO_RTABLE");
571: } else if (rtableid)
572: warnx("rtable only supported on AF_INET");
1.1 djm 573: if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
574: &on, sizeof(on)) == -1)
575: warn("reuse port");
576: if (bind(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
577: if (ai->ai_next == NULL)
578: err(1, "bind");
579: if (vflag)
580: warn("bind");
581: close(sock);
582: continue;
583: }
584: if (Sflag) {
585: if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
586: &Sflag, sizeof(Sflag)) == -1)
587: warn("set TCP receive buffer size");
588: }
589: if (listen(sock, 64) == -1) {
590: if (ai->ai_next == NULL)
591: err(1, "listen");
592: if (vflag)
593: warn("listen");
594: close(sock);
595: continue;
596: }
1.11 claudio 597: if (vflag >= 3)
598: fprintf(stderr, "listening on fd %d\n", sock);
599: lnfds++;
600: pfd[lnfds - 1].fd = sock;
601: pfd[lnfds - 1].events = POLLIN;
602:
1.1 djm 603: }
604: freeaddrinfo(aitop);
1.11 claudio 605: if (lnfds == 0)
1.1 djm 606: errx(1, "No working listen addresses found");
607:
1.11 claudio 608: return lnfds;
609: }
610:
611: static void
612: set_listening(struct pollfd *pfd, nfds_t lfds, int toggle) {
613: int i;
1.1 djm 614:
1.11 claudio 615: for (i = 0; i < (int)lfds; i++) {
616: if (toggle)
617: pfd[i].events = POLLIN;
618: else
619: pfd[i].events = 0;
620: }
621:
622: }
623: static void __dead
624: serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop)
625: {
626: socklen_t sslen;
627: struct pollfd *pfd;
628: char tmp[128], *buf;
629: struct statctx *psc;
630: struct sockaddr_storage ss;
631: nfds_t i, nfds, lfds;
632: size_t nalloc;
633: int r, sock, client_id;
634:
635: sslen = sizeof(ss);
636: nalloc = 128;
637: if ((pfd = calloc(sizeof(*pfd), nalloc)) == NULL)
638: err(1, "calloc");
639: if ((psc = calloc(sizeof(*psc), nalloc)) == NULL)
640: err(1, "calloc");
641: if ((buf = malloc(Bflag)) == NULL)
642: err(1, "malloc");
643: lfds = nfds = serverbind(pfd, nalloc - 1, aitop);
644: if (vflag >= 3)
645: fprintf(stderr, "listening on %d fds\n", lfds);
1.1 djm 646: if (setpgid(0, 0) == -1)
647: err(1, "setpgid");
1.11 claudio 648:
649: print_header();
650:
1.1 djm 651: client_id = 0;
1.11 claudio 652: while (!done) {
653: if (proc_slice) {
654: process_slice(psc + lfds, nfds - lfds);
655: stats_cleanslice();
656: proc_slice = 0;
657: }
658: if (vflag >= 3)
659: fprintf(stderr, "mainstats.nconns = %u\n",
660: mainstats.nconns);
1.1 djm 661: if ((r = poll(pfd, nfds, INFTIM)) == -1) {
662: if (errno == EINTR)
663: continue;
664: warn("poll");
665: break;
666: }
1.11 claudio 667:
1.1 djm 668: if (vflag >= 3)
669: fprintf(stderr, "poll: %d\n", r);
670: for (i = 0 ; r > 0 && i < nfds; i++) {
671: if ((pfd[i].revents & POLLIN) == 0)
672: continue;
1.11 claudio 673: if (pfd[i].fd == -1)
674: errx(1, "pfd insane");
675: r--;
1.1 djm 676: if (vflag >= 3)
1.11 claudio 677: fprintf(stderr, "fd %d active i = %d\n",
678: pfd[i].fd, i);
679: /* new connection */
680: if (i < lfds) {
681: if ((sock = accept(pfd[i].fd,
682: (struct sockaddr *)&ss,
683: &sslen)) == -1) {
684: if (errno == EINTR)
685: continue;
686: else if (errno == EMFILE ||
687: errno == ENFILE)
688: set_listening(pfd, lfds, 0);
689: warn("accept");
1.1 djm 690: continue;
1.11 claudio 691: }
692: if ((r = fcntl(sock, F_GETFL, 0)) == -1)
693: err(1, "fcntl(F_GETFL)");
694: r |= O_NONBLOCK;
695: if (fcntl(sock, F_SETFL, r) == -1)
696: err(1, "fcntl(F_SETFL, O_NONBLOCK)");
697: saddr_ntop((struct sockaddr *)&ss, sslen,
698: tmp, sizeof(tmp));
699: if (vflag)
700: fprintf(stderr,
701: "Accepted connection %d from "
702: "%s, fd = %d\n", client_id++, tmp,
703: sock);
704: /* alloc more space if we're full */
705: if (nfds == nalloc) {
706: nalloc *= 2;
707: if ((pfd = realloc(pfd,
708: sizeof(*pfd) * nalloc)) == NULL)
709: err(1, "realloc");
710: if ((psc = realloc(psc,
711: sizeof(*psc) * nalloc)) == NULL)
712: err(1, "realloc");
713: }
714: pfd[nfds].fd = sock;
715: pfd[nfds].events = POLLIN;
716: stats_prepare(&psc[nfds], sock, kvmh, ktcbtab);
717: nfds++;
718: if (!mainstats.nconns++)
719: set_timer(1);
720: continue;
1.1 djm 721: }
1.11 claudio 722: /* event in fd */
723: if (vflag >= 3)
724: fprintf(stderr,
725: "fd %d active", pfd[i].fd);
726: while (handle_connection(&psc[i], pfd[i].fd,
727: buf, Bflag) == -1) {
728: pfd[i] = pfd[nfds - 1];
729: pfd[nfds - 1].fd = -1;
730: psc[i] = psc[nfds - 1];
731: mainstats.nconns--;
732: nfds--;
733: /* stop display if no clients */
734: if (!mainstats.nconns) {
735: proc_slice = 1;
736: set_timer(0);
737: }
738: /* if we were full */
739: set_listening(pfd, lfds, 1);
740:
741: /* is there an event pending on the last fd? */
742: if (pfd[i].fd == -1 ||
743: (pfd[i].revents & POLLIN) == 0)
744: break;
1.1 djm 745: }
746: }
747: }
748: exit(1);
749: }
750:
1.14 claudio 751: void
752: clientconnect(struct addrinfo *aitop, struct pollfd *pfd, int nconn)
1.1 djm 753: {
1.14 claudio 754: char tmp[128];
1.13 claudio 755: struct addrinfo *ai;
1.14 claudio 756: int i, r, sock;
1.1 djm 757:
1.4 henning 758: for (i = 0; i < nconn; i++) {
759: for (sock = -1, ai = aitop; ai != NULL; ai = ai->ai_next) {
760: saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp,
761: sizeof(tmp));
1.14 claudio 762: if (vflag && i == 0)
1.4 henning 763: fprintf(stderr, "Trying %s\n", tmp);
764: if ((sock = socket(ai->ai_family, ai->ai_socktype,
765: ai->ai_protocol)) == -1) {
766: if (ai->ai_next == NULL)
767: err(1, "socket");
768: if (vflag)
769: warn("socket");
770: continue;
771: }
1.17 guenther 772: if (rtableid && ai->ai_family == AF_INET) {
773: if (setsockopt(sock, IPPROTO_IP, SO_RTABLE,
774: &rtableid, sizeof(rtableid)) == -1)
775: err(1, "setsockopt SO_RTABLE");
776: } else if (rtableid)
777: warnx("rtable only supported on AF_INET");
1.4 henning 778: if (Sflag) {
779: if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
780: &Sflag, sizeof(Sflag)) == -1)
781: warn("set TCP send buffer size");
782: }
783: if (connect(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
784: if (ai->ai_next == NULL)
785: err(1, "connect");
786: if (vflag)
787: warn("connect");
788: close(sock);
789: sock = -1;
790: continue;
791: }
792: break;
1.1 djm 793: }
1.4 henning 794: if (sock == -1)
795: errx(1, "No host found");
796:
797: if ((r = fcntl(sock, F_GETFL, 0)) == -1)
798: err(1, "fcntl(F_GETFL)");
799: r |= O_NONBLOCK;
800: if (fcntl(sock, F_SETFL, r) == -1)
801: err(1, "fcntl(F_SETFL, O_NONBLOCK)");
802:
803: pfd[i].fd = sock;
804: pfd[i].events = POLLOUT;
1.14 claudio 805: }
806: freeaddrinfo(aitop);
807:
808: if (vflag && nconn > 1)
809: fprintf(stderr, "%u connections established\n", nconn);
810: }
811:
812: static void __dead
813: clientloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop, int nconn)
814: {
815: struct statctx *psc;
816: struct pollfd *pfd;
817: char *buf;
1.18 claudio 818: int i;
1.14 claudio 819: ssize_t n;
820:
821: if ((pfd = calloc(nconn, sizeof(*pfd))) == NULL)
822: err(1, "clientloop pfd calloc");
823: if ((psc = calloc(nconn, sizeof(*psc))) == NULL)
824: err(1, "clientloop psc calloc");
825:
826: clientconnect(aitop, pfd, nconn);
827:
828: for (i = 0; i < nconn; i++) {
1.18 claudio 829: stats_prepare(psc + i, pfd[i].fd, kvmh, ktcbtab);
1.11 claudio 830: mainstats.nconns++;
1.1 djm 831: }
832:
1.14 claudio 833: if ((buf = malloc(Bflag)) == NULL)
834: err(1, "malloc");
1.1 djm 835: arc4random_buf(buf, Bflag);
836:
1.11 claudio 837: print_header();
838: set_timer(1);
1.1 djm 839:
840: while (!done) {
1.11 claudio 841: if (proc_slice) {
1.14 claudio 842: process_slice(psc, nconn);
1.11 claudio 843: stats_cleanslice();
844: proc_slice = 0;
1.1 djm 845: }
1.4 henning 846: if (poll(pfd, nconn, INFTIM) == -1) {
1.1 djm 847: if (errno == EINTR)
848: continue;
849: err(1, "poll");
850: }
1.4 henning 851: for (i = 0; i < nconn; i++) {
1.7 henning 852: if (pfd[i].revents & POLLOUT) {
1.4 henning 853: if ((n = write(pfd[i].fd, buf, Bflag)) == -1) {
854: if (errno == EINTR || errno == EAGAIN)
855: continue;
856: err(1, "write");
857: }
1.7 henning 858: if (n == 0) {
859: warnx("Remote end closed connection");
860: done = -1;
861: break;
862: }
863: if (vflag >= 3)
864: fprintf(stderr, "write: %zd bytes\n",
865: n);
1.11 claudio 866: stats_update(psc + i, n);
1.4 henning 867: }
1.1 djm 868: }
869: }
1.11 claudio 870:
1.1 djm 871: if (done > 0)
872: warnx("Terminated by signal %d", done);
873:
874: free(buf);
875: exit(0);
876: }
877:
878: static void
879: drop_gid(void)
880: {
881: gid_t gid;
882:
883: gid = getgid();
884: if (setresgid(gid, gid, gid) == -1)
885: err(1, "setresgid");
886: }
887:
888: int
889: main(int argc, char **argv)
890: {
891: extern int optind;
892: extern char *optarg;
893:
894: char kerr[_POSIX2_LINE_MAX], *tmp;
1.11 claudio 895: struct addrinfo *aitop, hints;
1.1 djm 896: const char *errstr;
1.11 claudio 897: kvm_t *kvmh = NULL;
898: struct rlimit rl;
1.1 djm 899: int ch, herr;
1.12 deraadt 900: struct nlist nl[] = { { "_tcbtable" }, { "" } };
1.1 djm 901: const char *host = NULL, *port = DEFAULT_PORT;
1.4 henning 902: int nconn = 1;
1.1 djm 903:
1.11 claudio 904: Bflag = DEFAULT_BUF;
1.17 guenther 905: Sflag = sflag = vflag = rtableid = 0;
1.11 claudio 906: kflag = NULL;
907: rflag = DEFAULT_STATS_INTERVAL;
1.1 djm 908:
1.9 claudio 909: while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) {
1.1 djm 910: switch (ch) {
911: case 'l':
912: list_kvars();
913: exit(0);
914: case 'k':
915: if ((tmp = strdup(optarg)) == NULL)
916: errx(1, "strdup");
917: kflag = check_prepare_kvars(tmp);
918: free(tmp);
919: break;
920: case 'r':
921: rflag = strtonum(optarg, 0, 60 * 60 * 24 * 1000,
922: &errstr);
923: if (errstr != NULL)
924: errx(1, "statistics interval is %s: %s",
925: errstr, optarg);
926: break;
927: case 'p':
928: port = optarg;
929: break;
930: case 's':
931: sflag = 1;
932: break;
933: case 'S':
934: Sflag = strtonum(optarg, 0, 1024*1024*1024,
935: &errstr);
936: if (errstr != NULL)
937: errx(1, "receive space interval is %s: %s",
938: errstr, optarg);
939: break;
940: case 'B':
941: Bflag = strtonum(optarg, 0, 1024*1024*1024,
942: &errstr);
943: if (errstr != NULL)
944: errx(1, "read/write buffer size is %s: %s",
945: errstr, optarg);
946: break;
947: case 'v':
1.7 henning 948: vflag++;
1.9 claudio 949: break;
950: case 'V':
1.17 guenther 951: rtableid = (unsigned int)strtonum(optarg, 0,
1.9 claudio 952: RT_TABLEID_MAX, &errstr);
953: if (errstr)
1.17 guenther 954: errx(1, "rtable value is %s: %s",
1.9 claudio 955: errstr, optarg);
1.1 djm 956: break;
1.4 henning 957: case 'n':
958: nconn = strtonum(optarg, 0, 65535, &errstr);
959: if (errstr != NULL)
960: errx(1, "number of connections is %s: %s",
961: errstr, optarg);
962: break;
1.1 djm 963: case 'h':
964: default:
965: usage();
966: }
967: }
968:
969: argv += optind;
970: argc -= optind;
971: if (argc != (sflag ? 0 : 1))
972: usage();
1.4 henning 973:
1.1 djm 974: if (!sflag)
975: host = argv[0];
976:
1.13 claudio 977: bzero(&hints, sizeof(hints));
978: hints.ai_socktype = SOCK_STREAM;
979: if (sflag)
1.4 henning 980: hints.ai_flags = AI_PASSIVE;
1.13 claudio 981: if ((herr = getaddrinfo(host, port, &hints, &aitop)) != 0) {
982: if (herr == EAI_SYSTEM)
983: err(1, "getaddrinfo");
984: else
985: errx(1, "getaddrinfo: %s", gai_strerror(herr));
1.1 djm 986: }
987:
988: if (kflag) {
989: if ((kvmh = kvm_openfiles(NULL, NULL, NULL,
990: O_RDONLY, kerr)) == NULL)
991: errx(1, "kvm_open: %s", kerr);
992: drop_gid();
993: if (kvm_nlist(kvmh, nl) < 0 || nl[0].n_type == 0)
994: errx(1, "kvm: no namelist");
995: } else
996: drop_gid();
997:
1.11 claudio 998: signal(SIGINT, exitsighand);
999: signal(SIGTERM, exitsighand);
1000: signal(SIGHUP, exitsighand);
1001: signal(SIGPIPE, SIG_IGN);
1002: signal(SIGALRM, alarmhandler);
1003:
1004: if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
1005: err(1, "getrlimit");
1006: if (rl.rlim_cur < MAX_FD)
1007: rl.rlim_cur = MAX_FD;
1008: if (setrlimit(RLIMIT_NOFILE, &rl))
1009: err(1, "setrlimit");
1010: if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
1011: err(1, "getrlimit");
1012:
1.1 djm 1013: if (sflag)
1.11 claudio 1014: serverloop(kvmh, nl[0].n_value, aitop);
1.1 djm 1015: else
1.13 claudio 1016: clientloop(kvmh, nl[0].n_value, aitop, nconn);
1.1 djm 1017:
1018: return 0;
1019: }