Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/tcpinput.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname port/tcpinput.c 1991/0424
## diff -e /dev/null /n/bootesdump/1991/0424/sys/src/9/port/tcpinput.c
0a
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

int tcpdbg = 0;
#define DPRINT	if(tcpdbg) print
#define LPRINT  print

extern Queue *Tcpoutput;
QLock	reseqlock;
Reseq	*reseqfree;

char *tcpstate[] = {
	"Closed", 	"Listen", 	"Syn_sent", "Syn_received",
	"Established", 	"Finwait1",	"Finwait2", "Close_wait",
	"Closing", 	"Last_ack", 	"Time_wait" };

void
tcp_input(Ipconv *ipc, Block *bp)
{
	Ipconv *s, *new, *etab;
	Tcpctl *tcb;		
	Tcphdr *h;
	Tcp seg;
	int hdrlen;	
	Block *oobbp;
	Ipaddr source, dest;
	char tos;
	ushort length;

	DPRINT("tcp_input.\n");

	h = (Tcphdr *)(bp->rptr);
	dest = nhgetl(h->tcpdst);
	source = nhgetl(h->tcpsrc);

	tos = h->tos;
	length = nhgets(h->length);

	if (dest == source) {
		if (!(bp = copyb(bp, blen(bp)))) {
			print("tcpin: allocb failure.");
			return;
		}
		DPRINT("tcpin: Duplicate packet %lux\n", bp);
	}

	h->Unused = 0;
	hnputs(h->tcplen, length - (TCP_IPLEN+TCP_PHDRSIZE));

	if(ptcl_csum(bp, TCP_EHSIZE+TCP_IPLEN, length - TCP_IPLEN)) {
		DPRINT("tcpin: Bad checksum.\n");
		freeb(bp);
		return;
	}

	if((hdrlen = ntohtcp(&seg, &bp)) < 0)
		return;

	/* Adjust the data length */
	length -= (hdrlen+TCP_IPLEN+TCP_PHDRSIZE);
	
	DPRINT("tcpin: lport = %d, rport = %d hdrlen %d",
		seg.dest, seg.source, hdrlen);
	DPRINT(" flags = 0x%lux, seqo = %d, seqi = %d len %d\n", 
		seg.flags, seg.seq, seg.ack, length);

	/* Trim the packet down to just the data */
	bp = btrim(bp, hdrlen+TCP_PKT, length);
	if(bp == 0)
		return;

	if (!(s = ip_conn(ipc, seg.dest, seg.source, source, IP_TCPPROTO))) {
		LPRINT("tcpin: look for listen on %d\n", seg.dest);

		if(!(seg.flags & SYN)) {
			LPRINT("tcpin: No SYN\n");
		clear:
			LPRINT("tcpin: call cleared\n");
			freeb(bp);   
                        reset(source, dest, tos, length, &seg);
                        return;
		}		

		if(!(s = ip_conn(ipc, seg.dest, 0, 0, IP_TCPPROTO))) {
			LPRINT("tcpin: No socket dest on %d\n", seg.dest);
			goto clear;
		}

		if(s->curlog >= s->backlog) {
			LPRINT("too many pending\n");
			goto clear;
		}

		/* Find a conversation to clone onto */
		etab = &ipc[conf.ip];
		for(new = ipc; new < etab; new++) {
			if(new->ref == 0 && canqlock(new)) {
				if(new->ref || new->tcpctl.state != CLOSED) {
					qunlock(new);
					continue;
				}
				new->ref++;
				qunlock(new);
				break;
			}
		}

		if(new == etab)
			goto clear;

		s->curlog++;
		LPRINT("tcpin: cloning socket\n");
		new->psrc = s->psrc;
		new->pdst = seg.source;
		new->dst = source;
		memmove(&new->tcpctl, &s->tcpctl, sizeof(Tcpctl));
		new->tcpctl.flags &= ~CLONE;
		new->tcpctl.timer.arg = new;
		new->tcpctl.timer.state = TIMER_STOP;
		new->tcpctl.acktimer.arg = new;
		new->tcpctl.acktimer.state = TIMER_STOP;

		new->ipinterface = s->ipinterface;
		s->ipinterface->ref++;

		/* Wake the sleeping dodo */
		wakeup(&s->listenr);
		s = new;
	}
	
	tcb = &s->tcpctl;
	qlock(tcb);

	switch(tcb->state) {
	case CLOSED:
		freeb(bp);
		reset(source, dest, tos, length, &seg);
		goto done;
	case LISTEN:
		if(seg.flags & RST) {
			freeb(bp);
			goto done;
		} 
		if(seg.flags & ACK) {
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}
		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			send_syn(tcb);
			setstate(s, SYN_RECEIVED);		
			if(length != 0 || (seg.flags & FIN)) 
				break;
			freeb(bp);
			goto output;
		}
		freeb(bp);
		goto done;
	case SYN_SENT:
		if(seg.flags & ACK) {
			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
				freeb(bp);
				reset(source, dest, tos, length, &seg);
				goto done;
			}
		}
		if(seg.flags & RST) {
			if(seg.flags & ACK)
				close_self(s, Econrefused);
			freeb(bp);
			goto done;
		}

		if((seg.flags & ACK) && PREC(tos) != PREC(tcb->tos)){
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}
		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			if(seg.flags & ACK){
				update(s, &seg);
				setstate(s, ESTABLISHED);
			}
			else 
				setstate(s, SYN_RECEIVED);

			if(length != 0 || (seg.flags & FIN))
				break;

			freeb(bp);
			goto output;
		}
		else 
			freeb(bp);
		goto done;
	}

	/* Trim segment to fit receive window. */
	if(trim(tcb, &seg, &bp, &length) == -1) {
		if(!(seg.flags & RST)) {
			tcb->flags |= FORCE;
			goto output;
		}
		goto done;
	}

	/* If we have no opens and the other end is sending data then
	 * reply with a reset
	 */
	if(s->readq == 0 && length) {
		freeb(bp);
		reset(source, dest, tos, length, &seg);
		goto done;
	}

	if(seg.seq != tcb->rcv.nxt
	 && (length != 0 || (seg.flags & (SYN|FIN)) )) {
		add_reseq(tcb, tos, &seg, bp, length);
		tcb->flags |= FORCE;
		goto output;
	}

	for(;;) {
		if(seg.flags & RST) {
			if(tcb->state == SYN_RECEIVED
			   && !(tcb->flags & (CLONE|ACTIVE))) 
				setstate(s, LISTEN);
			else
				close_self(s, Econrefused);

			freeb(bp);
			goto done;
		}

		if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}

		if(!(seg.flags & ACK)) {
			freeb(bp);	
			goto done;
		}

		switch(tcb->state) {
		case SYN_RECEIVED:
			if(seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
				update(s, &seg);
				setstate(s, ESTABLISHED);
			}
			else {
				freeb(bp);
				reset(source, dest, tos, length, &seg);
				goto done;
			}
			break;
		case ESTABLISHED:
		case CLOSE_WAIT:
			update(s, &seg);
			break;
		case FINWAIT1:
			update(s, &seg);
			if(tcb->sndcnt == 0)
				setstate(s, FINWAIT2);
			break;
		case FINWAIT2:
			update(s, &seg);
			break;
		case CLOSING:
			update(s, &seg);
			if(tcb->sndcnt == 0){
				setstate(s, TIME_WAIT);
				tcb->timer.start = MSL2 * (1000 / MSPTICK);
				start_timer(&tcb->timer);
			}
			break;
		case LAST_ACK:
			update(s, &seg);
			if(tcb->sndcnt == 0) {
				close_self(s, 0);
				goto done;
			}			
		case TIME_WAIT:
			tcb->flags |= FORCE;
			start_timer(&tcb->timer);
		}

		if ((seg.flags&URG) && seg.up) {
			DPRINT("tcpin: oob: up = %u seq = %u rcv.up = %u\n",
			       seg.up, seg.seq, tcb->rcv.up);
			if (seq_gt(seg.up + seg.seq, tcb->rcv.up)) {
				tcb->rcv.up = seg.up + seg.seq;
				tcb->oobflags &= ~(TCPOOB_HAVEDATA|TCPOOB_HADDATA);
				extract_oob(&bp, &oobbp, &seg);
				if (oobbp) {
					DPRINT("tcpin: oob delivered\n");
					appendb(&tcb->rcvoobq, oobbp);
					tcb->rcvoobcnt += blen(oobbp);
					tcb->oobmark = tcb->rcvcnt;
					tcb->oobflags |= TCPOOB_HAVEDATA;
#ifdef NOTIFY
					urg_signal(s);
#endif
				}
			}
		} 
		else if (seq_gt(tcb->rcv.nxt, tcb->rcv.up))
			tcb->rcv.up = tcb->rcv.nxt;

		DPRINT("tcpin: Append pkt len=%d state=%s\n", 
			length, tcpstate[tcb->state]);

		if(length != 0){
			switch(tcb->state){
			case SYN_RECEIVED:
			case ESTABLISHED:
			case FINWAIT1:
			case FINWAIT2:
				/* Place on receive queue */
				tcb->rcvcnt += blen(bp);
				if(s->readq && bp) {
					PUTNEXT(s->readq, bp);
					bp = 0;
				}
				tcb->rcv.nxt += length;

				tcprcvwin(s);
	
				start_timer(&tcb->acktimer);

				if (tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack)
					tcb->flags |= FORCE;
				break;
			default:
				/* Ignore segment text */
				freeb(bp);
				break;
			}
		}

		if(seg.flags & FIN) {
			tcb->flags |= FORCE;

			switch(tcb->state) {
			case SYN_RECEIVED:
			case ESTABLISHED:
				tcb->rcv.nxt++;
				setstate(s, CLOSE_WAIT);
				break;
			case FINWAIT1:
				tcb->rcv.nxt++;
				if(tcb->sndcnt == 0) {
					setstate(s, TIME_WAIT);
					tcb->timer.start = MSL2 * (1000/MSPTICK);
					start_timer(&tcb->timer);
				}
				else 
					setstate(s, CLOSING);
				break;
			case FINWAIT2:
				tcb->rcv.nxt++;
				setstate(s, TIME_WAIT);
				tcb->timer.start = MSL2 * (1000/MSPTICK);
				start_timer(&tcb->timer);
				break;
			case CLOSE_WAIT:
			case CLOSING:
			case LAST_ACK:
				break;
			case TIME_WAIT:
				start_timer(&tcb->timer);
				break;
			}
		}
		while(tcb->reseq != 0 &&
		      seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq)){
			get_reseq(tcb, &tos, &seg, &bp, &length);
			if(trim(tcb, &seg, &bp, &length) == 0)
				goto gotone;
		}
		break;
gotone:;
	}
output:
	tcp_output(s);
done:
	qunlock(tcb);
}

void
tcp_icmp(Ipconv *ipc, Ipaddr source, Ipaddr dest, char type, char code, Block **bpp)
{
	Tcp seg;
	Tcpctl *tcb;
	Ipconv *s;

	ntohtcp(&seg, bpp);
	if(!(s = ip_conn(ipc, seg.source, seg.dest, dest, IP_TCPPROTO)))
		return;

	tcb = &s->tcpctl;

	if(!seq_within(seg.seq, tcb->snd.una, tcb->snd.nxt))
		return;

	switch((uchar)type) {
	case ICMP_UNREACH:
		tcb->type = type;
		tcb->code = code;
		if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED)
			close_self(s, Enetunreach);
		break;
	case ICMP_TIMXCEED:
		tcb->type = type;
		tcb->code = code;
		if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED)
			close_self(s, Etimedout);
		break;
	case ICMP_SOURCEQUENCH:
		tcb->cwind /= 2;
		tcb->cwind = MAX(tcb->mss,tcb->cwind);
		break;
	}
}

void
reset(Ipaddr source, Ipaddr dest, char tos, ushort length, Tcp *seg)
{
	Block *hbp;
	Port tmp;
	char rflags;
	Tcphdr ph;

	if(seg->flags & RST)
		return;

	hnputl(ph.tcpsrc, dest);
	hnputl(ph.tcpdst, source);
	ph.proto = IP_TCPPROTO;
	hnputs(ph.tcplen, TCP_HDRSIZE);

	/* Swap port numbers */
	tmp = seg->dest;
	seg->dest = seg->source;
	seg->source = tmp;

	rflags = RST;
	if(seg->flags & ACK) {
		/* This reset is being sent to clear a half-open connection.
		 * Set the sequence number of the RST to the incoming ACK
		 * so it will be acceptable.
		 */
		seg->seq = seg->ack;
		seg->ack = 0;
	}
	else {
		/* We're rejecting a connect request (SYN) from LISTEN state
		 * so we have to "acknowledge" their SYN.
		 */
		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->up = 0;
	seg->mss = 0;
	if((hbp = htontcp(seg, 0, &ph)) == 0)
		return;

	DPRINT("Reset: seq = %lux ack = %d flags = %lux\n",
	       seg->seq, seg->ack, seg->flags);

	PUTNEXT(Tcpoutput, hbp);
}

void
update(Ipconv *s, Tcp *seg)
{
	ushort acked;
	ushort oobacked;
	ushort expand;
	Tcpctl *tcb = &s->tcpctl;

	if(seq_gt(seg->ack, tcb->snd.nxt)) {
		tcb->flags |= FORCE;
		return;
	}

	if(seq_gt(seg->seq,tcb->snd.wl1) || ((seg->seq == tcb->snd.wl1) 
	 && seq_ge(seg->ack,tcb->snd.wl2))) {
		if(tcb->snd.wnd == 0 && seg->wnd != 0)
			tcb->snd.ptr = tcb->snd.una;
		tcb->snd.wnd = seg->wnd;
		tcb->snd.wl1 = seg->seq;
		tcb->snd.wl2 = seg->ack;
	}

	if(!seq_gt(seg->ack, tcb->snd.una))
		return;	

	acked = seg->ack - tcb->snd.una;

	if(tcb->cwind < tcb->snd.wnd) {
		if(tcb->cwind < tcb->ssthresh)
			expand = MIN(acked,tcb->mss);
		else
			expand = ((long)tcb->mss * tcb->mss) / tcb->cwind;

		if(tcb->cwind + expand < tcb->cwind)
			expand = 65535 - tcb->cwind;

		if(tcb->cwind + expand > tcb->snd.wnd)
			expand = tcb->snd.wnd - tcb->cwind;

		if(expand != 0)
			tcb->cwind += expand;

	}

	/* Round trip time estimation */
	if(run_timer(&tcb->rtt_timer) && seq_ge(seg->ack, tcb->rttseq)) {
		stop_timer(&tcb->rtt_timer);
		if(!(tcb->flags & RETRAN)) {
			int rtt;	/* measured round trip time */
			int abserr;	/* abs(rtt - srtt) */

			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
			rtt *= MSPTICK;	

			if(rtt > tcb->srtt &&
			  (tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED))
				tcb->srtt = rtt;
			else {
				abserr = (rtt > tcb->srtt) ? rtt - tcb->srtt : tcb->srtt - rtt;
				tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt) / AGAIN;
				tcb->mdev = ((DGAIN-1)*tcb->mdev + abserr) / DGAIN;
				DPRINT("tcpout: rtt %d, srtt %d, mdev %d\n", 
					rtt, tcb->srtt, tcb->mdev);
			}

			tcb->backoff = 0;
		}
	}

	/* If we're waiting for an ack of our SYN, note it and adjust count */
	if(!(tcb->flags & SYNACK)){
		tcb->flags |= SYNACK;
		acked--;
		tcb->sndcnt--;
	}

	/* Acking some oob data if relevant */
	if(tcb->sndoobq && seq_ge(tcb->snd.up,tcb->snd.una) &&
	   seq_gt(seg->ack, tcb->snd.up)) {
		oobacked = seg->ack - tcb->snd.up;
		acked -= oobacked;
		copyupb(&tcb->sndoobq, 0, oobacked);
		tcb->sndoobcnt -= oobacked;
		DPRINT("update: oobacked = %d\n", oobacked);
	}

	copyupb(&tcb->sndq, 0, acked);

	/* This will include the FIN if there is one */
	tcb->sndcnt -= acked;
	tcb->snd.una = seg->ack;
	/* If ack includes some out-of-band data then update urgent pointer */
	if (seq_gt(seg->ack, tcb->snd.up))
		tcb->snd.up = seg->ack;

	/* Stop retransmission timer, but restart it if there is still
	 * unacknowledged data.
	 */	
	stop_timer(&tcb->timer);
	if(tcb->snd.una != tcb->snd.nxt)
		start_timer(&tcb->timer);

	/* If retransmissions have been occurring, make sure the
	 * send pointer doesn't repeat ancient history
	 */
	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
		tcb->snd.ptr = tcb->snd.una;

	/* Clear the retransmission flag since the oldest
	 * unacknowledged segment (the only one that is ever retransmitted)
	 * has now been acked.
	 */
	tcb->flags &= ~RETRAN;
	tcb->backoff = 0;
}

int
in_window(Tcpctl *tcb, int seq)
{
	return seq_within(seq, tcb->rcv.nxt, 
			 (int)(tcb->rcv.nxt+tcb->rcv.wnd-1));
}

void
proc_syn(Ipconv *s, char tos, Tcp *seg)
{
	Tcpctl *tcb = &s->tcpctl;
	ushort mtu;


	tcb->flags |= FORCE;

	if(PREC(tos) > PREC(tcb->tos))
		tcb->tos = tos;

	tcb->rcv.up = tcb->rcv.nxt = seg->seq + 1;	/* p 68 */
	tcb->snd.wl1 = tcb->irs = seg->seq;
	tcb->snd.wnd = seg->wnd;

	if(seg->mss != 0)
		tcb->mss = seg->mss;

	tcb->max_snd = seg->wnd;

	if((mtu = s->ipinterface->maxmtu) != 0) {
		mtu -= TCP_HDRSIZE + TCP_EHSIZE + TCP_PHDRSIZE; 
		tcb->cwind = tcb->mss = MIN(mtu, tcb->mss);
	}
}

/* Generate an initial sequence number and put a SYN on the send queue */
void
send_syn(Tcpctl *tcb)
{
	tcb->iss = iss();
	tcb->rttseq = tcb->snd.wl2 = tcb->snd.una = tcb->iss;
	tcb->snd.ptr = tcb->snd.nxt = tcb->rttseq;
	tcb->sndcnt++;
	tcb->flags |= FORCE;
}

void
add_reseq(Tcpctl *tcb, char tos, Tcp *seg, Block *bp, ushort length)
{
	Reseq *rp, *rp1;

	qlock(&reseqlock);
	if(!reseqfree) {
		qunlock(&reseqlock);
		print("tcp: no resequence descriptors\n");
		freeb(bp);
		return;
	}

	rp = reseqfree;
	reseqfree = rp->next;
	qunlock(&reseqlock);

	rp->seg = *seg;
	rp->tos = tos;
	rp->bp = bp;
	rp->length = length;

	/* Place on reassembly list sorting by starting seq number */
	rp1 = tcb->reseq;
	if(rp1 == 0 || seq_lt(seg->seq, rp1->seg.seq)) {
		rp->next = rp1;
		tcb->reseq = rp;
	} 
	else {
		for(;;){
			if(rp1->next == 0 ||
			   seq_lt(seg->seq, rp1->next->seg.seq)) {
				rp->next = rp1->next;
				rp1->next = rp;
				break;
			}
			rp1 = rp1->next;
		}
	}
}


void
get_reseq(Tcpctl *tcb, char *tos, Tcp *seg, Block **bp, ushort *length)
{
	Reseq *rp;

	if((rp = tcb->reseq) == 0)
		return;

	tcb->reseq = rp->next;

	*tos = rp->tos;
	*seg = rp->seg;
	*bp = rp->bp;
	*length = rp->length;

	qlock(&reseqlock);
	rp->next = reseqfree;
	reseqfree = rp;
	qunlock(&reseqlock);
}

int
trim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
{
	Block *nbp;
	long dupcnt,excess;
	ushort len;		/* Segment length including flags */
	char accept;

	accept = 0;
	len = *length;
	if(seg->flags & SYN)
		len++;
	if(seg->flags & FIN)
		len++;

	/* Acceptability tests */
	if(tcb->rcv.wnd == 0) {
		if(seg->seq == tcb->rcv.nxt && len == 0)
			return 0;
	} else {
		/* Some part of the segment must be in the window */
		if(in_window(tcb,seg->seq)) {
			accept++;
		}
		else if(len != 0) {
			if(in_window(tcb, (int)(seg->seq+len-1)) || 
			seq_within(tcb->rcv.nxt, seg->seq,(int)(seg->seq+len-1)))
				accept++;
		}
	}
	if(!accept) {
		freeb(*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->up > 1)
				seg->up--;
			else
				seg->flags &= ~URG;
			dupcnt--;
		}
		if(dupcnt > 0){
			copyupb(bp, 0, (ushort)dupcnt);
			seg->seq += dupcnt;
			*length -= dupcnt;

			if (seg->up > dupcnt)
				seg->up -= dupcnt;
			else {
				seg->flags &= ~URG;
				seg->up = 0;
			}
		}
	}
	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
	if(excess > 0){
		tcb->rerecv += excess;
		*length -= excess;
		nbp = copyb(*bp, *length);
		freeb(*bp);
		*bp = nbp;
		seg->flags &= ~FIN;
	}
	return 0;
}

void
extract_oob(Block **bp, Block **oobbp, Tcp *seg)

{
	DPRINT("extract_oob: size = %u\n", seg->up);

	if (*oobbp = allocb(seg->up))
		(*oobbp)->wptr = (*oobbp)->wptr +
			         copyupb(bp, (*oobbp)->rptr, seg->up);
	else
		copyupb(bp, 0, seg->up);
}

int
copyupb(Block **bph, uchar *data, int count)
{
	int n, bytes;
	Block *bp;

	bytes = 0;
	if(bph == 0)
		return 0;

	while(*bph && count != 0) {
		bp = *bph;
		n = MIN(count, BLEN(bp));
		if(data && n) {
			memmove(data, bp->rptr, n);
			data += n;
		}
		bytes += n;
		count -= n;
		bp->rptr += n;
		if(BLEN(bp) == 0) {
			*bph = bp->next;
			bp->next = 0;
			freeb(bp);
		}
	}

	return bytes;
}

void
appendb(Block **list, Block *bp)
{
	Block *f;

	if(f = *list) {
		while(f->next)
			f = f->next;
		f->next = bp;
	}
	else
		*list = bp;

	bp->next = 0;
}

int
dupb(Block **hp, Block *bp, int offset, int count)
{
	int i, blen, bytes = 0;
	uchar *addr;
	
	*hp = allocb(count);
	if(*hp == 0)
		return 0;

	/* Correct to front of data area */
	while(bp && offset && offset >= BLEN(bp)) {
		offset -= BLEN(bp);
		bp = bp->next;
	}
	if(bp == 0)
		return 0;

	addr = bp->rptr + offset;
	blen = BLEN(bp) - offset;

	while(count) {
		i = MIN(count, blen);
		memmove((*hp)->wptr, addr, i);
		(*hp)->wptr += i;
		bytes += i;
		count -= i;
		bp = bp->next;
		if(!bp)
			break;
		blen = BLEN(bp);
		addr = bp->rptr;
	}

	return bytes;
}

Block *
copyb(Block *bp, int count)
{
	Block *nbp;
	int i;

	nbp = allocb(count);
	if(nbp == 0)
		return 0;

	while(bp && count) {
		i = MIN(count, BLEN(bp));
		memmove(nbp->wptr, bp->rptr, i);
		nbp->wptr += i;
		count -= i;
		bp = bp->next;
	}

	return nbp;	
}

ushort tcp_mss = DEF_MSS;	/* Maximum segment size to be sent with SYN */
int tcp_irtt = DEF_RTT;		/* Initial guess at round trip time */

void
init_tcpctl(Ipconv *s)
{

	Tcpctl *tcb = &s->tcpctl;

	memset(tcb, 0, sizeof(Tcpctl));

	tcb->cwind = tcb->mss = tcp_mss;
	tcb->ssthresh = 65535;
	tcb->srtt = tcp_irtt;

	/* Initialize timer intervals */
	tcb->timer.start = tcb->srtt / MSPTICK;
	tcb->timer.func = (void(*)(void*))tcp_timeout;
	tcb->timer.arg = (void *)s;
	tcb->rtt_timer.start = MAX_TIME; 

	/* Initialise ack timer */
	tcb->acktimer.start = TCP_ACK / MSPTICK;
	tcb->acktimer.func = (void(*)(void*))tcp_acktimer;
	tcb->acktimer.arg = (void *)s;
}

void
close_self(Ipconv *s, int reason)
{
	Reseq *rp,*rp1;
	Tcpctl *tcb = &s->tcpctl;

	stop_timer(&tcb->timer);
	stop_timer(&tcb->rtt_timer);
	s->err = reason;

	/* Flush reassembly queue; nothing more can arrive */
	for(rp = tcb->reseq;rp != 0;rp = rp1){
		rp1 = rp->next;
		freeb(rp->bp);

		qlock(&reseqlock);
		rp->next = reseqfree;
		reseqfree = rp;
		qunlock(&reseqlock);
	}

	tcb->reseq = 0;
	s->err = reason;

	setstate(s, CLOSED);
}

int
iss(void)
{
	static int seq;

	seq += 250000;
	return seq;
}

int
seq_within(int x, int low, int high)
{
	if(low <= high){
		if(low <= x && x <= high)
			return 1;
	} else {
		if(low >= x && x >= high)
			return 1;
	}
	return 0;
}

int
seq_lt(int x, int y)
{
	return (long)(x-y) < 0;
}

int
seq_le(int x, int y)
{
	return (long)(x-y) <= 0;
}

int
seq_gt(int x, int y)
{
	return (long)(x-y) > 0;
}

int
seq_ge(int x, int y)
{
	return (long)(x-y) >= 0;
}

void
setstate(Ipconv *s, char newstate)
{
	char oldstate;
	Tcpctl *tcb = &s->tcpctl;

	oldstate = tcb->state;
	tcb->state = newstate;

	state_upcall(s, oldstate, newstate);
}

Block *
htontcp(Tcp *tcph, Block *data, Tcphdr *ph)
{
	ushort hdrlen;
	int dlen;
	ushort csum;
	Tcphdr *h;
	Block *bp;

	hdrlen = TCP_HDRSIZE;
	if(tcph->mss)
		hdrlen += MSS_LENGTH;

	if(data) {
		dlen = blen(data);	
		if((data = padb(data, hdrlen + TCP_PKT)) == 0)
			return 0;
		/* If we collected blocks delimit the end of the chain */
		for(bp = data; bp->next; bp = bp->next)
			bp->flags &= ~S_DELIM;
		bp->flags |= S_DELIM;
	}
	else {
		dlen = 0;
		data = allocb(hdrlen + TCP_PKT);
		if(data == 0)
			return 0;
		data->wptr += hdrlen + TCP_PKT;
		data->flags |= S_DELIM;
	}


	memmove(data->rptr, ph, TCP_PKT);
	
	h = (Tcphdr *)(data->rptr);
	h->proto = IP_TCPPROTO;
	hnputs(h->tcplen, hdrlen + dlen);
	hnputs(h->tcpsport, tcph->source);
	hnputs(h->tcpdport, tcph->dest);
	hnputl(h->tcpseq, tcph->seq);
	hnputl(h->tcpack, tcph->ack);
	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
	hnputs(h->tcpwin, tcph->wnd);
	h->tcpcksum[0] = 0;
	h->tcpcksum[1] = 0;
	h->Unused = 0;
	hnputs(h->tcpurg, tcph->up);

	if(tcph->mss != 0){
		h->tcpopt[0] = MSS_KIND;
		h->tcpopt[1] = MSS_LENGTH;
		hnputs(h->tcpmss, tcph->mss);
	}
	csum = ptcl_csum(data, TCP_EHSIZE+TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE);
	hnputs(h->tcpcksum, csum);

	return data;
}

int
ntohtcp(Tcp *tcph, Block **bpp)
{
	ushort hdrlen;
	ushort i, optlen;
	Block *nbp;
	Tcphdr *h;
	uchar *optr;

	*bpp = pullup(*bpp, TCP_PKT+TCP_HDRSIZE);
	if(*bpp == 0)
		return -1;

	h = (Tcphdr *)((*bpp)->rptr);
	tcph->source = nhgets(h->tcpsport);
	tcph->dest = nhgets(h->tcpdport);
	tcph->seq = nhgetl(h->tcpseq);
	tcph->ack = nhgetl(h->tcpack);

	hdrlen = (h->tcpflag[0] & 0xf0) >> 2;
	if(hdrlen < TCP_HDRSIZE) {
		freeb(*bpp);
		return -1;
	}

	tcph->flags = h->tcpflag[1];
	tcph->wnd = nhgets(h->tcpwin);
	tcph->up = nhgets(h->tcpurg);
	tcph->mss = 0;

	*bpp = pullup(*bpp, hdrlen+TCP_PKT);
	if(!*bpp)
		return -1;

	for(optr = h->tcpopt, i = TCP_HDRSIZE; i < hdrlen;) {
		switch(*optr++){
		case EOL_KIND:
			goto eol;
		case NOOP_KIND:
			i++;
			break;
		case MSS_KIND:
			optlen = *optr++;
			if(optlen == MSS_LENGTH)
				tcph->mss = nhgets(optr);
			i += optlen;
			break;
		}
	}
eol:
	return hdrlen;
}
.
## diffname port/tcpinput.c 1991/0625
## diff -e /n/bootesdump/1991/0424/sys/src/9/port/tcpinput.c /n/bootesdump/1991/0625/sys/src/9/port/tcpinput.c
660d
## diffname port/tcpinput.c 1991/0705
## diff -e /n/bootesdump/1991/0625/sys/src/9/port/tcpinput.c /n/bootesdump/1991/0705/sys/src/9/port/tcpinput.c
659a
		print("tcp: no resequence descriptors\n");
.
23a
tcpinit(void)
{
	Reseq *r;

	reseqfree = ialloc(sizeof(Reseq)*Nreseq, 0);
	for(r = reseqfree; r < &reseqfree[Nreseq-1]; r++)
		r->next = r+1;

	r->next = 0;
}

void
.
## diffname port/tcpinput.c 1991/0727
## diff -e /n/bootesdump/1991/0705/sys/src/9/port/tcpinput.c /n/bootesdump/1991/0727/sys/src/9/port/tcpinput.c
12c
#define LPRINT  if(tcpdbg) print
.
## diffname port/tcpinput.c 1991/0926
## diff -e /n/bootesdump/1991/0727/sys/src/9/port/tcpinput.c /n/bootesdump/1991/0926/sys/src/9/port/tcpinput.c
441c
		tcb->cwind = tcb->cwind/2;
.
## diffname port/tcpinput.c 1991/1019
## diff -e /n/bootesdump/1991/0926/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1019/sys/src/9/port/tcpinput.c
500c
	PUTNEXT(Ipoutput, hbp);
.
14d
## diffname port/tcpinput.c 1991/1023
## diff -e /n/bootesdump/1991/1019/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1023/sys/src/9/port/tcpinput.c
144a

.
142,143d
139a
		new->newcon = 1;
.
111,125c
		new = ipincoming(ipc);
		if(new == 0)
.
37c
	Ipconv *s, *new;
.
## diffname port/tcpinput.c 1991/1030
## diff -e /n/bootesdump/1991/1023/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1030/sys/src/9/port/tcpinput.c
1122d
1110c
			return hdrlen;
.
1010d
971c
	}
	else {
.
952d
824d
785,797d
774c
	if(excess > 0) {
.
732c
	}
	else {
.
691d
680,681c
			if(rp1->next == 0 || seq_lt(seg->seq, rp1->next->seg.seq)) {
.
632d
608,609c
	return seq_within(seq, tcb->rcv.nxt, (int)(tcb->rcv.nxt+tcb->rcv.wnd-1));
.
591,593d
580d
577d
565,574d
559c
	if((tcb->flags & SYNACK) == 0){
.
542d
537,538d
530d
527d
524d
515d
495a
	int rtt;
	int abserr;
.
493d
389c
		gotone:;
.
300,311c
				copyupb(&bp, 0, seg.up);
.
296,297d
42d
## diffname port/tcpinput.c 1991/1102
## diff -e /n/bootesdump/1991/1030/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1102/sys/src/9/port/tcpinput.c
133c
process:	
.
131c
				s = new;
				goto process;
			}
		}
	clear:
		LPRINT("tcpin: call cleared\n");
		freeb(bp);   
		reset(source, dest, tos, length, &seg);
		return;
.
126,129c
				new->newcon = 1;
				new->ipinterface = s->ipinterface;
				s->ipinterface->ref++;
				wakeup(&s->listenr);
.
114,124c
				s->curlog++;
				LPRINT("tcpin: cloning socket\n");
				new->psrc = seg.dest;
				new->pdst = seg.source;
				new->dst = source;
				memmove(&new->tcpctl, &s->tcpctl, sizeof(Tcpctl));
				new->tcpctl.flags &= ~CLONE;
				new->tcpctl.timer.arg = new;
				new->tcpctl.timer.state = TIMER_STOP;
				new->tcpctl.acktimer.arg = new;
				new->tcpctl.acktimer.state = TIMER_STOP;
.
110,112c
				new = ipincoming(ipc);
				if(new == 0)
					goto clear;
.
105,108c
		e = &ipc[conf.ip];
		for(s = ipc; s < e; s++) {
			if(s->tcpctl.state == LISTEN)
			if(s->pdst == 0)
			if(s->dst == 0) {
				if(s->curlog >= s->backlog)
					goto clear;
.
103d
91,101c
		if((seg.flags & SYN) == 0)
.
88c
	s = ip_conn(ipc, seg.dest, seg.source, source, IP_TCPPROTO);
	if (s == 0) {
.
37c
	Ipconv *s, *e, *new;
.
## diffname port/tcpinput.c 1991/1104
## diff -e /n/bootesdump/1991/1102/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1104/sys/src/9/port/tcpinput.c
469,471d
369,370c
		while(tcb->reseq != 0 && seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq)) {
.
304,306d
222,223c
	if(seg.seq != tcb->rcv.nxt && (length != 0 || (seg.flags & (SYN|FIN)) )) {
.
129d
123d
120a

.
118d
108d
90,91d
77,83d
67d
65d
60d
## diffname port/tcpinput.c 1991/1106
## diff -e /n/bootesdump/1991/1104/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1106/sys/src/9/port/tcpinput.c
55,61d
## diffname port/tcpinput.c 1991/1115
## diff -e /n/bootesdump/1991/1106/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1115/sys/src/9/port/tcpinput.c
84c
				new = ipincoming(ipc, s);
.
## diffname port/tcpinput.c 1991/1122
## diff -e /n/bootesdump/1991/1115/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1122/sys/src/9/port/tcpinput.c
818c
	return head;	
.
814a
		if(head == 0)
			head = nbp;
		else
			tail->next = nbp;

		tail = nbp;
.
811c
		i = BLEN(bp);
		nbp = allocb(i);
		if(i > nbp->lim-nbp->wptr) {
			if(head)
				freeb(head);
			return 0;
		}
.
806,809c
	head = 0;
.
803c
	Block *nbp, *head, *tail;
.
## diffname port/tcpinput.c 1991/1126
## diff -e /n/bootesdump/1991/1122/sys/src/9/port/tcpinput.c /n/bootesdump/1991/1126/sys/src/9/port/tcpinput.c
806c
	head = tail = 0;
.
747,762d
## diffname port/tcpinput.c 1991/12171
## diff -e /n/bootesdump/1991/1126/sys/src/9/port/tcpinput.c /n/bootesdump/1991/12171/sys/src/9/port/tcpinput.c
864c
	setstate(s, Closed);
.
495c
			  (tcb->state == Syn_sent || tcb->state == Syn_received))
.
383c
		if(tcb->state == Syn_sent || tcb->state == Syn_received)
.
377c
		if(tcb->state == Syn_sent || tcb->state == Syn_received)
.
338c
			case Time_wait:
.
334,336c
			case Close_wait:
			case Closing:
			case Last_ack:
.
330c
				setstate(s, Time_wait);
.
328c
			case Finwait2:
.
326c
					setstate(s, Closing);
.
321c
					setstate(s, Time_wait);
.
318c
			case Finwait1:
.
316c
				setstate(s, Close_wait);
.
313,314c
			case Syn_received:
			case Established:
.
283,286c
			case Syn_received:
			case Established:
			case Finwait1:
			case Finwait2:
.
267c
		case Time_wait:
.
261c
		case Last_ack:
.
256c
				setstate(s, Time_wait);
.
253c
		case Closing:
.
250c
		case Finwait2:
.
248c
				setstate(s, Finwait2);
.
245c
		case Finwait1:
.
241,242c
		case Established:
		case Close_wait:
.
233c
				setstate(s, Established);
.
230c
		case Syn_received:
.
210c
				setstate(s, Listen);
.
208c
			if(tcb->state == Syn_received
.
169c
				setstate(s, Syn_received);
.
166c
				setstate(s, Established);
.
142c
	case Syn_sent:
.
134c
			setstate(s, Syn_received);		
.
121c
	case Listen:
.
117c
	case Closed:
.
78c
			if(s->tcpctl.state == Listen)
.
## diffname port/tcpinput.c 1992/0105
## diff -e /n/bootesdump/1991/12171/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0105/sys/src/9/port/tcpinput.c
810,811c
	if(count) {
		nb = allocb(count);
		if(nb == 0)
			panic("copyb.2");
		memset(nb->wptr, 0, count);
		nb->wptr += count;
		*p = nb;
	}
	if(blen(head) == 0)
		print("copyb: zero length\n");
	return head;
.
808a
		if(bp == 0)
			break;
.
790,807c
	p = &head;
	while(count) {
		l = BLEN(bp);
		if(count < l)
			l = count;
		nb = allocb(l);
		if(nb == 0)
			panic("copyb.1");
		memmove(nb->wptr, bp->rptr, l);
		nb->wptr += l;
		count -= l;
		*p = nb;
		p = &nb->next;
.
787,788c
	Block *nb, *head, **p;
	int l;
.
## diffname port/tcpinput.c 1992/0106
## diff -e /n/bootesdump/1992/0105/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0106/sys/src/9/port/tcpinput.c
966a
	h->frag[0] = 0;
	h->frag[1] = 0;
.
816a

.
812a
		nb->flags |= S_DELIM;
.
800a
		if(bp->flags & S_DELIM)
			nb->flags |= S_DELIM;
.
## diffname port/tcpinput.c 1992/0111
## diff -e /n/bootesdump/1992/0106/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0111/sys/src/9/port/tcpinput.c
852c
close_self(Ipconv *s, char reason[])
.
264c
				close_self(s, Enoerror);
.
6c
#include	"../port/error.h"
.
## diffname port/tcpinput.c 1992/0128
## diff -e /n/bootesdump/1992/0111/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0128/sys/src/9/port/tcpinput.c
833c
	cleartcp(tcb);
.
826a
static void
cleartcp(struct Tctl *a)
{
	memset(a, 0, sizeof(struct Tctl));
}

.
## diffname port/tcpinput.c 1992/0306
## diff -e /n/bootesdump/1992/0128/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0306/sys/src/9/port/tcpinput.c
88a
				qunlock(s);
.
87a
				qlock(s);
.
## diffname port/tcpinput.c 1992/0310
## diff -e /n/bootesdump/1992/0306/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0310/sys/src/9/port/tcpinput.c
662d
651,652c
	long dupcnt;
	long excess;
	ushort len;
.
561c
	tcb->rcv.up = tcb->rcv.nxt = seg->seq + 1;
.
535,538c
	/* All data is acked now */
.
426,428d
418,421d
416a

	/* convince the other end that this reset is in band */
.
304,307d
301c
				if(tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack)
.
284a
			default:
				/* Ignore segment text */
				freeb(bp);
				break;
.
283c
		if(length != 0) {
.
280c
		else if(seq_gt(tcb->rcv.nxt, tcb->rcv.up))
.
274,275c
		if((seg.flags&URG) && seg.up) {
			if(seq_gt(seg.up + seg.seq, tcb->rcv.up)) {
.
257c
			if(tcb->sndcnt == 0) {
.
202c
	if(seg.seq != tcb->rcv.nxt)
	if(length != 0 || (seg.flags & (SYN|FIN))) {
.
## diffname port/tcpinput.c 1992/0313
## diff -e /n/bootesdump/1992/0310/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0313/sys/src/9/port/tcpinput.c
657c
		if(len == 0)
		if(seg->seq == tcb->rcv.nxt)
.
489a
		if(!(tcb->flags & RETRAN)) {
.
488d
486c
	if(run_timer(&tcb->rtt_timer))
	if(seq_ge(seg->ack, tcb->rttseq)) {
.
461a

.
458,460c
	if(seq_ge(seg->ack,tcb->snd.wl2))
	if(seq_gt(seg->seq,tcb->snd.wl1) || (seg->seq == tcb->snd.wl1)) {
		if(seg->wnd != 0)
		if(tcb->snd.wnd == 0)
.
361,396d
296c
				if(bp)
				if(s->readq) {
.
196c
	if(length)
	if(s->readq == 0) {
.
163a

.
159c
		if(seg.flags & ACK)
		if(PREC(tos) != PREC(tcb->tos)){
.
## diffname port/tcpinput.c 1992/0318
## diff -e /n/bootesdump/1992/0313/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0318/sys/src/9/port/tcpinput.c
751,790d
## diffname port/tcpinput.c 1992/0319
## diff -e /n/bootesdump/1992/0318/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0319/sys/src/9/port/tcpinput.c
978a
}

void
tcpdumpconv(Ipconv *c)
{
	if(c->tcpctl.state == Closed)
		return;

	print("%s %d -> %d.%d.%d.%d/%d snd %d recv %d una %d nxt %d ptr %d wnd %d\n",
	tcpstate[c->tcpctl.state],
	c->psrc,
	fmtaddr(c->dst),
	c->pdst,
	c->tcpctl.sndcnt,
	c->tcpctl.rcvcnt,
	c->tcpctl.snd.una,
	c->tcpctl.snd.nxt,
	c->tcpctl.snd.ptr,
	c->tcpctl.snd.wnd);
}

void
tcpdump(void)
{
	Ipifc *ep, *ifp;
	Ipconv *cp, *ecp;
	extern Ipifc *ipifc;

	ep = &ipifc[conf.ipif];
	for(ifp = ipifc; ifp < ep; ifp++)
		if(strcmp(ifp->name, "TCP") == 0) {
			ecp = &ifp->connections[conf.ip];
			for(cp = ifp->connections; cp < ecp; cp++)
				tcpdumpconv(cp);
			break;
		}
.
883c
		data = padb(data, hdrlen + TCP_PKT);
		if(data == 0)
.
350c
		while(tcb->reseq != 0) {
			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
				break;
.
300a
					print("bl %d ty %d\n", blen(bp), bp->rptr[0]);
					for(f = bp; bp->next; f = f->next)
						;
					if((f->flags&S_DELIM) == 0) {
						print("No delim upstream rcp");
						f->flags |= S_DELIM;
					}		
.
292a

.
44a
	Block *f;
.
## diffname port/tcpinput.c 1992/0320
## diff -e /n/bootesdump/1992/0319/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0320/sys/src/9/port/tcpinput.c
991,1026d
484d
478c
				if(rtt > tcb->srtt)
					abserr = rtt - tcb->srtt;
				else
					abserr = tcb->srtt - rtt;
.
470,471c
		if((tcb->flags&RETRAN) == 0) {
.
303,309d
47,48d
45d
## diffname port/tcpinput.c 1992/0321
## diff -e /n/bootesdump/1992/0320/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0321/sys/src/9/port/tcpinput.c
2c
#include	"../port/lib.h"
.
## diffname port/tcpinput.c 1992/0325
## diff -e /n/bootesdump/1992/0321/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0325/sys/src/9/port/tcpinput.c
810,818d
700,703d
688c
pullb(Block **bph, int count)
.
663c
			pullb(bp, (ushort)dupcnt);
.
545,546c
	static int start;

	start += 250000;
	tcb->iss = start;
	tcb->rttseq = tcb->iss;
	tcb->snd.wl2 = tcb->iss;
	tcb->snd.una = tcb->iss;
.
487c
	pullb(&tcb->sndq, acked);
.
366,413d
279c
				pullb(&bp, seg.up);
.
193,195c
	/* If we dont understand answer with a rst */
.
112c
process:
	/* The rest of the input state machine is run with the control block
	 * locked
	 */
.
68c
	
	/* Look for a connection, failing that attempt to establish a listen */
.
34a
reset(Ipaddr source, Ipaddr dest, char tos, ushort length, Tcp *seg)
{
	Block *hbp;
	Port tmp;
	char rflags;
	Tcphdr ph;

	if(seg->flags & RST)
		return;

	hnputl(ph.tcpsrc, dest);
	hnputl(ph.tcpdst, source);
	ph.proto = IP_TCPPROTO;
	hnputs(ph.tcplen, TCP_HDRSIZE);

	/* Swap port numbers */
	tmp = seg->dest;
	seg->dest = seg->source;
	seg->source = tmp;

	rflags = RST;

	/* convince the other end that this reset is in band */
	if(seg->flags & ACK) {
		seg->seq = seg->ack;
		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->up = 0;
	seg->mss = 0;
	if((hbp = htontcp(seg, 0, &ph)) == 0)
		return;

	PUTNEXT(Ipoutput, hbp);
}

void
.
## diffname port/tcpinput.c 1992/0406
## diff -e /n/bootesdump/1992/0325/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0406/sys/src/9/port/tcpinput.c
161c

.
156,159c
		if(s == 0){
			freeb(bp);   
			reset(source, dest, tos, length, &seg);
			return;
		}
.
154a
			if(spec)
				s = tcpincoming(ipc, spec, &seg, source);
			else if(gen)
				s = tcpincoming(ipc, gen, &seg, source);
			else
				s = 0;
.
120,153c
		if(seg.flags & SYN){
			/*
			 *  find a listener specific to this port (spec) or,
			 *  failing that, a general one (gen)
			 */
			spec = 0;
			gen = 0;
			e = &ipc[conf.ip];
			for(s = ipc; s < e; s++) {
				if(s->tcpctl.state == Listen)
				if(s->pdst == 0)
				if(s->dst == 0) {
					if(s->psrc == seg.dest){
						spec = s;
						break;
					}
					if(s->psrc == 0)
						gen = s;
				}
.
85c
	Ipconv *s, *e;
	Ipconv *spec, *gen;
.
81a
Ipconv*
tcpincoming(Ipconv *ipc, Ipconv *s, Tcp *segp, Ipaddr source)
{
	Ipconv *new;

	if(s->curlog >= s->backlog)
		return 0;

	new = ipincoming(ipc, s);
	if(new == 0)
		return 0;

	qlock(s);
	s->curlog++;
	qunlock(s);
	new->psrc = segp->dest;
	new->pdst = segp->source;
	new->dst = source;
	memmove(&new->tcpctl, &s->tcpctl, sizeof(Tcpctl));
	new->tcpctl.flags &= ~CLONE;
	new->tcpctl.timer.arg = new;
	new->tcpctl.timer.state = TIMER_STOP;
	new->tcpctl.acktimer.arg = new;
	new->tcpctl.acktimer.state = TIMER_STOP;
	new->newcon = 1;
	new->ipinterface = s->ipinterface;

	s->ipinterface->ref++;
	wakeup(&s->listenr);
	return new;
}

.
## diffname port/tcpinput.c 1992/0414
## diff -e /n/bootesdump/1992/0406/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0414/sys/src/9/port/tcpinput.c
106c
	new->newcon = s;
.
## diffname port/tcpinput.c 1992/0416
## diff -e /n/bootesdump/1992/0414/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0416/sys/src/9/port/tcpinput.c
822a

	/* flush receive queue */
	while(bp = getb(&tcb->rcvq))
		freeb(bp);
.
818a
	Block *bp;
.
813a
/*
 *  called with tcb locked
 */
.
373,375c
				if(bp){
					if(s->readq)
						PUTNEXT(s->readq, bp);
					else
						putb(&tcb->rcvq, bp);
.
272c
	if(s->readq == 0)
	if(tcb->state == Closed) {
.
100c
	tcpmove(&new->tcpctl, &s->tcpctl);
.
94d
92a
	}
.
91c
	if(new == 0){
		qunlock(s);
.
88a
	}
.
87c
	qlock(s);
	if(s->curlog >= s->backlog){
		qunlock(s);
.
81a
/*
 *  flush an incoming call; send a reset to the remote side and close the
 *  conversation
 */
void
tcpflushincoming(Ipconv *s)
{
	Tcp seg;
	Tcpctl *tcb;		

	tcb = &s->tcpctl;
	seg.source = s->pdst;
	seg.dest = s->psrc;
	seg.flags = ACK;	
	seg.seq = tcb->snd.ptr;
	seg.ack = tcb->last_ack = tcb->rcv.nxt;

	reset(s->dst, Myip[Myself], 0, 0, &seg);
	close_self(s, 0);
}

static void
tcpmove(struct Tctl *to, struct Tctl *from)
{
	memmove(to, from, sizeof(struct Tctl));
}

.
## diffname port/tcpinput.c 1992/0512
## diff -e /n/bootesdump/1992/0416/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0512/sys/src/9/port/tcpinput.c
392c
		if(length == 0)
			freeb(bp);
		else {
.
374a
				freeb(bp);
.
## diffname port/tcpinput.c 1992/0527
## diff -e /n/bootesdump/1992/0512/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0527/sys/src/9/port/tcpinput.c
399c
				if(bp)
					freeb(bp);
.
393,395c
		if(length == 0){
			if(bp)
				freeb(bp);
		} else {
.
## diffname port/tcpinput.c 1992/0606
## diff -e /n/bootesdump/1992/0527/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0606/sys/src/9/port/tcpinput.c
631c
		if((dropped++%256) == 0)
			print("tcp: no resequence descriptors\n");
.
626a
	static int dropped;
.
## diffname port/tcpinput.c 1992/0619
## diff -e /n/bootesdump/1992/0606/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0619/sys/src/9/port/tcpinput.c
877,881c
		free(rp);
.
680,683c
	free(rp);
.
638,641d
636d
629,634c
	rp = malloc(sizeof(Reseq));
	if(rp == 0)
.
627d
23,34d
14,16d
## diffname port/tcpinput.c 1992/0626
## diff -e /n/bootesdump/1992/0619/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0626/sys/src/9/port/tcpinput.c
586c
	if((mtu = s->ifc->maxmtu) != 0) {
.
192c
				s = tcpincoming(ifc, gen, &seg, source);
.
190c
				s = tcpincoming(ifc, spec, &seg, source);
.
176,177c
			etab = &ifc->conv[Nipconv];
			for(p = ifc->conv; p < etab && *p; p++) {
				s = *p;
.
167c
	s = ip_conn(ifc, seg.dest, seg.source, source, IP_TCPPROTO);
.
133c
	Ipconv *s, **p, **etab;
.
131c
tcp_input(Ipifc *ifc, Block *bp)
.
125d
123d
105c
	new = ipincoming(ifc, s);
.
95c
tcpincoming(Ipifc *ifc, Ipconv *s, Tcp *segp, Ipaddr source)
.
## diffname port/tcpinput.c 1992/0711
## diff -e /n/bootesdump/1992/0626/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0711/sys/src/9/port/tcpinput.c
972d
165c
	s = ip_conn(ifc, seg.dest, seg.source, source);
.
26a
	USED(tos);		/* is this right??? */

.
## diffname port/tcpinput.c 1992/0724
## diff -e /n/bootesdump/1992/0711/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0724/sys/src/9/port/tcpinput.c
616a
	}
.
615c
	if(rp == 0){
		freeb(bp);	/* bp always consumed by add_reseq */
.
411a
			case Finwait2:
				/* no process to read the data, send a reset */
				if(bp)
					freeb(bp);
				reset(source, dest, tos, length, &seg);
				goto done;
.
393d
166c
	/* Look for a connection. failing that look for a listener. */
.
## diffname port/tcpinput.c 1992/0807
## diff -e /n/bootesdump/1992/0724/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0807/sys/src/9/port/tcpinput.c
382c
		}
		else {
.
17c
	"Closing", 	"Last_ack", 	"Time_wait"
};
.
14c
char *tcpstate[] =
{
.
## diffname port/tcpinput.c 1992/0826
## diff -e /n/bootesdump/1992/0807/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0826/sys/src/9/port/tcpinput.c
842c
localclose(Ipconv *s, char reason[])
.
691c
		/* Some part of the segment should be in the window */
.
564d
554,556d
551c
	if(seq_gt(seg->ack, tcb->snd.up))
.
540d
536d
532,534c
				rtt = abs(rtt - tcb->srtt);
				tcb->mdev = ((DGAIN-1)*tcb->mdev + rtt) / DGAIN;
.
527,530d
522c
			rtt *= MSPTICK;
.
520a
			tcb->backoff = 0;
.
516c
	/* Adjust the timers acorrding to the round trip time */
.
500a
	/* Compute the new send window size */
.
479,480d
475a
	int rtt;
.
465d
462c
				break;
.
460a

.
459a

.
456a

.
418c
				sndrst(source, dest, tos, length, &seg);
.
396c
				/* If we still have some data place on receive queue */
.
364c
				localclose(s, Enoerror);
.
336c
				sndrst(source, dest, tos, length, &seg);
.
319c
			sndrst(source, dest, tos, length, &seg);
.
311c
				localclose(s, Econrefused);
.
297a
	/* The segment is beyond the current receive pointer so
	 * queue the data in the resequence queue
	 */
.
294c
		sndrst(source, dest, tos, length, &seg);
.
289c
	/* Cannot accept so answer with a rst */
.
280c
	/* Cut the data to fit the receive window */
.
256c
			sndrst(source, dest, tos, length, &seg);
.
248c
				localclose(s, Econrefused);
.
242c
				sndrst(source, dest, tos, length, &seg);
.
224c
			sndrst(source, dest, tos, length, &seg);
.
215c
		sndrst(source, dest, tos, length, &seg);
.
201c
			sndrst(source, dest, tos, length, &seg);
.
162c
	/* trim the packet to the size claimed by the datagram */
.
88,89c
	sndrst(s->dst, Myip[Myself], 0, 0, &seg);
	localclose(s, 0);
.
22c
sndrst(Ipaddr source, Ipaddr dest, char tos, ushort length, Tcp *seg)
.
## diffname port/tcpinput.c 1992/0903
## diff -e /n/bootesdump/1992/0826/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0903/sys/src/9/port/tcpinput.c
1007c
	optr = h->tcpopt;
	for(i = TCP_HDRSIZE; i < hdrlen;) {
.
909c
	tcpxstate(s, oldstate, newstate);
.
902c
tcpsetstate(Ipconv *s, char newstate)
.
860c
	tcpsetstate(s, Closed);
.
843,844c
	tcphalt(&tcb->timer);
	tcphalt(&tcb->rtt_timer);
.
829,830c
	tcb->acktimer.func = tcpacktimer;
	tcb->acktimer.arg = s;
.
826,827d
823,824c
	tcb->timer.func = tcptimeout;
	tcb->timer.arg = s;
.
821d
651c
	rp = tcb->reseq;
	if(rp == 0)
.
642a
		rp1 = rp1->next;
.
633,641c
		return;
	}

	for(;;) {
		if(rp1->next == 0 || seq_lt(seg->seq, rp1->next->seg.seq)) {
			rp->next = rp1->next;
			rp1->next = rp;
			break;
.
598c
tcpsndsyn(Tcpctl *tcb)
.
555c
		tcpgo(&tcb->timer);
.
553c
	tcphalt(&tcb->timer);
.
524c
		tcphalt(&tcb->rtt_timer);
.
473c
	tcpoutput(s);
.
461c
		while(tcb->reseq) {
.
456c
				tcpgo(&tcb->timer);
.
449c
				tcpgo(&tcb->timer);
.
447c
				tcpsetstate(s, Time_wait);
.
443c
					tcpsetstate(s, Closing);
.
440c
					tcpgo(&tcb->timer);
.
438c
					tcpsetstate(s, Time_wait);
.
433c
				tcpsetstate(s, Close_wait);
.
421c
				sndrst(source, dest, length, &seg);
.
412c
				tcpgo(&tcb->acktimer);
.
372c
			tcpgo(&tcb->timer);
.
360c
				tcpgo(&tcb->timer);
.
358c
				tcpsetstate(s, Time_wait);
.
350c
				tcpsetstate(s, Finwait2);
.
342c
			update(s, &seg);
			tcpsetstate(s, Established);
.
339c
				sndrst(source, dest, length, &seg);
.
333,337c
			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
.
322c
			sndrst(source, dest, length, &seg);
.
319a
		/* This tos stuff should be removed */
.
312c
				tcpsetstate(s, Listen);
.
294c
		sndrst(source, dest, length, &seg);
.
267c
				tcpsetstate(s, Syn_received);
.
264c
				tcpsetstate(s, Established);
.
256c
			sndrst(source, dest, length, &seg);
.
242c
				sndrst(source, dest, length, &seg);
.
229,230c
			tcpsndsyn(tcb);
			tcpsetstate(s, Syn_received);		
.
224c
			sndrst(source, dest, length, &seg);
.
215c
		sndrst(source, dest, length, &seg);
.
201c
			sndrst(source, dest, length, &seg);
.
133c
tcpinput(Ipifc *ifc, Block *bp)
.
88c
	sndrst(s->dst, Myip[Myself], 0, &seg);
.
29,30d
22c
sndrst(Ipaddr source, Ipaddr dest, ushort length, Tcp *seg)
.
## diffname port/tcpinput.c 1992/0906
## diff -e /n/bootesdump/1992/0903/sys/src/9/port/tcpinput.c /n/bootesdump/1992/0906/sys/src/9/port/tcpinput.c
913a
	ushort csum;
	ushort hdrlen;
.
911d
909d
900a
	tcb = &s->tcpctl;

.
899d
897a
	Tcpctl *tcb;
.
797,799d
686,687c
		else
		if(len != 0) {
.
684c
		if(in_window(tcb,seg->seq))
.
205c
	 * locked and implements the state machine directly out of the RFC
	 * Out-of-band data is ignored - it was always a bad idea.
.
141a
	Ipconv *spec, *gen;
	Ipaddr source, dest;
	Ipconv *s, **p, **etab;
.
140a
	Tcphdr *h;
	int hdrlen;	
	Tcpctl *tcb;		
.
138,139d
133,136d
123c
	new->tcpctl.acktimer.state = TimerOFF;
.
121c
	new->tcpctl.timer.state = TimerOFF;
.
10c
int	tcpdbg = 0;
ushort	tcp_mss = DEF_MSS;	/* Maximum segment size to be sent with SYN */
int	tcp_irtt = DEF_RTT;	/* Initial guess at round trip time */

.
## diffname port/tcpinput.c 1992/1221
## diff -e /n/bootesdump/1992/0906/sys/src/9/port/tcpinput.c /n/bootesdump/1992/1221/sys/src/9/port/tcpinput.c
561a

	if(tcb->sndfull && tcb->sndcnt < Streamhi/2){
		wakeup(&tcb->sndr);
		tcb->sndfull = 0;
	}
.
## diffname port/tcpinput.c 1993/0218
## diff -e /n/bootesdump/1992/1221/sys/src/9/port/tcpinput.c /n/bootesdump/1993/0218/sys/src/9/port/tcpinput.c
415a

.
414c
				if(tcb->rcv.nxt-tcb->last_ack > Streamhi/2)
.
412c
				if(tcb->acktimer.state != TimerON)
					tcpgo(&tcb->acktimer);
.
87c
	tcb->last_ack = tcb->rcv.nxt;
	seg.ack = tcb->rcv.nxt;
.
## diffname port/tcpinput.c 1993/0220
## diff -e /n/bootesdump/1993/0218/sys/src/9/port/tcpinput.c /n/bootesdump/1993/0220/sys/src/9/port/tcpinput.c
901c
	return x >= y;
.
899c
seq_ge(ulong x, ulong y)
.
895c
	return x > y;
.
893c
seq_gt(ulong x, ulong y)
.
889c
	return x <= y;
.
887c
seq_le(ulong x, ulong y)
.
883c
	return x < y;
.
881c
seq_lt(ulong x, ulong y)
.
867c
seq_within(ulong x, ulong low, ulong high)
.
700,701c
			if(in_window(tcb, seg->seq+len-1) || 
			seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
.
575c
	return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
.
473d
466c
				goto output;
.
464c
		/*
		 *  get next adjacent segment from the requence queue.
		 *  dump/trim any overlapping segments
		 */
		for(;;) {
			if(tcb->reseq == 0)
				goto output;

.
310a
	/*
	 *  keep looping till we've processed this packet plus any
	 *  adjacent packets in the resequence queue
	 */
.
## diffname port/tcpinput.c 1993/0501
## diff -e /n/bootesdump/1993/0220/sys/src/9/port/tcpinput.c /n/fornaxdump/1993/0501/sys/src/brazil/port/tcpinput.c
911c
	return (long)(x-y) >= 0;
.
909c
seq_ge(int x, int y)
.
905c
	return (long)(x-y) > 0;
.
903c
seq_gt(int x, int y)
.
899c
	return (long)(x-y) <= 0;
.
897c
seq_le(int x, int y)
.
893c
	return (long)(x-y) < 0;
.
891c
seq_lt(int x, int y)
.
877c
seq_within(int x, int low, int high)
.
710,711c
			if(in_window(tcb, (int)(seg->seq+len-1)) || 
			seq_within(tcb->rcv.nxt, seg->seq,(int)(seg->seq+len-1)))
.
585c
	return seq_within(seq, tcb->rcv.nxt, (int)(tcb->rcv.nxt+tcb->rcv.wnd-1));
.
483a
		break;
.
477c
				break;
.
468,475c
		while(tcb->reseq) {
.
422d
420c
				if(tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack)
.
417,418c
				tcpgo(&tcb->acktimer);
.
311,314d
87,88c
	seg.ack = tcb->last_ack = tcb->rcv.nxt;
.
## diffname port/tcpinput.c 1993/0804 # deleted
## diff -e /n/fornaxdump/1993/0501/sys/src/brazil/port/tcpinput.c /n/fornaxdump/1993/0804/sys/src/brazil/port/tcpinput.c
1,1026d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].