Use the new RNG for the networking stack
[akaros.git] / kern / src / net / tcp.c
index 5f8c942..841919c 100644 (file)
@@ -1,4 +1,31 @@
-// INFERNO
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ *
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
 #include <vfs.h>
 #include <kfs.h>
 #include <slab.h>
@@ -80,6 +107,7 @@ enum {
        RETRAN = 4,
        ACTIVE = 8,
        SYNACK = 16,
+       TSO = 32,
 
        LOGAGAIN = 3,
        LOGDGAIN = 2,
@@ -116,8 +144,8 @@ struct Tcptimer {
        Tcptimer *prev;
        Tcptimer *readynext;
        int state;
-       int start;
-       int count;
+       uint64_t start;
+       uint64_t count;
        void (*func) (void *);
        void *arg;
 };
@@ -253,8 +281,8 @@ struct Tcpctl {
        int srtt;                                       /* Shortened round trip */
        int mdev;                                       /* Mean deviation of round trip */
        int kacounter;                          /* count down for keep alive */
-       unsigned int sndsyntime;        /* time syn sent */
-       uint32_t time;                          /* time Finwait2 or Syn_received was sent */
+       uint64_t sndsyntime;            /* time syn sent */
+       uint64_t time;                          /* time Finwait2 or Syn_received was sent */
        int nochecksum;                         /* non-zero means don't send checksums */
        int flgcnt;                                     /* number of flags in the sequence (FIN,SEQ) */
 
@@ -290,7 +318,7 @@ struct Limbo {
        uint16_t mss;                           /* mss from the other end */
        uint16_t rcvscale;                      /* how much to scale rcvd windows */
        uint16_t sndscale;                      /* how much to scale sent windows */
-       uint32_t lastsend;                      /* last time we sent a synack */
+       uint64_t lastsend;                      /* last time we sent a synack */
        uint8_t version;                        /* v4 or v6 */
        uint8_t rexmits;                        /* number of retransmissions */
 };
@@ -454,7 +482,7 @@ static int tcpstate(struct conv *c, char *state, int n)
        s = (Tcpctl *) (c->ptcl);
 
        return snprintf(state, n,
-                                       "%s qin %d qout %d srtt %d mdev %d cwin %lu swin %lu>>%d rwin %lu>>%d timer.start %d timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
+                                       "%s qin %d qout %d srtt %d mdev %d cwin %u swin %u>>%d rwin %u>>%d timer.start %llu timer.count %llu rerecv %d katimer.start %d katimer.count %d\n",
                                        tcpstates[s->state],
                                        c->rq ? qlen(c->rq) : 0,
                                        c->wq ? qlen(c->wq) : 0,
@@ -536,11 +564,11 @@ void tcpkick(void *x)
 
        tcb = (Tcpctl *) s->ptcl;
 
+       qlock(&s->qlock);
        if (waserror()) {
                qunlock(&s->qlock);
                nexterror();
        }
-       qlock(&s->qlock);
 
        switch (tcb->state) {
                case Syn_sent:
@@ -585,11 +613,11 @@ void tcpacktimer(void *v)
        s = v;
        tcb = (Tcpctl *) s->ptcl;
 
+       qlock(&s->qlock);
        if (waserror()) {
                qunlock(&s->qlock);
                nexterror();
        }
-       qlock(&s->qlock);
        if (tcb->state != Closed) {
                tcb->flags |= FORCE;
                tcprcvwin(s);
@@ -602,7 +630,7 @@ void tcpacktimer(void *v)
 static void tcpcreate(struct conv *c)
 {
        c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
-       c->wq = qopen((3 * QMAX) / 2, Qkick, tcpkick, c);
+       c->wq = qopen(8 * QMAX, Qkick, tcpkick, c);
 }
 
 static void timerstate(struct tcppriv *priv, Tcptimer * t, int newstate)
@@ -648,7 +676,7 @@ void tcpackproc(void *a)
        priv = tcp->priv;
 
        for (;;) {
-               udelay_sched(MSPTICK * 1000);
+               kthread_usleep(MSPTICK * 1000);
 
                qlock(&priv->tl);
                timeo = NULL;
@@ -736,17 +764,20 @@ void localclose(struct conv *s, char *reason)
 
        if (tcb->state == Syn_sent)
                Fsconnected(s, reason);
-       if (s->state == Announced)
-               rendez_wakeup(&s->listenr);
 
        qhangup(s->rq, reason);
        qhangup(s->wq, reason);
 
        tcpsetstate(s, Closed);
+
+       /* listener will check the rq state */
+       if (s->state == Announced)
+               rendez_wakeup(&s->listenr);
 }
 
 /* mtu (- TCP + IP hdr len) of 1st hop */
-int tcpmtu(struct Proto *tcp, uint8_t * addr, int version, int *scale)
+int tcpmtu(struct Proto *tcp, uint8_t * addr, int version, int *scale,
+          uint8_t *flags)
 {
        struct Ipifc *ifc;
        int mtu;
@@ -765,6 +796,8 @@ int tcpmtu(struct Proto *tcp, uint8_t * addr, int version, int *scale)
                                mtu = ifc->maxtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE);
                        break;
        }
+       *flags &= ~TSO;
+
        if (ifc != NULL) {
                if (ifc->mbps > 100)
                        *scale = HaveWS | 3;
@@ -772,6 +805,8 @@ int tcpmtu(struct Proto *tcp, uint8_t * addr, int version, int *scale)
                        *scale = HaveWS | 1;
                else
                        *scale = HaveWS | 0;
+               if (ifc->feat & NETF_TSO)
+                       *flags |= TSO;
        } else
                *scale = HaveWS | 0;
 
@@ -854,14 +889,15 @@ void tcpstart(struct conv *s, int mode)
 {
        Tcpctl *tcb;
        struct tcppriv *tpriv;
-       char kpname[KNAMELEN];
+       /* tcpackproc needs to free this if it ever exits */
+       char *kpname = kmalloc(KNAMELEN, KMALLOC_WAIT);
 
        tpriv = s->p->priv;
 
        if (tpriv->ackprocstarted == 0) {
                qlock(&tpriv->apl);
                if (tpriv->ackprocstarted == 0) {
-                       snprintf(kpname, sizeof(kpname), "#I%dtcpack", s->p->f->dev);
+                       snprintf(kpname, KNAMELEN, "#I%dtcpack", s->p->f->dev);
                        ktask(kpname, tcpackproc, s->p);
                        tpriv->ackprocstarted = 1;
                }
@@ -1060,8 +1096,11 @@ struct block *htontcp4(Tcp * tcph, struct block *data, Tcp4hdr * ph,
        if (tcb != NULL && tcb->nochecksum) {
                h->tcpcksum[0] = h->tcpcksum[1] = 0;
        } else {
-               csum = ptclcsum(data, TCP4_IPLEN, hdrlen + dlen + TCP4_PHDRSIZE);
+               csum = ~ptclcsum(data, TCP4_IPLEN, TCP4_PHDRSIZE);
                hnputs(h->tcpcksum, csum);
+               data->checksum_start = TCP4_IPLEN + TCP4_PHDRSIZE;
+               data->checksum_offset = ph->tcpcksum - ph->tcpsport;
+               data->flag |= Btcpck;
        }
 
        return data;
@@ -1196,7 +1235,7 @@ int ntohtcp4(Tcp * tcph, struct block **bpp)
  */
 void tcpsndsyn(struct conv *s, Tcpctl * tcb)
 {
-       tcb->iss = (nrand(1 << 16) << 16) | nrand(1 << 16);
+       urandom_read(&tcb->iss, sizeof(tcb->iss));
        tcb->rttseq = tcb->iss;
        tcb->snd.wl2 = tcb->iss;
        tcb->snd.una = tcb->iss;
@@ -1207,7 +1246,8 @@ void tcpsndsyn(struct conv *s, Tcpctl * tcb)
        tcb->sndsyntime = NOW;
 
        /* set desired mss and scale */
-       tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
+       tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale,
+                         &tcb->flags);
 }
 
 void
@@ -1220,7 +1260,7 @@ sndrst(struct Proto *tcp, uint8_t * source, uint8_t * dest,
        Tcp4hdr ph4;
        Tcp6hdr ph6;
 
-       netlog(tcp->f, Logtcp, "sndrst: %s", reason);
+       netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
 
        tpriv = tcp->priv;
 
@@ -1309,7 +1349,7 @@ char *tcphangup(struct conv *s)
                poperror();
                return commonerror();
        }
-       if (s->raddr != 0) {
+       if (ipcmp(s->raddr, IPnoaddr)) {
                /* discard error style, poperror regardless */
                if (!waserror()) {
                        seg.flags = RST | ACK;
@@ -1352,6 +1392,7 @@ int sndsynack(struct Proto *tcp, Limbo * lp)
        Tcp6hdr ph6;
        Tcp seg;
        int scale;
+       uint8_t flag = 0;
 
        /* make pseudo header */
        switch (lp->version) {
@@ -1383,7 +1424,7 @@ int sndsynack(struct Proto *tcp, Limbo * lp)
        seg.ack = lp->irs + 1;
        seg.flags = SYN | ACK;
        seg.urg = 0;
-       seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
+       seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale, &flag);
        seg.wnd = QMAX;
 
        /* if the other side set scale, we should too */
@@ -1467,7 +1508,7 @@ limbo(struct conv *s, uint8_t * source, uint8_t * dest, Tcp * seg, int version)
                lp->mss = seg->mss;
                lp->rcvscale = seg->ws;
                lp->irs = seg->seq;
-               lp->iss = (nrand(1 << 16) << 16) | nrand(1 << 16);
+               urandom_read(&lp->iss, sizeof(lp->iss));
        }
 
        if (sndsynack(s->p, lp) < 0) {
@@ -1486,7 +1527,7 @@ static void limborexmit(struct Proto *tcp)
        Limbo **l, *lp;
        int h;
        int seen;
-       uint32_t now;
+       uint64_t now;
 
        tpriv = tcp->priv;
 
@@ -1590,7 +1631,7 @@ static struct conv *tcpincoming(struct conv *s, Tcp * segp, uint8_t * src,
        h = hashipa(src, segp->source);
        for (l = &tpriv->lht[h]; (lp = *l) != NULL; l = &lp->next) {
                netlog(s->p->f, Logtcp,
-                          "tcpincoming s %I,0x%x/%I,0x%x d %I,0x%x/%I,0x%x v %d/%d", src,
+                          "tcpincoming s %I!%d/%I!%d d %I!%d/%I!%d v %d/%d\n", src,
                           segp->source, lp->raddr, lp->rport, dst, segp->dest, lp->laddr,
                           lp->lport, version, lp->version);
 
@@ -1604,7 +1645,7 @@ static struct conv *tcpincoming(struct conv *s, Tcp * segp, uint8_t * src,
 
                /* we're assuming no data with the initial SYN */
                if (segp->seq != lp->irs + 1 || segp->ack != lp->iss + 1) {
-                       netlog(s->p->f, Logtcp, "tcpincoming s 0x%lx/0x%lx a 0x%lx 0x%lx",
+                       netlog(s->p->f, Logtcp, "tcpincoming s 0x%lx/0x%lx a 0x%lx 0x%lx\n",
                                   segp->seq, lp->irs + 1, segp->ack, lp->iss + 1);
                        lp = NULL;
                } else {
@@ -1732,7 +1773,7 @@ int seq_ge(uint32_t x, uint32_t y)
 void tcpsynackrtt(struct conv *s)
 {
        Tcpctl *tcb;
-       int delta;
+       uint64_t delta;
        struct tcppriv *tpriv;
 
        tcb = (Tcpctl *) s->ptcl;
@@ -2000,7 +2041,7 @@ void tcpiput(struct Proto *tcp, struct Ipifc *unused, struct block *bp)
        /* Look for a matching conversation */
        s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
        if (s == NULL) {
-               netlog(f, Logtcp, "iphtlook failed");
+               netlog(f, Logtcp, "iphtlook failed\n");
 reset:
                qunlock(&tcp->qlock);
                sndrst(tcp, source, dest, length, &seg, version, "no conversation");
@@ -2068,7 +2109,7 @@ reset:
                        }
                        if (seg.flags & RST) {
                                if (seg.flags & ACK)
-                                       localclose(s, Econrefused);
+                                       localclose(s, errno_to_string(ECONNREFUSED));
                                goto raise;
                        }
 
@@ -2173,7 +2214,7 @@ reset:
                                                 s->raddr, s->rport, s->laddr, s->lport, tcb->rcv.nxt,
                                                 seg.seq);
                        }
-                       localclose(s, Econrefused);
+                       localclose(s, errno_to_string(ECONNREFUSED));
                        goto raise;
                }
 
@@ -2439,8 +2480,33 @@ void tcpoutput(struct conv *s)
                                   tcb->snd.wnd, tcb->cwind);
                if (usable < ssize)
                        ssize = usable;
-               if (tcb->mss < ssize)
-                       ssize = tcb->mss;
+               if (ssize > tcb->mss) {
+                       if ((tcb->flags & TSO) == 0) {
+                               ssize = tcb->mss;
+                       } else {
+                               int segs, window;
+
+                               /*  Don't send too much.  32K is arbitrary..
+                                */
+                               if (ssize > 32 * 1024)
+                                       ssize = 32 * 1024;
+
+                               /* Clamp xmit to an integral MSS to
+                                * avoid ragged tail segments causing
+                                * poor link utilization.  Also
+                                * account for each segment sent in
+                                * msg heuristic, and round up to the
+                                * next multiple of 4, to ensure we
+                                * still yeild.
+                                */
+                               segs = ssize / tcb->mss;
+                               ssize = segs * tcb->mss;
+                               msgs += segs;
+                               if (segs > 3)
+                                       msgs = (msgs + 4) & ~3;
+                       }
+               }
+
                dsize = ssize;
                seg.urg = 0;
 
@@ -2496,6 +2562,10 @@ void tcpoutput(struct conv *s)
                                seg.flags |= FIN;
                                dsize--;
                        }
+                       if (BLEN(bp) > tcb->mss) {
+                               bp->flag |= Btso;
+                               bp->mss = tcb->mss;
+                       }
                }
 
                if (sent + dsize == sndcnt)
@@ -2610,7 +2680,7 @@ void tcpsendka(struct conv *s)
        seg.mss = 0;
        seg.ws = 0;
        if (tcpporthogdefense)
-               seg.seq = tcb->snd.una - (1 << 30) - nrand(1 << 20);
+               urandom_read(&seg.seq, sizeof(seg.seq));
        else
                seg.seq = tcb->snd.una - 1;
        seg.ack = tcb->rcv.nxt;
@@ -2666,14 +2736,14 @@ void tcpkeepalive(void *v)
 
        s = v;
        tcb = (Tcpctl *) s->ptcl;
+       qlock(&s->qlock);
        if (waserror()) {
                qunlock(&s->qlock);
                nexterror();
        }
-       qlock(&s->qlock);
        if (tcb->state != Closed) {
                if (--(tcb->kacounter) <= 0) {
-                       localclose(s, Etimedout);
+                       localclose(s, errno_to_string(ETIMEDOUT));
                } else {
                        tcpsendka(s);
                        tcpgo(s->p->priv, &tcb->katimer);
@@ -2752,11 +2822,11 @@ void tcptimeout(void *arg)
        tpriv = s->p->priv;
        tcb = (Tcpctl *) s->ptcl;
 
+       qlock(&s->qlock);
        if (waserror()) {
                qunlock(&s->qlock);
                nexterror();
        }
-       qlock(&s->qlock);
        switch (tcb->state) {
                default:
                        tcb->backoff++;
@@ -2766,10 +2836,10 @@ void tcptimeout(void *arg)
                                maxback = MAXBACKMS;
                        tcb->backedoff += tcb->timer.start * MSPTICK;
                        if (tcb->backedoff >= maxback) {
-                               localclose(s, Etimedout);
+                               localclose(s, errno_to_string(ETIMEDOUT));
                                break;
                        }
-                       netlog(s->p->f, Logtcprxmt, "timeout rexmit 0x%lx %d/%d\n",
+                       netlog(s->p->f, Logtcprxmt, "timeout rexmit 0x%lx %llu/%llu\n",
                                   tcb->snd.una, tcb->timer.start, NOW);
                        tcpsettimer(tcb);
                        tcprxmit(s);
@@ -3051,7 +3121,7 @@ int tcpstats(struct Proto *tcp, char *buf, int len)
        p = buf;
        e = p + len;
        for (i = 0; i < Nstats; i++)
-               p = seprintf(p, e, "%s: %lu\n", statnames[i], priv->stats[i]);
+               p = seprintf(p, e, "%s: %u\n", statnames[i], priv->stats[i]);
        return p - buf;
 }