net: Fix double-free snoop bug
[akaros.git] / kern / src / net / tcp.c
index 14c5b23..d544fd9 100644 (file)
@@ -501,6 +501,14 @@ static void tcpannounce(struct conv *c, char **argv, int argc)
        Fsconnected(c, NULL);
 }
 
+static void tcpbypass(struct conv *cv, char **argv, int argc)
+{
+       struct tcppriv *tpriv = cv->p->priv;
+
+       Fsstdbypass(cv, argv, argc);
+       iphtadd(&tpriv->ht, cv);
+}
+
 static void tcpshutdown(struct conv *c, int how)
 {
        Tcpctl *tcb = (Tcpctl*)c->ptcl;
@@ -1988,6 +1996,12 @@ void tcpiput(struct Proto *tcp, struct Ipifc *unused, struct block *bp)
                        return;
                }
 
+               s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
+               if (s && s->state == Bypass) {
+                       bypass_or_drop(s, bp);
+                       return;
+               }
+
                /* trim the packet to the size claimed by the datagram */
                length -= hdrlen + TCP4_PKT;
                bp = trimblock(bp, hdrlen + TCP4_PKT, length);
@@ -2029,6 +2043,12 @@ void tcpiput(struct Proto *tcp, struct Ipifc *unused, struct block *bp)
                        return;
                }
 
+               s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
+               if (s && s->state == Bypass) {
+                       bypass_or_drop(s, bp);
+                       return;
+               }
+
                /* trim the packet to the size claimed by the datagram */
                length -= hdrlen;
                bp = trimblock(bp, hdrlen + TCP6_PKT, length);
@@ -2040,20 +2060,19 @@ void tcpiput(struct Proto *tcp, struct Ipifc *unused, struct block *bp)
                }
        }
 
-       /* lock protocol while searching for a conversation */
-       qlock(&tcp->qlock);
-
-       /* Look for a matching conversation */
-       s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
+       /* s, the conv matching the n-tuple, was set above */
        if (s == NULL) {
                netlog(f, Logtcp, "iphtlook failed\n");
 reset:
-               qunlock(&tcp->qlock);
                sndrst(tcp, source, dest, length, &seg, version, "no conversation");
                freeblist(bp);
                return;
        }
 
+       /* lock protocol for unstate Plan 9 invariants.  funcs like limbo or
+        * incoming might rely on it. */
+       qlock(&tcp->qlock);
+
        /* if it's a listener, look for the right flags and get a new conv */
        tcb = (Tcpctl *) s->ptcl;
        if (tcb->state == Listen) {
@@ -2077,8 +2096,10 @@ reset:
                 *  return it in state Syn_received
                 */
                s = tcpincoming(s, &seg, source, dest, version);
-               if (s == NULL)
+               if (s == NULL) {
+                       qunlock(&tcp->qlock);
                        goto reset;
+               }
        }
 
        /* The rest of the input state machine is run with the control block
@@ -3062,7 +3083,6 @@ void tcpadvise(struct Proto *tcp, struct block *bp, char *msg)
        }
 
        /* Look for a connection */
-       qlock(&tcp->qlock);
        for (p = tcp->conv; *p; p++) {
                s = *p;
                tcb = (Tcpctl *) s->ptcl;
@@ -3072,7 +3092,6 @@ void tcpadvise(struct Proto *tcp, struct block *bp, char *msg)
                                        if (ipcmp(s->raddr, dest) == 0)
                                                if (ipcmp(s->laddr, source) == 0) {
                                                        qlock(&s->qlock);
-                                                       qunlock(&tcp->qlock);
                                                        switch (tcb->state) {
                                                                case Syn_sent:
                                                                        localclose(s, msg);
@@ -3083,7 +3102,6 @@ void tcpadvise(struct Proto *tcp, struct block *bp, char *msg)
                                                        return;
                                                }
        }
-       qunlock(&tcp->qlock);
        freeblist(bp);
 }
 
@@ -3197,6 +3215,7 @@ void tcpinit(struct Fs *fs)
        tcp->name = "tcp";
        tcp->connect = tcpconnect;
        tcp->announce = tcpannounce;
+       tcp->bypass = tcpbypass;
        tcp->ctl = tcpctl;
        tcp->state = tcpstate;
        tcp->create = tcpcreate;
@@ -3208,7 +3227,7 @@ void tcpinit(struct Fs *fs)
        tcp->inuse = tcpinuse;
        tcp->gc = tcpgc;
        tcp->ipproto = IP_TCPPROTO;
-       tcp->nc = scalednconv();
+       tcp->nc = 4096;
        tcp->ptclsize = sizeof(Tcpctl);
        tpriv->stats[MaxConn] = tcp->nc;