From 5266fe7f71e1b08924e2e907a08e439e54c8e90a Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 10 Jul 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-07-10 --- alphapc/clock.c | 13 -- alphapc/fns.h | 1 - bitsy/clock.c | 13 -- bitsy/fns.h | 1 - ip/arp.c | 18 +- ip/ethermedium.c | 10 +- ip/il.c | 36 ++-- ip/ip.c | 4 +- ip/ip.h | 18 +- ip/ipifc.c | 12 +- ip/ipv6.c | 4 +- ip/tcp.c | 416 +++++++++++++++++++++++++++++++++++++++++------ mtx/mem.h | 2 - pc/clock.c | 13 -- pc/devether.c | 6 +- pc/fns.h | 1 - pc/i8253.c | 13 -- port/alarm.c | 2 +- port/portclock.c | 36 ++++ port/portfns.h | 6 +- port/proc.c | 15 +- 21 files changed, 465 insertions(+), 175 deletions(-) diff --git a/alphapc/clock.c b/alphapc/clock.c index 7afef9faa7caad94a1694952ba50d5bc17bdb824..1f9abf7f1c7c0a5afba6c1d9b341829212244440 100644 --- a/alphapc/clock.c +++ b/alphapc/clock.c @@ -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; -} diff --git a/alphapc/fns.h b/alphapc/fns.h index dc5a85d48571d120fb8de007382cee8df0b75cbd..6dd9f756fb2148fe4377b073256a503c6edc014d 100644 --- a/alphapc/fns.h +++ b/alphapc/fns.h @@ -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) diff --git a/bitsy/clock.c b/bitsy/clock.c index 8d6eb3bd2654ead6d302fd4a87f5a0e994719f0c..aae9169117c76851815ddf6d2cd3452fa021fc6f 100644 --- a/bitsy/clock.c +++ b/bitsy/clock.c @@ -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; -} diff --git a/bitsy/fns.h b/bitsy/fns.h index 1173fd5bb82007b49f7bd96c0d0765000aa28a8e..c4220994d4e6316ed58ea0e60e8f3a03c646b997 100644 --- a/bitsy/fns.h +++ b/bitsy/fns.h @@ -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)) diff --git a/ip/arp.c b/ip/arp.c index e9b4fa83729f37f67e6d696412f43e3fe706441e..3cae5b14cee681376cf3d7f5acde27245d6b8951 100644 --- a/ip/arp.c +++ b/ip/arp.c @@ -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; diff --git a/ip/ethermedium.c b/ip/ethermedium.c index b8afe066c5bc7a5bcaafb8f9b144329988a296d3..b5fef99ca5f3f03ba8201568ed73851ef72afba5 100644 --- a/ip/ethermedium.c +++ b/ip/ethermedium.c @@ -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; diff --git a/ip/il.c b/ip/il.c index 8476d1445fb8a6302179017c09d72f78d3f0f094..2437f13a3bad242c00c18953f9c448c635aea3d3 100644 --- a/ip/il.c +++ b/ip/il.c @@ -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<mdev = DefRtt<rate = DefByteRate<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; }; diff --git a/ip/ip.c b/ip/ip.c index 33a8a7849db4387739157c3b406c0b739cc6d9da..18b68fe763d520d2b7bd42fa2338743c716ae865 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -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; } diff --git a/ip/ip.h b/ip/ip.h index 6988183ee33321edb2e0fee24a7e4b2dedba81fb..f886a1c6d1fa819732e017d640aa138be91ab704 100644 --- a/ip/ip.h +++ b/ip/ip.h @@ -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 diff --git a/ip/ipifc.c b/ip/ipifc.c index 100d330cb3dd297b7daac1cc14fb5aaa1574a1a3..3a662cc77906a776fa60d25f7db6af5c5b00a947 100644 --- a/ip/ipifc.c +++ b/ip/ipifc.c @@ -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; diff --git a/ip/ipv6.c b/ip/ipv6.c index 2baeb283f5387a6a99d59b0e402cebbf129918e6..165eb10e63fbc97be0d29d0c53b817719968daea 100644 --- a/ip/ipv6.c +++ b/ip/ipv6.c @@ -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); } diff --git a/ip/tcp.c b/ip/tcp.c index 6d9c4d059cc35255eb52b83d2e338c8d9a04feab..7895d75517c8623883de2f1732f5d2b93047edab 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -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<mdev = delta<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++; } diff --git a/mtx/mem.h b/mtx/mem.h index f774bd10d2a5dfbf2c4a8590d60408921c5f8006..05c01ff8a6c4efd147a0d678d874e178adf990ba 100644 --- a/mtx/mem.h +++ b/mtx/mem.h @@ -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) diff --git a/pc/clock.c b/pc/clock.c index 239e2cb83e42a3e6af74e8301a437c95c465f84f..0d834f0fcb46562144cc05bcc0866cc298a9d9d2 100644 --- a/pc/clock.c +++ b/pc/clock.c @@ -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; -} diff --git a/pc/devether.c b/pc/devether.c index 84172db49cb84e75f950235e418ccec25e1eb72e..6b2a89cd29fbee135a4f8dc4a00f790e75f4a963 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -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); diff --git a/pc/fns.h b/pc/fns.h index aac5ca4275347e4f9c350f77cff42c5ba9cbb93f..6728519b01090621f6ec1227151040e5b043b8be 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -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)) diff --git a/pc/i8253.c b/pc/i8253.c index 34e96c569e5a29a9d412c6d9e19a5e0f2cb27683..a4090f61c31288f3799dff1fbf40d25842994db8 100644 --- a/pc/i8253.c +++ b/pc/i8253.c @@ -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; -} diff --git a/port/alarm.c b/port/alarm.c index f8a140375eac4cf5c73539bbe00688515e62bf4f..e8463c852f75c3059307b44daf6df822096f8171 100644 --- a/port/alarm.c +++ b/port/alarm.c @@ -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) { diff --git a/port/portclock.c b/port/portclock.c index fec208181cafc2fee3374d30efe537ff4a8176fe..28c804c277f8a0a9c1a1d7c6ab6169e4c53acd5b 100644 --- a/port/portclock.c +++ b/port/portclock.c @@ -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; +} diff --git a/port/portfns.h b/port/portfns.h index ee8b9bd19a6a4520500d81cebc67a19139b8c9ba..2a65effca41728669dca5ef7ece7f4a09a7fec55 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -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); diff --git a/port/proc.c b/port/proc.c index 6d3d727dda84a35696e292702f6bfebcf21661f1..08ec1d7013f096015d580a2ba9354c2845fb77af 100644 --- a/port/proc.c +++ b/port/proc.c @@ -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