📄 tcp.c
字号:
switch(version){ case V4: tcb->protohdr.tcp4hdr.vihl = IP_VER4; hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb); if(hbp == nil) { freeblist(bp); return; } break; case V6: tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb); if(hbp == nil) { freeblist(bp); return; } break; default: hbp = nil; /* to suppress a warning */ panic("tcpoutput: version %d", version); } /* Start the transmission timers if there is new data and we * expect acknowledges */ if(ssize != 0){ if(tcb->timer.state != TcptimerON) tcpgo(tpriv, &tcb->timer); /* If round trip timer isn't running, start it. * measure the longest packet only in case the * transmission time dominates RTT */ if(tcb->rtt_timer.state != TcptimerON) if(ssize == tcb->mss) { tcpgo(tpriv, &tcb->rtt_timer); tcb->rttseq = tcb->snd.ptr; } } tpriv->stats[OutSegs]++; /* put off the next keep alive */ tcpgo(tpriv, &tcb->katimer); switch(version){ case V4: if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){ /* a negative return means no route */ localclose(s, "no route"); } break; case V6: if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){ /* a negative return means no route */ localclose(s, "no route"); } break; default: panic("tcpoutput2: version %d", version); } if((msgs%4) == 1){ qunlock(s); sched(); qlock(s); } }}/* * the BSD convention (hack?) for keep alives. resend last uchar acked. */voidtcpsendka(Conv *s){ Tcp seg; Tcpctl *tcb; Block *hbp,*dbp; tcb = (Tcpctl*)s->ptcl; dbp = nil; seg.urg = 0; seg.source = s->lport; seg.dest = s->rport; seg.flags = ACK|PSH; seg.mss = 0; seg.ws = 0; if(tcpporthogdefense) seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20); else seg.seq = tcb->snd.una-1; seg.ack = tcb->rcv.nxt; tcb->rcv.una = 0; seg.wnd = tcb->rcv.wnd; if(tcb->state == Finwait2){ seg.flags |= FIN; } else { dbp = allocb(1); dbp->wp++; } if(isv4(s->raddr)) { /* Build header, link data and compute cksum */ tcb->protohdr.tcp4hdr.vihl = IP_VER4; hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb); if(hbp == nil) { freeblist(dbp); return; } ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s); } else { /* Build header, link data and compute cksum */ tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6; hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb); if(hbp == nil) { freeblist(dbp); return; } ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s); }}/* * set connection to time out after 12 minutes */voidtcpsetkacounter(Tcpctl *tcb){ tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK); if(tcb->kacounter < 3) tcb->kacounter = 3;}/* * if we've timed out, close the connection * otherwise, send a keepalive and restart the timer */voidtcpkeepalive(void *v){ Tcpctl *tcb; Conv *s; s = v; tcb = (Tcpctl*)s->ptcl; if(waserror()){ qunlock(s); nexterror(); } qlock(s); if(tcb->state != Closed){ if(--(tcb->kacounter) <= 0) { localclose(s, Etimedout); } else { tcpsendka(s); tcpgo(s->p->priv, &tcb->katimer); } } qunlock(s); poperror();}/* * start keepalive timer */char*tcpstartka(Conv *s, char **f, int n){ Tcpctl *tcb; int x; tcb = (Tcpctl*)s->ptcl; if(tcb->state != Established) return "connection must be in Establised state"; if(n > 1){ x = atoi(f[1]); if(x >= MSPTICK) tcb->katimer.start = x/MSPTICK; } tcpsetkacounter(tcb); tcpgo(s->p->priv, &tcb->katimer); return nil;}/* * turn checksums on/off */char*tcpsetchecksum(Conv *s, char **f, int){ Tcpctl *tcb; tcb = (Tcpctl*)s->ptcl; tcb->nochecksum = !atoi(f[1]); return nil;}voidtcprxmit(Conv *s){ Tcpctl *tcb; tcb = (Tcpctl*)s->ptcl; tcb->flags |= RETRAN|FORCE; tcb->snd.ptr = tcb->snd.una; /* * We should be halving the slow start threshhold (down to one * mss) but leaving it at mss seems to work well enough */ tcb->ssthresh = tcb->mss; /* * pull window down to a single packet */ tcb->cwind = tcb->mss; tcpoutput(s);}voidtcptimeout(void *arg){ Conv *s; Tcpctl *tcb; int maxback; Tcppriv *tpriv; s = (Conv*)arg; tpriv = s->p->priv; tcb = (Tcpctl*)s->ptcl; if(waserror()){ qunlock(s); nexterror(); } qlock(s); switch(tcb->state){ default: tcb->backoff++; if(tcb->state == Syn_sent) maxback = MAXBACKMS/2; else maxback = MAXBACKMS; tcb->backedoff += tcb->timer.start * MSPTICK; if(tcb->backedoff >= maxback) { localclose(s, Etimedout); break; } netlog(s->p->f, Logtcprxmt, "timeout rexmit 0x%lux %d/%d\n", tcb->snd.una, tcb->timer.start, NOW); tcpsettimer(tcb); tcprxmit(s); tpriv->stats[RetransTimeouts]++; tcb->snd.dupacks = 0; break; case Time_wait: localclose(s, nil); break; case Closed: break; } qunlock(s); poperror();}intinwindow(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 */voidprocsyn(Conv *s, Tcp *seg){ Tcpctl *tcb; tcb = (Tcpctl*)s->ptcl; tcb->flags |= FORCE; tcb->rcv.nxt = seg->seq + 1; tcb->rcv.urg = tcb->rcv.nxt; tcb->irs = seg->seq; /* 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; /* the congestion window always starts out as a single segment */ tcb->snd.wnd = seg->wnd; tcb->cwind = tcb->mss;}intaddreseq(Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length){ Reseq *rp, *rp1; int i, rqlen; static int once; rp = malloc(sizeof(Reseq)); if(rp == nil){ freeblist(bp); /* bp always consumed by add_reseq */ return 0; } rp->seg = *seg; rp->bp = bp; rp->length = length; /* Place on reassembly list sorting by starting seq number */ rp1 = tcb->reseq; if(rp1 == nil || seq_lt(seg->seq, rp1->seg.seq)) { rp->next = rp1; tcb->reseq = rp; if(rp->next != nil) tpriv->stats[OutOfOrder]++; return 0; } rqlen = 0; for(i = 0;; i++) { rqlen += rp1->length; if(rp1->next == nil || seq_lt(seg->seq, rp1->next->seg.seq)) { rp->next = rp1->next; rp1->next = rp; if(rp->next != nil) tpriv->stats[OutOfOrder]++; break; } rp1 = rp1->next; } if(rqlen > QMAX && once++ == 0){ print("very long tcp resequence queue: %d\n", rqlen); for(rp1 = tcb->reseq, i = 0; i < 10 && rp1 != nil; rp1 = rp1->next, i++) print("0x%lux 0x%lux 0x%ux\n", rp1->seg.seq, rp1->seg.ack, rp1->seg.flags); return -1; } return 0;}voidgetreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length){ Reseq *rp; rp = tcb->reseq; if(rp == nil) return; tcb->reseq = rp->next; *seg = rp->seg; *bp = rp->bp; *length = rp->length; free(rp);}inttcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length){ ushort len; uchar accept; int dupcnt, excess; accept = 0; len = *length; if(seg->flags & SYN) len++; if(seg->flags & FIN) len++; if(tcb->rcv.wnd == 0) { if(len == 0 && seg->seq == tcb->rcv.nxt) return 0; } else { /* Some part of the segment should be in the window */ if(inwindow(tcb,seg->seq)) accept++; else if(len != 0) { if(inwindow(tcb, seg->seq+len-1) || seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1)) accept++; } } if(!accept) { freeblist(*bp); return -1; } dupcnt = tcb->rcv.nxt - seg->seq; if(dupcnt > 0){ tcb->rerecv += dupcnt; if(seg->flags & SYN){ seg->flags &= ~SYN; seg->seq++; if(seg->urg > 1) seg->urg--; else seg->flags &= ~URG; dupcnt--; } if(dupcnt > 0){ pullblock(bp, (ushort)dupcnt); seg->seq += dupcnt; *length -= dupcnt; if(seg->urg > dupcnt) seg->urg -= dupcnt; else { seg->flags &= ~URG; seg->urg = 0; } } } excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd); if(excess > 0) { tcb->rerecv += excess; *length -= excess; *bp = trimblock(*bp, 0, *length); if(*bp == nil) panic("presotto is a boofhead"); seg->flags &= ~FIN; } return 0;}voidtcpadvise(Proto *tcp, Block *bp, char *msg){ Tcp4hdr *h4; Tcp6hdr *h6; Tcpctl *tcb; uchar source[IPaddrlen]; uchar dest[IPaddrlen]; ushort psource, pdest; Conv *s, **p; h4 = (Tcp4hdr*)(bp->rp); h6 = (Tcp6hdr*)(bp->rp); if((h4->vihl&0xF0)==IP_VER4) { v4tov6(dest, h4->tcpdst); v4tov6(source, h4->tcpsrc); psource = nhgets(h4->tcpsport); pdest = nhgets(h4->tcpdport); } else { ipmove(dest, h6->tcpdst); ipmove(source, h6->tcpsrc); psource = nhgets(h6->tcpsport); pdest = nhgets(h6->tcpdport); } /* Look for a connection */ qlock(tcp); for(p = tcp->conv; *p; p++) { s = *p; tcb = (Tcpctl*)s->ptcl; if(s->rport == pdest) if(s->lport == psource) if(tcb->state != Closed) if(ipcmp(s->raddr, dest) == 0) if(ipcmp(s->laddr, source) == 0){ qlock(s); qunlock(tcp); switch(tcb->state){ case Syn_sent: localclose(s, msg); break; } qunlock(s); freeblist(bp); return; } } qunlock(tcp); freeblist(bp);}static char*tcpporthogdefensectl(char *val){ if(strcmp(val, "on") == 0) tcpporthogdefense = 1; else if(strcmp(val, "off") == 0) tcpporthogdefense = 0; else return "unknown value for tcpporthogdefense"; return nil;}/* called with c qlocked */char*tcpctl(Conv* c, char** f, int n){ if(n == 1 && strcmp(f[0], "hangup") == 0) return tcphangup(c); if(n >= 1 && strcmp(f[0], "keepalive") == 0) return tcpstartka(c, f, n); if(n >= 1 && strcmp(f[0], "checksum") == 0) return tcpsetchecksum(c, f, n); if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0) return tcpporthogdefensectl(f[1]); return "unknown control request";}inttcpstats(Proto *tcp, char *buf, int len){ Tcppriv *priv; char *p, *e; int i; priv = tcp->priv; p = buf; e = p+len; for(i = 0; i < Nstats; i++) p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]); return p - buf;}/* * garbage collect any stale conversations: * - SYN received but no SYN-ACK after 5 seconds (could be the SYN attack) * - Finwait2 after 5 minutes * * this is called whenever we run out of channels. Both checks are * of questionable validity so we try to use them only when we're * up against the wall. */inttcpgc(Proto *tcp){ Conv *c, **pp, **ep; int n; Tcpctl *tcb; n = 0; ep = &tcp->conv[tcp->nc]; for(pp = tcp->conv; pp < ep; pp++) { c = *pp; if(c == nil) break; if(!canqlock(c)) continue; tcb = (Tcpctl*)c->ptcl; switch(tcb->state){ case Syn_received: if(NOW - tcb->time > 5000){ localclose(c, "timed out"); n++; } break; case Finwait2: if(NOW - tcb->time > 5*60*1000){ localclose(c, "timed out"); n++; } break; } qunlock(c); } return n;}voidtcpsettimer(Tcpctl *tcb){ int x; /* round trip dependency */ x = backoff(tcb->backoff) * (tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK; /* bounded twixt 1/2 and 64 seconds */ if(x < 500/MSPTICK) x = 500/MSPTICK; else if(x > (64000/MSPTICK)) x = 64000/MSPTICK; tcb->timer.start = x;}voidtcpinit(Fs *fs){ Proto *tcp; Tcppriv *tpriv; tcp = smalloc(sizeof(Proto)); tpriv = tcp->priv = smalloc(sizeof(Tcppriv)); tcp->name = "tcp"; tcp->connect = tcpconnect; tcp->announce = tcpannounce; tcp->ctl = tcpctl; tcp->state = tcpstate; tcp->create = tcpcreate; tcp->close = tcpclose; tcp->rcv = tcpiput; tcp->advise = tcpadvise; tcp->stats = tcpstats; tcp->inuse = tcpinuse; tcp->gc = tcpgc; tcp->ipproto = IP_TCPPROTO; tcp->nc = scalednconv(); tcp->ptclsize = sizeof(Tcpctl); tpriv->stats[MaxConn] = tcp->nc; Fsproto(fs, tcp);}voidtcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale){ if(rcvscale){ tcb->rcv.scale = rcvscale & 0xff; tcb->snd.scale = sndscale & 0xff; tcb->window = QMAX<<tcb->snd.scale; qsetlimit(s->rq, tcb->window); } else { tcb->rcv.scale = 0; tcb->snd.scale = 0; tcb->window
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -