⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 + -