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

📄 tcp.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 5 页
字号:
	tcb->flags &= ~RETRAN;	tcb->backoff = 0;	tcb->backedoff = 0;}voidtcpiput(Proto *tcp, Ipifc*, Block *bp){	Tcp seg;	Tcp4hdr *h4;	Tcp6hdr *h6;	int hdrlen;	Tcpctl *tcb;	ushort length;	uchar source[IPaddrlen], dest[IPaddrlen];	Conv *s;	Fs *f;	Tcppriv *tpriv;	uchar version;	f = tcp->f;	tpriv = tcp->priv;	tpriv->stats[InSegs]++;	h4 = (Tcp4hdr*)(bp->rp);	h6 = (Tcp6hdr*)(bp->rp);	if((h4->vihl&0xF0)==IP_VER4) {		version = V4;		length = nhgets(h4->length);		v4tov6(dest, h4->tcpdst);		v4tov6(source, h4->tcpsrc);		h4->Unused = 0;		hnputs(h4->tcplen, length-TCP4_PKT);		if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {			tpriv->stats[CsumErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "bad tcp proto cksum\n");			freeblist(bp);			return;		}		hdrlen = ntohtcp4(&seg, &bp);		if(hdrlen < 0){			tpriv->stats[HlenErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "bad tcp hdr len\n");			return;		}		/* trim the packet to the size claimed by the datagram */		length -= hdrlen+TCP4_PKT;		bp = trimblock(bp, hdrlen+TCP4_PKT, length);		if(bp == nil){			tpriv->stats[LenErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "tcp len < 0 after trim\n");			return;		}	}	else {		int ttl = h6->ttl;		int proto = h6->proto;		version = V6;		length = nhgets(h6->ploadlen);		ipmove(dest, h6->tcpdst);		ipmove(source, h6->tcpsrc);		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;		h6->ttl = proto;		hnputl(h6->vcf, length);		if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&			ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) {			tpriv->stats[CsumErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "bad tcp proto cksum\n");			freeblist(bp);			return;		}		h6->ttl = ttl;		h6->proto = proto;		hnputs(h6->ploadlen, length);		hdrlen = ntohtcp6(&seg, &bp);		if(hdrlen < 0){			tpriv->stats[HlenErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "bad tcp hdr len\n");			return;		}		/* trim the packet to the size claimed by the datagram */		length -= hdrlen;		bp = trimblock(bp, hdrlen+TCP6_PKT, length);		if(bp == nil){			tpriv->stats[LenErrs]++;			tpriv->stats[InErrs]++;			netlog(f, Logtcp, "tcp len < 0 after trim\n");			return;		}	}	/* lock protocol while searching for a conversation */	qlock(tcp);	/* Look for a matching conversation */	s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);	if(s == nil){		netlog(f, Logtcp, "iphtlook failed");reset:		qunlock(tcp);		sndrst(tcp, source, dest, length, &seg, version, "no conversation");		freeblist(bp);		return;	}	/* if it's a listener, look for the right flags and get a new conv */	tcb = (Tcpctl*)s->ptcl;	if(tcb->state == Listen){		if(seg.flags & RST){			limborst(s, &seg, source, dest, version);			qunlock(tcp);			freeblist(bp);			return;		}		/* 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;	}	/* The rest of the input state machine is run with the control block	 * locked and implements the state machine directly out of the RFC.	 * Out-of-band data is ignored - it was always a bad idea.	 */	tcb = (Tcpctl*)s->ptcl;	if(waserror()){		qunlock(s);		nexterror();	}	qlock(s);	qunlock(tcp);	/* fix up window */	seg.wnd <<= tcb->rcv.scale;	/* every input packet in puts off the keep alive time out */	tcpsetkacounter(tcb);	switch(tcb->state) {	case Closed:		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");		goto raise;	case Syn_sent:		if(seg.flags & ACK) {			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {				sndrst(tcp, source, dest, length, &seg, version,					 "bad seq in Syn_sent");				goto raise;			}		}		if(seg.flags & RST) {			if(seg.flags & ACK)				localclose(s, Econrefused);			goto raise;		}		if(seg.flags & SYN) {			procsyn(s, &seg);			if(seg.flags & ACK){				update(s, &seg);				tcpsynackrtt(s);				tcpsetstate(s, Established);				tcpsetscale(s, tcb, seg.ws, tcb->scale);			}			else {				tcb->time = NOW;				tcpsetstate(s, Syn_received);	/* DLP - shouldn't this be a reset? */			}			if(length != 0 || (seg.flags & FIN))				break;			freeblist(bp);			goto output;		}		else			freeblist(bp);		qunlock(s);		poperror();		return;	case Syn_received:		/* doesn't matter if it's the correct ack, we're just trying to set timing */		if(seg.flags & ACK)			tcpsynackrtt(s);		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 && (seg.flags & RST) == 0){		if(tcpporthogdefense		&& seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){			print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",				source, seg.source, dest, seg.dest, seg.flags,				tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));			localclose(s, "stateless hog");		}	}	/* Cut the data to fit the receive window */	if(tcptrim(tcb, &seg, &bp, &length) == -1) {		netlog(f, Logtcp, "tcp len < 0, %lud %d\n", seg.seq, length);		update(s, &seg);		if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {			tcphalt(tpriv, &tcb->rtt_timer);			tcphalt(tpriv, &tcb->acktimer);			tcphalt(tpriv, &tcb->katimer);			tcpsetstate(s, Time_wait);			tcb->timer.start = MSL2*(1000 / MSPTICK);			tcpgo(tpriv, &tcb->timer);		}		if(!(seg.flags & RST)) {			tcb->flags |= FORCE;			goto output;		}		qunlock(s);		poperror();		return;	}	/* Cannot accept so answer with a rst */	if(length && tcb->state == Closed) {		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");		goto raise;	}	/* The segment is beyond the current receive pointer so	 * queue the data in the resequence queue	 */	if(seg.seq != tcb->rcv.nxt)	if(length != 0 || (seg.flags & (SYN|FIN))) {		update(s, &seg);		if(addreseq(tcb, tpriv, &seg, bp, length) < 0)			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport, s->laddr, s->lport);		tcb->flags |= FORCE;		goto output;	}	/*	 *  keep looping till we've processed this packet plus any	 *  adjacent packets in the resequence queue	 */	for(;;) {		if(seg.flags & RST) {			if(tcb->state == Established) {				tpriv->stats[EstabResets]++;				if(tcb->rcv.nxt != seg.seq)					print("out of order RST rcvd: %I.%d -> %I.%d, rcv.nxt %lux seq %lux\n", s->raddr, s->rport, s->laddr, s->lport, tcb->rcv.nxt, seg.seq);			}			localclose(s, Econrefused);			goto raise;		}		if((seg.flags&ACK) == 0)			goto raise;		switch(tcb->state) {		case Syn_received:			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){				sndrst(tcp, source, dest, length, &seg, version,					"bad seq in Syn_received");				goto raise;			}			update(s, &seg);			tcpsetstate(s, Established);		case Established:		case Close_wait:			update(s, &seg);			break;		case Finwait1:			update(s, &seg);			if(qlen(s->wq)+tcb->flgcnt == 0){				tcphalt(tpriv, &tcb->rtt_timer);				tcphalt(tpriv, &tcb->acktimer);				tcpsetkacounter(tcb);				tcb->time = NOW;				tcpsetstate(s, Finwait2);				tcb->katimer.start = MSL2 * (1000 / MSPTICK);				tcpgo(tpriv, &tcb->katimer);			}			break;		case Finwait2:			update(s, &seg);			break;		case Closing:			update(s, &seg);			if(qlen(s->wq)+tcb->flgcnt == 0) {				tcphalt(tpriv, &tcb->rtt_timer);				tcphalt(tpriv, &tcb->acktimer);				tcphalt(tpriv, &tcb->katimer);				tcpsetstate(s, Time_wait);				tcb->timer.start = MSL2*(1000 / MSPTICK);				tcpgo(tpriv, &tcb->timer);			}			break;		case Last_ack:			update(s, &seg);			if(qlen(s->wq)+tcb->flgcnt == 0) {				localclose(s, nil);				goto raise;			}		case Time_wait:			tcb->flags |= FORCE;			if(tcb->timer.state != TcptimerON)				tcpgo(tpriv, &tcb->timer);		}		if((seg.flags&URG) && seg.urg) {			if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {				tcb->rcv.urg = seg.urg + seg.seq;				pullblock(&bp, seg.urg);			}		}		else		if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))			tcb->rcv.urg = tcb->rcv.nxt;		if(length == 0) {			if(bp != nil)				freeblist(bp);		}		else {			switch(tcb->state){			default:				/* Ignore segment text */				if(bp != nil)					freeblist(bp);				break;			case Syn_received:			case Established:			case Finwait1:				/* If we still have some data place on				 * receive queue				 */				if(bp) {					bp = packblock(bp);					if(bp == nil)						panic("tcp packblock");					qpassnolim(s->rq, bp);					bp = nil;					/*					 *  Force an ack every 2 data messages.  This is					 *  a hack for rob to make his home system run					 *  faster.					 *					 *  this also keeps the standard TCP congestion					 *  control working since it needs an ack every					 *  2 max segs worth.  This is not quite that,					 *  but under a real stream is equivalent since					 *  every packet has a max seg in it.					 */					if(++(tcb->rcv.una) >= 2)						tcb->flags |= FORCE;				}				tcb->rcv.nxt += length;				/*				 *  update our rcv window				 */				tcprcvwin(s);				/*				 *  turn on the acktimer if there's something				 *  to ack				 */				if(tcb->acktimer.state != TcptimerON)					tcpgo(tpriv, &tcb->acktimer);				break;			case Finwait2:				/* no process to read the data, send a reset */				if(bp != nil)					freeblist(bp);				sndrst(tcp, source, dest, length, &seg, version,					"send to Finwait2");				qunlock(s);				poperror();				return;			}		}		if(seg.flags & FIN) {			tcb->flags |= FORCE;			switch(tcb->state) {			case Syn_received:			case Established:				tcb->rcv.nxt++;				tcpsetstate(s, Close_wait);				break;			case Finwait1:				tcb->rcv.nxt++;				if(qlen(s->wq)+tcb->flgcnt == 0) {					tcphalt(tpriv, &tcb->rtt_timer);					tcphalt(tpriv, &tcb->acktimer);					tcphalt(tpriv, &tcb->katimer);					tcpsetstate(s, Time_wait);					tcb->timer.start = MSL2*(1000/MSPTICK);					tcpgo(tpriv, &tcb->timer);				}				else					tcpsetstate(s, Closing);				break;			case Finwait2:				tcb->rcv.nxt++;				tcphalt(tpriv, &tcb->rtt_timer);				tcphalt(tpriv, &tcb->acktimer);				tcphalt(tpriv, &tcb->katimer);				tcpsetstate(s, Time_wait);				tcb->timer.start = MSL2 * (1000/MSPTICK);				tcpgo(tpriv, &tcb->timer);				break;			case Close_wait:			case Closing:			case Last_ack:				break;			case Time_wait:				tcpgo(tpriv, &tcb->timer);				break;			}		}		/*		 *  get next adjacent segment from the resequence queue.		 *  dump/trim any overlapping segments		 */		for(;;) {			if(tcb->reseq == nil)				goto output;			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)				goto output;			getreseq(tcb, &seg, &bp, &length);			if(tcptrim(tcb, &seg, &bp, &length) == 0)				break;		}	}output:	tcpoutput(s);	qunlock(s);	poperror();	return;raise:	qunlock(s);	poperror();	freeblist(bp);	tcpkick(s);}/* *  always enters and exits with the s locked.  We drop *  the lock to ipoput the packet so some care has to be *  taken by callers. */voidtcpoutput(Conv *s){	Tcp seg;	int msgs;	Tcpctl *tcb;	Block *hbp, *bp;	int sndcnt, n;	ulong ssize, dsize, usable, sent;	Fs *f;	Tcppriv *tpriv;	uchar version;	f = s->p->f;	tpriv = s->p->priv;	version = s->ipversion;	for(msgs = 0; msgs < 100; msgs++) {		tcb = (Tcpctl*)s->ptcl;		switch(tcb->state) {		case Listen:		case Closed:		case Finwait2:			return;		}		/* force an ack when a window has opened up */		if(tcb->rcv.blocked && tcb->rcv.wnd > 0){			tcb->rcv.blocked = 0;			tcb->flags |= FORCE;		}		sndcnt = qlen(s->wq)+tcb->flgcnt;		sent = tcb->snd.ptr - tcb->snd.una;		/* Don't send anything else until our SYN has been acked */		if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)			break;		/* Compute usable segment based on offered window and limit		 * window probes to one		 */		if(tcb->snd.wnd == 0){			if(sent != 0) {				if((tcb->flags&FORCE) == 0)					break;//				tcb->snd.ptr = tcb->snd.una;			}			usable = 1;		}		else {			usable = tcb->cwind;			if(tcb->snd.wnd < usable)				usable = tcb->snd.wnd;			usable -= sent;		}		ssize = sndcnt-sent;		if(ssize && usable < 2)			netlog(s->p->f, Logtcp, "throttled snd.wnd %lud cwind %lud\n",				tcb->snd.wnd, tcb->cwind);		if(usable < ssize)			ssize = usable;		if(tcb->mss < ssize)			ssize = tcb->mss;		dsize = ssize;		seg.urg = 0;		if(ssize == 0)		if((tcb->flags&FORCE) == 0)			break;		tcb->flags &= ~FORCE;		tcprcvwin(s);		/* By default we will generate an ack */		tcphalt(tpriv, &tcb->acktimer);		tcb->rcv.una = 0;		seg.source = s->lport;		seg.dest = s->rport;		seg.flags = ACK;		seg.mss = 0;		seg.ws = 0;		switch(tcb->state){		case Syn_sent:			seg.flags = 0;			if(tcb->snd.ptr == tcb->iss){				seg.flags |= SYN;				dsize--;				seg.mss = tcb->mss;				seg.ws = tcb->scale;			}			break;		case Syn_received:			/*			 *  don't send any data with a SYN/ACK packet			 *  because Linux rejects the packet in its			 *  attempt to solve the SYN attack problem			 */			if(tcb->snd.ptr == tcb->iss){				seg.flags |= SYN;				dsize = 0;				ssize = 1;				seg.mss = tcb->mss;				seg.ws = tcb->scale;			}			break;		}		seg.seq = tcb->snd.ptr;		seg.ack = tcb->rcv.nxt;		seg.wnd = tcb->rcv.wnd;		/* Pull out data to send */		bp = nil;		if(dsize != 0) {			bp = qcopy(s->wq, dsize, sent);			if(BLEN(bp) != dsize) {				seg.flags |= FIN;				dsize--;			}		}		if(sent+dsize == sndcnt)			seg.flags |= PSH;		/* keep track of balance of resent data */		if(seq_lt(tcb->snd.ptr, tcb->snd.nxt)) {			n = tcb->snd.nxt - tcb->snd.ptr;			if(ssize < n)				n = ssize;			tcb->resent += n;			netlog(f, Logtcp, "rexmit: %I.%d -> %I.%d ptr %lux nxt %lux\n",				s->raddr, s->rport, s->laddr, s->lport, tcb->snd.ptr, tcb->snd.nxt);			tpriv->stats[RetransSegs]++;		}		tcb->snd.ptr += ssize;		/* Pull up the send pointer so we can accept acks		 * for this window		 */		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))			tcb->snd.nxt = tcb->snd.ptr;		/* Build header, link data and compute cksum */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -