Annotation of src/usr.bin/tcpbench/tcpbench.c, Revision 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: }