📄 tcp.c
字号:
seg->ack = 0; } else { rflags |= ACK; seg->ack = seg->seq; seg->seq = 0; if(seg->flags & SYN) seg->ack++; seg->ack += length; if(seg->flags & FIN) seg->ack++; } seg->flags = rflags; seg->wnd = 0; seg->urg = 0; seg->mss = 0; seg->ws = 0; switch(version) { case V4: hbp = htontcp4(seg, nil, &ph4, nil); if(hbp == nil) return; ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); break; case V6: hbp = htontcp6(seg, nil, &ph6, nil); if(hbp == nil) return; ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); break; default: panic("sndrst2: version %d", version); }}/* * send a reset to the remote side and close the conversation * called with s qlocked */char*tcphangup(Conv *s){ Tcp seg; Tcpctl *tcb; Block *hbp; tcb = (Tcpctl*)s->ptcl; if(waserror()) return commonerror(); if(s->raddr != 0) { if(!waserror()){ seg.flags = RST | ACK; seg.ack = tcb->rcv.nxt; tcb->rcv.una = 0; seg.seq = tcb->snd.ptr; seg.wnd = 0; seg.urg = 0; seg.mss = 0; seg.ws = 0; switch(s->ipversion) { case V4: tcb->protohdr.tcp4hdr.vihl = IP_VER4; hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb); ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s); break; case V6: tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb); ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s); break; default: panic("tcphangup: version %d", s->ipversion); } poperror(); } } localclose(s, nil); poperror(); return nil;}/* * (re)send a SYN ACK */intsndsynack(Proto *tcp, Limbo *lp){ Block *hbp; Tcp4hdr ph4; Tcp6hdr ph6; Tcp seg; int scale; /* 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.urg = 0; seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale); seg.wnd = QMAX; /* if the other side set scale, we should too */ if(lp->rcvscale){ seg.ws = scale; lp->sndscale = scale; } else { seg.ws = 0; lp->sndscale = 0; } switch(lp->version) { case V4: hbp = htontcp4(&seg, nil, &ph4, nil); if(hbp == nil) return -1; ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); break; case V6: hbp = htontcp6(&seg, nil, &ph6, nil); if(hbp == nil) return -1; ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil); break; default: panic("sndsnack: version %d", lp->version); } lp->lastsend = NOW; return 0;}#define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )/* * put a call into limbo and respond with a SYN ACK * * called with proto locked */static voidlimbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version){ Limbo *lp, **l; Tcppriv *tpriv; int h; tpriv = s->p->priv; h = hashipa(source, seg->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 retransmits */ 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->rcvscale = seg->ws; 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 SYNACK_RXTIMER ms. */static voidlimborexmit(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 < NLHT && seen < tpriv->nlimbo; h++){ for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){ lp = *l; seen++; if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER) continue; /* time it out after 1 second */ if(++(lp->rexmits) > 5){ 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, throw it out. * * called with proto locked */static voidlimborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version){ Limbo *lp, **l; int h; Tcppriv *tpriv; tpriv = s->p->priv; /* find a call in limbo */ h = hashipa(src, segp->source); 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; /* RST can only follow the SYN */ if(segp->seq == lp->irs+1){ tpriv->nlimbo--; *l = lp->next; free(lp); } break; }}/* * come here when we finally get an ACK to our SYN-ACK. * 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){ Conv *new; Tcpctl *tcb; 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 */ h = hashipa(src, segp->source); for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){ netlog(s->p->f, Logtcp, "tcpincoming s %I,%ux/%I,%ux d %I,%ux/%I,%ux v %d/%d", src, segp->source, lp->raddr, lp->rport, dst, segp->dest, lp->laddr, lp->lport, version, lp->version ); 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){ netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux", 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) return nil; memmove(new->ptcl, s->ptcl, sizeof(Tcpctl)); tcb = (Tcpctl*)new->ptcl; tcb->flags &= ~CLONE; tcb->timer.arg = new; tcb->timer.state = TcptimerOFF; tcb->acktimer.arg = new; tcb->acktimer.state = TcptimerOFF; tcb->katimer.arg = new; tcb->katimer.state = TcptimerOFF; 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; /* window scaling */ tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale); /* 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+lp->rexmits*SYNACK_RXTIMER; tcpsynackrtt(new); free(lp); /* set up proto header */ switch(version){ case V4: h4 = &tcb->protohdr.tcp4hdr; memset(h4, 0, sizeof(*h4)); h4->proto = IP_TCPPROTO; hnputs(h4->tcpsport, new->lport); hnputs(h4->tcpdport, new->rport); v6tov4(h4->tcpsrc, dst); v6tov4(h4->tcpdst, src); break; case V6: h6 = &tcb->protohdr.tcp6hdr; memset(h6, 0, sizeof(*h6)); h6->proto = IP_TCPPROTO; hnputs(h6->tcpsport, new->lport); hnputs(h6->tcpdport, new->rport); ipmove(h6->tcpsrc, dst); ipmove(h6->tcpdst, src); break; default: panic("tcpincoming: version %d", new->ipversion); } tcpsetstate(new, Established); iphtadd(&tpriv->ht, new); return new;}intseq_within(ulong x, ulong low, ulong high){ if(low <= high){ if(low <= x && x <= high) return 1; } else { if(x >= low || x <= high) return 1; } return 0;}intseq_lt(ulong x, ulong y){ return (int)(x-y) < 0;}intseq_le(ulong x, ulong y){ return (int)(x-y) <= 0;}intseq_gt(ulong x, ulong y){ return (int)(x-y) > 0;}intseq_ge(ulong x, ulong y){ return (int)(x-y) >= 0;}/* * use the time between the first SYN and it's ack as the * initial round trip time */voidtcpsynackrtt(Conv *s){ Tcpctl *tcb; int delta; Tcppriv *tpriv; tcb = (Tcpctl*)s->ptcl; tpriv = s->p->priv; delta = NOW - tcb->sndsyntime; tcb->srtt = delta<<LOGAGAIN; tcb->mdev = delta<<LOGDGAIN; /* halt round trip timer */ tcphalt(tpriv, &tcb->rtt_timer);}voidupdate(Conv *s, Tcp *seg){ int rtt, delta; Tcpctl *tcb; ulong acked; ulong expand; Tcppriv *tpriv; tpriv = s->p->priv; tcb = (Tcpctl*)s->ptcl; /* if everything has been acked, force output(?) */ if(seq_gt(seg->ack, tcb->snd.nxt)) { tcb->flags |= FORCE; return; } /* added by Dong Lin for fast retransmission */ if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt && seg->len == 0 && seg->wnd == tcb->snd.wnd) { /* this is a pure ack w/o window update */ netlog(s->p->f, Logtcprxmt, "dupack %lud ack %lud sndwnd %d advwin %d\n", tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd); if(++tcb->snd.dupacks == TCPREXMTTHRESH) { /* * tahoe tcp rxt the packet, half sshthresh, * and set cwnd to one packet */ tcb->snd.recovery = 1; tcb->snd.rxt = tcb->snd.nxt; netlog(s->p->f, Logtcprxmt, "fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt); tcprxmit(s); } else { /* do reno tcp here. */ } } /* * update window */ if(seq_gt(seg->ack, tcb->snd.wl2) || (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){ tcb->snd.wnd = seg->wnd; 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 * we're still getting acks */ if((tcb->flags&RETRAN) && tcb->snd.wnd == 0){ tcb->backedoff = MAXBACKMS/4; } return; } /* * any positive ack turns off fast rxt, * (should we do new-reno on partial acks?) */ if(!tcb->snd.recovery || seq_ge(seg->ack, tcb->snd.rxt)) { tcb->snd.dupacks = 0; tcb->snd.recovery = 0; } else netlog(s->p->f, Logtcp, "rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind); /* Compute the new send window size */ acked = seg->ack - tcb->snd.una; /* avoid slow start and timers for SYN acks */ if((tcb->flags & SYNACK) == 0) { tcb->flags |= SYNACK; acked--; tcb->flgcnt--; goto done; } /* slow start as long as we're not recovering from lost packets */ if(tcb->cwind < tcb->snd.wnd && !tcb->snd.recovery) { if(tcb->cwind < tcb->ssthresh) { expand = tcb->mss; if(acked < expand) expand = acked; } else expand = ((int)tcb->mss * tcb->mss) / tcb->cwind; if(tcb->cwind + expand < tcb->cwind) expand = tcb->snd.wnd - tcb->cwind; if(tcb->cwind + expand > tcb->snd.wnd) expand = tcb->snd.wnd - tcb->cwind; tcb->cwind += expand; } /* Adjust the timers according to the round trip time */ if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) { tcphalt(tpriv, &tcb->rtt_timer); if((tcb->flags&RETRAN) == 0) { tcb->backoff = 0; tcb->backedoff = 0; rtt = tcb->rtt_timer.start - tcb->rtt_timer.count; if(rtt == 0) rtt = 1; /* otherwise all close systems will rexmit in 0 time */ rtt *= MSPTICK; if(tcb->srtt == 0) { tcb->srtt = rtt << LOGAGAIN; tcb->mdev = rtt << LOGDGAIN; } else { delta = rtt - (tcb->srtt>>LOGAGAIN); tcb->srtt += delta; if(tcb->srtt <= 0) tcb->srtt = 1; delta = abs(delta) - (tcb->mdev>>LOGDGAIN); tcb->mdev += delta; if(tcb->mdev <= 0) tcb->mdev = 1; } tcpsettimer(tcb); } }done: if(qdiscard(s->wq, acked) < acked) tcb->flgcnt--; tcb->snd.una = seg->ack; if(seq_gt(seg->ack, tcb->snd.urg)) tcb->snd.urg = seg->ack; if(tcb->snd.una != tcb->snd.nxt) tcpgo(tpriv, &tcb->timer); else tcphalt(tpriv, &tcb->timer); if(seq_lt(tcb->snd.ptr, tcb->snd.una)) tcb->snd.ptr = tcb->snd.una;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -