M alphapc/clock.c => alphapc/clock.c +0 -13
@@ 94,16 94,3 @@ clock(Ureg *ureg)
timerintr(ureg, 0);
}
-
-ulong
-TK2MS(ulong ticks)
-{
- uvlong t, hz;
-
- t = ticks;
- hz = HZ;
- t *= 1000L;
- t = t/hz;
- ticks = t;
- return ticks;
-}
M alphapc/fns.h => alphapc/fns.h +0 -1
@@ 110,7 110,6 @@ void upafree(ulong, int);
#define userureg(ur) ((ur)->status & UMODE)
void wrent(int, void*);
void wrvptptr(uvlong);
-ulong TK2MS(ulong); /* ticks to milliseconds */
#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
#define kmapperm(x) kmap(x)
M bitsy/clock.c => bitsy/clock.c +0 -13
@@ 162,16 162,3 @@ microdelay(int µs)
}
}
}
-
-ulong
-TK2MS(ulong ticks)
-{
- uvlong t, hz;
-
- t = ticks;
- hz = HZ;
- t *= 1000L;
- t = t/hz;
- ticks = t;
- return ticks;
-}
M bitsy/fns.h => bitsy/fns.h +0 -1
@@ 121,7 121,6 @@ int unsac(uchar*, uchar*, int, int);
void vectors(void);
void vtable(void);
void wbflush(void);
-ulong TK2MS(ulong); /* ticks to milliseconds */
#define KADDR(a) (void*)mmu_kaddr((ulong)(a))
#define PADDR(a) mmu_paddr((ulong)(a))
M ip/arp.c => ip/arp.c +9 -9
@@ 124,11 124,11 @@ newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt)
*l = a;
memmove(a->ip, ip, sizeof(a->ip));
- a->used = msec;
+ a->used = NOW;
a->time = 0;
a->type = m;
- a->rxtat = msec + ReTransTimer;
+ a->rxtat = NOW + ReTransTimer;
a->rxtsrem = MAX_MULTICAST_SOLICIT;
a->ifc = ifc;
a->ifcid = ifc->ifcid;
@@ 227,7 227,7 @@ arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac)
a = newarp6(arp, ip, ifc, (version != V4));
a->state = AWAIT;
}
- a->used = msec;
+ a->used = NOW;
if(a->state == AWAIT){
if(bp != nil){
if(a->hold)
@@ 280,7 280,7 @@ arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
memmove(a->mac, mac, type->maclen);
a->type = type;
a->state = AOK;
- a->used = msec;
+ a->used = NOW;
bp = a->hold;
a->hold = nil;
qunlock(arp);
@@ 375,7 375,7 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
freeb(bp);
bp = next;
}
- a->used = msec;
+ a->used = NOW;
return;
}
}
@@ 532,7 532,7 @@ rxmitsols(Arp *arp)
nrxt = 0;
goto dodrops; //return nrxt;
}
- nrxt = a->rxtat - msec;
+ nrxt = a->rxtat - NOW;
if(nrxt > 3*ReTransTimer/4)
goto dodrops; //return nrxt;
@@ 589,14 589,14 @@ rxmitsols(Arp *arp)
*l = a;
a->rxtsrem--;
a->nextrxt = nil;
- a->time = msec;
- a->rxtat = msec + ReTransTimer;
+ a->time = NOW;
+ a->rxtat = NOW + ReTransTimer;
a = arp->rxmt;
if(a==nil)
nrxt = 0;
else
- nrxt = a->rxtat - msec;
+ nrxt = a->rxtat - NOW;
dodrops:
xp = arp->dropf;
M ip/ethermedium.c => ip/ethermedium.c +5 -5
@@ 435,7 435,7 @@ sendarp(Ipifc *ifc, Arpent *a)
Etherrock *er = ifc->arg;
/* don't do anything if it's been less than a second since the last */
- if(msec - a->time < 1000){
+ if(NOW - a->time < 1000){
arprelease(er->f->arp, a);
return;
}
@@ 449,7 449,7 @@ sendarp(Ipifc *ifc, Arpent *a)
}
/* try to keep it around for a second more */
- a->time = msec;
+ a->time = NOW;
arprelease(er->f->arp, a);
n = sizeof(Etherarp);
@@ 486,7 486,7 @@ resolveaddr6(Ipifc *ifc, Arpent *a)
uchar ipsrc[IPaddrlen];
/* don't do anything if it's been less than a second since the last */
- if(msec - a->time < ReTransTimer){
+ if(NOW - a->time < ReTransTimer){
arprelease(er->f->arp, a);
return;
}
@@ 500,8 500,8 @@ resolveaddr6(Ipifc *ifc, Arpent *a)
}
/* try to keep it around for a second more */
- a->time = msec;
- a->rxtat = msec + ReTransTimer;
+ a->time = NOW;
+ a->rxtat = NOW + ReTransTimer;
if(a->rxtsrem <= 0) {
arprelease(er->f->arp, a);
return;
M ip/il.c => ip/il.c +18 -18
@@ 387,7 387,7 @@ ilkick(Conv *c)
ack = ic->recvd;
hnputl(ih->ilack, ack);
ic->acksent = ack;
- ic->acktime = msec + AckDelay;
+ ic->acktime = NOW + AckDelay;
ih->iltype = Ildata;
ih->ilspec = 0;
ih->ilsum[0] = 0;
@@ 407,7 407,7 @@ ilkick(Conv *c)
ic->rttlen = dlen + IL_IPSIZE + IL_HDRSIZE;
}
- if(later(msec, ic->timeout, nil))
+ if(later(NOW, ic->timeout, nil))
ilsettimeout(ic);
ipoput4(f, bp, 0, c->ttl, c->tos);
priv->stats[OutMsgs]++;
@@ 641,8 641,8 @@ _ilprocess(Conv *s, Ilhdr *h, Block *bp)
ic = (Ilcb*)s->ptcl;
- ic->lastrecv = msec;
- ic->querytime = msec + QueryTime;
+ ic->lastrecv = NOW;
+ ic->querytime = NOW + QueryTime;
priv = s->p->priv;
priv->stats[InMsgs]++;
@@ 1007,7 1007,7 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec)
hnputl(ih->ilack, ack);
ic = (Ilcb*)ipc->ptcl;
ic->acksent = ack;
- ic->acktime = msec;
+ ic->acktime = NOW;
ttl = ipc->ttl;
tos = ipc->tos;
}
@@ 1076,7 1076,7 @@ ilsettimeout(Ilcb *ic)
+ AckDelay;
if(pt > MaxTimeout)
pt = MaxTimeout;
- ic->timeout = msec + pt;
+ ic->timeout = NOW + pt;
}
void
@@ 1093,10 1093,10 @@ ilbackoff(Ilcb *ic)
pt = pt + (pt>>1);
if(pt > MaxTimeout)
pt = MaxTimeout;
- ic->timeout = msec + pt;
+ ic->timeout = NOW + pt;
if(ic->fasttimeout)
- ic->timeout = msec+Iltickms;
+ ic->timeout = NOW+Iltickms;
ic->rexmit++;
}
@@ 1144,7 1144,7 @@ loop:
case Illistening:
break;
case Ilclosing:
- if(later(msec, ic->timeout, "timeout0")) {
+ if(later(NOW, ic->timeout, "timeout0")) {
if(ic->rexmit > MaxRexmit){
ilhangup(p, nil);
break;
@@ 1156,7 1156,7 @@ loop:
case Ilsyncee:
case Ilsyncer:
- if(later(msec, ic->timeout, "timeout1")) {
+ if(later(NOW, ic->timeout, "timeout1")) {
if(ic->rexmit > MaxRexmit){
ilhangup(p, etime);
break;
@@ 1168,21 1168,21 @@ loop:
case Ilestablished:
if(ic->recvd != ic->acksent)
- if(later(msec, ic->acktime, "acktime"))
+ if(later(NOW, ic->acktime, "acktime"))
ilsendctl(p, nil, Ilack, ic->next, ic->recvd, 0);
- if(later(msec, ic->querytime, "querytime")){
- if(later(msec, ic->lastrecv+DeathTime, "deathtime")){
+ if(later(NOW, ic->querytime, "querytime")){
+ if(later(NOW, ic->lastrecv+DeathTime, "deathtime")){
netlog(il->f, Logil, "il: hangup: deathtime\n");
ilhangup(p, etime);
break;
}
ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic));
- ic->querytime = msec + QueryTime;
+ ic->querytime = NOW + QueryTime;
}
if(ic->unacked != nil)
- if(later(msec, ic->timeout, "timeout2")) {
+ if(later(NOW, ic->timeout, "timeout2")) {
if(ic->rexmit > MaxRexmit){
netlog(il->f, Logil, "il: hangup: too many rexmits\n");
ilhangup(p, etime);
@@ 1218,8 1218,8 @@ ilcbinit(Ilcb *ic)
ic->delay = DefRtt<<LogAGain;
ic->mdev = DefRtt<<LogDGain;
ic->rate = DefByteRate<<LogAGain;
- ic->querytime = msec + QueryTime;
- ic->lastrecv = msec; /* or we'll timeout right away */
+ ic->querytime = NOW + QueryTime;
+ ic->lastrecv = NOW; /* or we'll timeout right away */
ilsettimeout(ic);
}
@@ 1253,7 1253,7 @@ ilstart(Conv *c, int type, int fasttimeout)
if(fasttimeout){
/* timeout if we can't connect quickly */
ic->fasttimeout = 1;
- ic->timeout = msec+Iltickms;
+ ic->timeout = NOW+Iltickms;
ic->rexmit = MaxRexmit - 4;
};
M ip/ip.c => ip/ip.c +2 -2
@@ 585,7 585,7 @@ ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
fnext = f->next; /* because ipfragfree4 changes the list */
if(f->src == src && f->dst == dst && f->id == id)
break;
- if(f->age < msec){
+ if(f->age < NOW){
ip->stats[ReasmTimeout]++;
ipfragfree4(ip, f);
}
@@ 765,7 765,7 @@ ipfragallo4(IP *ip)
ip->fragfree4 = f->next;
f->next = ip->flisthead4;
ip->flisthead4 = f;
- f->age = msec + 30000;
+ f->age = NOW + 30000;
return f;
}
M ip/ip.h => ip/ip.h +10 -8
@@ 68,7 68,7 @@ enum
};
/*
- * contained in each conversation
+ * one per conversation directory
*/
struct Conv
{
@@ 77,16 77,18 @@ struct Conv
int x; /* conversation index */
Proto* p;
- uchar laddr[IPaddrlen]; /* local IP address */
- uchar raddr[IPaddrlen]; /* remote IP address */
int restricted; /* remote port is restricted */
- ushort lport; /* local port number */
- ushort rport; /* remote port number */
uint ttl; /* max time to live */
uint tos; /* type of service */
- uchar ipversion; /* IPv4 or IPv6 */
int ignoreadvice; /* don't terminate connection on icmp errors */
+ uchar ipversion;
+ uchar laddr[IPaddrlen]; /* local IP address */
+ uchar raddr[IPaddrlen]; /* remote IP address */
+ ushort lport; /* local port number */
+ ushort rport; /* remote port number */
+
+
char *owner; /* protections */
int perm;
int inuse; /* opens of listen/data/ctl */
@@ 550,8 552,8 @@ extern void v4tov6(uchar *v6, uchar *v4);
extern int v6tov4(uchar *v4, uchar *v6);
extern int eipfmt(Fmt*);
-#define ipcmp(x, y) memcmp(x, y, IPaddrlen)
#define ipmove(x, y) memmove(x, y, IPaddrlen)
+#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
extern uchar IPv4bcast[IPaddrlen];
extern uchar IPv4bcastobs[IPaddrlen];
@@ 561,7 563,7 @@ extern uchar IPnoaddr[IPaddrlen];
extern uchar v4prefix[IPaddrlen];
extern uchar IPallbits[IPaddrlen];
-#define msec TK2MS(MACHP(0)->ticks)
+#define NOW TK2MS(MACHP(0)->ticks)
/*
* media
M ip/ipifc.c => ip/ipifc.c +6 -6
@@ 445,7 445,7 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
lifc->autoflag = 1;
lifc->validlt = 0xffffffff;
lifc->preflt = 0xffffffff;
- lifc->origint = msec / 10^3;
+ lifc->origint = NOW / 10^3;
}
lifc->next = nil;
@@ 940,7 940,7 @@ static void
iplinkfree(Iplink *p)
{
Iplink **l, *np;
- ulong now = msec;
+ ulong now = NOW;
l = &freeiplink;
for(np = *l; np; np = *l){
@@ 959,7 959,7 @@ static void
ipselffree(Ipself *p)
{
Ipself **l, *np;
- ulong now = msec;
+ ulong now = NOW;
l = &freeipself;
for(np = *l; np; np = *l){
@@ 1205,7 1205,7 @@ v6addrtype(uchar *addr)
return unknownv6;
}
-#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (msec/10^3) ) || ( (lifc)->preflt == 0xffffffff ))
+#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (NOW/10^3) ) || ( (lifc)->preflt == 0xffffffff ))
void
findprimaryip6(Fs *f, uchar *local)
@@ 1732,7 1732,7 @@ ipifcaddgate6(Fs *f, Ipifc *ifc, char**argv, int argc)
r[j].ifc = ifc;
r[j].ifcid = ifc->ifcid;
ipmove(r[j].routeraddr, routeraddr);
- r[j].ltorigin = msec / 10^3;
+ r[j].ltorigin = NOW / 10^3;
r[j].rp.mflag = (mflag!=0);
r[j].rp.oflag = (oflag!=0);
r[j].rp.rxmitra = rxmitra;
@@ 1769,7 1769,7 @@ ipifcaddpref6(Ipifc *ifc, char**argv, int argc)
uchar autoflag = 1;
long validlt = 0xffffffff;
long preflt = 0xffffffff;
- long origint = msec / 10^3;
+ long origint = NOW / 10^3;
uchar prefix[IPaddrlen];
int plen = 64;
Iplifc *lifc;
M ip/ipv6.c => ip/ipv6.c +2 -2
@@ 490,7 490,7 @@ ipfragallo6(IP *ip)
ip->fragfree6 = f->next;
f->next = ip->flisthead6;
ip->flisthead6 = f;
- f->age = msec + 30000;
+ f->age = NOW + 30000;
return f;
}
@@ 599,7 599,7 @@ ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
fnext = f->next;
if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
break;
- if(f->age < msec){
+ if(f->age < NOW){
ip->stats[ReasmTimeout]++;
ipfragfree6(ip, f);
}
M ip/tcp.c => ip/tcp.c +366 -50
@@ 61,6 61,7 @@ enum
LOGAGAIN = 3,
LOGDGAIN = 2,
+
Closed = 0, /* Connection states */
Listen,
Syn_sent,
@@ 71,7 72,11 @@ enum
Close_wait,
Closing,
Last_ack,
- Time_wait
+ Time_wait,
+
+ Maxlimbo = 1000, /* maximum procs waiting for response to SYN ACK */
+ NLHT = 256, /* hash table size, must be a power of 2 */
+ LHTMASK = NLHT-1
};
/* Must correspond to the enumeration above */
@@ 95,6 100,10 @@ struct Tcptimer
void *arg;
};
+/*
+ * v4 and v6 pseudo headers used for
+ * checksuming tcp
+ */
typedef struct Tcp4hdr Tcp4hdr;
struct Tcp4hdr
{
@@ 143,7 152,12 @@ struct Tcp6hdr
uchar tcpmss[2];
};
-
+/*
+ * this represents the control info
+ * for a single packet. It is derived from
+ * a packet in ntohtcp{4,6}() and stuck into
+ * a packet in htontcp{4,6}().
+ */
typedef struct Tcp Tcp;
struct Tcp
{
@@ 158,6 172,10 @@ struct Tcp
ushort len; /* size of data */
};
+/*
+ * this header is malloc'd to thread together fragments
+ * waiting to be coalesced
+ */
typedef struct Reseq Reseq;
struct Reseq
{
@@ 202,7 220,6 @@ struct Tcpctl
ushort mss; /* Mean segment size */
int rerecv; /* Overlap of data rerecevived */
ushort window; /* Recevive window */
- int max_snd; /* Max send */
ulong last_ack; /* Last acknowledege received */
uchar backoff; /* Exponential backoff counter */
int backedoff; /* ms we've backed off for rexmits */
@@ 219,7 236,7 @@ struct Tcpctl
uint sndsyntime; /* time syn sent */
ulong time; /* time Finwait2 or Syn_received was sent */
int nochecksum; /* non-zero means don't send checksums */
- int flgcnt; /* 1 when we're waiting for a SYN/FIN ACK */
+ int flgcnt; /* number of flags in the sequence (FIN,SEQ) */
union {
Tcp4hdr tcp4hdr;
@@ 227,6 244,36 @@ struct Tcpctl
} protohdr; /* prototype header */
};
+/*
+ * New calls are put in limbo rather than having a conversation structure
+ * allocated. Thus, a SYN attack results in lots of limbo'd calls but not
+ * any real Conv structures mucking things up. Calls in limbo rexmit their
+ * SYN ACK every 250 ms up to 4 times, i.e., they disappear after 1 second.
+ *
+ * In particular they aren't on a listener's queue so that they don't figure
+ * in the input queue limit.
+ *
+ * If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
+ * of 70000 limbo'd calls. Not great for a linear list but doable. Therefore
+ * there is no hashing of this list.
+ */
+typedef struct Limbo Limbo;
+struct Limbo
+{
+ Limbo *next;
+
+ uchar laddr[IPaddrlen];
+ uchar raddr[IPaddrlen];
+ ushort lport;
+ ushort rport;
+ ulong irs; /* initial received sequence */
+ ulong iss; /* initial sent sequence */
+ ushort mss; /* mss from the other end */
+ ulong lastsend; /* last time we sent a synack */
+ uchar version; /* v4 or v6 */
+ uchar rexmits; /* number of retransmissions */
+};
+
int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
ushort tcp_mss = DEF_MSS; /* Maximum segment size to be sent */
@@ 275,18 322,24 @@ static char *statnames[] =
typedef struct Tcppriv Tcppriv;
struct Tcppriv
{
- Tcptimer *timers; /* List of active timers */
- QLock tl; /* Protect timer list */
+ /* List of active timers */
+ QLock tl;
+ Tcptimer *timers;
+
Rendez tcpr; /* used by tcpackproc */
/* hash table for matching conversations */
Ipht ht;
- ulong stats[Nstats];
+ /* calls in limbo waiting for an ACK to our SYN ACK */
+ int nlimbo;
+ Limbo *lht[NLHT];
/* for keeping track of tcpackproc */
- int ackprocstarted;
QLock apl;
+ int ackprocstarted;
+
+ ulong stats[Nstats];
};
int addreseq(Tcpctl*, Tcp*, Block*, ushort);
@@ 305,6 358,10 @@ void tcpkeepalive(void*);
void tcpsetkacounter(Tcpctl*);
void tcprxmit(Conv*);
void tcpsettimer(Tcpctl*);
+void tcpsynackrtt(Conv*);
+
+static void limborexmit(Proto*);
+static void limbo(Conv*, uchar*, uchar*, Tcp*, int);
void
tcpsetstate(Conv *s, uchar newstate)
@@ 457,11 514,6 @@ tcpkick(Conv *s)
qlock(s);
switch(tcb->state) {
- case Listen:
- tcb->flags |= ACTIVE;
- tcpsndsyn(tcb);
- tcpsetstate(s, Syn_sent);
- /* No break */
case Syn_sent:
case Syn_received:
case Established:
@@ 599,6 651,8 @@ tcpackproc(void *a)
poperror();
}
}
+
+ limborexmit(tcp);
}
}
@@ 672,14 726,12 @@ localclose(Conv *s, char *reason) /* called with tcb locked */
/* mtu (- TCP + IP hdr len) of 1st hop */
int
-tcpmtu(Conv *s)
+tcpmtu(Proto *tcp, uchar *addr, int version)
{
Ipifc *ifc;
int mtu;
- uchar version;
- version = s->ipversion;
- ifc = findipifc(s->p->f, s->raddr, 0);
+ ifc = findipifc(tcp->f, addr, 0);
switch(version){
default:
case V4:
@@ 753,7 805,7 @@ inittcpctl(Conv *s, int mode)
}
}
- tcb->mss = tcb->cwind = tcpmtu(s);
+ tcb->mss = tcb->cwind = tcpmtu(s->p, s->laddr, s->ipversion);
}
/*
@@ 1056,7 1108,10 @@ ntohtcp4(Tcp *tcph, Block **bpp)
return hdrlen;
}
-/* Generate an initial sequence number and put a SYN on the send queue */
+/*
+ * For outgiing calls, generate an initial sequence
+ * number and put a SYN on the send queue
+ */
void
tcpsndsyn(Tcpctl *tcb)
{
@@ 1068,7 1123,7 @@ tcpsndsyn(Tcpctl *tcb)
tcb->snd.nxt = tcb->rttseq;
tcb->flgcnt++;
tcb->flags |= FORCE;
- tcb->sndsyntime = msec;
+ tcb->sndsyntime = NOW;
}
void
@@ 1193,6 1248,200 @@ tcphangup(Conv *s)
return nil;
}
+/*
+ * (re)send a SYN ACK
+ */
+int
+sndsynack(Proto *tcp, Limbo *lp)
+{
+ Block *hbp;
+ Tcp4hdr ph4;
+ Tcp6hdr ph6;
+ Tcp seg;
+
+ /* make pseudo header */
+ switch(lp->version) {
+ case V4:
+ memset(&ph4, 0, sizeof(ph4));
+ ph4.vihl = IP_VER4;
+ v6tov4(ph4.tcpsrc, lp->laddr);
+ v6tov4(ph4.tcpdst, lp->raddr);
+ ph4.proto = IP_TCPPROTO;
+ hnputs(ph4.tcplen, TCP4_HDRSIZE);
+ hnputs(ph4.tcpsport, lp->lport);
+ hnputs(ph4.tcpdport, lp->rport);
+ break;
+ case V6:
+ memset(&ph6, 0, sizeof(ph6));
+ ph6.vcf[0] = IP_VER6;
+ ipmove(ph6.tcpsrc, lp->laddr);
+ ipmove(ph6.tcpdst, lp->raddr);
+ ph6.proto = IP_TCPPROTO;
+ hnputs(ph6.ploadlen, TCP6_HDRSIZE);
+ hnputs(ph6.tcpsport, lp->lport);
+ hnputs(ph6.tcpdport, lp->rport);
+ break;
+ default:
+ panic("sndrst: version %d", lp->version);
+ }
+
+ seg.seq = lp->iss;
+ seg.ack = lp->irs+1;
+ seg.flags = SYN|ACK;
+ seg.wnd = 0;
+ seg.urg = 0;
+ seg.mss = tcpmtu(tcp, lp->laddr, lp->version);
+
+ switch(lp->version) {
+ case V4:
+ hbp = htontcp4(&seg, nil, &ph4, nil);
+ if(hbp == nil)
+ return -1;
+ ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS);
+ break;
+ case V6:
+ hbp = htontcp6(&seg, nil, &ph6, nil);
+ if(hbp == nil)
+ return -1;
+ ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS);
+ break;
+ default:
+ panic("sndsnack: version %d", lp->version);
+ }
+ lp->lastsend = NOW;
+ return 0;
+}
+
+/*
+ * hash an address, walk the permutation
+ */
+static int
+limbohash(uchar *perm, uchar *addr)
+{
+ int i;
+ uchar x;
+
+ x = 0;
+ for(i = 0; i < IPaddrlen; i++)
+ x = perm[(addr[i]+x) & 0xff];
+ return x;
+}
+
+#define hashipa(a) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] )&LHTMASK )
+
+/*
+ * put a call into limbo and respond with a SYN ACK
+ *
+ * called with proto locked
+ */
+static void
+limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
+{
+ Limbo *lp, **l;
+ Tcppriv *tpriv;
+ int h;
+
+ tpriv = s->p->priv;
+ h = hashipa(source);
+
+ for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
+ lp = *l;
+ if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
+ continue;
+ if(ipcmp(lp->raddr, source) != 0)
+ continue;
+ if(ipcmp(lp->laddr, dest) != 0)
+ continue;
+
+ /* each new SYN restarts the retramsmits */
+ lp->irs = seg->seq;
+ break;
+ }
+ lp = *l;
+ if(lp == nil){
+ if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
+ lp = tpriv->lht[h];
+ tpriv->lht[h] = lp->next;
+ lp->next = nil;
+ } else {
+ lp = malloc(sizeof(*lp));
+ if(lp == nil)
+ return;
+ tpriv->nlimbo++;
+ }
+ *l = lp;
+ lp->version = version;
+ ipmove(lp->laddr, dest);
+ ipmove(lp->raddr, source);
+ lp->lport = seg->dest;
+ lp->rport = seg->source;
+ lp->mss = seg->mss;
+ lp->irs = seg->seq;
+ lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
+ }
+
+ if(sndsynack(s->p, lp) < 0){
+ *l = lp->next;
+ tpriv->nlimbo--;
+ free(lp);
+ }
+}
+
+/*
+ * resend SYN ACK's once every 250 ms.
+ */
+static void
+limborexmit(Proto *tcp)
+{
+ Tcppriv *tpriv;
+ Limbo **l, *lp;
+ int h;
+ int seen;
+ ulong now;
+
+ tpriv = tcp->priv;
+
+ if(!canqlock(tcp))
+ return;
+ seen = 0;
+ now = NOW;
+ for(h = 0; h < nelem(tpriv->lht) && seen < tpriv->nlimbo; h++){
+ for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
+ lp = *l;
+ seen++;
+ if(now - lp->lastsend < 250)
+ continue;
+
+ /* time it out after 1 second */
+ if(++(lp->rexmits) > 4){
+ tpriv->nlimbo--;
+ *l = lp->next;
+ free(lp);
+ continue;
+ }
+
+ /* if we're being attacked, don't bother resending SYN ACK's */
+ if(tpriv->nlimbo > 100)
+ continue;
+
+ if(sndsynack(tcp, lp) < 0){
+ tpriv->nlimbo--;
+ *l = lp->next;
+ free(lp);
+ continue;
+ }
+
+ l = &lp->next;
+ }
+ }
+ qunlock(tcp);
+}
+
+/*
+ * lookup call in limbo. if found, create a new conversation
+ *
+ * called with proto locked
+ */
static Conv*
tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
{
@@ 1201,6 1450,38 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
Tcppriv *tpriv;
Tcp4hdr *h4;
Tcp6hdr *h6;
+ Limbo *lp, **l;
+ int h;
+
+ /* unless it's just an ack, it can't be someone coming out of limbo */
+ if((segp->flags & SYN) || (segp->flags & ACK) == 0)
+ return nil;
+
+ tpriv = s->p->priv;
+
+ /* find a call in limbo */
+ lp = nil;
+ h = hashipa(src);
+ for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
+ lp = *l;
+ if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
+ continue;
+ if(ipcmp(lp->laddr, dst) != 0)
+ continue;
+ if(ipcmp(lp->raddr, src) != 0)
+ continue;
+
+ /* we're assuming no data with the initial SYN */
+ if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1)
+ lp = nil;
+ else{
+ tpriv->nlimbo--;
+ *l = lp->next;
+ }
+ break;
+ }
+ if(lp == nil)
+ return nil;
new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
if(new == nil)
@@ 1218,6 1499,34 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
tcb->rtt_timer.arg = new;
tcb->rtt_timer.state = TcptimerOFF;
+ tcb->irs = lp->irs;
+ tcb->rcv.nxt = tcb->irs+1;
+ tcb->rcv.urg = tcb->rcv.nxt;
+
+ tcb->iss = lp->iss;
+ tcb->rttseq = tcb->iss;
+ tcb->snd.wl2 = tcb->iss;
+ tcb->snd.una = tcb->iss+1;
+ tcb->snd.ptr = tcb->iss+1;
+ tcb->snd.nxt = tcb->iss+1;
+ tcb->flgcnt = 0;
+ tcb->flags |= SYNACK;
+
+ /* our sending max segment size cannot be bigger than what he asked for */
+ if(lp->mss != 0 && lp->mss < tcb->mss)
+ tcb->mss = lp->mss;
+
+ /* the congestion window always starts out as a single segment */
+ tcb->snd.wnd = segp->wnd;
+ tcb->cwind = tcb->mss;
+
+ /* set initial round trip time */
+ tcb->sndsyntime = lp->lastsend;
+ tcpsynackrtt(new);
+
+ free(lp);
+
+ /* set up proto header */
switch(version){
case V4:
h4 = &tcb->protohdr.tcp4hdr;
@@ 1241,7 1550,8 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
panic("tcpincoming: version %d", new->ipversion);
}
- tpriv = new->p->priv;
+ tcpsetstate(new, Established);
+
iphtadd(&tpriv->ht, new);
return new;
@@ 1299,7 1609,7 @@ tcpsynackrtt(Conv *s)
tcb = (Tcpctl*)s->ptcl;
tpriv = s->p->priv;
- delta = msec - tcb->sndsyntime;
+ delta = NOW - tcb->sndsyntime;
tcb->srtt = delta<<LOGAGAIN;
tcb->mdev = delta<<LOGDGAIN;
@@ 1357,7 1667,6 @@ update(Conv *s, Tcp *seg)
tcb->snd.wl2 = seg->ack;
}
-
if(!seq_gt(seg->ack, tcb->snd.una)){
/*
* don't let us hangup if sending into a closed window and
@@ 1491,6 1800,7 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
tpriv->stats[CsumErrs]++;
tpriv->stats[InErrs]++;
+print("cksum is %ux\n", ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN));
netlog(f, Logtcp, "bad tcp proto cksum\n");
freeblist(bp);
return;
@@ 1578,9 1888,19 @@ reset:
freeblist(bp);
return;
}
- if((seg.flags & SYN) == 0 || (seg.flags & ACK) != 0)
- goto reset;
+ /* if this is a new SYN, put the call into limbo */
+ if((seg.flags & SYN) && (seg.flags & ACK) == 0){
+ limbo(s, source, dest, &seg, version);
+ qunlock(tcp);
+ freeblist(bp);
+ return;
+ }
+
+ /*
+ * if there's a matching call in limbo, tcpincoming will
+ * return it in state Syn_received
+ */
s = tcpincoming(s, &seg, source, dest, version);
if(s == nil)
goto reset;
@@ 1607,16 1927,6 @@ reset:
case Closed:
sndrst(tcp, source, dest, length, &seg, version);
goto raise;
- case Listen:
- if(seg.flags & SYN) {
- procsyn(s, &seg);
- tcpsndsyn(tcb);
- tcb->time = msec;
- tcpsetstate(s, Syn_received);
- if(length != 0 || (seg.flags & FIN))
- break;
- }
- goto raise;
case Syn_sent:
if(seg.flags & ACK) {
if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
@@ 1638,7 1948,7 @@ reset:
tcpsetstate(s, Established);
}
else {
- tcb->time = msec;
+ tcb->time = NOW;
tcpsetstate(s, Syn_received);
}
@@ 1661,15 1971,16 @@ reset:
break;
}
+ /*
+ * One DOS attack is to open connections to us and then forget about them,
+ * thereby tying up a conv at no long term cost to the attacker.
+ * This is an attempt to defeat these stateless DOS attacks. See
+ * corresponding code in tcpsendka().
+ */
if(tcb->state != Syn_received){
- /*
- * One DOS attack is to open connections to us and then forget about them,
- * thereby tying up a conv at no long term cost to the attacker.
- * This is an attempt to defeat these stateless DOS attacks. See
- * corresponding code in tcpsendka().
- */
if(seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
- print("stateless hog %lux - %lux - %lux\n", tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
+ print("stateless hog %lux - %lux - %lux\n", tcb->snd.una-(1<<31), seg.ack,
+ tcb->snd.una-(1<<29));
localclose(s, "stateless hog");
}
}
@@ 1747,7 2058,7 @@ reset:
tcphalt(tpriv, &tcb->rtt_timer);
tcphalt(tpriv, &tcb->acktimer);
tcpsetkacounter(tcb);
- tcb->time = msec;
+ tcb->time = NOW;
tcpsetstate(s, Finwait2);
tcb->katimer.start = MSL2 * (1000 / MSPTICK);
tcpgo(tpriv, &tcb->katimer);
@@ 2011,7 2322,7 @@ tcpoutput(Conv *s)
if(tcb->snd.ptr == tcb->iss){
seg.flags |= SYN;
dsize--;
- seg.mss = tcpmtu(s);
+ seg.mss = tcpmtu(s->p, s->laddr, s->ipversion);
}
break;
case Syn_received:
@@ 2024,7 2335,7 @@ tcpoutput(Conv *s)
seg.flags |= SYN;
dsize = 0;
ssize = 1;
- seg.mss = tcpmtu(s);
+ seg.mss = tcpmtu(s->p, s->laddr, s->ipversion);
}
break;
}
@@ 2329,6 2640,9 @@ inwindow(Tcpctl *tcb, int seq)
return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
}
+/*
+ * set up state for a received SYN (or SYN ACK) packet
+ */
void
procsyn(Conv *s, Tcp *seg)
{
@@ 2340,11 2654,13 @@ procsyn(Conv *s, Tcp *seg)
tcb->rcv.nxt = seg->seq + 1;
tcb->rcv.urg = tcb->rcv.nxt;
tcb->irs = seg->seq;
- tcb->snd.wnd = seg->wnd;
+ /* our sending max segment size cannot be bigger than what he asked for */
if(seg->mss != 0 && seg->mss < tcb->mss)
tcb->mss = seg->mss;
- tcb->max_snd = seg->wnd;
+
+ /* the congestion window always starts out as a single segment */
+ tcb->snd.wnd = seg->wnd;
tcb->cwind = tcb->mss;
}
@@ 2591,13 2907,13 @@ tcpgc(Proto *tcp)
tcb = (Tcpctl*)c->ptcl;
switch(tcb->state){
case Syn_received:
- if(msec - tcb->time > 5000){
+ if(NOW - tcb->time > 5000){
localclose(c, "timed out");
n++;
}
break;
case Finwait2:
- if(msec - tcb->time > 5*60*1000){
+ if(NOW - tcb->time > 5*60*1000){
localclose(c, "timed out");
n++;
}
M mtx/mem.h => mtx/mem.h +0 -2
@@ 32,9 32,7 @@
* Time
*/
#define HZ 100 /* clock frequency */
-#define MS2HZ (1000/HZ)
#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
-#define TK2MS(t) ((t)*MS2HZ) /* ticks to milliseconds */
/*
* Standard PPC Special Purpose Registers (OEA and VEA)
M pc/clock.c => pc/clock.c +0 -13
@@ 43,16 43,3 @@ microdelay(int microsecs)
microsecs = 1;
aamloop(microsecs);
}
-
-ulong
-TK2MS(ulong ticks)
-{
- uvlong t, hz;
-
- t = ticks;
- hz = HZ;
- t *= 1000L;
- t = t/hz;
- ticks = t;
- return ticks;
-}
M pc/devether.c => pc/devether.c +3 -3
@@ 414,14 414,14 @@ etherprobe(int cardno, int ctlrno)
print(buf);
if(ether->mbps >= 100){
- netifinit(ether, name, Ntypes, 256*1024);
+ netifinit(ether, name, Ntypes, 512*1024);
if(ether->oq == 0)
ether->oq = qopen(256*1024, 1, 0, 0);
}
else{
- netifinit(ether, name, Ntypes, 65*1024);
+ netifinit(ether, name, Ntypes, 128*1024);
if(ether->oq == 0)
- ether->oq = qopen(65*1024, 1, 0, 0);
+ ether->oq = qopen(128*1024, 1, 0, 0);
}
if(ether->oq == 0)
panic("etherreset %s", name);
M pc/fns.h => pc/fns.h +0 -1
@@ 146,7 146,6 @@ void wrmsr(int, vlong);
void wbflush(void);
void wbinvd(void);
int xchgw(ushort*, int);
-ulong TK2MS(ulong); /* ticks to milliseconds */
#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
#define KADDR(a) ((void*)((ulong)(a)|KZERO))
M pc/i8253.c => pc/i8253.c +0 -13
@@ 322,16 322,3 @@ microdelay(int microsecs)
microsecs = 1;
aamloop(microsecs);
}
-
-ulong
-TK2MS(ulong ticks)
-{
- uvlong t, hz;
-
- t = ticks;
- hz = HZ;
- t *= 1000L;
- t = t/hz;
- ticks = t;
- return ticks;
-}
M port/alarm.c => port/alarm.c +1 -1
@@ 82,7 82,7 @@ procalarm(ulong time)
ulong when, old;
if(up->alarm)
- old = TK2MS(up->alarm - MACHP(0)->ticks);
+ old = tk2ms(up->alarm - MACHP(0)->ticks);
else
old = 0;
if(time == 0) {
M port/portclock.c => port/portclock.c +36 -0
@@ 141,6 141,13 @@ timerintr(Ureg *u, uvlong)
Timers *tt;
uvlong when, now;
int callhzclock;
+ static int intimer;
+
+ if(intimer){
+ print("!");
+ return;
+ }
+ intimer = 1;
intrcount[m->machno]++;
callhzclock = 0;
@@ 154,6 161,7 @@ timerintr(Ureg *u, uvlong)
timerset(when);
if(callhzclock)
hzclock(u);
+ intimer = 0;
return;
}
tt->head = t->next;
@@ 171,6 179,7 @@ timerintr(Ureg *u, uvlong)
}
}
iunlock(tt);
+ intimer = 0;
}
uvlong hzperiod;
@@ 209,3 218,30 @@ addclock0link(void (*f)(void))
*/
iunlock(&timers[0]);
}
+
+/*
+ * This tk2ms avoids overflows that the macro version is prone to.
+ * It is a LOT slower so shouldn't be used if you're just converting
+ * a delta.
+ */
+ulong
+tk2ms(ulong ticks)
+{
+ uvlong t, hz;
+
+ t = ticks;
+ hz = HZ;
+ t *= 1000L;
+ t = t/hz;
+ ticks = t;
+ return ticks;
+}
+
+ulong
+ms2tk(ulong ms)
+{
+ /* avoid overflows at the cost of precision */
+ if(ms >= 1000000000/HZ)
+ return (ms/1000)*HZ;
+ return (ms*HZ+500)/1000;
+}
M port/portfns.h => port/portfns.h +4 -2
@@ 157,7 157,7 @@ char* logctl(Log*, int, char**, Logflag*);
void logn(Log*, int, void*, int);
long logread(Log*, void*, ulong, long);
void log(Log*, int, char*, ...);
-Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int);
+Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int);
Page* lookpage(Image*, ulong);
void machinit(void);
void* mallocz(ulong, int);
@@ 173,7 173,7 @@ Chan* mntauth(Chan*, char*);
void mntdump(void);
long mntversion(Chan*, char*, int, int);
void mountfree(Mount*);
-ulong ms2tk(ulong);
+ulong ms2tk(ulong);
ulong msize(void*);
ulong ms2tk(ulong);
uvlong ms2fastticks(ulong);
@@ 318,6 318,8 @@ void timerdel(Timer*);
void timersinit(void);
void timerintr(Ureg*, uvlong);
void timerset(uvlong);
+ulong tk2ms(ulong);
+#define TK2MS(x) ((x)*(1000/HZ))
vlong todget(vlong*);
void todfix(void);
void todsetfreq(vlong);
M port/proc.c => port/proc.c +3 -12
@@ 529,15 529,6 @@ tfn(void *arg)
return MACHP(0)->ticks >= up->twhen || up->tfn(arg);
}
-ulong
-ms2tk(ulong ms)
-{
- /* avoid overflows at the cost of precision */
- if(ms >= 1000000000/HZ)
- return (ms/1000)*HZ;
- return (ms*HZ+500)/1000;
-}
-
void
tsleep(Rendez *r, int (*fn)(void*), void *arg, int ms)
{
@@ 818,9 809,9 @@ pexit(char *exitstr, int freemem)
wq->w.pid = up->pid;
utime = up->time[TUser] + up->time[TCUser];
stime = up->time[TSys] + up->time[TCSys];
- wq->w.time[TUser] = TK2MS(utime);
- wq->w.time[TSys] = TK2MS(stime);
- wq->w.time[TReal] = TK2MS(MACHP(0)->ticks - up->time[TReal]);
+ wq->w.time[TUser] = tk2ms(utime);
+ wq->w.time[TSys] = tk2ms(stime);
+ wq->w.time[TReal] = tk2ms(MACHP(0)->ticks - up->time[TReal]);
if(exitstr && exitstr[0])
snprint(wq->w.msg, sizeof(wq->w.msg), "%s %lud: %s", up->text, up->pid, exitstr);
else