[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

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