[BACK]Return to tcpbench.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tcpbench

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: }