| plan 9 kernel history: overview | file list | diff list |
2002/0710/ip/tcp.c (diff list | history)
| 2002/0704/sys/src/9/ip/tcp.c:61,66 – 2002/0710/sys/src/9/ip/tcp.c:61,67 (short | long | prev | next) | ||
| 1997/0327 | LOGAGAIN = 3, LOGDGAIN = 2, | |
| 2002/0710 | ||
| 1997/0327 | Closed = 0, /* Connection states */ Listen, Syn_sent, | |
| 2002/0704/sys/src/9/ip/tcp.c:71,77 – 2002/0710/sys/src/9/ip/tcp.c:72,82 | ||
| 1997/0327 | Close_wait, Closing, Last_ack, | |
| 2002/0710 | 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 | |
| 1997/0327 | }; /* Must correspond to the enumeration above */ | |
| 2002/0704/sys/src/9/ip/tcp.c:95,100 – 2002/0710/sys/src/9/ip/tcp.c:100,109 | ||
| 1997/0327 | void *arg; }; | |
| 2002/0710 | /* * v4 and v6 pseudo headers used for * checksuming tcp */ | |
| 2002/0507 | typedef struct Tcp4hdr Tcp4hdr; struct Tcp4hdr | |
| 1997/0327 | { | |
| 2002/0704/sys/src/9/ip/tcp.c:143,149 – 2002/0710/sys/src/9/ip/tcp.c:152,163 | ||
| 2002/0507 | uchar tcpmss[2]; }; | |
| 2002/0710 | /* * 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}(). */ | |
| 1997/0327 | typedef struct Tcp Tcp; struct Tcp { | |
| 2002/0704/sys/src/9/ip/tcp.c:158,163 – 2002/0710/sys/src/9/ip/tcp.c:172,181 | ||
| 2002/0507 | ushort len; /* size of data */ | |
| 1997/0327 | }; | |
| 2002/0710 | /* * this header is malloc'd to thread together fragments * waiting to be coalesced */ | |
| 1997/0327 | typedef struct Reseq Reseq; struct Reseq { | |
| 2002/0704/sys/src/9/ip/tcp.c:202,208 – 2002/0710/sys/src/9/ip/tcp.c:220,225 | ||
| 1997/0327 | ushort mss; /* Mean segment size */ int rerecv; /* Overlap of data rerecevived */ ushort window; /* Recevive window */ | |
| 1998/0306 | uchar backoff; /* Exponential backoff counter */ | |
| 1999/0607 | int backedoff; /* ms we've backed off for rexmits */ | |
| 2002/0704/sys/src/9/ip/tcp.c:219,225 – 2002/0710/sys/src/9/ip/tcp.c:236,242 | ||
| 1997/0327 | uint sndsyntime; /* time syn sent */ | |
| 2000/0424 | ulong time; /* time Finwait2 or Syn_received was sent */ | |
| 2001/0119 | int nochecksum; /* non-zero means don't send checksums */ | |
| 2001/0306 |
| |
| 2002/0710 | int flgcnt; /* number of flags in the sequence (FIN,SEQ) */ | |
| 1998/0306 | ||
| 2002/0507 | union { Tcp4hdr tcp4hdr; | |
| 2002/0704/sys/src/9/ip/tcp.c:227,232 – 2002/0710/sys/src/9/ip/tcp.c:244,279 | ||
| 2002/0507 | } protohdr; /* prototype header */ | |
| 1997/0327 | }; | |
| 2002/0710 | /* * 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 */ }; | |
| 1997/0327 | int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */ ushort tcp_mss = DEF_MSS; /* Maximum segment size to be sent */ | |
| 2002/0704/sys/src/9/ip/tcp.c:275,292 – 2002/0710/sys/src/9/ip/tcp.c:322,345 | ||
| 1998/0313 | typedef struct Tcppriv Tcppriv; struct Tcppriv { | |
| 2002/0507 |
| |
| 1998/0313 |
| |
| 2002/0710 | /* List of active timers */ QLock tl; Tcptimer *timers; | |
| 1998/0313 | Rendez tcpr; /* used by tcpackproc */ | |
| 2001/0301 | /* hash table for matching conversations */ Ipht ht; | |
| 2000/0706 |
| |
| 2002/0710 | /* calls in limbo waiting for an ACK to our SYN ACK */ int nlimbo; Limbo *lht[NLHT]; | |
| 1998/0313 | ||
| 1998/0924 | /* for keeping track of tcpackproc */ | |
| 2002/0710 | int ackprocstarted; ulong stats[Nstats]; | |
| 1998/0313 | }; | |
| 1999/1006 | int addreseq(Tcpctl*, Tcp*, Block*, ushort); | |
| 2002/0704/sys/src/9/ip/tcp.c:305,311 – 2002/0710/sys/src/9/ip/tcp.c:358,368 | ||
| 1999/0529 | void tcpsetkacounter(Tcpctl*); | |
| 2000/0102 | void tcprxmit(Conv*); | |
| 2001/0530 | void tcpsettimer(Tcpctl*); | |
| 2002/0710 | void tcpsynackrtt(Conv*); | |
| 1997/0327 | ||
| 2002/0710 | static void limborexmit(Proto*); static void limbo(Conv*, uchar*, uchar*, Tcp*, int); | |
| 1997/0327 | void | |
| 1998/0306 | tcpsetstate(Conv *s, uchar newstate) | |
| 1997/0327 | { | |
| 2002/0704/sys/src/9/ip/tcp.c:457,467 – 2002/0710/sys/src/9/ip/tcp.c:514,519 | ||
| 2000/1220 | qlock(s); | |
| 1997/0327 | switch(tcb->state) { | |
| 2002/0704/sys/src/9/ip/tcp.c:599,604 – 2002/0710/sys/src/9/ip/tcp.c:651,658 | ||
| 2001/0504 | poperror(); } | |
| 1997/0327 | } | |
| 2002/0710 | limborexmit(tcp); | |
| 1997/0327 | } } | |
| 2002/0704/sys/src/9/ip/tcp.c:672,685 – 2002/0710/sys/src/9/ip/tcp.c:726,737 | ||
| 1997/0327 | ||
| 1999/0401 | /* mtu (- TCP + IP hdr len) of 1st hop */ int | |
| 2002/0710 | tcpmtu(Proto *tcp, uchar *addr, int version) | |
| 1999/0401 | { Ipifc *ifc; int mtu; | |
| 2002/0601 |
| |
| 1999/0401 | ||
| 2002/0601 |
| |
| 1999/0401 |
| |
| 2002/0710 | ifc = findipifc(tcp->f, addr, 0); | |
| 2002/0704 | switch(version){ default: case V4: | |
| 2002/0704/sys/src/9/ip/tcp.c:753,759 – 2002/0710/sys/src/9/ip/tcp.c:805,811 | ||
| 2002/0601 | } | |
| 2002/0507 | } | |
| 1999/0401 |
| |
| 2002/0710 | tcb->mss = tcb->cwind = tcpmtu(s->p, s->laddr, s->ipversion); | |
| 1997/0327 | } | |
| 2000/1220 | /* | |
| 2002/0704/sys/src/9/ip/tcp.c:1056,1062 – 2002/0710/sys/src/9/ip/tcp.c:1108,1117 | ||
| 1997/0327 | return hdrlen; } | |
| 2002/0710 | /* * For outgiing calls, generate an initial sequence * number and put a SYN on the send queue */ | |
| 1997/0327 | void tcpsndsyn(Tcpctl *tcb) { | |
| 2002/0704/sys/src/9/ip/tcp.c:1068,1074 – 2002/0710/sys/src/9/ip/tcp.c:1123,1129 | ||
| 1997/0327 | tcb->snd.nxt = tcb->rttseq; | |
| 2001/0306 | tcb->flgcnt++; | |
| 1997/0327 | tcb->flags |= FORCE; | |
| 2002/0710 | tcb->sndsyntime = NOW; | |
| 1997/0327 | } void | |
| 2002/0704/sys/src/9/ip/tcp.c:1193,1198 – 2002/0710/sys/src/9/ip/tcp.c:1248,1447 | ||
| 1997/0327 | return nil; } | |
| 2002/0710 | /* * (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 */ | |
| 2002/0601 | static Conv* tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version) | |
| 1997/0327 | { | |
| 2002/0704/sys/src/9/ip/tcp.c:1201,1207 – 2002/0710/sys/src/9/ip/tcp.c:1450,1488 | ||
| 2001/0301 | Tcppriv *tpriv; | |
| 2002/0601 | Tcp4hdr *h4; Tcp6hdr *h6; | |
| 2002/0710 | Limbo *lp, **l; int h; | |
| 1997/0327 | ||
| 2002/0710 | /* 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; | |
| 2002/0601 | new = Fsnewcall(s, src, segp->source, dst, segp->dest, version); | |
| 1997/0327 | if(new == nil) return nil; | |
| 2002/0704/sys/src/9/ip/tcp.c:1218,1223 – 2002/0710/sys/src/9/ip/tcp.c:1499,1532 | ||
| 1998/1204 | tcb->rtt_timer.arg = new; | |
| 2002/0405 | tcb->rtt_timer.state = TcptimerOFF; | |
| 1997/0327 | ||
| 2002/0710 | 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 */ | |
| 2002/0601 | switch(version){ case V4: h4 = &tcb->protohdr.tcp4hdr; | |
| 2002/0704/sys/src/9/ip/tcp.c:1241,1247 – 2002/0710/sys/src/9/ip/tcp.c:1550,1557 | ||
| 2002/0601 | panic("tcpincoming: version %d", new->ipversion); | |
| 2002/0507 | } | |
| 1998/0306 | ||
| 2001/0301 |
| |
| 2002/0710 | tcpsetstate(new, Established); | |
| 2001/0301 | iphtadd(&tpriv->ht, new); | |
| 1997/0327 | return new; | |
| 2002/0704/sys/src/9/ip/tcp.c:1299,1305 – 2002/0710/sys/src/9/ip/tcp.c:1609,1615 | ||
| 1997/0327 | tcb = (Tcpctl*)s->ptcl; | |
| 1998/0313 | tpriv = s->p->priv; | |
| 1997/0327 |
| |
| 2002/0710 | delta = NOW - tcb->sndsyntime; | |
| 1997/0327 | tcb->srtt = delta<<LOGAGAIN; tcb->mdev = delta<<LOGDGAIN; | |
| 2002/0704/sys/src/9/ip/tcp.c:1357,1363 – 2002/0710/sys/src/9/ip/tcp.c:1667,1672 | ||
| 1997/0327 | tcb->snd.wl2 = seg->ack; } | |
| 2001/0119 | ||
| 2001/0308 | if(!seq_gt(seg->ack, tcb->snd.una)){ /* * don't let us hangup if sending into a closed window and | |
| 2002/0704/sys/src/9/ip/tcp.c:1491,1496 – 2002/0710/sys/src/9/ip/tcp.c:1800,1806 | ||
| 2002/0507 | ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) { tpriv->stats[CsumErrs]++; tpriv->stats[InErrs]++; | |
| 2002/0710 | print("cksum is %ux\n", ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)); | |
| 2002/0507 | netlog(f, Logtcp, "bad tcp proto cksum\n"); freeblist(bp); return; | |
| 2002/0704/sys/src/9/ip/tcp.c:1578,1586 – 2002/0710/sys/src/9/ip/tcp.c:1888,1906 | ||
| 1997/0327 | freeblist(bp); return; } | |
| 2001/0301 |
| |
| 1999/0302 | ||
| 2002/0710 | /* 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 */ | |
| 2002/0601 | s = tcpincoming(s, &seg, source, dest, version); | |
| 2001/0301 | if(s == nil) goto reset; | |
| 2002/0704/sys/src/9/ip/tcp.c:1607,1622 – 2002/0710/sys/src/9/ip/tcp.c:1927,1932 | ||
| 1997/0327 | case Closed: | |
| 2002/0507 | sndrst(tcp, source, dest, length, &seg, version); | |
| 1997/0327 | goto raise; | |
| 2000/0424 |
| |
| 1997/0327 |
| |
| 2002/0704/sys/src/9/ip/tcp.c:1638,1644 – 2002/0710/sys/src/9/ip/tcp.c:1948,1954 | ||
| 1997/0327 | tcpsetstate(s, Established); } | |
| 2000/0424 | else { | |
| 2002/0710 | tcb->time = NOW; | |
| 1997/0327 | tcpsetstate(s, Syn_received); | |
| 2000/0424 | } | |
| 1997/0327 | ||
| 2002/0704/sys/src/9/ip/tcp.c:1661,1675 – 2002/0710/sys/src/9/ip/tcp.c:1971,1986 | ||
| 1997/0327 | break; } | |
| 2002/0710 | /* * 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(). */ | |
| 2002/0704 | if(tcb->state != Syn_received){ | |
| 2002/0710 | print("stateless hog %lux - %lux - %lux\n", tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29)); | |
| 2002/0704 | localclose(s, "stateless hog"); } } | |
| 2002/0704/sys/src/9/ip/tcp.c:1747,1753 – 2002/0710/sys/src/9/ip/tcp.c:2058,2064 | ||
| 1998/1204 | tcphalt(tpriv, &tcb->rtt_timer); tcphalt(tpriv, &tcb->acktimer); | |
| 1999/0529 | tcpsetkacounter(tcb); | |
| 2000/0424 |
| |
| 2002/0710 | tcb->time = NOW; | |
| 1997/0327 | tcpsetstate(s, Finwait2); | |
| 1998/1118 | tcb->katimer.start = MSL2 * (1000 / MSPTICK); | |
| 1998/1202 | tcpgo(tpriv, &tcb->katimer); | |
| 2002/0704/sys/src/9/ip/tcp.c:2011,2017 – 2002/0710/sys/src/9/ip/tcp.c:2322,2328 | ||
| 1997/0327 | if(tcb->snd.ptr == tcb->iss){ seg.flags |= SYN; dsize--; | |
| 1998/0813 |
| |
| 2002/0710 | seg.mss = tcpmtu(s->p, s->laddr, s->ipversion); | |
| 1998/0813 | } break; case Syn_received: | |
| 2002/0704/sys/src/9/ip/tcp.c:2024,2030 – 2002/0710/sys/src/9/ip/tcp.c:2335,2341 | ||
| 1998/0813 | seg.flags |= SYN; dsize = 0; | |
| 1998/0831 | ssize = 1; | |
| 1997/0327 |
| |
| 2002/0710 | seg.mss = tcpmtu(s->p, s->laddr, s->ipversion); | |
| 1997/0327 | } break; } | |
| 2002/0704/sys/src/9/ip/tcp.c:2329,2334 – 2002/0710/sys/src/9/ip/tcp.c:2640,2648 | ||
| 1997/0327 | return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1); } | |
| 2002/0710 | /* * set up state for a received SYN (or SYN ACK) packet */ | |
| 1997/0327 | void procsyn(Conv *s, Tcp *seg) { | |
| 2002/0704/sys/src/9/ip/tcp.c:2340,2350 – 2002/0710/sys/src/9/ip/tcp.c:2654,2666 | ||
| 1997/0327 | tcb->rcv.nxt = seg->seq + 1; tcb->rcv.urg = tcb->rcv.nxt; tcb->irs = seg->seq; | |
| 2002/0710 | /* our sending max segment size cannot be bigger than what he asked for */ | |
| 2002/0704 | if(seg->mss != 0 && seg->mss < tcb->mss) | |
| 1997/0327 | tcb->mss = seg->mss; | |
| 2002/0710 | /* the congestion window always starts out as a single segment */ tcb->snd.wnd = seg->wnd; | |
| 1997/0327 | tcb->cwind = tcb->mss; } | |
| 2002/0704/sys/src/9/ip/tcp.c:2591,2603 – 2002/0710/sys/src/9/ip/tcp.c:2907,2919 | ||
| 2000/0424 | tcb = (Tcpctl*)c->ptcl; switch(tcb->state){ case Syn_received: | |
| 2002/0710 | if(NOW - tcb->time > 5000){ | |
| 2000/0424 | localclose(c, "timed out"); n++; } break; case Finwait2: | |
| 2002/0710 | if(NOW - tcb->time > 5*60*1000){ | |
| 2000/0424 | localclose(c, "timed out"); n++; } | |