M ip/devip.c => ip/devip.c +3 -0
@@ 1084,6 1084,7 @@ Fsprotoclone(Proto *p, char *user)
{
Conv *c, **pp, **ep;
+retry:
c = nil;
ep = &p->conv[p->nc];
for(pp = p->conv; pp < ep; pp++) {
@@ 1120,6 1121,8 @@ Fsprotoclone(Proto *p, char *user)
}
}
if(pp >= ep) {
+ if(p->gc != nil && (*p->gc)(p))
+ goto retry;
return nil;
}
M ip/icmp.c => ip/icmp.c +1 -0
@@ 465,6 465,7 @@ icmpinit(Fs *fs)
icmp->stats = icmpstats;
icmp->ctl = nil;
icmp->advise = icmpadvise;
+ icmp->gc = nil;
icmp->ipproto = IP_ICMPPROTO;
icmp->nc = 16;
icmp->ptclsize = 0;
M ip/il.c => ip/il.c +1 -0
@@ 1341,6 1341,7 @@ ilinit(Fs *f)
il->advise = iladvise;
il->stats = ilxstats;
il->inuse = ilinuse;
+ il->gc = nil;
il->ipproto = IP_ILPROTO;
il->nc = Nchans;
il->ptclsize = sizeof(Ilcb);
M ip/ip.h => ip/ip.h +2 -1
@@ 27,7 27,7 @@ enum
Maxproto= 20,
Nhash= 64,
Maxincall= 5,
- Nchans= 256,
+ Nchans= 512,
MAClen= 16, /* longest mac address */
MAXTTL= 255,
@@ 218,6 218,7 @@ struct Proto
int (*local)(Conv*, char*, int);
int (*remote)(Conv*, char*, int);
int (*inuse)(Conv*);
+ int (*gc)(Proto*); /* returns true if any conversations are freed */
Fs *f; /* file system this proto is part of */
Conv **conv; /* array of conversations */
M ip/tcp.c => ip/tcp.c +53 -2
@@ 123,7 123,7 @@ struct Tcp
ushort wnd;
ushort urg;
ushort mss;
- ushort len; /* size of data */
+ ushort len; /* size of data */
};
typedef struct Reseq Reseq;
@@ 188,6 188,7 @@ struct Tcpctl
int mdev; /* Mean deviation of round trip */
int kacounter; /* count down for keep alive */
uint sndsyntime; /* time syn sent */
+ ulong time; /* time Finwait2 or Syn_received was sent */
Tcphdr protohdr; /* prototype header */
};
@@ 1340,6 1341,7 @@ tcpiput(Proto *tcp, uchar*, Block *bp)
if(seg.flags & SYN) {
procsyn(s, &seg);
tcpsndsyn(tcb);
+ tcb->time = msec;
tcpsetstate(s, Syn_received);
if(length != 0 || (seg.flags & FIN))
break;
@@ 1365,8 1367,10 @@ tcpiput(Proto *tcp, uchar*, Block *bp)
tcpsynackrtt(s);
tcpsetstate(s, Established);
}
- else
+ else {
+ tcb->time = msec;
tcpsetstate(s, Syn_received);
+ }
if(length != 0 || (seg.flags & FIN))
break;
@@ 1460,6 1464,7 @@ tcpiput(Proto *tcp, uchar*, Block *bp)
tcphalt(tpriv, &tcb->rtt_timer);
tcphalt(tpriv, &tcb->acktimer);
tcpsetkacounter(tcb);
+ tcb->time = msec;
tcpsetstate(s, Finwait2);
tcb->katimer.start = MSL2 * (1000 / MSPTICK);
tcpgo(tpriv, &tcb->katimer);
@@ 2238,6 2243,51 @@ tcpstats(Proto *tcp, char *buf, int len)
tpriv->tstats.OutRsts);
}
+/*
+ * garbage collect any stale conversations:
+ * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
+ * - Finwait2 after 5 minutes
+ *
+ * this is called whenever we run out of channels. Both checks are
+ * of questionable validity so we try to use them only when we're
+ * up against the wall.
+ */
+int
+tcpgc(Proto *tcp)
+{
+ Conv *c, **pp, **ep;
+ int n;
+ Tcpctl *tcb;
+
+
+ n = 0;
+ ep = &tcp->conv[tcp->nc];
+ for(pp = tcp->conv; pp < ep; pp++) {
+ c = *pp;
+ if(c == nil)
+ break;
+ if(!canqlock(c))
+ continue;
+ tcb = (Tcpctl*)c->ptcl;
+ switch(tcb->state){
+ case Syn_received:
+ if(msec - tcb->time > 5000){
+ localclose(c, "timed out");
+ n++;
+ }
+ break;
+ case Finwait2:
+ if(msec - tcb->time > 5*60*1000){
+ localclose(c, "timed out");
+ n++;
+ }
+ break;
+ }
+ qunlock(c);
+ }
+ return n;
+}
+
void
tcpinit(Fs *fs)
{
@@ 2258,6 2308,7 @@ tcpinit(Fs *fs)
tcp->advise = tcpadvise;
tcp->stats = tcpstats;
tcp->inuse = tcpinuse;
+ tcp->gc = tcpgc;
tcp->ipproto = IP_TCPPROTO;
tcp->nc = Nchans;
tcp->ptclsize = sizeof(Tcpctl);
M ip/udp.c => ip/udp.c +1 -0
@@ 497,6 497,7 @@ udpinit(Fs *fs)
udp->rcv = udpiput;
udp->advise = udpadvise;
udp->stats = udpstats;
+ udp->gc = nil;
udp->ipproto = IP_UDPPROTO;
udp->nc = Nchans;
udp->ptclsize = sizeof(Udpcb);