xref: /plan9/sys/src/9/ip/tcp.c (revision 8e860520aa6bfda4bc4f23391be5d54c6baed7e6)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"../port/error.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier #include	"ip.h"
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier enum
117dd7cddfSDavid du Colombier {
127dd7cddfSDavid du Colombier 	QMAX		= 64*1024-1,
137dd7cddfSDavid du Colombier 	IP_TCPPROTO	= 6,
143ff48bf5SDavid du Colombier 
153ff48bf5SDavid du Colombier 	TCP4_IPLEN	= 8,
163ff48bf5SDavid du Colombier 	TCP4_PHDRSIZE	= 12,
173ff48bf5SDavid du Colombier 	TCP4_HDRSIZE	= 20,
183ff48bf5SDavid du Colombier 	TCP4_TCBPHDRSZ	= 40,
193ff48bf5SDavid du Colombier 	TCP4_PKT	= TCP4_IPLEN+TCP4_PHDRSIZE,
203ff48bf5SDavid du Colombier 
213ff48bf5SDavid du Colombier 	TCP6_IPLEN	= 0,
223ff48bf5SDavid du Colombier 	TCP6_PHDRSIZE	= 40,
233ff48bf5SDavid du Colombier 	TCP6_HDRSIZE	= 20,
243ff48bf5SDavid du Colombier 	TCP6_TCBPHDRSZ	= 60,
253ff48bf5SDavid du Colombier 	TCP6_PKT	= TCP6_IPLEN+TCP6_PHDRSIZE,
263ff48bf5SDavid du Colombier 
279a747e4fSDavid du Colombier 	TcptimerOFF	= 0,
289a747e4fSDavid du Colombier 	TcptimerON	= 1,
299a747e4fSDavid du Colombier 	TcptimerDONE	= 2,
307dd7cddfSDavid du Colombier 	MAX_TIME 	= (1<<20),	/* Forever */
31ef9eff0bSDavid du Colombier 	TCP_ACK		= 50,		/* Timed ack sequence in ms */
321b0f96a1SDavid du Colombier 	MAXBACKMS	= 9*60*1000,	/* longest backoff time (ms) before hangup */
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier 	URG		= 0x20,		/* Data marked urgent */
357dd7cddfSDavid du Colombier 	ACK		= 0x10,		/* Acknowledge is valid */
367dd7cddfSDavid du Colombier 	PSH		= 0x08,		/* Whole data pipe is pushed */
377dd7cddfSDavid du Colombier 	RST		= 0x04,		/* Reset connection */
387dd7cddfSDavid du Colombier 	SYN		= 0x02,		/* Pkt. is synchronise */
397dd7cddfSDavid du Colombier 	FIN		= 0x01,		/* Start close down */
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier 	EOLOPT		= 0,
427dd7cddfSDavid du Colombier 	NOOPOPT		= 1,
437dd7cddfSDavid du Colombier 	MSSOPT		= 2,
447ec5746aSDavid du Colombier 	MSS_LENGTH	= 4,		/* Maximum segment size */
453f695129SDavid du Colombier 	WSOPT		= 3,
463f695129SDavid du Colombier 	WS_LENGTH	= 3,		/* Bits to scale window size by */
477dd7cddfSDavid du Colombier 	MSL2		= 10,
487dd7cddfSDavid du Colombier 	MSPTICK		= 50,		/* Milliseconds per timer tick */
497ec5746aSDavid du Colombier 	DEF_MSS		= 1460,		/* Default maximum segment */
507ec5746aSDavid du Colombier 	DEF_MSS6	= 1280,		/* Default maximum segment (min) for v6 */
5180ee5cbfSDavid du Colombier 	DEF_RTT		= 500,		/* Default round trip */
52ed8191acSDavid du Colombier 	DEF_KAT		= 120000,	/* Default time (ms) between keep alives */
537dd7cddfSDavid du Colombier 	TCP_LISTEN	= 0,		/* Listen connection */
547dd7cddfSDavid du Colombier 	TCP_CONNECT	= 1,		/* Outgoing connection */
553ff48bf5SDavid du Colombier 	SYNACK_RXTIMER	= 250,		/* ms between SYNACK retransmits */
567dd7cddfSDavid du Colombier 
577dd7cddfSDavid du Colombier 	TCPREXMTTHRESH	= 3,		/* dupack threshhold for rxt */
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier 	FORCE		= 1,
607dd7cddfSDavid du Colombier 	CLONE		= 2,
617dd7cddfSDavid du Colombier 	RETRAN		= 4,
627dd7cddfSDavid du Colombier 	ACTIVE		= 8,
637dd7cddfSDavid du Colombier 	SYNACK		= 16,
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier 	LOGAGAIN	= 3,
667dd7cddfSDavid du Colombier 	LOGDGAIN	= 2,
673ff48bf5SDavid du Colombier 
687dd7cddfSDavid du Colombier 	Closed		= 0,		/* Connection states */
697dd7cddfSDavid du Colombier 	Listen,
707dd7cddfSDavid du Colombier 	Syn_sent,
717dd7cddfSDavid du Colombier 	Syn_received,
727dd7cddfSDavid du Colombier 	Established,
737dd7cddfSDavid du Colombier 	Finwait1,
747dd7cddfSDavid du Colombier 	Finwait2,
757dd7cddfSDavid du Colombier 	Close_wait,
767dd7cddfSDavid du Colombier 	Closing,
777dd7cddfSDavid du Colombier 	Last_ack,
783ff48bf5SDavid du Colombier 	Time_wait,
793ff48bf5SDavid du Colombier 
803ff48bf5SDavid du Colombier 	Maxlimbo	= 1000,		/* maximum procs waiting for response to SYN ACK */
813ff48bf5SDavid du Colombier 	NLHT		= 256,		/* hash table size, must be a power of 2 */
823f695129SDavid du Colombier 	LHTMASK		= NLHT-1,
833f695129SDavid du Colombier 
841ce6c95fSDavid du Colombier 	/*
851ce6c95fSDavid du Colombier 	 * window is 64kb * 2ⁿ
861ce6c95fSDavid du Colombier 	 * these factors determine the ultimate bandwidth-delay product.
871ce6c95fSDavid du Colombier 	 * 64kb * 2⁵ = 2mb, or 2× overkill for 100mbps * 70ms.
881ce6c95fSDavid du Colombier 	 */
891ce6c95fSDavid du Colombier 	Maxqscale	= 4,		/* maximum queuing scale */
901ce6c95fSDavid du Colombier 	Defadvscale	= 4,		/* default advertisement */
917dd7cddfSDavid du Colombier };
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier /* Must correspond to the enumeration above */
947dd7cddfSDavid du Colombier char *tcpstates[] =
957dd7cddfSDavid du Colombier {
967dd7cddfSDavid du Colombier 	"Closed", 	"Listen", 	"Syn_sent", "Syn_received",
977dd7cddfSDavid du Colombier 	"Established", 	"Finwait1",	"Finwait2", "Close_wait",
987dd7cddfSDavid du Colombier 	"Closing", 	"Last_ack", 	"Time_wait"
997dd7cddfSDavid du Colombier };
1007dd7cddfSDavid du Colombier 
1019a747e4fSDavid du Colombier typedef struct Tcptimer Tcptimer;
1029a747e4fSDavid du Colombier struct Tcptimer
1037dd7cddfSDavid du Colombier {
1049a747e4fSDavid du Colombier 	Tcptimer	*next;
1059a747e4fSDavid du Colombier 	Tcptimer	*prev;
1069a747e4fSDavid du Colombier 	Tcptimer	*readynext;
1077dd7cddfSDavid du Colombier 	int	state;
1087dd7cddfSDavid du Colombier 	int	start;
1097dd7cddfSDavid du Colombier 	int	count;
1107dd7cddfSDavid du Colombier 	void	(*func)(void*);
1117dd7cddfSDavid du Colombier 	void	*arg;
1127dd7cddfSDavid du Colombier };
1137dd7cddfSDavid du Colombier 
1143ff48bf5SDavid du Colombier /*
1153ff48bf5SDavid du Colombier  *  v4 and v6 pseudo headers used for
1163ff48bf5SDavid du Colombier  *  checksuming tcp
1173ff48bf5SDavid du Colombier  */
1183ff48bf5SDavid du Colombier typedef struct Tcp4hdr Tcp4hdr;
1193ff48bf5SDavid du Colombier struct Tcp4hdr
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
1227dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
1237dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
1247dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
1257dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
1267dd7cddfSDavid du Colombier 	uchar	Unused;
1277dd7cddfSDavid du Colombier 	uchar	proto;
1287dd7cddfSDavid du Colombier 	uchar	tcplen[2];
1297dd7cddfSDavid du Colombier 	uchar	tcpsrc[4];
1307dd7cddfSDavid du Colombier 	uchar	tcpdst[4];
131*8e860520SDavid du Colombier 	/* same as v6 from here on */
1327dd7cddfSDavid du Colombier 	uchar	tcpsport[2];
1337dd7cddfSDavid du Colombier 	uchar	tcpdport[2];
1347dd7cddfSDavid du Colombier 	uchar	tcpseq[4];
1357dd7cddfSDavid du Colombier 	uchar	tcpack[4];
1367dd7cddfSDavid du Colombier 	uchar	tcpflag[2];
1377dd7cddfSDavid du Colombier 	uchar	tcpwin[2];
1387dd7cddfSDavid du Colombier 	uchar	tcpcksum[2];
1397dd7cddfSDavid du Colombier 	uchar	tcpurg[2];
1407dd7cddfSDavid du Colombier 	/* Options segment */
1413f695129SDavid du Colombier 	uchar	tcpopt[1];
1427dd7cddfSDavid du Colombier };
1437dd7cddfSDavid du Colombier 
1443ff48bf5SDavid du Colombier typedef struct Tcp6hdr Tcp6hdr;
1453ff48bf5SDavid du Colombier struct Tcp6hdr
1463ff48bf5SDavid du Colombier {
1473ff48bf5SDavid du Colombier 	uchar	vcf[4];
1483ff48bf5SDavid du Colombier 	uchar	ploadlen[2];
1493ff48bf5SDavid du Colombier 	uchar	proto;
1503ff48bf5SDavid du Colombier 	uchar	ttl;
1513ff48bf5SDavid du Colombier 	uchar	tcpsrc[IPaddrlen];
1523ff48bf5SDavid du Colombier 	uchar	tcpdst[IPaddrlen];
153*8e860520SDavid du Colombier 	/* same as v4 from here on */
1543ff48bf5SDavid du Colombier 	uchar	tcpsport[2];
1553ff48bf5SDavid du Colombier 	uchar	tcpdport[2];
1563ff48bf5SDavid du Colombier 	uchar	tcpseq[4];
1573ff48bf5SDavid du Colombier 	uchar	tcpack[4];
1583ff48bf5SDavid du Colombier 	uchar	tcpflag[2];
1593ff48bf5SDavid du Colombier 	uchar	tcpwin[2];
1603ff48bf5SDavid du Colombier 	uchar	tcpcksum[2];
1613ff48bf5SDavid du Colombier 	uchar	tcpurg[2];
1623ff48bf5SDavid du Colombier 	/* Options segment */
1633f695129SDavid du Colombier 	uchar	tcpopt[1];
1643ff48bf5SDavid du Colombier };
1653ff48bf5SDavid du Colombier 
1663ff48bf5SDavid du Colombier /*
1673ff48bf5SDavid du Colombier  *  this represents the control info
1683ff48bf5SDavid du Colombier  *  for a single packet.  It is derived from
1693ff48bf5SDavid du Colombier  *  a packet in ntohtcp{4,6}() and stuck into
1703ff48bf5SDavid du Colombier  *  a packet in htontcp{4,6}().
1713ff48bf5SDavid du Colombier  */
1727dd7cddfSDavid du Colombier typedef struct Tcp Tcp;
1737dd7cddfSDavid du Colombier struct	Tcp
1747dd7cddfSDavid du Colombier {
1757dd7cddfSDavid du Colombier 	ushort	source;
1767dd7cddfSDavid du Colombier 	ushort	dest;
1777dd7cddfSDavid du Colombier 	ulong	seq;
1787dd7cddfSDavid du Colombier 	ulong	ack;
1797dd7cddfSDavid du Colombier 	uchar	flags;
1801ce6c95fSDavid du Colombier 	uchar	update;
1811ce6c95fSDavid du Colombier 	ushort	ws;	/* window scale option */
1821ce6c95fSDavid du Colombier 	ulong	wnd;	/* prescaled window*/
1837dd7cddfSDavid du Colombier 	ushort	urg;
1843f695129SDavid du Colombier 	ushort	mss;	/* max segment size option (if not zero) */
1857dd7cddfSDavid du Colombier 	ushort	len;	/* size of data */
1867dd7cddfSDavid du Colombier };
1877dd7cddfSDavid du Colombier 
1883ff48bf5SDavid du Colombier /*
1893ff48bf5SDavid du Colombier  *  this header is malloc'd to thread together fragments
1903ff48bf5SDavid du Colombier  *  waiting to be coalesced
1913ff48bf5SDavid du Colombier  */
1927dd7cddfSDavid du Colombier typedef struct Reseq Reseq;
1937dd7cddfSDavid du Colombier struct Reseq
1947dd7cddfSDavid du Colombier {
1957dd7cddfSDavid du Colombier 	Reseq	*next;
1967dd7cddfSDavid du Colombier 	Tcp	seg;
1977dd7cddfSDavid du Colombier 	Block	*bp;
1987dd7cddfSDavid du Colombier 	ushort	length;
1997dd7cddfSDavid du Colombier };
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier /*
2027dd7cddfSDavid du Colombier  *  the qlock in the Conv locks this structure
2037dd7cddfSDavid du Colombier  */
2047dd7cddfSDavid du Colombier typedef struct Tcpctl Tcpctl;
2057dd7cddfSDavid du Colombier struct Tcpctl
2067dd7cddfSDavid du Colombier {
2077dd7cddfSDavid du Colombier 	uchar	state;			/* Connection state */
2087dd7cddfSDavid du Colombier 	uchar	type;			/* Listening or active connection */
2097dd7cddfSDavid du Colombier 	uchar	code;			/* Icmp code */
2107dd7cddfSDavid du Colombier 	struct {
2117dd7cddfSDavid du Colombier 		ulong	una;		/* Unacked data pointer */
2127dd7cddfSDavid du Colombier 		ulong	nxt;		/* Next sequence expected */
2137dd7cddfSDavid du Colombier 		ulong	ptr;		/* Data pointer */
2143f695129SDavid du Colombier 		ulong	wnd;		/* Tcp send window */
2157dd7cddfSDavid du Colombier 		ulong	urg;		/* Urgent data pointer */
2167dd7cddfSDavid du Colombier 		ulong	wl2;
2171ce6c95fSDavid du Colombier 		uint	scale;		/* how much to right shift window */
2181ce6c95fSDavid du Colombier 					/* in xmitted packets */
2197dd7cddfSDavid du Colombier 		/* to implement tahoe and reno TCP */
2207dd7cddfSDavid du Colombier 		ulong	dupacks;	/* number of duplicate acks rcvd */
2211ce6c95fSDavid du Colombier 		ulong	partialack;
2227dd7cddfSDavid du Colombier 		int	recovery;	/* loss recovery flag */
2231ce6c95fSDavid du Colombier 		int	retransmit;	/* retransmit 1 packet @ una flag */
2241ce6c95fSDavid du Colombier 		int	rto;
2257dd7cddfSDavid du Colombier 		ulong	rxt;		/* right window marker for recovery */
2261ce6c95fSDavid du Colombier 					/* "recover" rfc3782 */
2277dd7cddfSDavid du Colombier 	} snd;
2287dd7cddfSDavid du Colombier 	struct {
2297dd7cddfSDavid du Colombier 		ulong	nxt;		/* Receive pointer to next uchar slot */
2303f695129SDavid du Colombier 		ulong	wnd;		/* Receive window incoming */
2311ce6c95fSDavid du Colombier 		ulong	wsnt;		/* Last wptr sent.  important to */
2321ce6c95fSDavid du Colombier 					/* track for large bdp */
2331ce6c95fSDavid du Colombier 		ulong	wptr;
2347dd7cddfSDavid du Colombier 		ulong	urg;		/* Urgent pointer */
2351ce6c95fSDavid du Colombier 		ulong	ackptr;		/* last acked sequence */
2367dd7cddfSDavid du Colombier 		int	blocked;
2371ce6c95fSDavid du Colombier 		uint	scale;		/* how much to left shift window in */
2381ce6c95fSDavid du Colombier 					/* rcv'd packets */
2397dd7cddfSDavid du Colombier 	} rcv;
2407dd7cddfSDavid du Colombier 	ulong	iss;			/* Initial sequence number */
2413f695129SDavid du Colombier 	ulong	cwind;			/* Congestion window */
2421ce6c95fSDavid du Colombier 	ulong	abcbytes;		/* appropriate byte counting rfc 3465 */
2431ce6c95fSDavid du Colombier 	uint	scale;			/* desired snd.scale */
2441ce6c95fSDavid du Colombier 	ulong	ssthresh;		/* Slow start threshold */
2457dd7cddfSDavid du Colombier 	int	resent;			/* Bytes just resent */
2467dd7cddfSDavid du Colombier 	int	irs;			/* Initial received squence */
2477ec5746aSDavid du Colombier 	ushort	mss;			/* Maximum segment size */
2487dd7cddfSDavid du Colombier 	int	rerecv;			/* Overlap of data rerecevived */
2491ce6c95fSDavid du Colombier 	ulong	window;			/* Our receive window (queue) */
2501ce6c95fSDavid du Colombier 	uint	qscale;			/* Log2 of our receive window (queue) */
2517dd7cddfSDavid du Colombier 	uchar	backoff;		/* Exponential backoff counter */
2527dd7cddfSDavid du Colombier 	int	backedoff;		/* ms we've backed off for rexmits */
2537dd7cddfSDavid du Colombier 	uchar	flags;			/* State flags */
2547dd7cddfSDavid du Colombier 	Reseq	*reseq;			/* Resequencing queue */
2551ce6c95fSDavid du Colombier 	int	nreseq;
2561ce6c95fSDavid du Colombier 	int	reseqlen;
2579a747e4fSDavid du Colombier 	Tcptimer	timer;			/* Activity timer */
2589a747e4fSDavid du Colombier 	Tcptimer	acktimer;		/* Acknowledge timer */
2599a747e4fSDavid du Colombier 	Tcptimer	rtt_timer;		/* Round trip timer */
2609a747e4fSDavid du Colombier 	Tcptimer	katimer;		/* keep alive timer */
2617dd7cddfSDavid du Colombier 	ulong	rttseq;			/* Round trip sequence */
2621ce6c95fSDavid du Colombier 	int	srtt;			/* Smoothed round trip */
2637dd7cddfSDavid du Colombier 	int	mdev;			/* Mean deviation of round trip */
2647dd7cddfSDavid du Colombier 	int	kacounter;		/* count down for keep alive */
2657dd7cddfSDavid du Colombier 	uint	sndsyntime;		/* time syn sent */
2667dd7cddfSDavid du Colombier 	ulong	time;			/* time Finwait2 or Syn_received was sent */
2671ce6c95fSDavid du Colombier 	ulong	timeuna;		/* snd.una when time was set */
26859cc4ca5SDavid du Colombier 	int	nochecksum;		/* non-zero means don't send checksums */
2693ff48bf5SDavid du Colombier 	int	flgcnt;			/* number of flags in the sequence (FIN,SEQ) */
2707dd7cddfSDavid du Colombier 
2713ff48bf5SDavid du Colombier 	union {
2723ff48bf5SDavid du Colombier 		Tcp4hdr	tcp4hdr;
2733ff48bf5SDavid du Colombier 		Tcp6hdr	tcp6hdr;
2743ff48bf5SDavid du Colombier 	} protohdr;		/* prototype header */
2753ff48bf5SDavid du Colombier };
2763ff48bf5SDavid du Colombier 
2773ff48bf5SDavid du Colombier /*
2783ff48bf5SDavid du Colombier  *  New calls are put in limbo rather than having a conversation structure
2793ff48bf5SDavid du Colombier  *  allocated.  Thus, a SYN attack results in lots of limbo'd calls but not
2803ff48bf5SDavid du Colombier  *  any real Conv structures mucking things up.  Calls in limbo rexmit their
2813ff48bf5SDavid du Colombier  *  SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second.
2823ff48bf5SDavid du Colombier  *
2833ff48bf5SDavid du Colombier  *  In particular they aren't on a listener's queue so that they don't figure
2843ff48bf5SDavid du Colombier  *  in the input queue limit.
2853ff48bf5SDavid du Colombier  *
2863ff48bf5SDavid du Colombier  *  If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
2873ff48bf5SDavid du Colombier  *  of 70000 limbo'd calls.  Not great for a linear list but doable.  Therefore
2883ff48bf5SDavid du Colombier  *  there is no hashing of this list.
2893ff48bf5SDavid du Colombier  */
2903ff48bf5SDavid du Colombier typedef struct Limbo Limbo;
2913ff48bf5SDavid du Colombier struct Limbo
2923ff48bf5SDavid du Colombier {
2933ff48bf5SDavid du Colombier 	Limbo	*next;
2943ff48bf5SDavid du Colombier 
2953ff48bf5SDavid du Colombier 	uchar	laddr[IPaddrlen];
2963ff48bf5SDavid du Colombier 	uchar	raddr[IPaddrlen];
2973ff48bf5SDavid du Colombier 	ushort	lport;
2983ff48bf5SDavid du Colombier 	ushort	rport;
2993ff48bf5SDavid du Colombier 	ulong	irs;		/* initial received sequence */
3003ff48bf5SDavid du Colombier 	ulong	iss;		/* initial sent sequence */
3013ff48bf5SDavid du Colombier 	ushort	mss;		/* mss from the other end */
3023f695129SDavid du Colombier 	ushort	rcvscale;	/* how much to scale rcvd windows */
3033f695129SDavid du Colombier 	ushort	sndscale;	/* how much to scale sent windows */
3043ff48bf5SDavid du Colombier 	ulong	lastsend;	/* last time we sent a synack */
3053ff48bf5SDavid du Colombier 	uchar	version;	/* v4 or v6 */
3063ff48bf5SDavid du Colombier 	uchar	rexmits;	/* number of retransmissions */
3077dd7cddfSDavid du Colombier };
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier int	tcp_irtt = DEF_RTT;	/* Initial guess at round trip time */
3107dd7cddfSDavid du Colombier 
31159cc4ca5SDavid du Colombier enum {
31259cc4ca5SDavid du Colombier 	/* MIB stats */
31359cc4ca5SDavid du Colombier 	MaxConn,
3147ec5746aSDavid du Colombier 	Mss,
31559cc4ca5SDavid du Colombier 	ActiveOpens,
31659cc4ca5SDavid du Colombier 	PassiveOpens,
31759cc4ca5SDavid du Colombier 	EstabResets,
31859cc4ca5SDavid du Colombier 	CurrEstab,
31959cc4ca5SDavid du Colombier 	InSegs,
32059cc4ca5SDavid du Colombier 	OutSegs,
32159cc4ca5SDavid du Colombier 	RetransSegs,
3221ce6c95fSDavid du Colombier 	RetransSegsSent,
32359cc4ca5SDavid du Colombier 	RetransTimeouts,
32459cc4ca5SDavid du Colombier 	InErrs,
32559cc4ca5SDavid du Colombier 	OutRsts,
32659cc4ca5SDavid du Colombier 
32759cc4ca5SDavid du Colombier 	/* non-MIB stats */
32859cc4ca5SDavid du Colombier 	CsumErrs,
32959cc4ca5SDavid du Colombier 	HlenErrs,
33059cc4ca5SDavid du Colombier 	LenErrs,
3311ce6c95fSDavid du Colombier 	Resequenced,
33259cc4ca5SDavid du Colombier 	OutOfOrder,
3331ce6c95fSDavid du Colombier 	ReseqBytelim,
3341ce6c95fSDavid du Colombier 	ReseqPktlim,
3351ce6c95fSDavid du Colombier 	Delayack,
3361ce6c95fSDavid du Colombier 	Wopenack,
3371ce6c95fSDavid du Colombier 
3381ce6c95fSDavid du Colombier 	Recovery,
3391ce6c95fSDavid du Colombier 	RecoveryDone,
3401ce6c95fSDavid du Colombier 	RecoveryRTO,
3411ce6c95fSDavid du Colombier 	RecoveryNoSeq,
3421ce6c95fSDavid du Colombier 	RecoveryCwind,
3431ce6c95fSDavid du Colombier 	RecoveryPA,
34459cc4ca5SDavid du Colombier 
34559cc4ca5SDavid du Colombier 	Nstats
34659cc4ca5SDavid du Colombier };
34759cc4ca5SDavid du Colombier 
3481ce6c95fSDavid du Colombier static char *statnames[Nstats] =
3497dd7cddfSDavid du Colombier {
35059cc4ca5SDavid du Colombier [MaxConn]	"MaxConn",
3517ec5746aSDavid du Colombier [Mss]		"MaxSegment",
35259cc4ca5SDavid du Colombier [ActiveOpens]	"ActiveOpens",
35359cc4ca5SDavid du Colombier [PassiveOpens]	"PassiveOpens",
35459cc4ca5SDavid du Colombier [EstabResets]	"EstabResets",
35559cc4ca5SDavid du Colombier [CurrEstab]	"CurrEstab",
35659cc4ca5SDavid du Colombier [InSegs]	"InSegs",
35759cc4ca5SDavid du Colombier [OutSegs]	"OutSegs",
35859cc4ca5SDavid du Colombier [RetransSegs]	"RetransSegs",
3591ce6c95fSDavid du Colombier [RetransSegsSent]	"RetransSegsSent",
36059cc4ca5SDavid du Colombier [RetransTimeouts]	"RetransTimeouts",
36159cc4ca5SDavid du Colombier [InErrs]	"InErrs",
36259cc4ca5SDavid du Colombier [OutRsts]	"OutRsts",
36359cc4ca5SDavid du Colombier [CsumErrs]	"CsumErrs",
36459cc4ca5SDavid du Colombier [HlenErrs]	"HlenErrs",
36559cc4ca5SDavid du Colombier [LenErrs]	"LenErrs",
36659cc4ca5SDavid du Colombier [OutOfOrder]	"OutOfOrder",
3671ce6c95fSDavid du Colombier [Resequenced]	"Resequenced",
3681ce6c95fSDavid du Colombier [ReseqBytelim]	"ReseqBytelim",
3691ce6c95fSDavid du Colombier [ReseqPktlim]	"ReseqPktlim",
3701ce6c95fSDavid du Colombier [Delayack]	"Delayack",
3711ce6c95fSDavid du Colombier [Wopenack]	"Wopenack",
3721ce6c95fSDavid du Colombier 
3731ce6c95fSDavid du Colombier [Recovery]	"Recovery",
3741ce6c95fSDavid du Colombier [RecoveryDone]	"RecoveryDone",
3751ce6c95fSDavid du Colombier [RecoveryRTO]	"RecoveryRTO",
3761ce6c95fSDavid du Colombier 
3771ce6c95fSDavid du Colombier [RecoveryNoSeq]	"RecoveryNoSeq",
3781ce6c95fSDavid du Colombier [RecoveryCwind]	"RecoveryCwind",
3791ce6c95fSDavid du Colombier [RecoveryPA]	"RecoveryPA",
3807dd7cddfSDavid du Colombier };
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier typedef struct Tcppriv Tcppriv;
3837dd7cddfSDavid du Colombier struct Tcppriv
3847dd7cddfSDavid du Colombier {
3853ff48bf5SDavid du Colombier 	/* List of active timers */
3863ff48bf5SDavid du Colombier 	QLock 	tl;
3873ff48bf5SDavid du Colombier 	Tcptimer *timers;
3883ff48bf5SDavid du Colombier 
38980ee5cbfSDavid du Colombier 	/* hash table for matching conversations */
39080ee5cbfSDavid du Colombier 	Ipht	ht;
39180ee5cbfSDavid du Colombier 
3923ff48bf5SDavid du Colombier 	/* calls in limbo waiting for an ACK to our SYN ACK */
3933ff48bf5SDavid du Colombier 	int	nlimbo;
3943ff48bf5SDavid du Colombier 	Limbo	*lht[NLHT];
3957dd7cddfSDavid du Colombier 
3967dd7cddfSDavid du Colombier 	/* for keeping track of tcpackproc */
3977dd7cddfSDavid du Colombier 	QLock	apl;
3983ff48bf5SDavid du Colombier 	int	ackprocstarted;
3993ff48bf5SDavid du Colombier 
4005e27dea9SDavid du Colombier 	uvlong	stats[Nstats];
4017dd7cddfSDavid du Colombier };
4027dd7cddfSDavid du Colombier 
4033ff48bf5SDavid du Colombier /*
4043ff48bf5SDavid du Colombier  *  Setting tcpporthogdefense to non-zero enables Dong Lin's
4053ff48bf5SDavid du Colombier  *  solution to hijacked systems staking out port's as a form
4063ff48bf5SDavid du Colombier  *  of DoS attack.
4073ff48bf5SDavid du Colombier  *
4083ff48bf5SDavid du Colombier  *  To avoid stateless Conv hogs, we pick a sequence number at random.  If
40909911ebdSDavid du Colombier  *  that number gets acked by the other end, we shut down the connection.
41009911ebdSDavid du Colombier  *  Look for tcpporthogdefense in the code.
4113ff48bf5SDavid du Colombier  */
4123ff48bf5SDavid du Colombier int tcpporthogdefense = 0;
4133ff48bf5SDavid du Colombier 
4141ce6c95fSDavid du Colombier static	int	addreseq(Fs*, Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
4151ce6c95fSDavid du Colombier static	int	dumpreseq(Tcpctl*);
4161ce6c95fSDavid du Colombier static	void	getreseq(Tcpctl*, Tcp*, Block**, ushort*);
4173ff48bf5SDavid du Colombier static	void	limbo(Conv*, uchar*, uchar*, Tcp*, int);
4181ce6c95fSDavid du Colombier static	void	limborexmit(Proto*);
4191ce6c95fSDavid du Colombier static	void	localclose(Conv*, char*);
4201ce6c95fSDavid du Colombier static	void	procsyn(Conv*, Tcp*);
4211ce6c95fSDavid du Colombier static	void	tcpacktimer(void*);
4221ce6c95fSDavid du Colombier static	void	tcpiput(Proto*, Ipifc*, Block*);
4231ce6c95fSDavid du Colombier static	void	tcpkeepalive(void*);
4241ce6c95fSDavid du Colombier static	void	tcpoutput(Conv*);
4251ce6c95fSDavid du Colombier static	void	tcprcvwin(Conv*);
4261ce6c95fSDavid du Colombier static	void	tcprxmit(Conv*);
4271ce6c95fSDavid du Colombier static	void	tcpsetkacounter(Tcpctl*);
4281ce6c95fSDavid du Colombier static	void	tcpsetscale(Conv*, Tcpctl*, ushort, ushort);
4291ce6c95fSDavid du Colombier static	void	tcpsettimer(Tcpctl*);
4301ce6c95fSDavid du Colombier static	void	tcpsndsyn(Conv*, Tcpctl*);
4311ce6c95fSDavid du Colombier static	void	tcpstart(Conv*, int);
4321ce6c95fSDavid du Colombier static	void	tcpsynackrtt(Conv*);
4331ce6c95fSDavid du Colombier static	void	tcptimeout(void*);
4341ce6c95fSDavid du Colombier static	int	tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
4357dd7cddfSDavid du Colombier 
4361ce6c95fSDavid du Colombier static void
tcpsetstate(Conv * s,uchar newstate)4377dd7cddfSDavid du Colombier tcpsetstate(Conv *s, uchar newstate)
4387dd7cddfSDavid du Colombier {
4397dd7cddfSDavid du Colombier 	Tcpctl *tcb;
4407dd7cddfSDavid du Colombier 	uchar oldstate;
4417dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
4447dd7cddfSDavid du Colombier 
4457dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
4467dd7cddfSDavid du Colombier 
4477dd7cddfSDavid du Colombier 	oldstate = tcb->state;
4487dd7cddfSDavid du Colombier 	if(oldstate == newstate)
4497dd7cddfSDavid du Colombier 		return;
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier 	if(oldstate == Established)
45259cc4ca5SDavid du Colombier 		tpriv->stats[CurrEstab]--;
4537dd7cddfSDavid du Colombier 	if(newstate == Established)
45459cc4ca5SDavid du Colombier 		tpriv->stats[CurrEstab]++;
4557dd7cddfSDavid du Colombier 
4567dd7cddfSDavid du Colombier 	switch(newstate) {
4577dd7cddfSDavid du Colombier 	case Closed:
4587dd7cddfSDavid du Colombier 		qclose(s->rq);
4597dd7cddfSDavid du Colombier 		qclose(s->wq);
4607dd7cddfSDavid du Colombier 		qclose(s->eq);
4617dd7cddfSDavid du Colombier 		break;
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier 	case Close_wait:		/* Remote closes */
4647dd7cddfSDavid du Colombier 		qhangup(s->rq, nil);
4657dd7cddfSDavid du Colombier 		break;
4667dd7cddfSDavid du Colombier 	}
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier 	tcb->state = newstate;
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier 	if(oldstate == Syn_sent && newstate != Closed)
4717dd7cddfSDavid du Colombier 		Fsconnected(s, nil);
4727dd7cddfSDavid du Colombier }
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier static char*
tcpconnect(Conv * c,char ** argv,int argc)4757dd7cddfSDavid du Colombier tcpconnect(Conv *c, char **argv, int argc)
4767dd7cddfSDavid du Colombier {
4777dd7cddfSDavid du Colombier 	char *e;
47884363d68SDavid du Colombier 	Tcpctl *tcb;
47984363d68SDavid du Colombier 
48084363d68SDavid du Colombier 	tcb = (Tcpctl*)(c->ptcl);
48184363d68SDavid du Colombier 	if(tcb->state != Closed)
48284363d68SDavid du Colombier 		return Econinuse;
4837dd7cddfSDavid du Colombier 
4847dd7cddfSDavid du Colombier 	e = Fsstdconnect(c, argv, argc);
4857dd7cddfSDavid du Colombier 	if(e != nil)
4867dd7cddfSDavid du Colombier 		return e;
4873f695129SDavid du Colombier 	tcpstart(c, TCP_CONNECT);
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 	return nil;
4907dd7cddfSDavid du Colombier }
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier static int
tcpstate(Conv * c,char * state,int n)4937dd7cddfSDavid du Colombier tcpstate(Conv *c, char *state, int n)
4947dd7cddfSDavid du Colombier {
4957dd7cddfSDavid du Colombier 	Tcpctl *s;
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	s = (Tcpctl*)(c->ptcl);
4987dd7cddfSDavid du Colombier 
4997dd7cddfSDavid du Colombier 	return snprint(state, n,
5001ce6c95fSDavid du Colombier 		"%s qin %d qout %d rq %d.%d srtt %d mdev %d sst %lud cwin %lud "
5011ce6c95fSDavid du Colombier 		"swin %lud>>%d rwin %lud>>%d qscale %d timer.start %d "
5021ce6c95fSDavid du Colombier 		"timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
50325332fe6SDavid du Colombier 		tcpstates[s->state],
50425332fe6SDavid du Colombier 		c->rq ? qlen(c->rq) : 0,
50525332fe6SDavid du Colombier 		c->wq ? qlen(c->wq) : 0,
5061ce6c95fSDavid du Colombier 		s->nreseq, s->reseqlen,
5071ce6c95fSDavid du Colombier 		s->srtt, s->mdev, s->ssthresh,
5083f695129SDavid du Colombier 		s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
5091ce6c95fSDavid du Colombier 		s->qscale,
51040ef9009SDavid du Colombier 		s->timer.start, s->timer.count, s->rerecv,
51140ef9009SDavid du Colombier 		s->katimer.start, s->katimer.count);
5127dd7cddfSDavid du Colombier }
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier static int
tcpinuse(Conv * c)5157dd7cddfSDavid du Colombier tcpinuse(Conv *c)
5167dd7cddfSDavid du Colombier {
5177dd7cddfSDavid du Colombier 	Tcpctl *s;
5187dd7cddfSDavid du Colombier 
5197dd7cddfSDavid du Colombier 	s = (Tcpctl*)(c->ptcl);
5207dd7cddfSDavid du Colombier 	return s->state != Closed;
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier 
5237dd7cddfSDavid du Colombier static char*
tcpannounce(Conv * c,char ** argv,int argc)5247dd7cddfSDavid du Colombier tcpannounce(Conv *c, char **argv, int argc)
5257dd7cddfSDavid du Colombier {
5267dd7cddfSDavid du Colombier 	char *e;
52784363d68SDavid du Colombier 	Tcpctl *tcb;
52884363d68SDavid du Colombier 
52984363d68SDavid du Colombier 	tcb = (Tcpctl*)(c->ptcl);
53084363d68SDavid du Colombier 	if(tcb->state != Closed)
53184363d68SDavid du Colombier 		return Econinuse;
5327dd7cddfSDavid du Colombier 
5337dd7cddfSDavid du Colombier 	e = Fsstdannounce(c, argv, argc);
5347dd7cddfSDavid du Colombier 	if(e != nil)
5357dd7cddfSDavid du Colombier 		return e;
5363f695129SDavid du Colombier 	tcpstart(c, TCP_LISTEN);
5377dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
5387dd7cddfSDavid du Colombier 
5397dd7cddfSDavid du Colombier 	return nil;
5407dd7cddfSDavid du Colombier }
5417dd7cddfSDavid du Colombier 
54280ee5cbfSDavid du Colombier /*
54380ee5cbfSDavid du Colombier  *  tcpclose is always called with the q locked
54480ee5cbfSDavid du Colombier  */
5457dd7cddfSDavid du Colombier static void
tcpclose(Conv * c)5467dd7cddfSDavid du Colombier tcpclose(Conv *c)
5477dd7cddfSDavid du Colombier {
5487dd7cddfSDavid du Colombier 	Tcpctl *tcb;
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)c->ptcl;
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier 	qhangup(c->rq, nil);
5537dd7cddfSDavid du Colombier 	qhangup(c->wq, nil);
5547dd7cddfSDavid du Colombier 	qhangup(c->eq, nil);
55580ee5cbfSDavid du Colombier 	qflush(c->rq);
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	switch(tcb->state) {
5587dd7cddfSDavid du Colombier 	case Listen:
5597dd7cddfSDavid du Colombier 		/*
5607dd7cddfSDavid du Colombier 		 *  reset any incoming calls to this listener
5617dd7cddfSDavid du Colombier 		 */
5627dd7cddfSDavid du Colombier 		Fsconnected(c, "Hangup");
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier 		localclose(c, nil);
5657dd7cddfSDavid du Colombier 		break;
5667dd7cddfSDavid du Colombier 	case Closed:
5677dd7cddfSDavid du Colombier 	case Syn_sent:
5687dd7cddfSDavid du Colombier 		localclose(c, nil);
5697dd7cddfSDavid du Colombier 		break;
5707dd7cddfSDavid du Colombier 	case Syn_received:
5717dd7cddfSDavid du Colombier 	case Established:
57280ee5cbfSDavid du Colombier 		tcb->flgcnt++;
5737dd7cddfSDavid du Colombier 		tcb->snd.nxt++;
5747dd7cddfSDavid du Colombier 		tcpsetstate(c, Finwait1);
5757dd7cddfSDavid du Colombier 		tcpoutput(c);
5767dd7cddfSDavid du Colombier 		break;
5777dd7cddfSDavid du Colombier 	case Close_wait:
57880ee5cbfSDavid du Colombier 		tcb->flgcnt++;
5797dd7cddfSDavid du Colombier 		tcb->snd.nxt++;
5807dd7cddfSDavid du Colombier 		tcpsetstate(c, Last_ack);
5817dd7cddfSDavid du Colombier 		tcpoutput(c);
5827dd7cddfSDavid du Colombier 		break;
5837dd7cddfSDavid du Colombier 	}
5847dd7cddfSDavid du Colombier }
5857dd7cddfSDavid du Colombier 
5861ce6c95fSDavid du Colombier static void
tcpkick(void * x)5873ff48bf5SDavid du Colombier tcpkick(void *x)
5887dd7cddfSDavid du Colombier {
5893ff48bf5SDavid du Colombier 	Conv *s = x;
5907dd7cddfSDavid du Colombier 	Tcpctl *tcb;
5917dd7cddfSDavid du Colombier 
5927dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
5937dd7cddfSDavid du Colombier 
59480ee5cbfSDavid du Colombier 	if(waserror()){
59580ee5cbfSDavid du Colombier 		qunlock(s);
59680ee5cbfSDavid du Colombier 		nexterror();
59780ee5cbfSDavid du Colombier 	}
59880ee5cbfSDavid du Colombier 	qlock(s);
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier 	switch(tcb->state) {
6017dd7cddfSDavid du Colombier 	case Syn_sent:
6027dd7cddfSDavid du Colombier 	case Syn_received:
6037dd7cddfSDavid du Colombier 	case Established:
6047dd7cddfSDavid du Colombier 	case Close_wait:
6057dd7cddfSDavid du Colombier 		/*
6067dd7cddfSDavid du Colombier 		 * Push data
6077dd7cddfSDavid du Colombier 		 */
6087dd7cddfSDavid du Colombier 		tcpoutput(s);
6097dd7cddfSDavid du Colombier 		break;
6107dd7cddfSDavid du Colombier 	default:
6117dd7cddfSDavid du Colombier 		localclose(s, "Hangup");
61280ee5cbfSDavid du Colombier 		break;
6137dd7cddfSDavid du Colombier 	}
61480ee5cbfSDavid du Colombier 
61580ee5cbfSDavid du Colombier 	qunlock(s);
61680ee5cbfSDavid du Colombier 	poperror();
6177dd7cddfSDavid du Colombier }
6187dd7cddfSDavid du Colombier 
6191ce6c95fSDavid du Colombier static int seq_lt(ulong, ulong);
6201ce6c95fSDavid du Colombier 
6211ce6c95fSDavid du Colombier static void
tcprcvwin(Conv * s)6227dd7cddfSDavid du Colombier tcprcvwin(Conv *s)				/* Call with tcb locked */
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier 	int w;
6257dd7cddfSDavid du Colombier 	Tcpctl *tcb;
6267dd7cddfSDavid du Colombier 
6277dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6283f695129SDavid du Colombier 	w = tcb->window - qlen(s->rq);
6297dd7cddfSDavid du Colombier 	if(w < 0)
6307dd7cddfSDavid du Colombier 		w = 0;
6311ce6c95fSDavid du Colombier 	/* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6321ce6c95fSDavid du Colombier 	if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6331ce6c95fSDavid du Colombier 		w = tcb->rcv.wptr - tcb->rcv.nxt;
6341ce6c95fSDavid du Colombier 	if(w != tcb->rcv.wnd)
6351ce6c95fSDavid du Colombier 	if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6367dd7cddfSDavid du Colombier 		tcb->rcv.blocked = 1;
6371ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6381ce6c95fSDavid du Colombier 			tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6391ce6c95fSDavid du Colombier 	}
6401ce6c95fSDavid du Colombier 	tcb->rcv.wnd = w;
6411ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt + w;
6427dd7cddfSDavid du Colombier }
6437dd7cddfSDavid du Colombier 
6441ce6c95fSDavid du Colombier static void
tcpacktimer(void * v)6457dd7cddfSDavid du Colombier tcpacktimer(void *v)
6467dd7cddfSDavid du Colombier {
6477dd7cddfSDavid du Colombier 	Tcpctl *tcb;
6487dd7cddfSDavid du Colombier 	Conv *s;
6497dd7cddfSDavid du Colombier 
6507dd7cddfSDavid du Colombier 	s = v;
6517dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6527dd7cddfSDavid du Colombier 
6537dd7cddfSDavid du Colombier 	if(waserror()){
6547dd7cddfSDavid du Colombier 		qunlock(s);
6557dd7cddfSDavid du Colombier 		nexterror();
6567dd7cddfSDavid du Colombier 	}
6577dd7cddfSDavid du Colombier 	qlock(s);
6587dd7cddfSDavid du Colombier 	if(tcb->state != Closed){
6597dd7cddfSDavid du Colombier 		tcb->flags |= FORCE;
6607dd7cddfSDavid du Colombier 		tcpoutput(s);
6617dd7cddfSDavid du Colombier 	}
6627dd7cddfSDavid du Colombier 	qunlock(s);
6637dd7cddfSDavid du Colombier 	poperror();
6647dd7cddfSDavid du Colombier }
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6671ce6c95fSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6681ce6c95fSDavid du Colombier {
6691ce6c95fSDavid du Colombier 	ulong inflight;
6701ce6c95fSDavid du Colombier 
6711ce6c95fSDavid du Colombier 	inflight = tcb->snd.nxt - tcb->snd.una;
6721ce6c95fSDavid du Colombier 	if(inflight > tcb->cwind)
6731ce6c95fSDavid du Colombier 		inflight = tcb->cwind;
6741ce6c95fSDavid du Colombier 	tcb->ssthresh = inflight / 2;
6751ce6c95fSDavid du Colombier 	if(tcb->ssthresh < 2*tcb->mss)
6761ce6c95fSDavid du Colombier 		tcb->ssthresh = 2*tcb->mss;
6771ce6c95fSDavid du Colombier }
6781ce6c95fSDavid du Colombier 
6791ce6c95fSDavid du Colombier enum {
6801ce6c95fSDavid du Colombier 	L	= 2,	/* aggressive slow start; legal values ∈ (1.0, 2.0) */
6811ce6c95fSDavid du Colombier };
6821ce6c95fSDavid du Colombier 
6831ce6c95fSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)6841ce6c95fSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
6851ce6c95fSDavid du Colombier {
6861ce6c95fSDavid du Colombier 	uint limit;
6871ce6c95fSDavid du Colombier 
6881ce6c95fSDavid du Colombier 	tcb->abcbytes += acked;
6891ce6c95fSDavid du Colombier 	if(tcb->cwind < tcb->ssthresh){
6901ce6c95fSDavid du Colombier 		/* slow start */
6911ce6c95fSDavid du Colombier 		if(tcb->snd.rto)
6921ce6c95fSDavid du Colombier 			limit = tcb->mss;
6931ce6c95fSDavid du Colombier 		else
6941ce6c95fSDavid du Colombier 			limit = L*tcb->mss;
6951ce6c95fSDavid du Colombier 		tcb->cwind += MIN(tcb->abcbytes, limit);
6961ce6c95fSDavid du Colombier 		tcb->abcbytes = 0;
6971ce6c95fSDavid du Colombier 	} else {
6981ce6c95fSDavid du Colombier 		tcb->snd.rto = 0;
6991ce6c95fSDavid du Colombier 		/* avoidance */
7001ce6c95fSDavid du Colombier 		if(tcb->abcbytes >= tcb->cwind){
7011ce6c95fSDavid du Colombier 			tcb->abcbytes -= tcb->cwind;
7021ce6c95fSDavid du Colombier 			tcb->cwind += tcb->mss;
7031ce6c95fSDavid du Colombier 		}
7041ce6c95fSDavid du Colombier 	}
7051ce6c95fSDavid du Colombier }
7061ce6c95fSDavid du Colombier 
7071ce6c95fSDavid du Colombier static void
tcpcreate(Conv * c)7087dd7cddfSDavid du Colombier tcpcreate(Conv *c)
7097dd7cddfSDavid du Colombier {
7103ff48bf5SDavid du Colombier 	c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7111ce6c95fSDavid du Colombier 	c->wq = qopen(QMAX, Qkick, tcpkick, c);
7127dd7cddfSDavid du Colombier }
7137dd7cddfSDavid du Colombier 
7147dd7cddfSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7159a747e4fSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7167dd7cddfSDavid du Colombier {
7179a747e4fSDavid du Colombier 	if(newstate != TcptimerON){
7189a747e4fSDavid du Colombier 		if(t->state == TcptimerON){
71941dd6b47SDavid du Colombier 			/* unchain */
7207dd7cddfSDavid du Colombier 			if(priv->timers == t){
7217dd7cddfSDavid du Colombier 				priv->timers = t->next;
7227dd7cddfSDavid du Colombier 				if(t->prev != nil)
7237dd7cddfSDavid du Colombier 					panic("timerstate1");
7247dd7cddfSDavid du Colombier 			}
7257dd7cddfSDavid du Colombier 			if(t->next)
7267dd7cddfSDavid du Colombier 				t->next->prev = t->prev;
7277dd7cddfSDavid du Colombier 			if(t->prev)
7287dd7cddfSDavid du Colombier 				t->prev->next = t->next;
7297dd7cddfSDavid du Colombier 			t->next = t->prev = nil;
7307dd7cddfSDavid du Colombier 		}
7317dd7cddfSDavid du Colombier 	} else {
7329a747e4fSDavid du Colombier 		if(t->state != TcptimerON){
73341dd6b47SDavid du Colombier 			/* chain */
7347dd7cddfSDavid du Colombier 			if(t->prev != nil || t->next != nil)
7357dd7cddfSDavid du Colombier 				panic("timerstate2");
7367dd7cddfSDavid du Colombier 			t->prev = nil;
7377dd7cddfSDavid du Colombier 			t->next = priv->timers;
7387dd7cddfSDavid du Colombier 			if(t->next)
7397dd7cddfSDavid du Colombier 				t->next->prev = t;
7407dd7cddfSDavid du Colombier 			priv->timers = t;
7417dd7cddfSDavid du Colombier 		}
7427dd7cddfSDavid du Colombier 	}
7437dd7cddfSDavid du Colombier 	t->state = newstate;
7447dd7cddfSDavid du Colombier }
7457dd7cddfSDavid du Colombier 
7461ce6c95fSDavid du Colombier static void
tcpackproc(void * a)7477dd7cddfSDavid du Colombier tcpackproc(void *a)
7487dd7cddfSDavid du Colombier {
7499a747e4fSDavid du Colombier 	Tcptimer *t, *tp, *timeo;
7507dd7cddfSDavid du Colombier 	Proto *tcp;
7517dd7cddfSDavid du Colombier 	Tcppriv *priv;
7527dd7cddfSDavid du Colombier 	int loop;
7537dd7cddfSDavid du Colombier 
7547dd7cddfSDavid du Colombier 	tcp = a;
7557dd7cddfSDavid du Colombier 	priv = tcp->priv;
7567dd7cddfSDavid du Colombier 
7577dd7cddfSDavid du Colombier 	for(;;) {
758dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, MSPTICK);
7597dd7cddfSDavid du Colombier 
7607dd7cddfSDavid du Colombier 		qlock(&priv->tl);
7617dd7cddfSDavid du Colombier 		timeo = nil;
7627dd7cddfSDavid du Colombier 		loop = 0;
7637dd7cddfSDavid du Colombier 		for(t = priv->timers; t != nil; t = tp) {
7647dd7cddfSDavid du Colombier 			if(loop++ > 10000)
7657dd7cddfSDavid du Colombier 				panic("tcpackproc1");
7667dd7cddfSDavid du Colombier 			tp = t->next;
7679a747e4fSDavid du Colombier  			if(t->state == TcptimerON) {
7687dd7cddfSDavid du Colombier 				t->count--;
7697dd7cddfSDavid du Colombier 				if(t->count == 0) {
7709a747e4fSDavid du Colombier 					timerstate(priv, t, TcptimerDONE);
7717dd7cddfSDavid du Colombier 					t->readynext = timeo;
7727dd7cddfSDavid du Colombier 					timeo = t;
7737dd7cddfSDavid du Colombier 				}
7747dd7cddfSDavid du Colombier 			}
7757dd7cddfSDavid du Colombier 		}
7767dd7cddfSDavid du Colombier 		qunlock(&priv->tl);
7777dd7cddfSDavid du Colombier 
7787dd7cddfSDavid du Colombier 		loop = 0;
7797dd7cddfSDavid du Colombier 		for(t = timeo; t != nil; t = t->readynext) {
7807dd7cddfSDavid du Colombier 			if(loop++ > 10000)
7817dd7cddfSDavid du Colombier 				panic("tcpackproc2");
7829a747e4fSDavid du Colombier 			if(t->state == TcptimerDONE && t->func != nil && !waserror()){
7837dd7cddfSDavid du Colombier 				(*t->func)(t->arg);
7849a747e4fSDavid du Colombier 				poperror();
7859a747e4fSDavid du Colombier 			}
7867dd7cddfSDavid du Colombier 		}
7873ff48bf5SDavid du Colombier 
7883ff48bf5SDavid du Colombier 		limborexmit(tcp);
7897dd7cddfSDavid du Colombier 	}
7907dd7cddfSDavid du Colombier }
7917dd7cddfSDavid du Colombier 
7921ce6c95fSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)7939a747e4fSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
7947dd7cddfSDavid du Colombier {
7957dd7cddfSDavid du Colombier 	if(t == nil || t->start == 0)
7967dd7cddfSDavid du Colombier 		return;
7977dd7cddfSDavid du Colombier 
7987dd7cddfSDavid du Colombier 	qlock(&priv->tl);
7997dd7cddfSDavid du Colombier 	t->count = t->start;
8009a747e4fSDavid du Colombier 	timerstate(priv, t, TcptimerON);
8017dd7cddfSDavid du Colombier 	qunlock(&priv->tl);
8027dd7cddfSDavid du Colombier }
8037dd7cddfSDavid du Colombier 
8041ce6c95fSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8059a747e4fSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8067dd7cddfSDavid du Colombier {
8077dd7cddfSDavid du Colombier 	if(t == nil)
8087dd7cddfSDavid du Colombier 		return;
8097dd7cddfSDavid du Colombier 
8107dd7cddfSDavid du Colombier 	qlock(&priv->tl);
8119a747e4fSDavid du Colombier 	timerstate(priv, t, TcptimerOFF);
8127dd7cddfSDavid du Colombier 	qunlock(&priv->tl);
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier 
8151ce6c95fSDavid du Colombier static int
backoff(int n)8167dd7cddfSDavid du Colombier backoff(int n)
8177dd7cddfSDavid du Colombier {
8187dd7cddfSDavid du Colombier 	return 1 << n;
8197dd7cddfSDavid du Colombier }
8207dd7cddfSDavid du Colombier 
8211ce6c95fSDavid du Colombier static void
localclose(Conv * s,char * reason)8227dd7cddfSDavid du Colombier localclose(Conv *s, char *reason)	/* called with tcb locked */
8237dd7cddfSDavid du Colombier {
8247dd7cddfSDavid du Colombier 	Tcpctl *tcb;
8257dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
8267dd7cddfSDavid du Colombier 
8277dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
8287dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
8297dd7cddfSDavid du Colombier 
83080ee5cbfSDavid du Colombier 	iphtrem(&tpriv->ht, s);
83180ee5cbfSDavid du Colombier 
8327dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->timer);
8337dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
8347dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->acktimer);
8357dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->katimer);
8367dd7cddfSDavid du Colombier 
8377dd7cddfSDavid du Colombier 	/* Flush reassembly queue; nothing more can arrive */
8381ce6c95fSDavid du Colombier 	dumpreseq(tcb);
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 	if(tcb->state == Syn_sent)
8417dd7cddfSDavid du Colombier 		Fsconnected(s, reason);
8427dd7cddfSDavid du Colombier 	if(s->state == Announced)
8437dd7cddfSDavid du Colombier 		wakeup(&s->listenr);
8447dd7cddfSDavid du Colombier 
8457dd7cddfSDavid du Colombier 	qhangup(s->rq, reason);
8467dd7cddfSDavid du Colombier 	qhangup(s->wq, reason);
8477dd7cddfSDavid du Colombier 
8487dd7cddfSDavid du Colombier 	tcpsetstate(s, Closed);
8497dd7cddfSDavid du Colombier }
8507dd7cddfSDavid du Colombier 
8517dd7cddfSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8521ce6c95fSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8531ce6c95fSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8547dd7cddfSDavid du Colombier {
8557dd7cddfSDavid du Colombier 	Ipifc *ifc;
8567dd7cddfSDavid du Colombier 	int mtu;
8577dd7cddfSDavid du Colombier 
8583ff48bf5SDavid du Colombier 	ifc = findipifc(tcp->f, addr, 0);
8593ff48bf5SDavid du Colombier 	switch(version){
8603ff48bf5SDavid du Colombier 	default:
8613ff48bf5SDavid du Colombier 	case V4:
8627dd7cddfSDavid du Colombier 		mtu = DEF_MSS;
8633ff48bf5SDavid du Colombier 		if(ifc != nil)
8643f695129SDavid du Colombier 			mtu = ifc->maxtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8653ff48bf5SDavid du Colombier 		break;
8663ff48bf5SDavid du Colombier 	case V6:
8673ff48bf5SDavid du Colombier 		mtu = DEF_MSS6;
8683ff48bf5SDavid du Colombier 		if(ifc != nil)
8693f695129SDavid du Colombier 			mtu = ifc->maxtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE);
8703ff48bf5SDavid du Colombier 		break;
8713ff48bf5SDavid du Colombier 	}
8721ce6c95fSDavid du Colombier 	/*
8731ce6c95fSDavid du Colombier 	 * set the ws.  it doesn't commit us to anything.
8741ce6c95fSDavid du Colombier 	 * ws is the ultimate limit to the bandwidth-delay product.
8751ce6c95fSDavid du Colombier 	 */
8761ce6c95fSDavid du Colombier 	*scale = Defadvscale;
8773ff48bf5SDavid du Colombier 
8783ff48bf5SDavid du Colombier 	return mtu;
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier 
8811ce6c95fSDavid du Colombier static void
inittcpctl(Conv * s,int mode)88280ee5cbfSDavid du Colombier inittcpctl(Conv *s, int mode)
8837dd7cddfSDavid du Colombier {
8847dd7cddfSDavid du Colombier 	Tcpctl *tcb;
8853ff48bf5SDavid du Colombier 	Tcp4hdr* h4;
8863ff48bf5SDavid du Colombier 	Tcp6hdr* h6;
8877ec5746aSDavid du Colombier 	Tcppriv *tpriv;
8883f695129SDavid du Colombier 	int mss;
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
8917dd7cddfSDavid du Colombier 
8927dd7cddfSDavid du Colombier 	memset(tcb, 0, sizeof(Tcpctl));
8937dd7cddfSDavid du Colombier 
8941ce6c95fSDavid du Colombier 	tcb->ssthresh = QMAX;			/* reset by tcpsetscale() */
89580ee5cbfSDavid du Colombier 	tcb->srtt = tcp_irtt<<LOGAGAIN;
89680ee5cbfSDavid du Colombier 	tcb->mdev = 0;
8977dd7cddfSDavid du Colombier 
89880ee5cbfSDavid du Colombier 	/* setup timers */
8997dd7cddfSDavid du Colombier 	tcb->timer.start = tcp_irtt / MSPTICK;
9007dd7cddfSDavid du Colombier 	tcb->timer.func = tcptimeout;
9017dd7cddfSDavid du Colombier 	tcb->timer.arg = s;
9027dd7cddfSDavid du Colombier 	tcb->rtt_timer.start = MAX_TIME;
9037dd7cddfSDavid du Colombier 	tcb->acktimer.start = TCP_ACK / MSPTICK;
9047dd7cddfSDavid du Colombier 	tcb->acktimer.func = tcpacktimer;
9057dd7cddfSDavid du Colombier 	tcb->acktimer.arg = s;
9067dd7cddfSDavid du Colombier 	tcb->katimer.start = DEF_KAT / MSPTICK;
9077dd7cddfSDavid du Colombier 	tcb->katimer.func = tcpkeepalive;
9087dd7cddfSDavid du Colombier 	tcb->katimer.arg = s;
9097dd7cddfSDavid du Colombier 
9103f695129SDavid du Colombier 	mss = DEF_MSS;
9113f695129SDavid du Colombier 
9127dd7cddfSDavid du Colombier 	/* create a prototype(pseudo) header */
9133ff48bf5SDavid du Colombier 	if(mode != TCP_LISTEN){
9147dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, IPnoaddr) == 0)
9157dd7cddfSDavid du Colombier 			findlocalip(s->p->f, s->laddr, s->raddr);
9167dd7cddfSDavid du Colombier 
9173ff48bf5SDavid du Colombier 		switch(s->ipversion){
9183ff48bf5SDavid du Colombier 		case V4:
9193ff48bf5SDavid du Colombier 			h4 = &tcb->protohdr.tcp4hdr;
9203ff48bf5SDavid du Colombier 			memset(h4, 0, sizeof(*h4));
9213ff48bf5SDavid du Colombier 			h4->proto = IP_TCPPROTO;
9223ff48bf5SDavid du Colombier 			hnputs(h4->tcpsport, s->lport);
9233ff48bf5SDavid du Colombier 			hnputs(h4->tcpdport, s->rport);
9243ff48bf5SDavid du Colombier 			v6tov4(h4->tcpsrc, s->laddr);
9253ff48bf5SDavid du Colombier 			v6tov4(h4->tcpdst, s->raddr);
9263ff48bf5SDavid du Colombier 			break;
9273ff48bf5SDavid du Colombier 		case V6:
9283ff48bf5SDavid du Colombier 			h6 = &tcb->protohdr.tcp6hdr;
9293ff48bf5SDavid du Colombier 			memset(h6, 0, sizeof(*h6));
9303ff48bf5SDavid du Colombier 			h6->proto = IP_TCPPROTO;
9313ff48bf5SDavid du Colombier 			hnputs(h6->tcpsport, s->lport);
9323ff48bf5SDavid du Colombier 			hnputs(h6->tcpdport, s->rport);
9333ff48bf5SDavid du Colombier 			ipmove(h6->tcpsrc, s->laddr);
9343ff48bf5SDavid du Colombier 			ipmove(h6->tcpdst, s->raddr);
9353f695129SDavid du Colombier 			mss = DEF_MSS6;
9363ff48bf5SDavid du Colombier 			break;
9373ff48bf5SDavid du Colombier 		default:
9383ff48bf5SDavid du Colombier 			panic("inittcpctl: version %d", s->ipversion);
9393ff48bf5SDavid du Colombier 		}
9403ff48bf5SDavid du Colombier 	}
9413ff48bf5SDavid du Colombier 
9423f695129SDavid du Colombier 	tcb->mss = tcb->cwind = mss;
9431ce6c95fSDavid du Colombier 	tcb->abcbytes = 0;
9447ec5746aSDavid du Colombier 	tpriv = s->p->priv;
9457ec5746aSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
9463f695129SDavid du Colombier 
9473f695129SDavid du Colombier 	/* default is no window scaling */
9481ce6c95fSDavid du Colombier 	tcpsetscale(s, tcb, 0, 0);
9497dd7cddfSDavid du Colombier }
9507dd7cddfSDavid du Colombier 
95180ee5cbfSDavid du Colombier /*
95280ee5cbfSDavid du Colombier  *  called with s qlocked
95380ee5cbfSDavid du Colombier  */
9541ce6c95fSDavid du Colombier static void
tcpstart(Conv * s,int mode)9553f695129SDavid du Colombier tcpstart(Conv *s, int mode)
9567dd7cddfSDavid du Colombier {
9577dd7cddfSDavid du Colombier 	Tcpctl *tcb;
9587dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
9599a747e4fSDavid du Colombier 	char kpname[KNAMELEN];
9607dd7cddfSDavid du Colombier 
9617dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
9627dd7cddfSDavid du Colombier 
9637dd7cddfSDavid du Colombier 	if(tpriv->ackprocstarted == 0){
9647dd7cddfSDavid du Colombier 		qlock(&tpriv->apl);
9657dd7cddfSDavid du Colombier 		if(tpriv->ackprocstarted == 0){
9661ce6c95fSDavid du Colombier 			snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9677dd7cddfSDavid du Colombier 			kproc(kpname, tcpackproc, s->p);
9687dd7cddfSDavid du Colombier 			tpriv->ackprocstarted = 1;
9697dd7cddfSDavid du Colombier 		}
9707dd7cddfSDavid du Colombier 		qunlock(&tpriv->apl);
9717dd7cddfSDavid du Colombier 	}
9727dd7cddfSDavid du Colombier 
9737dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
9747dd7cddfSDavid du Colombier 
97580ee5cbfSDavid du Colombier 	inittcpctl(s, mode);
9767dd7cddfSDavid du Colombier 
97780ee5cbfSDavid du Colombier 	iphtadd(&tpriv->ht, s);
9787dd7cddfSDavid du Colombier 	switch(mode) {
9797dd7cddfSDavid du Colombier 	case TCP_LISTEN:
98059cc4ca5SDavid du Colombier 		tpriv->stats[PassiveOpens]++;
9817dd7cddfSDavid du Colombier 		tcb->flags |= CLONE;
9827dd7cddfSDavid du Colombier 		tcpsetstate(s, Listen);
9837dd7cddfSDavid du Colombier 		break;
9847dd7cddfSDavid du Colombier 
9857dd7cddfSDavid du Colombier 	case TCP_CONNECT:
98659cc4ca5SDavid du Colombier 		tpriv->stats[ActiveOpens]++;
9877dd7cddfSDavid du Colombier 		tcb->flags |= ACTIVE;
9883f695129SDavid du Colombier 		tcpsndsyn(s, tcb);
9897dd7cddfSDavid du Colombier 		tcpsetstate(s, Syn_sent);
9907dd7cddfSDavid du Colombier 		tcpoutput(s);
9917dd7cddfSDavid du Colombier 		break;
9927dd7cddfSDavid du Colombier 	}
9937dd7cddfSDavid du Colombier }
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)9961ce6c95fSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
9977dd7cddfSDavid du Colombier {
9981ce6c95fSDavid du Colombier 	char *p;
9997dd7cddfSDavid du Colombier 
10001ce6c95fSDavid du Colombier 	p = seprint(buf, e, "%d", flag>>10);	/* Head len */
10017dd7cddfSDavid du Colombier 	if(flag & URG)
10021ce6c95fSDavid du Colombier 		p = seprint(p, e, " URG");
10037dd7cddfSDavid du Colombier 	if(flag & ACK)
10041ce6c95fSDavid du Colombier 		p = seprint(p, e, " ACK");
10057dd7cddfSDavid du Colombier 	if(flag & PSH)
10061ce6c95fSDavid du Colombier 		p = seprint(p, e, " PSH");
10077dd7cddfSDavid du Colombier 	if(flag & RST)
10081ce6c95fSDavid du Colombier 		p = seprint(p, e, " RST");
10097dd7cddfSDavid du Colombier 	if(flag & SYN)
10101ce6c95fSDavid du Colombier 		p = seprint(p, e, " SYN");
10117dd7cddfSDavid du Colombier 	if(flag & FIN)
10121ce6c95fSDavid du Colombier 		p = seprint(p, e, " FIN");
10131ce6c95fSDavid du Colombier 	USED(p);
10147dd7cddfSDavid du Colombier 	return buf;
10157dd7cddfSDavid du Colombier }
10167dd7cddfSDavid du Colombier 
10171ce6c95fSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10183ff48bf5SDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10197dd7cddfSDavid du Colombier {
10207dd7cddfSDavid du Colombier 	int dlen;
10213ff48bf5SDavid du Colombier 	Tcp6hdr *h;
10227dd7cddfSDavid du Colombier 	ushort csum;
10233f695129SDavid du Colombier 	ushort hdrlen, optpad = 0;
10243f695129SDavid du Colombier 	uchar *opt;
10257dd7cddfSDavid du Colombier 
10263ff48bf5SDavid du Colombier 	hdrlen = TCP6_HDRSIZE;
10273f695129SDavid du Colombier 	if(tcph->flags & SYN){
10287dd7cddfSDavid du Colombier 		if(tcph->mss)
10297dd7cddfSDavid du Colombier 			hdrlen += MSS_LENGTH;
10303f695129SDavid du Colombier 		if(tcph->ws)
10313f695129SDavid du Colombier 			hdrlen += WS_LENGTH;
10323f695129SDavid du Colombier 		optpad = hdrlen & 3;
10333f695129SDavid du Colombier 		if(optpad)
10343f695129SDavid du Colombier 			optpad = 4 - optpad;
10353f695129SDavid du Colombier 		hdrlen += optpad;
10363f695129SDavid du Colombier 	}
10377dd7cddfSDavid du Colombier 
10387dd7cddfSDavid du Colombier 	if(data) {
10397dd7cddfSDavid du Colombier 		dlen = blocklen(data);
10403ff48bf5SDavid du Colombier 		data = padblock(data, hdrlen + TCP6_PKT);
10417dd7cddfSDavid du Colombier 		if(data == nil)
10427dd7cddfSDavid du Colombier 			return nil;
10437dd7cddfSDavid du Colombier 	}
10447dd7cddfSDavid du Colombier 	else {
10457dd7cddfSDavid du Colombier 		dlen = 0;
10463ff48bf5SDavid du Colombier 		data = allocb(hdrlen + TCP6_PKT + 64);	/* the 64 pad is to meet mintu's */
10477dd7cddfSDavid du Colombier 		if(data == nil)
10487dd7cddfSDavid du Colombier 			return nil;
10493ff48bf5SDavid du Colombier 		data->wp += hdrlen + TCP6_PKT;
10507dd7cddfSDavid du Colombier 	}
10517dd7cddfSDavid du Colombier 
10527dd7cddfSDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
10533ff48bf5SDavid du Colombier 	h = (Tcp6hdr *)(data->rp);
10543ff48bf5SDavid du Colombier 	memmove(h, ph, TCP6_TCBPHDRSZ);
10553ff48bf5SDavid du Colombier 
10563ff48bf5SDavid du Colombier 	/* compose pseudo tcp header, do cksum calculation */
10573ff48bf5SDavid du Colombier 	hnputl(h->vcf, hdrlen + dlen);
10583ff48bf5SDavid du Colombier 	h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10593ff48bf5SDavid du Colombier 	h->ttl = ph->proto;
10603ff48bf5SDavid du Colombier 
10613ff48bf5SDavid du Colombier 	/* copy in variable bits */
10623ff48bf5SDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
10633ff48bf5SDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
10643ff48bf5SDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10653f695129SDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10663ff48bf5SDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
10673ff48bf5SDavid du Colombier 
10683f695129SDavid du Colombier 	if(tcph->flags & SYN){
10693f695129SDavid du Colombier 		opt = h->tcpopt;
10703ff48bf5SDavid du Colombier 		if(tcph->mss != 0){
10713f695129SDavid du Colombier 			*opt++ = MSSOPT;
10723f695129SDavid du Colombier 			*opt++ = MSS_LENGTH;
10733f695129SDavid du Colombier 			hnputs(opt, tcph->mss);
10743f695129SDavid du Colombier 			opt += 2;
10753ff48bf5SDavid du Colombier 		}
10763f695129SDavid du Colombier 		if(tcph->ws != 0){
10773f695129SDavid du Colombier 			*opt++ = WSOPT;
10783f695129SDavid du Colombier 			*opt++ = WS_LENGTH;
10793f695129SDavid du Colombier 			*opt++ = tcph->ws;
10803f695129SDavid du Colombier 		}
10813f695129SDavid du Colombier 		while(optpad-- > 0)
10823f695129SDavid du Colombier 			*opt++ = NOOPOPT;
10833f695129SDavid du Colombier 	}
10843f695129SDavid du Colombier 
10853ff48bf5SDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
10863ff48bf5SDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
10873ff48bf5SDavid du Colombier 	} else {
10883ff48bf5SDavid du Colombier 		csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
10893ff48bf5SDavid du Colombier 		hnputs(h->tcpcksum, csum);
10903ff48bf5SDavid du Colombier 	}
10913ff48bf5SDavid du Colombier 
10923ff48bf5SDavid du Colombier 	/* move from pseudo header back to normal ip header */
10933ff48bf5SDavid du Colombier 	memset(h->vcf, 0, 4);
10943ff48bf5SDavid du Colombier 	h->vcf[0] = IP_VER6;
10953ff48bf5SDavid du Colombier 	hnputs(h->ploadlen, hdrlen+dlen);
10963ff48bf5SDavid du Colombier 	h->proto = ph->proto;
10973ff48bf5SDavid du Colombier 
10983ff48bf5SDavid du Colombier 	return data;
10993ff48bf5SDavid du Colombier }
11003ff48bf5SDavid du Colombier 
11011ce6c95fSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11023ff48bf5SDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11033ff48bf5SDavid du Colombier {
11043ff48bf5SDavid du Colombier 	int dlen;
11053ff48bf5SDavid du Colombier 	Tcp4hdr *h;
11063ff48bf5SDavid du Colombier 	ushort csum;
11073f695129SDavid du Colombier 	ushort hdrlen, optpad = 0;
11083f695129SDavid du Colombier 	uchar *opt;
11093ff48bf5SDavid du Colombier 
11103ff48bf5SDavid du Colombier 	hdrlen = TCP4_HDRSIZE;
11113f695129SDavid du Colombier 	if(tcph->flags & SYN){
11123ff48bf5SDavid du Colombier 		if(tcph->mss)
11133ff48bf5SDavid du Colombier 			hdrlen += MSS_LENGTH;
11141ce6c95fSDavid du Colombier 		if(1)
11153f695129SDavid du Colombier 			hdrlen += WS_LENGTH;
11163f695129SDavid du Colombier 		optpad = hdrlen & 3;
11173f695129SDavid du Colombier 		if(optpad)
11183f695129SDavid du Colombier 			optpad = 4 - optpad;
11193f695129SDavid du Colombier 		hdrlen += optpad;
11203f695129SDavid du Colombier 	}
11213ff48bf5SDavid du Colombier 
11223ff48bf5SDavid du Colombier 	if(data) {
11233ff48bf5SDavid du Colombier 		dlen = blocklen(data);
11243ff48bf5SDavid du Colombier 		data = padblock(data, hdrlen + TCP4_PKT);
11253ff48bf5SDavid du Colombier 		if(data == nil)
11263ff48bf5SDavid du Colombier 			return nil;
11273ff48bf5SDavid du Colombier 	}
11283ff48bf5SDavid du Colombier 	else {
11293ff48bf5SDavid du Colombier 		dlen = 0;
11303ff48bf5SDavid du Colombier 		data = allocb(hdrlen + TCP4_PKT + 64);	/* the 64 pad is to meet mintu's */
11313ff48bf5SDavid du Colombier 		if(data == nil)
11323ff48bf5SDavid du Colombier 			return nil;
11333ff48bf5SDavid du Colombier 		data->wp += hdrlen + TCP4_PKT;
11343ff48bf5SDavid du Colombier 	}
11353ff48bf5SDavid du Colombier 
11363ff48bf5SDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
11373ff48bf5SDavid du Colombier 	h = (Tcp4hdr *)(data->rp);
11383ff48bf5SDavid du Colombier 	memmove(h, ph, TCP4_TCBPHDRSZ);
11397dd7cddfSDavid du Colombier 
11407dd7cddfSDavid du Colombier 	/* copy in variable bits */
11417dd7cddfSDavid du Colombier 	hnputs(h->tcplen, hdrlen + dlen);
11427dd7cddfSDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
11437dd7cddfSDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
11447dd7cddfSDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11453f695129SDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11467dd7cddfSDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
11477dd7cddfSDavid du Colombier 
11483f695129SDavid du Colombier 	if(tcph->flags & SYN){
11493f695129SDavid du Colombier 		opt = h->tcpopt;
11507dd7cddfSDavid du Colombier 		if(tcph->mss != 0){
11513f695129SDavid du Colombier 			*opt++ = MSSOPT;
11523f695129SDavid du Colombier 			*opt++ = MSS_LENGTH;
11533f695129SDavid du Colombier 			hnputs(opt, tcph->mss);
11543f695129SDavid du Colombier 			opt += 2;
11557dd7cddfSDavid du Colombier 		}
11561ce6c95fSDavid du Colombier 		/* always offer.  rfc1323 §2.2 */
11571ce6c95fSDavid du Colombier 		if(1){
11583f695129SDavid du Colombier 			*opt++ = WSOPT;
11593f695129SDavid du Colombier 			*opt++ = WS_LENGTH;
11603f695129SDavid du Colombier 			*opt++ = tcph->ws;
11613f695129SDavid du Colombier 		}
11623f695129SDavid du Colombier 		while(optpad-- > 0)
11633f695129SDavid du Colombier 			*opt++ = NOOPOPT;
11643f695129SDavid du Colombier 	}
11653f695129SDavid du Colombier 
116659cc4ca5SDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
116759cc4ca5SDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
116859cc4ca5SDavid du Colombier 	} else {
11693ff48bf5SDavid du Colombier 		csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
11707dd7cddfSDavid du Colombier 		hnputs(h->tcpcksum, csum);
117159cc4ca5SDavid du Colombier 	}
11727dd7cddfSDavid du Colombier 
11737dd7cddfSDavid du Colombier 	return data;
11747dd7cddfSDavid du Colombier }
11757dd7cddfSDavid du Colombier 
11761ce6c95fSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)11773ff48bf5SDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
11787dd7cddfSDavid du Colombier {
11793ff48bf5SDavid du Colombier 	Tcp6hdr *h;
11807dd7cddfSDavid du Colombier 	uchar *optr;
11817dd7cddfSDavid du Colombier 	ushort hdrlen;
11827dd7cddfSDavid du Colombier 	ushort optlen;
11837dd7cddfSDavid du Colombier 	int n;
11847dd7cddfSDavid du Colombier 
11853ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
11867dd7cddfSDavid du Colombier 	if(*bpp == nil)
11877dd7cddfSDavid du Colombier 		return -1;
11887dd7cddfSDavid du Colombier 
11893ff48bf5SDavid du Colombier 	h = (Tcp6hdr *)((*bpp)->rp);
11907dd7cddfSDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
11917dd7cddfSDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
11927dd7cddfSDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
11937dd7cddfSDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
11943f695129SDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
11953ff48bf5SDavid du Colombier 	if(hdrlen < TCP6_HDRSIZE) {
11967dd7cddfSDavid du Colombier 		freeblist(*bpp);
11977dd7cddfSDavid du Colombier 		return -1;
11987dd7cddfSDavid du Colombier 	}
11997dd7cddfSDavid du Colombier 
12007dd7cddfSDavid du Colombier 	tcph->flags = h->tcpflag[1];
12017dd7cddfSDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12027dd7cddfSDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12037dd7cddfSDavid du Colombier 	tcph->mss = 0;
12043f695129SDavid du Colombier 	tcph->ws = 0;
12051ce6c95fSDavid du Colombier 	tcph->update = 0;
12063ff48bf5SDavid du Colombier 	tcph->len = nhgets(h->ploadlen) - hdrlen;
12077dd7cddfSDavid du Colombier 
12083ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12097dd7cddfSDavid du Colombier 	if(*bpp == nil)
12107dd7cddfSDavid du Colombier 		return -1;
12117dd7cddfSDavid du Colombier 
12127dd7cddfSDavid du Colombier 	optr = h->tcpopt;
12133ff48bf5SDavid du Colombier 	n = hdrlen - TCP6_HDRSIZE;
12147dd7cddfSDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
12157dd7cddfSDavid du Colombier 		if(*optr == NOOPOPT) {
12167dd7cddfSDavid du Colombier 			n--;
12177dd7cddfSDavid du Colombier 			optr++;
12187dd7cddfSDavid du Colombier 			continue;
12197dd7cddfSDavid du Colombier 		}
12207dd7cddfSDavid du Colombier 		optlen = optr[1];
12217dd7cddfSDavid du Colombier 		if(optlen < 2 || optlen > n)
12227dd7cddfSDavid du Colombier 			break;
12237dd7cddfSDavid du Colombier 		switch(*optr) {
12247dd7cddfSDavid du Colombier 		case MSSOPT:
12257dd7cddfSDavid du Colombier 			if(optlen == MSS_LENGTH)
12267dd7cddfSDavid du Colombier 				tcph->mss = nhgets(optr+2);
12277dd7cddfSDavid du Colombier 			break;
12283f695129SDavid du Colombier 		case WSOPT:
12293f695129SDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
12301ce6c95fSDavid du Colombier 				tcph->ws = *(optr+2);
12313f695129SDavid du Colombier 			break;
12327dd7cddfSDavid du Colombier 		}
12337dd7cddfSDavid du Colombier 		n -= optlen;
12347dd7cddfSDavid du Colombier 		optr += optlen;
12357dd7cddfSDavid du Colombier 	}
12367dd7cddfSDavid du Colombier 	return hdrlen;
12377dd7cddfSDavid du Colombier }
12387dd7cddfSDavid du Colombier 
12391ce6c95fSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12403ff48bf5SDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12413ff48bf5SDavid du Colombier {
12423ff48bf5SDavid du Colombier 	Tcp4hdr *h;
12433ff48bf5SDavid du Colombier 	uchar *optr;
12443ff48bf5SDavid du Colombier 	ushort hdrlen;
12453ff48bf5SDavid du Colombier 	ushort optlen;
12463ff48bf5SDavid du Colombier 	int n;
12473ff48bf5SDavid du Colombier 
12483ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12493ff48bf5SDavid du Colombier 	if(*bpp == nil)
12503ff48bf5SDavid du Colombier 		return -1;
12513ff48bf5SDavid du Colombier 
12523ff48bf5SDavid du Colombier 	h = (Tcp4hdr *)((*bpp)->rp);
12533ff48bf5SDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
12543ff48bf5SDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
12553ff48bf5SDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
12563ff48bf5SDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
12573ff48bf5SDavid du Colombier 
12583f695129SDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
12593ff48bf5SDavid du Colombier 	if(hdrlen < TCP4_HDRSIZE) {
12603ff48bf5SDavid du Colombier 		freeblist(*bpp);
12613ff48bf5SDavid du Colombier 		return -1;
12623ff48bf5SDavid du Colombier 	}
12633ff48bf5SDavid du Colombier 
12643ff48bf5SDavid du Colombier 	tcph->flags = h->tcpflag[1];
12653ff48bf5SDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12663ff48bf5SDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12673ff48bf5SDavid du Colombier 	tcph->mss = 0;
12683f695129SDavid du Colombier 	tcph->ws = 0;
12691ce6c95fSDavid du Colombier 	tcph->update = 0;
12703ff48bf5SDavid du Colombier 	tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
12713ff48bf5SDavid du Colombier 
12723ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
12733ff48bf5SDavid du Colombier 	if(*bpp == nil)
12743ff48bf5SDavid du Colombier 		return -1;
12753ff48bf5SDavid du Colombier 
12763ff48bf5SDavid du Colombier 	optr = h->tcpopt;
12773ff48bf5SDavid du Colombier 	n = hdrlen - TCP4_HDRSIZE;
12783ff48bf5SDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
12793ff48bf5SDavid du Colombier 		if(*optr == NOOPOPT) {
12803ff48bf5SDavid du Colombier 			n--;
12813ff48bf5SDavid du Colombier 			optr++;
12823ff48bf5SDavid du Colombier 			continue;
12833ff48bf5SDavid du Colombier 		}
12843ff48bf5SDavid du Colombier 		optlen = optr[1];
12853ff48bf5SDavid du Colombier 		if(optlen < 2 || optlen > n)
12863ff48bf5SDavid du Colombier 			break;
12873ff48bf5SDavid du Colombier 		switch(*optr) {
12883ff48bf5SDavid du Colombier 		case MSSOPT:
12891ce6c95fSDavid du Colombier 			if(optlen == MSS_LENGTH)
12903ff48bf5SDavid du Colombier 				tcph->mss = nhgets(optr+2);
12913ff48bf5SDavid du Colombier 			break;
12923f695129SDavid du Colombier 		case WSOPT:
12933f695129SDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
12941ce6c95fSDavid du Colombier 				tcph->ws = *(optr+2);
12953f695129SDavid du Colombier 			break;
12963ff48bf5SDavid du Colombier 		}
12973ff48bf5SDavid du Colombier 		n -= optlen;
12983ff48bf5SDavid du Colombier 		optr += optlen;
12993ff48bf5SDavid du Colombier 	}
13003ff48bf5SDavid du Colombier 	return hdrlen;
13013ff48bf5SDavid du Colombier }
13023ff48bf5SDavid du Colombier 
13033ff48bf5SDavid du Colombier /*
13041ce6c95fSDavid du Colombier  *  For outgoing calls, generate an initial sequence
13053ff48bf5SDavid du Colombier  *  number and put a SYN on the send queue
13063ff48bf5SDavid du Colombier  */
13071ce6c95fSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13083f695129SDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13097dd7cddfSDavid du Colombier {
13107ec5746aSDavid du Colombier 	Tcppriv *tpriv;
13117ec5746aSDavid du Colombier 
13127dd7cddfSDavid du Colombier 	tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13137dd7cddfSDavid du Colombier 	tcb->rttseq = tcb->iss;
13147dd7cddfSDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
13157dd7cddfSDavid du Colombier 	tcb->snd.una = tcb->iss;
13161ce6c95fSDavid du Colombier 	tcb->snd.rxt = tcb->iss;
13177dd7cddfSDavid du Colombier 	tcb->snd.ptr = tcb->rttseq;
13187dd7cddfSDavid du Colombier 	tcb->snd.nxt = tcb->rttseq;
131980ee5cbfSDavid du Colombier 	tcb->flgcnt++;
13207dd7cddfSDavid du Colombier 	tcb->flags |= FORCE;
13213ff48bf5SDavid du Colombier 	tcb->sndsyntime = NOW;
13223f695129SDavid du Colombier 
13233f695129SDavid du Colombier 	/* set desired mss and scale */
13243f695129SDavid du Colombier 	tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13257ec5746aSDavid du Colombier 	tpriv = s->p->priv;
13267ec5746aSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
13277dd7cddfSDavid du Colombier }
13287dd7cddfSDavid du Colombier 
13297dd7cddfSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13303ff48bf5SDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13317dd7cddfSDavid du Colombier {
13327dd7cddfSDavid du Colombier 	Block *hbp;
13337dd7cddfSDavid du Colombier 	uchar rflags;
13347dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
13353ff48bf5SDavid du Colombier 	Tcp4hdr ph4;
13363ff48bf5SDavid du Colombier 	Tcp6hdr ph6;
13373ff48bf5SDavid du Colombier 
133884363d68SDavid du Colombier 	netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13397dd7cddfSDavid du Colombier 
13407dd7cddfSDavid du Colombier 	tpriv = tcp->priv;
13417dd7cddfSDavid du Colombier 
13427dd7cddfSDavid du Colombier 	if(seg->flags & RST)
13437dd7cddfSDavid du Colombier 		return;
13447dd7cddfSDavid du Colombier 
13457dd7cddfSDavid du Colombier 	/* make pseudo header */
13463ff48bf5SDavid du Colombier 	switch(version) {
13473ff48bf5SDavid du Colombier 	case V4:
13483ff48bf5SDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
13493ff48bf5SDavid du Colombier 		ph4.vihl = IP_VER4;
13503ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpsrc, dest);
13513ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpdst, source);
13523ff48bf5SDavid du Colombier 		ph4.proto = IP_TCPPROTO;
13533ff48bf5SDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
13543ff48bf5SDavid du Colombier 		hnputs(ph4.tcpsport, seg->dest);
13553ff48bf5SDavid du Colombier 		hnputs(ph4.tcpdport, seg->source);
13563ff48bf5SDavid du Colombier 		break;
13573ff48bf5SDavid du Colombier 	case V6:
13583ff48bf5SDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
13593ff48bf5SDavid du Colombier 		ph6.vcf[0] = IP_VER6;
13603ff48bf5SDavid du Colombier 		ipmove(ph6.tcpsrc, dest);
13613ff48bf5SDavid du Colombier 		ipmove(ph6.tcpdst, source);
13623ff48bf5SDavid du Colombier 		ph6.proto = IP_TCPPROTO;
13633ff48bf5SDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13643ff48bf5SDavid du Colombier 		hnputs(ph6.tcpsport, seg->dest);
13653ff48bf5SDavid du Colombier 		hnputs(ph6.tcpdport, seg->source);
13663ff48bf5SDavid du Colombier 		break;
13673ff48bf5SDavid du Colombier 	default:
13683ff48bf5SDavid du Colombier 		panic("sndrst: version %d", version);
13693ff48bf5SDavid du Colombier 	}
13707dd7cddfSDavid du Colombier 
137159cc4ca5SDavid du Colombier 	tpriv->stats[OutRsts]++;
13727dd7cddfSDavid du Colombier 	rflags = RST;
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier 	/* convince the other end that this reset is in band */
13757dd7cddfSDavid du Colombier 	if(seg->flags & ACK) {
13767dd7cddfSDavid du Colombier 		seg->seq = seg->ack;
13777dd7cddfSDavid du Colombier 		seg->ack = 0;
13787dd7cddfSDavid du Colombier 	}
13797dd7cddfSDavid du Colombier 	else {
13807dd7cddfSDavid du Colombier 		rflags |= ACK;
13817dd7cddfSDavid du Colombier 		seg->ack = seg->seq;
13827dd7cddfSDavid du Colombier 		seg->seq = 0;
13837dd7cddfSDavid du Colombier 		if(seg->flags & SYN)
13847dd7cddfSDavid du Colombier 			seg->ack++;
13857dd7cddfSDavid du Colombier 		seg->ack += length;
13867dd7cddfSDavid du Colombier 		if(seg->flags & FIN)
13877dd7cddfSDavid du Colombier 			seg->ack++;
13887dd7cddfSDavid du Colombier 	}
13897dd7cddfSDavid du Colombier 	seg->flags = rflags;
13907dd7cddfSDavid du Colombier 	seg->wnd = 0;
13917dd7cddfSDavid du Colombier 	seg->urg = 0;
13927dd7cddfSDavid du Colombier 	seg->mss = 0;
13933f695129SDavid du Colombier 	seg->ws = 0;
13943ff48bf5SDavid du Colombier 	switch(version) {
13953ff48bf5SDavid du Colombier 	case V4:
13963ff48bf5SDavid du Colombier 		hbp = htontcp4(seg, nil, &ph4, nil);
13977dd7cddfSDavid du Colombier 		if(hbp == nil)
13987dd7cddfSDavid du Colombier 			return;
1399a6a9e072SDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14003ff48bf5SDavid du Colombier 		break;
14013ff48bf5SDavid du Colombier 	case V6:
14023ff48bf5SDavid du Colombier 		hbp = htontcp6(seg, nil, &ph6, nil);
14033ff48bf5SDavid du Colombier 		if(hbp == nil)
14043ff48bf5SDavid du Colombier 			return;
1405a6a9e072SDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14063ff48bf5SDavid du Colombier 		break;
14073ff48bf5SDavid du Colombier 	default:
14083ff48bf5SDavid du Colombier 		panic("sndrst2: version %d", version);
14093ff48bf5SDavid du Colombier 	}
14107dd7cddfSDavid du Colombier }
14117dd7cddfSDavid du Colombier 
14127dd7cddfSDavid du Colombier /*
14137dd7cddfSDavid du Colombier  *  send a reset to the remote side and close the conversation
141480ee5cbfSDavid du Colombier  *  called with s qlocked
14157dd7cddfSDavid du Colombier  */
14161ce6c95fSDavid du Colombier static char*
tcphangup(Conv * s)14177dd7cddfSDavid du Colombier tcphangup(Conv *s)
14187dd7cddfSDavid du Colombier {
14197dd7cddfSDavid du Colombier 	Tcp seg;
14207dd7cddfSDavid du Colombier 	Tcpctl *tcb;
14217dd7cddfSDavid du Colombier 	Block *hbp;
14227dd7cddfSDavid du Colombier 
14237dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
142480ee5cbfSDavid du Colombier 	if(waserror())
14257dd7cddfSDavid du Colombier 		return commonerror();
1426c02046c4SDavid du Colombier 	if(ipcmp(s->raddr, IPnoaddr) != 0) {
14275fab9909SDavid du Colombier 		if(!waserror()){
142810ae047aSDavid du Colombier 			memset(&seg, 0, sizeof seg);
14297dd7cddfSDavid du Colombier 			seg.flags = RST | ACK;
14307dd7cddfSDavid du Colombier 			seg.ack = tcb->rcv.nxt;
14311ce6c95fSDavid du Colombier 			tcb->rcv.ackptr = seg.ack;
14327dd7cddfSDavid du Colombier 			seg.seq = tcb->snd.ptr;
14337dd7cddfSDavid du Colombier 			seg.wnd = 0;
14347dd7cddfSDavid du Colombier 			seg.urg = 0;
14357dd7cddfSDavid du Colombier 			seg.mss = 0;
14363f695129SDavid du Colombier 			seg.ws = 0;
14373ff48bf5SDavid du Colombier 			switch(s->ipversion) {
14383ff48bf5SDavid du Colombier 			case V4:
14393ff48bf5SDavid du Colombier 				tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14403ff48bf5SDavid du Colombier 				hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
1441a6a9e072SDavid du Colombier 				ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14423ff48bf5SDavid du Colombier 				break;
14433ff48bf5SDavid du Colombier 			case V6:
14443ff48bf5SDavid du Colombier 				tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14453ff48bf5SDavid du Colombier 				hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
1446a6a9e072SDavid du Colombier 				ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14473ff48bf5SDavid du Colombier 				break;
14483ff48bf5SDavid du Colombier 			default:
14493ff48bf5SDavid du Colombier 				panic("tcphangup: version %d", s->ipversion);
14503ff48bf5SDavid du Colombier 			}
14515fab9909SDavid du Colombier 			poperror();
14525fab9909SDavid du Colombier 		}
14537dd7cddfSDavid du Colombier 	}
14547dd7cddfSDavid du Colombier 	localclose(s, nil);
14557dd7cddfSDavid du Colombier 	poperror();
14567dd7cddfSDavid du Colombier 	return nil;
14577dd7cddfSDavid du Colombier }
14587dd7cddfSDavid du Colombier 
14593ff48bf5SDavid du Colombier /*
14603ff48bf5SDavid du Colombier  *  (re)send a SYN ACK
14613ff48bf5SDavid du Colombier  */
14621ce6c95fSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)14633ff48bf5SDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
14643ff48bf5SDavid du Colombier {
14653ff48bf5SDavid du Colombier 	Block *hbp;
14663ff48bf5SDavid du Colombier 	Tcp4hdr ph4;
14673ff48bf5SDavid du Colombier 	Tcp6hdr ph6;
14683ff48bf5SDavid du Colombier 	Tcp seg;
14691ce6c95fSDavid du Colombier 	uint scale;
14703ff48bf5SDavid du Colombier 
14713ff48bf5SDavid du Colombier 	/* make pseudo header */
14723ff48bf5SDavid du Colombier 	switch(lp->version) {
14733ff48bf5SDavid du Colombier 	case V4:
14743ff48bf5SDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
14753ff48bf5SDavid du Colombier 		ph4.vihl = IP_VER4;
14763ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpsrc, lp->laddr);
14773ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpdst, lp->raddr);
14783ff48bf5SDavid du Colombier 		ph4.proto = IP_TCPPROTO;
14793ff48bf5SDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
14803ff48bf5SDavid du Colombier 		hnputs(ph4.tcpsport, lp->lport);
14813ff48bf5SDavid du Colombier 		hnputs(ph4.tcpdport, lp->rport);
14823ff48bf5SDavid du Colombier 		break;
14833ff48bf5SDavid du Colombier 	case V6:
14843ff48bf5SDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
14853ff48bf5SDavid du Colombier 		ph6.vcf[0] = IP_VER6;
14863ff48bf5SDavid du Colombier 		ipmove(ph6.tcpsrc, lp->laddr);
14873ff48bf5SDavid du Colombier 		ipmove(ph6.tcpdst, lp->raddr);
14883ff48bf5SDavid du Colombier 		ph6.proto = IP_TCPPROTO;
14893ff48bf5SDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
14903ff48bf5SDavid du Colombier 		hnputs(ph6.tcpsport, lp->lport);
14913ff48bf5SDavid du Colombier 		hnputs(ph6.tcpdport, lp->rport);
14923ff48bf5SDavid du Colombier 		break;
14933ff48bf5SDavid du Colombier 	default:
14943ff48bf5SDavid du Colombier 		panic("sndrst: version %d", lp->version);
14953ff48bf5SDavid du Colombier 	}
14963ff48bf5SDavid du Colombier 
149710ae047aSDavid du Colombier 	memset(&seg, 0, sizeof seg);
14983ff48bf5SDavid du Colombier 	seg.seq = lp->iss;
14993ff48bf5SDavid du Colombier 	seg.ack = lp->irs+1;
15003ff48bf5SDavid du Colombier 	seg.flags = SYN|ACK;
15013ff48bf5SDavid du Colombier 	seg.urg = 0;
15023f695129SDavid du Colombier 	seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15033ff48bf5SDavid du Colombier 	seg.wnd = QMAX;
15043ff48bf5SDavid du Colombier 
15053f695129SDavid du Colombier 	/* if the other side set scale, we should too */
15063f695129SDavid du Colombier 	if(lp->rcvscale){
15073f695129SDavid du Colombier 		seg.ws = scale;
15083f695129SDavid du Colombier 		lp->sndscale = scale;
15093f695129SDavid du Colombier 	} else {
15103f695129SDavid du Colombier 		seg.ws = 0;
15113f695129SDavid du Colombier 		lp->sndscale = 0;
15123f695129SDavid du Colombier 	}
15133f695129SDavid du Colombier 
15143ff48bf5SDavid du Colombier 	switch(lp->version) {
15153ff48bf5SDavid du Colombier 	case V4:
15163ff48bf5SDavid du Colombier 		hbp = htontcp4(&seg, nil, &ph4, nil);
15173ff48bf5SDavid du Colombier 		if(hbp == nil)
15183ff48bf5SDavid du Colombier 			return -1;
1519a6a9e072SDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15203ff48bf5SDavid du Colombier 		break;
15213ff48bf5SDavid du Colombier 	case V6:
15223ff48bf5SDavid du Colombier 		hbp = htontcp6(&seg, nil, &ph6, nil);
15233ff48bf5SDavid du Colombier 		if(hbp == nil)
15243ff48bf5SDavid du Colombier 			return -1;
1525a6a9e072SDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15263ff48bf5SDavid du Colombier 		break;
15273ff48bf5SDavid du Colombier 	default:
15283ff48bf5SDavid du Colombier 		panic("sndsnack: version %d", lp->version);
15293ff48bf5SDavid du Colombier 	}
15303ff48bf5SDavid du Colombier 	lp->lastsend = NOW;
15313ff48bf5SDavid du Colombier 	return 0;
15323ff48bf5SDavid du Colombier }
15333ff48bf5SDavid du Colombier 
15343ff48bf5SDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15353ff48bf5SDavid du Colombier 
15363ff48bf5SDavid du Colombier /*
15373ff48bf5SDavid du Colombier  *  put a call into limbo and respond with a SYN ACK
15383ff48bf5SDavid du Colombier  *
15393ff48bf5SDavid du Colombier  *  called with proto locked
15403ff48bf5SDavid du Colombier  */
15413ff48bf5SDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15423ff48bf5SDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15433ff48bf5SDavid du Colombier {
15443ff48bf5SDavid du Colombier 	Limbo *lp, **l;
15453ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
15463ff48bf5SDavid du Colombier 	int h;
15473ff48bf5SDavid du Colombier 
15483ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
15493ff48bf5SDavid du Colombier 	h = hashipa(source, seg->source);
15503ff48bf5SDavid du Colombier 
15513ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15523ff48bf5SDavid du Colombier 		lp = *l;
15533ff48bf5SDavid du Colombier 		if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15543ff48bf5SDavid du Colombier 			continue;
15553ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, source) != 0)
15563ff48bf5SDavid du Colombier 			continue;
15573ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dest) != 0)
15583ff48bf5SDavid du Colombier 			continue;
15593ff48bf5SDavid du Colombier 
1560ef9eff0bSDavid du Colombier 		/* each new SYN restarts the retransmits */
15613ff48bf5SDavid du Colombier 		lp->irs = seg->seq;
15623ff48bf5SDavid du Colombier 		break;
15633ff48bf5SDavid du Colombier 	}
15643ff48bf5SDavid du Colombier 	lp = *l;
15653ff48bf5SDavid du Colombier 	if(lp == nil){
15663ff48bf5SDavid du Colombier 		if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
15673ff48bf5SDavid du Colombier 			lp = tpriv->lht[h];
15683ff48bf5SDavid du Colombier 			tpriv->lht[h] = lp->next;
15693ff48bf5SDavid du Colombier 			lp->next = nil;
15703ff48bf5SDavid du Colombier 		} else {
15713ff48bf5SDavid du Colombier 			lp = malloc(sizeof(*lp));
15723ff48bf5SDavid du Colombier 			if(lp == nil)
15733ff48bf5SDavid du Colombier 				return;
15743ff48bf5SDavid du Colombier 			tpriv->nlimbo++;
15753ff48bf5SDavid du Colombier 		}
15763ff48bf5SDavid du Colombier 		*l = lp;
15773ff48bf5SDavid du Colombier 		lp->version = version;
15783ff48bf5SDavid du Colombier 		ipmove(lp->laddr, dest);
15793ff48bf5SDavid du Colombier 		ipmove(lp->raddr, source);
15803ff48bf5SDavid du Colombier 		lp->lport = seg->dest;
15813ff48bf5SDavid du Colombier 		lp->rport = seg->source;
15823ff48bf5SDavid du Colombier 		lp->mss = seg->mss;
15833f695129SDavid du Colombier 		lp->rcvscale = seg->ws;
15843ff48bf5SDavid du Colombier 		lp->irs = seg->seq;
15853ff48bf5SDavid du Colombier 		lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
15863ff48bf5SDavid du Colombier 	}
15873ff48bf5SDavid du Colombier 
15883ff48bf5SDavid du Colombier 	if(sndsynack(s->p, lp) < 0){
15893ff48bf5SDavid du Colombier 		*l = lp->next;
15903ff48bf5SDavid du Colombier 		tpriv->nlimbo--;
15913ff48bf5SDavid du Colombier 		free(lp);
15923ff48bf5SDavid du Colombier 	}
15933ff48bf5SDavid du Colombier }
15943ff48bf5SDavid du Colombier 
15953ff48bf5SDavid du Colombier /*
15963ff48bf5SDavid du Colombier  *  resend SYN ACK's once every SYNACK_RXTIMER ms.
15973ff48bf5SDavid du Colombier  */
15983ff48bf5SDavid du Colombier static void
limborexmit(Proto * tcp)15993ff48bf5SDavid du Colombier limborexmit(Proto *tcp)
16003ff48bf5SDavid du Colombier {
16013ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
16023ff48bf5SDavid du Colombier 	Limbo **l, *lp;
16033ff48bf5SDavid du Colombier 	int h;
16043ff48bf5SDavid du Colombier 	int seen;
16053ff48bf5SDavid du Colombier 	ulong now;
16063ff48bf5SDavid du Colombier 
16073ff48bf5SDavid du Colombier 	tpriv = tcp->priv;
16083ff48bf5SDavid du Colombier 
16093ff48bf5SDavid du Colombier 	if(!canqlock(tcp))
16103ff48bf5SDavid du Colombier 		return;
16113ff48bf5SDavid du Colombier 	seen = 0;
16123ff48bf5SDavid du Colombier 	now = NOW;
16133ff48bf5SDavid du Colombier 	for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16143ff48bf5SDavid du Colombier 		for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16153ff48bf5SDavid du Colombier 			lp = *l;
16163ff48bf5SDavid du Colombier 			seen++;
16173ff48bf5SDavid du Colombier 			if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16183ff48bf5SDavid du Colombier 				continue;
16193ff48bf5SDavid du Colombier 
16203ff48bf5SDavid du Colombier 			/* time it out after 1 second */
16213ff48bf5SDavid du Colombier 			if(++(lp->rexmits) > 5){
16223ff48bf5SDavid du Colombier 				tpriv->nlimbo--;
16233ff48bf5SDavid du Colombier 				*l = lp->next;
16243ff48bf5SDavid du Colombier 				free(lp);
16253ff48bf5SDavid du Colombier 				continue;
16263ff48bf5SDavid du Colombier 			}
16273ff48bf5SDavid du Colombier 
16283ff48bf5SDavid du Colombier 			/* if we're being attacked, don't bother resending SYN ACK's */
16293ff48bf5SDavid du Colombier 			if(tpriv->nlimbo > 100)
16303ff48bf5SDavid du Colombier 				continue;
16313ff48bf5SDavid du Colombier 
16323ff48bf5SDavid du Colombier 			if(sndsynack(tcp, lp) < 0){
16333ff48bf5SDavid du Colombier 				tpriv->nlimbo--;
16343ff48bf5SDavid du Colombier 				*l = lp->next;
16353ff48bf5SDavid du Colombier 				free(lp);
16363ff48bf5SDavid du Colombier 				continue;
16373ff48bf5SDavid du Colombier 			}
16383ff48bf5SDavid du Colombier 
16393ff48bf5SDavid du Colombier 			l = &lp->next;
16403ff48bf5SDavid du Colombier 		}
16413ff48bf5SDavid du Colombier 	}
16423ff48bf5SDavid du Colombier 	qunlock(tcp);
16433ff48bf5SDavid du Colombier }
16443ff48bf5SDavid du Colombier 
16453ff48bf5SDavid du Colombier /*
16463ff48bf5SDavid du Colombier  *  lookup call in limbo.  if found, throw it out.
16473ff48bf5SDavid du Colombier  *
16483ff48bf5SDavid du Colombier  *  called with proto locked
16493ff48bf5SDavid du Colombier  */
16503ff48bf5SDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16513ff48bf5SDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16523ff48bf5SDavid du Colombier {
16533ff48bf5SDavid du Colombier 	Limbo *lp, **l;
16543ff48bf5SDavid du Colombier 	int h;
16553ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
16563ff48bf5SDavid du Colombier 
16573ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
16583ff48bf5SDavid du Colombier 
16593ff48bf5SDavid du Colombier 	/* find a call in limbo */
16603ff48bf5SDavid du Colombier 	h = hashipa(src, segp->source);
16613ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
16623ff48bf5SDavid du Colombier 		lp = *l;
16633ff48bf5SDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
16643ff48bf5SDavid du Colombier 			continue;
16653ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
16663ff48bf5SDavid du Colombier 			continue;
16673ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
16683ff48bf5SDavid du Colombier 			continue;
16693ff48bf5SDavid du Colombier 
16703ff48bf5SDavid du Colombier 		/* RST can only follow the SYN */
16713ff48bf5SDavid du Colombier 		if(segp->seq == lp->irs+1){
16723ff48bf5SDavid du Colombier 			tpriv->nlimbo--;
16733ff48bf5SDavid du Colombier 			*l = lp->next;
16743ff48bf5SDavid du Colombier 			free(lp);
16753ff48bf5SDavid du Colombier 		}
16763ff48bf5SDavid du Colombier 		break;
16773ff48bf5SDavid du Colombier 	}
16783ff48bf5SDavid du Colombier }
16793ff48bf5SDavid du Colombier 
16801ce6c95fSDavid du Colombier static void
initialwindow(Tcpctl * tcb)16811ce6c95fSDavid du Colombier initialwindow(Tcpctl *tcb)
16821ce6c95fSDavid du Colombier {
16831ce6c95fSDavid du Colombier 	/* RFC 3390 initial window */
16841ce6c95fSDavid du Colombier 	if(tcb->mss < 1095)
16851ce6c95fSDavid du Colombier 		tcb->cwind = 4*tcb->mss;
16861ce6c95fSDavid du Colombier 	else if(tcb->mss < 2190)
16871ce6c95fSDavid du Colombier 		tcb->cwind = 2*2190;
16881ce6c95fSDavid du Colombier 	else
16891ce6c95fSDavid du Colombier 		tcb->cwind = 2*tcb->mss;
16901ce6c95fSDavid du Colombier }
16911ce6c95fSDavid du Colombier 
16923ff48bf5SDavid du Colombier /*
16933f695129SDavid du Colombier  *  come here when we finally get an ACK to our SYN-ACK.
16943ff48bf5SDavid du Colombier  *  lookup call in limbo.  if found, create a new conversation
16953ff48bf5SDavid du Colombier  *
16963ff48bf5SDavid du Colombier  *  called with proto locked
16973ff48bf5SDavid du Colombier  */
16983ff48bf5SDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16993ff48bf5SDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17007dd7cddfSDavid du Colombier {
17017dd7cddfSDavid du Colombier 	Conv *new;
17027dd7cddfSDavid du Colombier 	Tcpctl *tcb;
170380ee5cbfSDavid du Colombier 	Tcppriv *tpriv;
17043ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
17053ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
17063ff48bf5SDavid du Colombier 	Limbo *lp, **l;
17073ff48bf5SDavid du Colombier 	int h;
17087dd7cddfSDavid du Colombier 
17093ff48bf5SDavid du Colombier 	/* unless it's just an ack, it can't be someone coming out of limbo */
17103ff48bf5SDavid du Colombier 	if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17113ff48bf5SDavid du Colombier 		return nil;
17123ff48bf5SDavid du Colombier 
17133ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
17143ff48bf5SDavid du Colombier 
17153ff48bf5SDavid du Colombier 	/* find a call in limbo */
17163ff48bf5SDavid du Colombier 	h = hashipa(src, segp->source);
17173ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17187133e0eeSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17193ff48bf5SDavid du Colombier 			src, segp->source, lp->raddr, lp->rport,
17203ff48bf5SDavid du Colombier 			dst, segp->dest, lp->laddr, lp->lport,
17213ff48bf5SDavid du Colombier 			version, lp->version
17223ff48bf5SDavid du Colombier  		);
17233ff48bf5SDavid du Colombier 
17243ff48bf5SDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17253ff48bf5SDavid du Colombier 			continue;
17263ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
17273ff48bf5SDavid du Colombier 			continue;
17283ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
17293ff48bf5SDavid du Colombier 			continue;
17303ff48bf5SDavid du Colombier 
17313ff48bf5SDavid du Colombier 		/* we're assuming no data with the initial SYN */
17323ff48bf5SDavid du Colombier 		if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
173384363d68SDavid du Colombier 			netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17343ff48bf5SDavid du Colombier 				segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17353ff48bf5SDavid du Colombier 			lp = nil;
17363ff48bf5SDavid du Colombier 		} else {
17373ff48bf5SDavid du Colombier 			tpriv->nlimbo--;
17383ff48bf5SDavid du Colombier 			*l = lp->next;
17393ff48bf5SDavid du Colombier 		}
17403ff48bf5SDavid du Colombier 		break;
17413ff48bf5SDavid du Colombier 	}
17423ff48bf5SDavid du Colombier 	if(lp == nil)
17433ff48bf5SDavid du Colombier 		return nil;
17443ff48bf5SDavid du Colombier 
17453ff48bf5SDavid du Colombier 	new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17467dd7cddfSDavid du Colombier 	if(new == nil)
17477dd7cddfSDavid du Colombier 		return nil;
17487dd7cddfSDavid du Colombier 
17497dd7cddfSDavid du Colombier 	memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17507dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)new->ptcl;
17517dd7cddfSDavid du Colombier 	tcb->flags &= ~CLONE;
17527dd7cddfSDavid du Colombier 	tcb->timer.arg = new;
17539a747e4fSDavid du Colombier 	tcb->timer.state = TcptimerOFF;
17547dd7cddfSDavid du Colombier 	tcb->acktimer.arg = new;
17559a747e4fSDavid du Colombier 	tcb->acktimer.state = TcptimerOFF;
17567dd7cddfSDavid du Colombier 	tcb->katimer.arg = new;
17579a747e4fSDavid du Colombier 	tcb->katimer.state = TcptimerOFF;
17587dd7cddfSDavid du Colombier 	tcb->rtt_timer.arg = new;
17599a747e4fSDavid du Colombier 	tcb->rtt_timer.state = TcptimerOFF;
17607dd7cddfSDavid du Colombier 
17613ff48bf5SDavid du Colombier 	tcb->irs = lp->irs;
17623ff48bf5SDavid du Colombier 	tcb->rcv.nxt = tcb->irs+1;
17631ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
17641ce6c95fSDavid du Colombier 	tcb->rcv.wsnt = 0;
17653ff48bf5SDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
17667dd7cddfSDavid du Colombier 
17673ff48bf5SDavid du Colombier 	tcb->iss = lp->iss;
17683ff48bf5SDavid du Colombier 	tcb->rttseq = tcb->iss;
17693ff48bf5SDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
17703ff48bf5SDavid du Colombier 	tcb->snd.una = tcb->iss+1;
17713ff48bf5SDavid du Colombier 	tcb->snd.ptr = tcb->iss+1;
17723ff48bf5SDavid du Colombier 	tcb->snd.nxt = tcb->iss+1;
17731ce6c95fSDavid du Colombier 	tcb->snd.rxt = tcb->iss+1;
17743ff48bf5SDavid du Colombier 	tcb->flgcnt = 0;
17753ff48bf5SDavid du Colombier 	tcb->flags |= SYNACK;
17763ff48bf5SDavid du Colombier 
17773ff48bf5SDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
17787ec5746aSDavid du Colombier 	if(lp->mss != 0 && lp->mss < tcb->mss) {
17793ff48bf5SDavid du Colombier 		tcb->mss = lp->mss;
17807ec5746aSDavid du Colombier 		tpriv->stats[Mss] = tcb->mss;
17817ec5746aSDavid du Colombier 	}
17823ff48bf5SDavid du Colombier 
17833f695129SDavid du Colombier 	/* window scaling */
17843f695129SDavid du Colombier 	tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
17853f695129SDavid du Colombier 
17861ce6c95fSDavid du Colombier 	/* congestion window */
17873ff48bf5SDavid du Colombier 	tcb->snd.wnd = segp->wnd;
17881ce6c95fSDavid du Colombier 	initialwindow(tcb);
17893ff48bf5SDavid du Colombier 
17903ff48bf5SDavid du Colombier 	/* set initial round trip time */
17913ff48bf5SDavid du Colombier 	tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
17923ff48bf5SDavid du Colombier 	tcpsynackrtt(new);
17933ff48bf5SDavid du Colombier 
17943ff48bf5SDavid du Colombier 	free(lp);
17953ff48bf5SDavid du Colombier 
17963ff48bf5SDavid du Colombier 	/* set up proto header */
17973ff48bf5SDavid du Colombier 	switch(version){
17983ff48bf5SDavid du Colombier 	case V4:
17993ff48bf5SDavid du Colombier 		h4 = &tcb->protohdr.tcp4hdr;
18003ff48bf5SDavid du Colombier 		memset(h4, 0, sizeof(*h4));
18013ff48bf5SDavid du Colombier 		h4->proto = IP_TCPPROTO;
18023ff48bf5SDavid du Colombier 		hnputs(h4->tcpsport, new->lport);
18033ff48bf5SDavid du Colombier 		hnputs(h4->tcpdport, new->rport);
18043ff48bf5SDavid du Colombier 		v6tov4(h4->tcpsrc, dst);
18053ff48bf5SDavid du Colombier 		v6tov4(h4->tcpdst, src);
18063ff48bf5SDavid du Colombier 		break;
18073ff48bf5SDavid du Colombier 	case V6:
18083ff48bf5SDavid du Colombier 		h6 = &tcb->protohdr.tcp6hdr;
18093ff48bf5SDavid du Colombier 		memset(h6, 0, sizeof(*h6));
18103ff48bf5SDavid du Colombier 		h6->proto = IP_TCPPROTO;
18113ff48bf5SDavid du Colombier 		hnputs(h6->tcpsport, new->lport);
18123ff48bf5SDavid du Colombier 		hnputs(h6->tcpdport, new->rport);
18133ff48bf5SDavid du Colombier 		ipmove(h6->tcpsrc, dst);
18143ff48bf5SDavid du Colombier 		ipmove(h6->tcpdst, src);
18153ff48bf5SDavid du Colombier 		break;
18163ff48bf5SDavid du Colombier 	default:
18173ff48bf5SDavid du Colombier 		panic("tcpincoming: version %d", new->ipversion);
18183ff48bf5SDavid du Colombier 	}
18193ff48bf5SDavid du Colombier 
18203ff48bf5SDavid du Colombier 	tcpsetstate(new, Established);
18213ff48bf5SDavid du Colombier 
182280ee5cbfSDavid du Colombier 	iphtadd(&tpriv->ht, new);
182380ee5cbfSDavid du Colombier 
18247dd7cddfSDavid du Colombier 	return new;
18257dd7cddfSDavid du Colombier }
18267dd7cddfSDavid du Colombier 
18271ce6c95fSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18287dd7cddfSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18297dd7cddfSDavid du Colombier {
18307dd7cddfSDavid du Colombier 	if(low <= high){
18317dd7cddfSDavid du Colombier 		if(low <= x && x <= high)
18327dd7cddfSDavid du Colombier 			return 1;
18337dd7cddfSDavid du Colombier 	}
18347dd7cddfSDavid du Colombier 	else {
18357dd7cddfSDavid du Colombier 		if(x >= low || x <= high)
18367dd7cddfSDavid du Colombier 			return 1;
18377dd7cddfSDavid du Colombier 	}
18387dd7cddfSDavid du Colombier 	return 0;
18397dd7cddfSDavid du Colombier }
18407dd7cddfSDavid du Colombier 
18411ce6c95fSDavid du Colombier static int
seq_lt(ulong x,ulong y)18427dd7cddfSDavid du Colombier seq_lt(ulong x, ulong y)
18437dd7cddfSDavid du Colombier {
18447dd7cddfSDavid du Colombier 	return (int)(x-y) < 0;
18457dd7cddfSDavid du Colombier }
18467dd7cddfSDavid du Colombier 
18471ce6c95fSDavid du Colombier static int
seq_le(ulong x,ulong y)18487dd7cddfSDavid du Colombier seq_le(ulong x, ulong y)
18497dd7cddfSDavid du Colombier {
18507dd7cddfSDavid du Colombier 	return (int)(x-y) <= 0;
18517dd7cddfSDavid du Colombier }
18527dd7cddfSDavid du Colombier 
18531ce6c95fSDavid du Colombier static int
seq_gt(ulong x,ulong y)18547dd7cddfSDavid du Colombier seq_gt(ulong x, ulong y)
18557dd7cddfSDavid du Colombier {
18567dd7cddfSDavid du Colombier 	return (int)(x-y) > 0;
18577dd7cddfSDavid du Colombier }
18587dd7cddfSDavid du Colombier 
18591ce6c95fSDavid du Colombier static int
seq_ge(ulong x,ulong y)18607dd7cddfSDavid du Colombier seq_ge(ulong x, ulong y)
18617dd7cddfSDavid du Colombier {
18627dd7cddfSDavid du Colombier 	return (int)(x-y) >= 0;
18637dd7cddfSDavid du Colombier }
18647dd7cddfSDavid du Colombier 
18657dd7cddfSDavid du Colombier /*
18667dd7cddfSDavid du Colombier  *  use the time between the first SYN and it's ack as the
18677dd7cddfSDavid du Colombier  *  initial round trip time
18687dd7cddfSDavid du Colombier  */
18691ce6c95fSDavid du Colombier static void
tcpsynackrtt(Conv * s)18707dd7cddfSDavid du Colombier tcpsynackrtt(Conv *s)
18717dd7cddfSDavid du Colombier {
18727dd7cddfSDavid du Colombier 	Tcpctl *tcb;
18737dd7cddfSDavid du Colombier 	int delta;
18747dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
18757dd7cddfSDavid du Colombier 
18767dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
18777dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
18787dd7cddfSDavid du Colombier 
18793ff48bf5SDavid du Colombier 	delta = NOW - tcb->sndsyntime;
18807dd7cddfSDavid du Colombier 	tcb->srtt = delta<<LOGAGAIN;
18817dd7cddfSDavid du Colombier 	tcb->mdev = delta<<LOGDGAIN;
18827dd7cddfSDavid du Colombier 
18837dd7cddfSDavid du Colombier 	/* halt round trip timer */
18847dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
18857dd7cddfSDavid du Colombier }
18867dd7cddfSDavid du Colombier 
18871ce6c95fSDavid du Colombier static void
update(Conv * s,Tcp * seg)18887dd7cddfSDavid du Colombier update(Conv *s, Tcp *seg)
18897dd7cddfSDavid du Colombier {
18907dd7cddfSDavid du Colombier 	int rtt, delta;
18917dd7cddfSDavid du Colombier 	Tcpctl *tcb;
18923f695129SDavid du Colombier 	ulong acked;
18937dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
18947dd7cddfSDavid du Colombier 
18951ce6c95fSDavid du Colombier 	if(seg->update)
18961ce6c95fSDavid du Colombier 		return;
18971ce6c95fSDavid du Colombier 	seg->update = 1;
18981ce6c95fSDavid du Colombier 
18997dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
19007dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
19017dd7cddfSDavid du Colombier 
19021ce6c95fSDavid du Colombier 	/* catch zero-window updates, update window & recover */
19031ce6c95fSDavid du Colombier 	if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19041ce6c95fSDavid du Colombier 	    seq_lt(seg->ack, tcb->snd.ptr)){
19051ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19061ce6c95fSDavid du Colombier 			seg->ack,  tcb->snd.una, tcb->snd.ptr, seg->wnd);
19071ce6c95fSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19081ce6c95fSDavid du Colombier 		goto recovery;
19097dd7cddfSDavid du Colombier 	}
19107dd7cddfSDavid du Colombier 
19111ce6c95fSDavid du Colombier 	/* newreno fast retransmit */
19121ce6c95fSDavid du Colombier 	if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19131ce6c95fSDavid du Colombier 	    ++tcb->snd.dupacks == 3){		/* was TCPREXMTTHRESH */
19141ce6c95fSDavid du Colombier recovery:
19151ce6c95fSDavid du Colombier 		if(tcb->snd.recovery){
19161ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryCwind]++;
19171ce6c95fSDavid du Colombier 			tcb->cwind += tcb->mss;
19181ce6c95fSDavid du Colombier 		}else if(seq_le(tcb->snd.rxt, seg->ack)){
19191ce6c95fSDavid du Colombier 			tpriv->stats[Recovery]++;
19201ce6c95fSDavid du Colombier 			tcb->abcbytes = 0;
19217dd7cddfSDavid du Colombier 			tcb->snd.recovery = 1;
19221ce6c95fSDavid du Colombier 			tcb->snd.partialack = 0;
19237dd7cddfSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
19241ce6c95fSDavid du Colombier 			tcpcongestion(tcb);
19251ce6c95fSDavid du Colombier 			tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19261ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19271ce6c95fSDavid du Colombier 				tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19287dd7cddfSDavid du Colombier 			tcprxmit(s);
19297dd7cddfSDavid du Colombier 		}else{
19301ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryNoSeq]++;
19311ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19321ce6c95fSDavid du Colombier 				tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19331ce6c95fSDavid du Colombier 			/* don't enter fast retransmit, don't change ssthresh */
19347dd7cddfSDavid du Colombier 		}
19351ce6c95fSDavid du Colombier 	}else if(tcb->snd.recovery){
19361ce6c95fSDavid du Colombier 		tpriv->stats[RecoveryCwind]++;
19371ce6c95fSDavid du Colombier 		tcb->cwind += tcb->mss;
19387dd7cddfSDavid du Colombier 	}
19397dd7cddfSDavid du Colombier 
194080ee5cbfSDavid du Colombier 	/*
194180ee5cbfSDavid du Colombier 	 *  update window
194280ee5cbfSDavid du Colombier 	 */
194380ee5cbfSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.wl2)
194480ee5cbfSDavid du Colombier 	|| (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19451ce6c95fSDavid du Colombier 		/* clear dupack if we advance wl2 */
19461ce6c95fSDavid du Colombier 		if(tcb->snd.wl2 != seg->ack)
19471ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;
19487dd7cddfSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19497dd7cddfSDavid du Colombier 		tcb->snd.wl2 = seg->ack;
19507dd7cddfSDavid du Colombier 	}
19517dd7cddfSDavid du Colombier 
195280ee5cbfSDavid du Colombier 	if(!seq_gt(seg->ack, tcb->snd.una)){
195380ee5cbfSDavid du Colombier 		/*
195480ee5cbfSDavid du Colombier 		 *  don't let us hangup if sending into a closed window and
195580ee5cbfSDavid du Colombier 		 *  we're still getting acks
195680ee5cbfSDavid du Colombier 		 */
19571ce6c95fSDavid du Colombier 		if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
195880ee5cbfSDavid du Colombier 			tcb->backedoff = MAXBACKMS/4;
19597dd7cddfSDavid du Colombier 		return;
196080ee5cbfSDavid du Colombier 	}
19617dd7cddfSDavid du Colombier 
19627dd7cddfSDavid du Colombier 	/* Compute the new send window size */
19637dd7cddfSDavid du Colombier 	acked = seg->ack - tcb->snd.una;
19647dd7cddfSDavid du Colombier 
19657dd7cddfSDavid du Colombier 	/* avoid slow start and timers for SYN acks */
19667dd7cddfSDavid du Colombier 	if((tcb->flags & SYNACK) == 0) {
19677dd7cddfSDavid du Colombier 		tcb->flags |= SYNACK;
19687dd7cddfSDavid du Colombier 		acked--;
196980ee5cbfSDavid du Colombier 		tcb->flgcnt--;
19707dd7cddfSDavid du Colombier 		goto done;
19717dd7cddfSDavid du Colombier 	}
19727dd7cddfSDavid du Colombier 
19731ce6c95fSDavid du Colombier 	/*
19741ce6c95fSDavid du Colombier 	 * congestion control
19751ce6c95fSDavid du Colombier 	 */
19761ce6c95fSDavid du Colombier 	if(tcb->snd.recovery){
19771ce6c95fSDavid du Colombier 		if(seq_ge(seg->ack, tcb->snd.rxt)){
19781ce6c95fSDavid du Colombier 			/* recovery finished; deflate window */
19791ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryDone]++;
19801ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;
19811ce6c95fSDavid du Colombier 			tcb->snd.recovery = 0;
19821ce6c95fSDavid du Colombier 			tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
19831ce6c95fSDavid du Colombier 			if(tcb->ssthresh < tcb->cwind)
19841ce6c95fSDavid du Colombier 				tcb->cwind = tcb->ssthresh;
19851ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
19861ce6c95fSDavid du Colombier 				tcb->cwind, tcb->ssthresh);
19871ce6c95fSDavid du Colombier 		} else {
19881ce6c95fSDavid du Colombier 			/* partial ack; we lost more than one segment */
19891ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryPA]++;
19901ce6c95fSDavid du Colombier 			if(tcb->cwind > acked)
19911ce6c95fSDavid du Colombier 				tcb->cwind -= acked;
19921ce6c95fSDavid du Colombier 			else{
19931ce6c95fSDavid du Colombier 				netlog(s->p->f, Logtcpwin, "partial ack neg\n");
19941ce6c95fSDavid du Colombier 				tcb->cwind = tcb->mss;
19957dd7cddfSDavid du Colombier 			}
19961ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
19971ce6c95fSDavid du Colombier 				acked, tcb->snd.rxt - seg->ack, tcb->cwind);
19987dd7cddfSDavid du Colombier 
19991ce6c95fSDavid du Colombier 			if(acked >= tcb->mss)
20001ce6c95fSDavid du Colombier 				tcb->cwind += tcb->mss;
20011ce6c95fSDavid du Colombier 			tcb->snd.partialack++;
20027dd7cddfSDavid du Colombier 		}
20031ce6c95fSDavid du Colombier 	} else
20041ce6c95fSDavid du Colombier 		tcpabcincr(tcb, acked);
20057dd7cddfSDavid du Colombier 
20067dd7cddfSDavid du Colombier 	/* Adjust the timers according to the round trip time */
20071ce6c95fSDavid du Colombier 	/* TODO: fix sloppy treatment of overflow cases here. */
20089a747e4fSDavid du Colombier 	if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20097dd7cddfSDavid du Colombier 		tcphalt(tpriv, &tcb->rtt_timer);
20107dd7cddfSDavid du Colombier 		if((tcb->flags&RETRAN) == 0) {
20117dd7cddfSDavid du Colombier 			tcb->backoff = 0;
20127dd7cddfSDavid du Colombier 			tcb->backedoff = 0;
20137dd7cddfSDavid du Colombier 			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20147dd7cddfSDavid du Colombier 			if(rtt == 0)
20151ce6c95fSDavid du Colombier 				rtt = 1; /* else all close sys's will rexmit in 0 time */
20167dd7cddfSDavid du Colombier 			rtt *= MSPTICK;
20177dd7cddfSDavid du Colombier 			if(tcb->srtt == 0) {
20187dd7cddfSDavid du Colombier 				tcb->srtt = rtt << LOGAGAIN;
20197dd7cddfSDavid du Colombier 				tcb->mdev = rtt << LOGDGAIN;
20207dd7cddfSDavid du Colombier 			} else {
20217dd7cddfSDavid du Colombier 				delta = rtt - (tcb->srtt>>LOGAGAIN);
20227dd7cddfSDavid du Colombier 				tcb->srtt += delta;
20237dd7cddfSDavid du Colombier 				if(tcb->srtt <= 0)
20247dd7cddfSDavid du Colombier 					tcb->srtt = 1;
20257dd7cddfSDavid du Colombier 
20267dd7cddfSDavid du Colombier 				delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20277dd7cddfSDavid du Colombier 				tcb->mdev += delta;
20287dd7cddfSDavid du Colombier 				if(tcb->mdev <= 0)
20297dd7cddfSDavid du Colombier 					tcb->mdev = 1;
20307dd7cddfSDavid du Colombier 			}
20319a747e4fSDavid du Colombier 			tcpsettimer(tcb);
20327dd7cddfSDavid du Colombier 		}
20337dd7cddfSDavid du Colombier 	}
20347dd7cddfSDavid du Colombier 
20357dd7cddfSDavid du Colombier done:
203680ee5cbfSDavid du Colombier 	if(qdiscard(s->wq, acked) < acked)
203780ee5cbfSDavid du Colombier 		tcb->flgcnt--;
20387dd7cddfSDavid du Colombier 	tcb->snd.una = seg->ack;
20391ce6c95fSDavid du Colombier 
20401ce6c95fSDavid du Colombier 	/* newreno fast recovery */
20411ce6c95fSDavid du Colombier 	if(tcb->snd.recovery)
20421ce6c95fSDavid du Colombier 		tcprxmit(s);
20431ce6c95fSDavid du Colombier 
20447dd7cddfSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.urg))
20457dd7cddfSDavid du Colombier 		tcb->snd.urg = seg->ack;
20467dd7cddfSDavid du Colombier 
20471ce6c95fSDavid du Colombier 	if(tcb->snd.una != tcb->snd.nxt){
20481ce6c95fSDavid du Colombier 		/* `impatient' variant */
20491ce6c95fSDavid du Colombier 		if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20501ce6c95fSDavid du Colombier 			tcb->time = NOW;
20511ce6c95fSDavid du Colombier 			tcb->timeuna = tcb->snd.una;
20527dd7cddfSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
20531ce6c95fSDavid du Colombier 		}
20541ce6c95fSDavid du Colombier 	} else
20553ff48bf5SDavid du Colombier 		tcphalt(tpriv, &tcb->timer);
20567dd7cddfSDavid du Colombier 
20577dd7cddfSDavid du Colombier 	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
20587dd7cddfSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
20597dd7cddfSDavid du Colombier 
20601ce6c95fSDavid du Colombier 	if(!tcb->snd.recovery)
20617dd7cddfSDavid du Colombier 		tcb->flags &= ~RETRAN;
20627dd7cddfSDavid du Colombier 	tcb->backoff = 0;
20637dd7cddfSDavid du Colombier 	tcb->backedoff = 0;
20647dd7cddfSDavid du Colombier }
20657dd7cddfSDavid du Colombier 
20661ce6c95fSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)20679a747e4fSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
20687dd7cddfSDavid du Colombier {
20697dd7cddfSDavid du Colombier 	Tcp seg;
20703ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
20713ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
20727dd7cddfSDavid du Colombier 	int hdrlen;
20737dd7cddfSDavid du Colombier 	Tcpctl *tcb;
207484363d68SDavid du Colombier 	ushort length, csum;
20757dd7cddfSDavid du Colombier 	uchar source[IPaddrlen], dest[IPaddrlen];
207680ee5cbfSDavid du Colombier 	Conv *s;
20777dd7cddfSDavid du Colombier 	Fs *f;
20787dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
20793ff48bf5SDavid du Colombier 	uchar version;
20807dd7cddfSDavid du Colombier 
20817dd7cddfSDavid du Colombier 	f = tcp->f;
20827dd7cddfSDavid du Colombier 	tpriv = tcp->priv;
20837dd7cddfSDavid du Colombier 
208459cc4ca5SDavid du Colombier 	tpriv->stats[InSegs]++;
20857dd7cddfSDavid du Colombier 
20863ff48bf5SDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
20873ff48bf5SDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
20887dd7cddfSDavid du Colombier 
20893ff48bf5SDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
20903ff48bf5SDavid du Colombier 		version = V4;
20913ff48bf5SDavid du Colombier 		length = nhgets(h4->length);
20923ff48bf5SDavid du Colombier 		v4tov6(dest, h4->tcpdst);
20933ff48bf5SDavid du Colombier 		v4tov6(source, h4->tcpsrc);
20947dd7cddfSDavid du Colombier 
20953ff48bf5SDavid du Colombier 		h4->Unused = 0;
20963ff48bf5SDavid du Colombier 		hnputs(h4->tcplen, length-TCP4_PKT);
20976b6b9ac8SDavid du Colombier 		if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
20983ff48bf5SDavid du Colombier 			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
209959cc4ca5SDavid du Colombier 			tpriv->stats[CsumErrs]++;
210059cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21017dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "bad tcp proto cksum\n");
21027dd7cddfSDavid du Colombier 			freeblist(bp);
21037dd7cddfSDavid du Colombier 			return;
21047dd7cddfSDavid du Colombier 		}
21057dd7cddfSDavid du Colombier 
21063ff48bf5SDavid du Colombier 		hdrlen = ntohtcp4(&seg, &bp);
21077dd7cddfSDavid du Colombier 		if(hdrlen < 0){
210859cc4ca5SDavid du Colombier 			tpriv->stats[HlenErrs]++;
210959cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21107dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "bad tcp hdr len\n");
21117dd7cddfSDavid du Colombier 			return;
21127dd7cddfSDavid du Colombier 		}
21137dd7cddfSDavid du Colombier 
21147dd7cddfSDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
21153ff48bf5SDavid du Colombier 		length -= hdrlen+TCP4_PKT;
21163ff48bf5SDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21177dd7cddfSDavid du Colombier 		if(bp == nil){
211859cc4ca5SDavid du Colombier 			tpriv->stats[LenErrs]++;
211959cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21207dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "tcp len < 0 after trim\n");
21217dd7cddfSDavid du Colombier 			return;
21227dd7cddfSDavid du Colombier 		}
21233ff48bf5SDavid du Colombier 	}
21243ff48bf5SDavid du Colombier 	else {
21253ff48bf5SDavid du Colombier 		int ttl = h6->ttl;
21263ff48bf5SDavid du Colombier 		int proto = h6->proto;
21273ff48bf5SDavid du Colombier 
21283ff48bf5SDavid du Colombier 		version = V6;
21293ff48bf5SDavid du Colombier 		length = nhgets(h6->ploadlen);
21303ff48bf5SDavid du Colombier 		ipmove(dest, h6->tcpdst);
21313ff48bf5SDavid du Colombier 		ipmove(source, h6->tcpsrc);
21323ff48bf5SDavid du Colombier 
21333ff48bf5SDavid du Colombier 		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21343ff48bf5SDavid du Colombier 		h6->ttl = proto;
21353ff48bf5SDavid du Colombier 		hnputl(h6->vcf, length);
21363ff48bf5SDavid du Colombier 		if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
213784363d68SDavid du Colombier 		    (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21383ff48bf5SDavid du Colombier 			tpriv->stats[CsumErrs]++;
21393ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
214084363d68SDavid du Colombier 			netlog(f, Logtcp,
214184363d68SDavid du Colombier 			    "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
214284363d68SDavid du Colombier 				h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21433ff48bf5SDavid du Colombier 			freeblist(bp);
21443ff48bf5SDavid du Colombier 			return;
21453ff48bf5SDavid du Colombier 		}
21463ff48bf5SDavid du Colombier 		h6->ttl = ttl;
21473ff48bf5SDavid du Colombier 		h6->proto = proto;
21483ff48bf5SDavid du Colombier 		hnputs(h6->ploadlen, length);
21493ff48bf5SDavid du Colombier 
21503ff48bf5SDavid du Colombier 		hdrlen = ntohtcp6(&seg, &bp);
21513ff48bf5SDavid du Colombier 		if(hdrlen < 0){
21523ff48bf5SDavid du Colombier 			tpriv->stats[HlenErrs]++;
21533ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
215484363d68SDavid du Colombier 			netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21553ff48bf5SDavid du Colombier 			return;
21563ff48bf5SDavid du Colombier 		}
21573ff48bf5SDavid du Colombier 
21583ff48bf5SDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
21593ff48bf5SDavid du Colombier 		length -= hdrlen;
21603ff48bf5SDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP6_PKT, length);
21613ff48bf5SDavid du Colombier 		if(bp == nil){
21623ff48bf5SDavid du Colombier 			tpriv->stats[LenErrs]++;
21633ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
216484363d68SDavid du Colombier 			netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
21653ff48bf5SDavid du Colombier 			return;
21663ff48bf5SDavid du Colombier 		}
21673ff48bf5SDavid du Colombier 	}
21687dd7cddfSDavid du Colombier 
21697dd7cddfSDavid du Colombier 	/* lock protocol while searching for a conversation */
21707dd7cddfSDavid du Colombier 	qlock(tcp);
21717dd7cddfSDavid du Colombier 
217280ee5cbfSDavid du Colombier 	/* Look for a matching conversation */
217380ee5cbfSDavid du Colombier 	s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
217480ee5cbfSDavid du Colombier 	if(s == nil){
21757133e0eeSDavid du Colombier 		netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
21767133e0eeSDavid du Colombier 			source, seg.source, dest, seg.dest);
217780ee5cbfSDavid du Colombier reset:
21787dd7cddfSDavid du Colombier 		qunlock(tcp);
21793ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "no conversation");
21807dd7cddfSDavid du Colombier 		freeblist(bp);
21817dd7cddfSDavid du Colombier 		return;
21827dd7cddfSDavid du Colombier 	}
218380ee5cbfSDavid du Colombier 
218480ee5cbfSDavid du Colombier 	/* if it's a listener, look for the right flags and get a new conv */
218580ee5cbfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
218680ee5cbfSDavid du Colombier 	if(tcb->state == Listen){
21877dd7cddfSDavid du Colombier 		if(seg.flags & RST){
21883ff48bf5SDavid du Colombier 			limborst(s, &seg, source, dest, version);
21897dd7cddfSDavid du Colombier 			qunlock(tcp);
21907dd7cddfSDavid du Colombier 			freeblist(bp);
21917dd7cddfSDavid du Colombier 			return;
21927dd7cddfSDavid du Colombier 		}
21937dd7cddfSDavid du Colombier 
21943ff48bf5SDavid du Colombier 		/* if this is a new SYN, put the call into limbo */
21953ff48bf5SDavid du Colombier 		if((seg.flags & SYN) && (seg.flags & ACK) == 0){
21963ff48bf5SDavid du Colombier 			limbo(s, source, dest, &seg, version);
21973ff48bf5SDavid du Colombier 			qunlock(tcp);
21983ff48bf5SDavid du Colombier 			freeblist(bp);
21993ff48bf5SDavid du Colombier 			return;
22003ff48bf5SDavid du Colombier 		}
22013ff48bf5SDavid du Colombier 
22023ff48bf5SDavid du Colombier 		/*
22033ff48bf5SDavid du Colombier 		 *  if there's a matching call in limbo, tcpincoming will
22043ff48bf5SDavid du Colombier 		 *  return it in state Syn_received
22053ff48bf5SDavid du Colombier 		 */
22063ff48bf5SDavid du Colombier 		s = tcpincoming(s, &seg, source, dest, version);
220780ee5cbfSDavid du Colombier 		if(s == nil)
220880ee5cbfSDavid du Colombier 			goto reset;
22097dd7cddfSDavid du Colombier 	}
22107dd7cddfSDavid du Colombier 
22117dd7cddfSDavid du Colombier 	/* The rest of the input state machine is run with the control block
22127dd7cddfSDavid du Colombier 	 * locked and implements the state machine directly out of the RFC.
22137dd7cddfSDavid du Colombier 	 * Out-of-band data is ignored - it was always a bad idea.
22147dd7cddfSDavid du Colombier 	 */
22157dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
22167dd7cddfSDavid du Colombier 	if(waserror()){
22177dd7cddfSDavid du Colombier 		qunlock(s);
22187dd7cddfSDavid du Colombier 		nexterror();
22197dd7cddfSDavid du Colombier 	}
22207dd7cddfSDavid du Colombier 	qlock(s);
22217dd7cddfSDavid du Colombier 	qunlock(tcp);
22227dd7cddfSDavid du Colombier 
22233f695129SDavid du Colombier 	/* fix up window */
22243f695129SDavid du Colombier 	seg.wnd <<= tcb->rcv.scale;
22253f695129SDavid du Colombier 
222640ef9009SDavid du Colombier 	/* every input packet in puts off the keep alive time out */
2227e288d156SDavid du Colombier 	tcpsetkacounter(tcb);
22287dd7cddfSDavid du Colombier 
22297dd7cddfSDavid du Colombier 	switch(tcb->state) {
22307dd7cddfSDavid du Colombier 	case Closed:
22313ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22327dd7cddfSDavid du Colombier 		goto raise;
22337dd7cddfSDavid du Colombier 	case Syn_sent:
22347dd7cddfSDavid du Colombier 		if(seg.flags & ACK) {
22357dd7cddfSDavid du Colombier 			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22363ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
22373ff48bf5SDavid du Colombier 					 "bad seq in Syn_sent");
22387dd7cddfSDavid du Colombier 				goto raise;
22397dd7cddfSDavid du Colombier 			}
22407dd7cddfSDavid du Colombier 		}
22417dd7cddfSDavid du Colombier 		if(seg.flags & RST) {
22427dd7cddfSDavid du Colombier 			if(seg.flags & ACK)
22437dd7cddfSDavid du Colombier 				localclose(s, Econrefused);
22447dd7cddfSDavid du Colombier 			goto raise;
22457dd7cddfSDavid du Colombier 		}
22467dd7cddfSDavid du Colombier 
22477dd7cddfSDavid du Colombier 		if(seg.flags & SYN) {
22487dd7cddfSDavid du Colombier 			procsyn(s, &seg);
22497dd7cddfSDavid du Colombier 			if(seg.flags & ACK){
22507dd7cddfSDavid du Colombier 				update(s, &seg);
22517dd7cddfSDavid du Colombier 				tcpsynackrtt(s);
22527dd7cddfSDavid du Colombier 				tcpsetstate(s, Established);
22533f695129SDavid du Colombier 				tcpsetscale(s, tcb, seg.ws, tcb->scale);
22547dd7cddfSDavid du Colombier 			}
22557dd7cddfSDavid du Colombier 			else {
22563ff48bf5SDavid du Colombier 				tcb->time = NOW;
22573f695129SDavid du Colombier 				tcpsetstate(s, Syn_received);	/* DLP - shouldn't this be a reset? */
22587dd7cddfSDavid du Colombier 			}
22597dd7cddfSDavid du Colombier 
22607dd7cddfSDavid du Colombier 			if(length != 0 || (seg.flags & FIN))
22617dd7cddfSDavid du Colombier 				break;
22627dd7cddfSDavid du Colombier 
22637dd7cddfSDavid du Colombier 			freeblist(bp);
22647dd7cddfSDavid du Colombier 			goto output;
22657dd7cddfSDavid du Colombier 		}
22667dd7cddfSDavid du Colombier 		else
22677dd7cddfSDavid du Colombier 			freeblist(bp);
22687dd7cddfSDavid du Colombier 
22697dd7cddfSDavid du Colombier 		qunlock(s);
22707dd7cddfSDavid du Colombier 		poperror();
22717dd7cddfSDavid du Colombier 		return;
22727dd7cddfSDavid du Colombier 	case Syn_received:
22737dd7cddfSDavid du Colombier 		/* doesn't matter if it's the correct ack, we're just trying to set timing */
22747dd7cddfSDavid du Colombier 		if(seg.flags & ACK)
22757dd7cddfSDavid du Colombier 			tcpsynackrtt(s);
22767dd7cddfSDavid du Colombier 		break;
22777dd7cddfSDavid du Colombier 	}
22787dd7cddfSDavid du Colombier 
22793ff48bf5SDavid du Colombier 	/*
22803ff48bf5SDavid du Colombier 	 *  One DOS attack is to open connections to us and then forget about them,
22813ff48bf5SDavid du Colombier 	 *  thereby tying up a conv at no long term cost to the attacker.
22823ff48bf5SDavid du Colombier 	 *  This is an attempt to defeat these stateless DOS attacks.  See
22833ff48bf5SDavid du Colombier 	 *  corresponding code in tcpsendka().
22843ff48bf5SDavid du Colombier 	 */
22853ff48bf5SDavid du Colombier 	if(tcb->state != Syn_received && (seg.flags & RST) == 0){
22863ff48bf5SDavid du Colombier 		if(tcpporthogdefense
22873ff48bf5SDavid du Colombier 		&& seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
22883ff48bf5SDavid du Colombier 			print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
22893ff48bf5SDavid du Colombier 				source, seg.source, dest, seg.dest, seg.flags,
22903ff48bf5SDavid du Colombier 				tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
22913ff48bf5SDavid du Colombier 			localclose(s, "stateless hog");
22923ff48bf5SDavid du Colombier 		}
22933ff48bf5SDavid du Colombier 	}
22943ff48bf5SDavid du Colombier 
22957dd7cddfSDavid du Colombier 	/* Cut the data to fit the receive window */
22961ce6c95fSDavid du Colombier 	tcprcvwin(s);
22977dd7cddfSDavid du Colombier 	if(tcptrim(tcb, &seg, &bp, &length) == -1) {
22981ce6c95fSDavid du Colombier 		if(seg.seq+1 != tcb->rcv.nxt || length != 1)
22991ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23001ce6c95fSDavid du Colombier 			"%lud-%lud l %d from %I\n", seg.seq,
23011ce6c95fSDavid du Colombier 			seg.seq + length - 1, tcb->rcv.nxt,
23021ce6c95fSDavid du Colombier 			tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23037dd7cddfSDavid du Colombier 		update(s, &seg);
230480ee5cbfSDavid du Colombier 		if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23057dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->rtt_timer);
23067dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->acktimer);
23077dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->katimer);
23087dd7cddfSDavid du Colombier 			tcpsetstate(s, Time_wait);
23097dd7cddfSDavid du Colombier 			tcb->timer.start = MSL2*(1000 / MSPTICK);
23107dd7cddfSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
23117dd7cddfSDavid du Colombier 		}
23127dd7cddfSDavid du Colombier 		if(!(seg.flags & RST)) {
23137dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
23147dd7cddfSDavid du Colombier 			goto output;
23157dd7cddfSDavid du Colombier 		}
23167dd7cddfSDavid du Colombier 		qunlock(s);
23177dd7cddfSDavid du Colombier 		poperror();
23187dd7cddfSDavid du Colombier 		return;
23197dd7cddfSDavid du Colombier 	}
23207dd7cddfSDavid du Colombier 
23217dd7cddfSDavid du Colombier 	/* Cannot accept so answer with a rst */
23227dd7cddfSDavid du Colombier 	if(length && tcb->state == Closed) {
23233ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23247dd7cddfSDavid du Colombier 		goto raise;
23257dd7cddfSDavid du Colombier 	}
23267dd7cddfSDavid du Colombier 
23277dd7cddfSDavid du Colombier 	/* The segment is beyond the current receive pointer so
23287dd7cddfSDavid du Colombier 	 * queue the data in the resequence queue
23297dd7cddfSDavid du Colombier 	 */
23307dd7cddfSDavid du Colombier 	if(seg.seq != tcb->rcv.nxt)
23317dd7cddfSDavid du Colombier 	if(length != 0 || (seg.flags & (SYN|FIN))) {
23327dd7cddfSDavid du Colombier 		update(s, &seg);
23331ce6c95fSDavid du Colombier 		if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23341ce6c95fSDavid du Colombier 			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23351ce6c95fSDavid du Colombier 				s->laddr, s->lport);
23361ce6c95fSDavid du Colombier 		tcb->flags |= FORCE;	/* force duplicate ack; RFC 5681 §3.2 */
23377dd7cddfSDavid du Colombier 		goto output;
23387dd7cddfSDavid du Colombier 	}
23397dd7cddfSDavid du Colombier 
23401ce6c95fSDavid du Colombier 	if(tcb->nreseq > 0)
23411ce6c95fSDavid du Colombier 		tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23421ce6c95fSDavid du Colombier 
23437dd7cddfSDavid du Colombier 	/*
23447dd7cddfSDavid du Colombier 	 *  keep looping till we've processed this packet plus any
23457dd7cddfSDavid du Colombier 	 *  adjacent packets in the resequence queue
23467dd7cddfSDavid du Colombier 	 */
23477dd7cddfSDavid du Colombier 	for(;;) {
23487dd7cddfSDavid du Colombier 		if(seg.flags & RST) {
234951711cb6SDavid du Colombier 			if(tcb->state == Established) {
235059cc4ca5SDavid du Colombier 				tpriv->stats[EstabResets]++;
235151711cb6SDavid du Colombier 				if(tcb->rcv.nxt != seg.seq)
23526b7c5dceSDavid du Colombier 					netlog(f, Logtcp, "out of order RST "
23536b7c5dceSDavid du Colombier 						"rcvd: %I.%d -> %I.%d, rcv.nxt "
23546b7c5dceSDavid du Colombier 						"%lux seq %lux\n",
23551ce6c95fSDavid du Colombier 						s->raddr, s->rport, s->laddr,
23561ce6c95fSDavid du Colombier 						s->lport, tcb->rcv.nxt, seg.seq);
235751711cb6SDavid du Colombier 			}
23587dd7cddfSDavid du Colombier 			localclose(s, Econrefused);
23597dd7cddfSDavid du Colombier 			goto raise;
23607dd7cddfSDavid du Colombier 		}
23617dd7cddfSDavid du Colombier 
23627dd7cddfSDavid du Colombier 		if((seg.flags&ACK) == 0)
23637dd7cddfSDavid du Colombier 			goto raise;
23647dd7cddfSDavid du Colombier 
23657dd7cddfSDavid du Colombier 		switch(tcb->state) {
23667dd7cddfSDavid du Colombier 		case Syn_received:
23677dd7cddfSDavid du Colombier 			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
23683ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
23693ff48bf5SDavid du Colombier 					"bad seq in Syn_received");
23707dd7cddfSDavid du Colombier 				goto raise;
23717dd7cddfSDavid du Colombier 			}
23727dd7cddfSDavid du Colombier 			update(s, &seg);
23737dd7cddfSDavid du Colombier 			tcpsetstate(s, Established);
23747dd7cddfSDavid du Colombier 		case Established:
23757dd7cddfSDavid du Colombier 		case Close_wait:
23767dd7cddfSDavid du Colombier 			update(s, &seg);
23777dd7cddfSDavid du Colombier 			break;
23787dd7cddfSDavid du Colombier 		case Finwait1:
23797dd7cddfSDavid du Colombier 			update(s, &seg);
238080ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0){
23817dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
23827dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
23837dd7cddfSDavid du Colombier 				tcpsetkacounter(tcb);
23843ff48bf5SDavid du Colombier 				tcb->time = NOW;
23857dd7cddfSDavid du Colombier 				tcpsetstate(s, Finwait2);
23867dd7cddfSDavid du Colombier 				tcb->katimer.start = MSL2 * (1000 / MSPTICK);
23877dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->katimer);
23887dd7cddfSDavid du Colombier 			}
23897dd7cddfSDavid du Colombier 			break;
23907dd7cddfSDavid du Colombier 		case Finwait2:
23917dd7cddfSDavid du Colombier 			update(s, &seg);
23927dd7cddfSDavid du Colombier 			break;
23937dd7cddfSDavid du Colombier 		case Closing:
23947dd7cddfSDavid du Colombier 			update(s, &seg);
239580ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
23967dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
23977dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
23987dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
23997dd7cddfSDavid du Colombier 				tcpsetstate(s, Time_wait);
24007dd7cddfSDavid du Colombier 				tcb->timer.start = MSL2*(1000 / MSPTICK);
24017dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24027dd7cddfSDavid du Colombier 			}
24037dd7cddfSDavid du Colombier 			break;
24047dd7cddfSDavid du Colombier 		case Last_ack:
24057dd7cddfSDavid du Colombier 			update(s, &seg);
240680ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
24077dd7cddfSDavid du Colombier 				localclose(s, nil);
24087dd7cddfSDavid du Colombier 				goto raise;
24097dd7cddfSDavid du Colombier 			}
24107dd7cddfSDavid du Colombier 		case Time_wait:
24117dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
24129a747e4fSDavid du Colombier 			if(tcb->timer.state != TcptimerON)
24137dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24147dd7cddfSDavid du Colombier 		}
24157dd7cddfSDavid du Colombier 
24167dd7cddfSDavid du Colombier 		if((seg.flags&URG) && seg.urg) {
24177dd7cddfSDavid du Colombier 			if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24187dd7cddfSDavid du Colombier 				tcb->rcv.urg = seg.urg + seg.seq;
24197dd7cddfSDavid du Colombier 				pullblock(&bp, seg.urg);
24207dd7cddfSDavid du Colombier 			}
24217dd7cddfSDavid du Colombier 		}
24227dd7cddfSDavid du Colombier 		else
24237dd7cddfSDavid du Colombier 		if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24247dd7cddfSDavid du Colombier 			tcb->rcv.urg = tcb->rcv.nxt;
24257dd7cddfSDavid du Colombier 
24267dd7cddfSDavid du Colombier 		if(length == 0) {
24277dd7cddfSDavid du Colombier 			if(bp != nil)
24287dd7cddfSDavid du Colombier 				freeblist(bp);
24297dd7cddfSDavid du Colombier 		}
24307dd7cddfSDavid du Colombier 		else {
24317dd7cddfSDavid du Colombier 			switch(tcb->state){
24327dd7cddfSDavid du Colombier 			default:
24337dd7cddfSDavid du Colombier 				/* Ignore segment text */
24347dd7cddfSDavid du Colombier 				if(bp != nil)
24357dd7cddfSDavid du Colombier 					freeblist(bp);
24367dd7cddfSDavid du Colombier 				break;
24377dd7cddfSDavid du Colombier 
24387dd7cddfSDavid du Colombier 			case Syn_received:
24397dd7cddfSDavid du Colombier 			case Established:
24407dd7cddfSDavid du Colombier 			case Finwait1:
24417dd7cddfSDavid du Colombier 				/* If we still have some data place on
24427dd7cddfSDavid du Colombier 				 * receive queue
24437dd7cddfSDavid du Colombier 				 */
24447dd7cddfSDavid du Colombier 				if(bp) {
24457dd7cddfSDavid du Colombier 					bp = packblock(bp);
24467dd7cddfSDavid du Colombier 					if(bp == nil)
24477dd7cddfSDavid du Colombier 						panic("tcp packblock");
24487dd7cddfSDavid du Colombier 					qpassnolim(s->rq, bp);
24497dd7cddfSDavid du Colombier 					bp = nil;
24507dd7cddfSDavid du Colombier 				}
24517dd7cddfSDavid du Colombier 				tcb->rcv.nxt += length;
245280ee5cbfSDavid du Colombier 
245380ee5cbfSDavid du Colombier 				/*
245480ee5cbfSDavid du Colombier 				 *  turn on the acktimer if there's something
245580ee5cbfSDavid du Colombier 				 *  to ack
245680ee5cbfSDavid du Colombier 				 */
24579a747e4fSDavid du Colombier 				if(tcb->acktimer.state != TcptimerON)
24587dd7cddfSDavid du Colombier 					tcpgo(tpriv, &tcb->acktimer);
24597dd7cddfSDavid du Colombier 
24607dd7cddfSDavid du Colombier 				break;
24617dd7cddfSDavid du Colombier 			case Finwait2:
24627dd7cddfSDavid du Colombier 				/* no process to read the data, send a reset */
24637dd7cddfSDavid du Colombier 				if(bp != nil)
24647dd7cddfSDavid du Colombier 					freeblist(bp);
24653ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
24663ff48bf5SDavid du Colombier 					"send to Finwait2");
24677dd7cddfSDavid du Colombier 				qunlock(s);
24687dd7cddfSDavid du Colombier 				poperror();
24697dd7cddfSDavid du Colombier 				return;
24707dd7cddfSDavid du Colombier 			}
24717dd7cddfSDavid du Colombier 		}
24727dd7cddfSDavid du Colombier 
24737dd7cddfSDavid du Colombier 		if(seg.flags & FIN) {
24747dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
24757dd7cddfSDavid du Colombier 
24767dd7cddfSDavid du Colombier 			switch(tcb->state) {
24777dd7cddfSDavid du Colombier 			case Syn_received:
24787dd7cddfSDavid du Colombier 			case Established:
24797dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
24807dd7cddfSDavid du Colombier 				tcpsetstate(s, Close_wait);
24817dd7cddfSDavid du Colombier 				break;
24827dd7cddfSDavid du Colombier 			case Finwait1:
24837dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
248480ee5cbfSDavid du Colombier 				if(qlen(s->wq)+tcb->flgcnt == 0) {
24857dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->rtt_timer);
24867dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->acktimer);
24877dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->katimer);
24887dd7cddfSDavid du Colombier 					tcpsetstate(s, Time_wait);
24897dd7cddfSDavid du Colombier 					tcb->timer.start = MSL2*(1000/MSPTICK);
24907dd7cddfSDavid du Colombier 					tcpgo(tpriv, &tcb->timer);
24917dd7cddfSDavid du Colombier 				}
24927dd7cddfSDavid du Colombier 				else
24937dd7cddfSDavid du Colombier 					tcpsetstate(s, Closing);
24947dd7cddfSDavid du Colombier 				break;
24957dd7cddfSDavid du Colombier 			case Finwait2:
24967dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
24977dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
24987dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
24997dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
25007dd7cddfSDavid du Colombier 				tcpsetstate(s, Time_wait);
25017dd7cddfSDavid du Colombier 				tcb->timer.start = MSL2 * (1000/MSPTICK);
25027dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25037dd7cddfSDavid du Colombier 				break;
25047dd7cddfSDavid du Colombier 			case Close_wait:
25057dd7cddfSDavid du Colombier 			case Closing:
25067dd7cddfSDavid du Colombier 			case Last_ack:
25077dd7cddfSDavid du Colombier 				break;
25087dd7cddfSDavid du Colombier 			case Time_wait:
25097dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25107dd7cddfSDavid du Colombier 				break;
25117dd7cddfSDavid du Colombier 			}
25127dd7cddfSDavid du Colombier 		}
25137dd7cddfSDavid du Colombier 
25147dd7cddfSDavid du Colombier 		/*
25157dd7cddfSDavid du Colombier 		 *  get next adjacent segment from the resequence queue.
25167dd7cddfSDavid du Colombier 		 *  dump/trim any overlapping segments
25177dd7cddfSDavid du Colombier 		 */
25187dd7cddfSDavid du Colombier 		for(;;) {
25197dd7cddfSDavid du Colombier 			if(tcb->reseq == nil)
25207dd7cddfSDavid du Colombier 				goto output;
25217dd7cddfSDavid du Colombier 
25227dd7cddfSDavid du Colombier 			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25237dd7cddfSDavid du Colombier 				goto output;
25247dd7cddfSDavid du Colombier 
25257dd7cddfSDavid du Colombier 			getreseq(tcb, &seg, &bp, &length);
25267dd7cddfSDavid du Colombier 
25271ce6c95fSDavid du Colombier 			tcprcvwin(s);
25281ce6c95fSDavid du Colombier 			if(tcptrim(tcb, &seg, &bp, &length) == 0){
25291ce6c95fSDavid du Colombier 				tcb->flags |= FORCE;
25307dd7cddfSDavid du Colombier 				break;
25317dd7cddfSDavid du Colombier 			}
25327dd7cddfSDavid du Colombier 		}
25331ce6c95fSDavid du Colombier 	}
25347dd7cddfSDavid du Colombier output:
25357dd7cddfSDavid du Colombier 	tcpoutput(s);
25367dd7cddfSDavid du Colombier 	qunlock(s);
25377dd7cddfSDavid du Colombier 	poperror();
25387dd7cddfSDavid du Colombier 	return;
25397dd7cddfSDavid du Colombier raise:
25407dd7cddfSDavid du Colombier 	qunlock(s);
25417dd7cddfSDavid du Colombier 	poperror();
25427dd7cddfSDavid du Colombier 	freeblist(bp);
254380ee5cbfSDavid du Colombier 	tcpkick(s);
25447dd7cddfSDavid du Colombier }
25457dd7cddfSDavid du Colombier 
25467dd7cddfSDavid du Colombier /*
254780ee5cbfSDavid du Colombier  *  always enters and exits with the s locked.  We drop
254880ee5cbfSDavid du Colombier  *  the lock to ipoput the packet so some care has to be
254980ee5cbfSDavid du Colombier  *  taken by callers.
25507dd7cddfSDavid du Colombier  */
25511ce6c95fSDavid du Colombier static void
tcpoutput(Conv * s)25527dd7cddfSDavid du Colombier tcpoutput(Conv *s)
25537dd7cddfSDavid du Colombier {
25547dd7cddfSDavid du Colombier 	Tcp seg;
25551ce6c95fSDavid du Colombier 	uint msgs;
25567dd7cddfSDavid du Colombier 	Tcpctl *tcb;
25577dd7cddfSDavid du Colombier 	Block *hbp, *bp;
25581ce6c95fSDavid du Colombier 	int sndcnt;
25591ce6c95fSDavid du Colombier 	ulong ssize, dsize, sent;
25607dd7cddfSDavid du Colombier 	Fs *f;
25617dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
25623ff48bf5SDavid du Colombier 	uchar version;
25637dd7cddfSDavid du Colombier 
25647dd7cddfSDavid du Colombier 	f = s->p->f;
25657dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
25663ff48bf5SDavid du Colombier 	version = s->ipversion;
25677dd7cddfSDavid du Colombier 
25687dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
25697dd7cddfSDavid du Colombier 
25701ce6c95fSDavid du Colombier 	/* force ack every 2*mss */
25711ce6c95fSDavid du Colombier 	if((tcb->flags & FORCE) == 0 &&
25721ce6c95fSDavid du Colombier 	    tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
25731ce6c95fSDavid du Colombier 		tpriv->stats[Delayack]++;
25741ce6c95fSDavid du Colombier 		tcb->flags |= FORCE;
25751ce6c95fSDavid du Colombier 	}
25761ce6c95fSDavid du Colombier 
25771ce6c95fSDavid du Colombier 	/* force ack if window opening */
25781ce6c95fSDavid du Colombier 	if((tcb->flags & FORCE) == 0){
25791ce6c95fSDavid du Colombier 		tcprcvwin(s);
25801ce6c95fSDavid du Colombier 		if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
25811ce6c95fSDavid du Colombier 			tpriv->stats[Wopenack]++;
25821ce6c95fSDavid du Colombier 			tcb->flags |= FORCE;
25831ce6c95fSDavid du Colombier 		}
25841ce6c95fSDavid du Colombier 	}
25851ce6c95fSDavid du Colombier 
25861ce6c95fSDavid du Colombier 	for(msgs = 0; msgs < 100; msgs++) {
25877dd7cddfSDavid du Colombier 		switch(tcb->state) {
25887dd7cddfSDavid du Colombier 		case Listen:
25897dd7cddfSDavid du Colombier 		case Closed:
25907dd7cddfSDavid du Colombier 		case Finwait2:
2591e6c6b7f8SDavid du Colombier 			return;
25927dd7cddfSDavid du Colombier 		}
25937dd7cddfSDavid du Colombier 
25941ce6c95fSDavid du Colombier 		/* Don't send anything else until our SYN has been acked */
25951ce6c95fSDavid du Colombier 		if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
25961ce6c95fSDavid du Colombier 			break;
25971ce6c95fSDavid du Colombier 
25987dd7cddfSDavid du Colombier 		/* force an ack when a window has opened up */
25991ce6c95fSDavid du Colombier 		tcprcvwin(s);
26007dd7cddfSDavid du Colombier 		if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26017dd7cddfSDavid du Colombier 			tcb->rcv.blocked = 0;
26027dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
26037dd7cddfSDavid du Colombier 		}
26047dd7cddfSDavid du Colombier 
260580ee5cbfSDavid du Colombier 		sndcnt = qlen(s->wq)+tcb->flgcnt;
26067dd7cddfSDavid du Colombier 		sent = tcb->snd.ptr - tcb->snd.una;
26071ce6c95fSDavid du Colombier 		ssize = sndcnt;
26087dd7cddfSDavid du Colombier 		if(tcb->snd.wnd == 0){
26091ce6c95fSDavid du Colombier 			/* zero window probe */
26101ce6c95fSDavid du Colombier 			if(sent > 0 && !(tcb->flags & FORCE))
26111ce6c95fSDavid du Colombier 				break;	/* already probing, rto re-probes */
26121ce6c95fSDavid du Colombier 			if(ssize < sent)
26131ce6c95fSDavid du Colombier 				ssize = 0;
26147dd7cddfSDavid du Colombier 			else{
26151ce6c95fSDavid du Colombier 				ssize -= sent;
26161ce6c95fSDavid du Colombier 				if(ssize > 0)
26171ce6c95fSDavid du Colombier 					ssize = 1;
26187dd7cddfSDavid du Colombier 			}
26191ce6c95fSDavid du Colombier 		} else {
26201ce6c95fSDavid du Colombier 			/* calculate usable segment size */
26211ce6c95fSDavid du Colombier 			if(ssize > tcb->cwind)
26221ce6c95fSDavid du Colombier 				ssize = tcb->cwind;
26231ce6c95fSDavid du Colombier 			if(ssize > tcb->snd.wnd)
26241ce6c95fSDavid du Colombier 				ssize = tcb->snd.wnd;
26251ce6c95fSDavid du Colombier 
26261ce6c95fSDavid du Colombier 			if(ssize < sent)
26271ce6c95fSDavid du Colombier 				ssize = 0;
26281ce6c95fSDavid du Colombier 			else {
26291ce6c95fSDavid du Colombier 				ssize -= sent;
26301ce6c95fSDavid du Colombier 				if(ssize > tcb->mss)
26317dd7cddfSDavid du Colombier 					ssize = tcb->mss;
26321ce6c95fSDavid du Colombier 			}
26331ce6c95fSDavid du Colombier 		}
26341ce6c95fSDavid du Colombier 
26357dd7cddfSDavid du Colombier 		dsize = ssize;
26367dd7cddfSDavid du Colombier 		seg.urg = 0;
26377dd7cddfSDavid du Colombier 
26381ce6c95fSDavid du Colombier 		if(!(tcb->flags & FORCE))
26391ce6c95fSDavid du Colombier 			if(ssize == 0 ||
26401ce6c95fSDavid du Colombier 			    ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26411ce6c95fSDavid du Colombier 			    sent > TCPREXMTTHRESH * tcb->mss)
26427dd7cddfSDavid du Colombier 				break;
26437dd7cddfSDavid du Colombier 
26447dd7cddfSDavid du Colombier 		tcb->flags &= ~FORCE;
26457dd7cddfSDavid du Colombier 
26467dd7cddfSDavid du Colombier 		/* By default we will generate an ack */
26473f695129SDavid du Colombier 		tcphalt(tpriv, &tcb->acktimer);
26487dd7cddfSDavid du Colombier 		seg.source = s->lport;
26497dd7cddfSDavid du Colombier 		seg.dest = s->rport;
26507dd7cddfSDavid du Colombier 		seg.flags = ACK;
26517dd7cddfSDavid du Colombier 		seg.mss = 0;
26523f695129SDavid du Colombier 		seg.ws = 0;
26531ce6c95fSDavid du Colombier 		seg.update = 0;
26547dd7cddfSDavid du Colombier 		switch(tcb->state){
26557dd7cddfSDavid du Colombier 		case Syn_sent:
26567dd7cddfSDavid du Colombier 			seg.flags = 0;
26577dd7cddfSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
26587dd7cddfSDavid du Colombier 				seg.flags |= SYN;
26597dd7cddfSDavid du Colombier 				dsize--;
26603f695129SDavid du Colombier 				seg.mss = tcb->mss;
26613f695129SDavid du Colombier 				seg.ws = tcb->scale;
26627dd7cddfSDavid du Colombier 			}
26637dd7cddfSDavid du Colombier 			break;
26647dd7cddfSDavid du Colombier 		case Syn_received:
26657dd7cddfSDavid du Colombier 			/*
26667dd7cddfSDavid du Colombier 			 *  don't send any data with a SYN/ACK packet
26677dd7cddfSDavid du Colombier 			 *  because Linux rejects the packet in its
26687dd7cddfSDavid du Colombier 			 *  attempt to solve the SYN attack problem
26697dd7cddfSDavid du Colombier 			 */
26707dd7cddfSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
26717dd7cddfSDavid du Colombier 				seg.flags |= SYN;
26727dd7cddfSDavid du Colombier 				dsize = 0;
26737dd7cddfSDavid du Colombier 				ssize = 1;
26743f695129SDavid du Colombier 				seg.mss = tcb->mss;
26753f695129SDavid du Colombier 				seg.ws = tcb->scale;
26767dd7cddfSDavid du Colombier 			}
26777dd7cddfSDavid du Colombier 			break;
26787dd7cddfSDavid du Colombier 		}
26797dd7cddfSDavid du Colombier 		seg.seq = tcb->snd.ptr;
26807dd7cddfSDavid du Colombier 		seg.ack = tcb->rcv.nxt;
26817dd7cddfSDavid du Colombier 		seg.wnd = tcb->rcv.wnd;
26827dd7cddfSDavid du Colombier 
26837dd7cddfSDavid du Colombier 		/* Pull out data to send */
26847dd7cddfSDavid du Colombier 		bp = nil;
26857dd7cddfSDavid du Colombier 		if(dsize != 0) {
26867dd7cddfSDavid du Colombier 			bp = qcopy(s->wq, dsize, sent);
26877dd7cddfSDavid du Colombier 			if(BLEN(bp) != dsize) {
26887dd7cddfSDavid du Colombier 				seg.flags |= FIN;
26897dd7cddfSDavid du Colombier 				dsize--;
26907dd7cddfSDavid du Colombier 			}
26917dd7cddfSDavid du Colombier 		}
26927dd7cddfSDavid du Colombier 
26931ce6c95fSDavid du Colombier 		if(sent+dsize == sndcnt && dsize)
26947dd7cddfSDavid du Colombier 			seg.flags |= PSH;
26957dd7cddfSDavid du Colombier 
26967dd7cddfSDavid du Colombier 		tcb->snd.ptr += ssize;
26977dd7cddfSDavid du Colombier 
26987dd7cddfSDavid du Colombier 		/* Pull up the send pointer so we can accept acks
26997dd7cddfSDavid du Colombier 		 * for this window
27007dd7cddfSDavid du Colombier 		 */
27017dd7cddfSDavid du Colombier 		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27027dd7cddfSDavid du Colombier 			tcb->snd.nxt = tcb->snd.ptr;
27037dd7cddfSDavid du Colombier 
27047dd7cddfSDavid du Colombier 		/* Build header, link data and compute cksum */
27053ff48bf5SDavid du Colombier 		switch(version){
27063ff48bf5SDavid du Colombier 		case V4:
27073ff48bf5SDavid du Colombier 			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27083ff48bf5SDavid du Colombier 			hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27097dd7cddfSDavid du Colombier 			if(hbp == nil) {
27107dd7cddfSDavid du Colombier 				freeblist(bp);
2711e6c6b7f8SDavid du Colombier 				return;
27127dd7cddfSDavid du Colombier 			}
27133ff48bf5SDavid du Colombier 			break;
27143ff48bf5SDavid du Colombier 		case V6:
27153ff48bf5SDavid du Colombier 			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27163ff48bf5SDavid du Colombier 			hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27173ff48bf5SDavid du Colombier 			if(hbp == nil) {
27183ff48bf5SDavid du Colombier 				freeblist(bp);
2719e6c6b7f8SDavid du Colombier 				return;
27203ff48bf5SDavid du Colombier 			}
27213ff48bf5SDavid du Colombier 			break;
27223ff48bf5SDavid du Colombier 		default:
27233ff48bf5SDavid du Colombier 			hbp = nil;	/* to suppress a warning */
27243ff48bf5SDavid du Colombier 			panic("tcpoutput: version %d", version);
27253ff48bf5SDavid du Colombier 		}
27267dd7cddfSDavid du Colombier 
27277dd7cddfSDavid du Colombier 		/* Start the transmission timers if there is new data and we
27287dd7cddfSDavid du Colombier 		 * expect acknowledges
27297dd7cddfSDavid du Colombier 		 */
27307dd7cddfSDavid du Colombier 		if(ssize != 0){
27311ce6c95fSDavid du Colombier 			if(tcb->timer.state != TcptimerON){
27321ce6c95fSDavid du Colombier 				tcb->time = NOW;
27331ce6c95fSDavid du Colombier 				tcb->timeuna = tcb->snd.una;
27347dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
27351ce6c95fSDavid du Colombier 			}
27367dd7cddfSDavid du Colombier 
27377dd7cddfSDavid du Colombier 			/*  If round trip timer isn't running, start it.
27387dd7cddfSDavid du Colombier 			 *  measure the longest packet only in case the
27397dd7cddfSDavid du Colombier 			 *  transmission time dominates RTT
27407dd7cddfSDavid du Colombier 			 */
27411ce6c95fSDavid du Colombier 			if(tcb->snd.retransmit == 0)
27429a747e4fSDavid du Colombier 			if(tcb->rtt_timer.state != TcptimerON)
27437dd7cddfSDavid du Colombier 			if(ssize == tcb->mss) {
27447dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->rtt_timer);
27457dd7cddfSDavid du Colombier 				tcb->rttseq = tcb->snd.ptr;
27467dd7cddfSDavid du Colombier 			}
27477dd7cddfSDavid du Colombier 		}
27487dd7cddfSDavid du Colombier 
274959cc4ca5SDavid du Colombier 		tpriv->stats[OutSegs]++;
27501ce6c95fSDavid du Colombier 		if(tcb->snd.retransmit)
27511ce6c95fSDavid du Colombier 			tpriv->stats[RetransSegsSent]++;
27521ce6c95fSDavid du Colombier 		tcb->rcv.ackptr = seg.ack;
27531ce6c95fSDavid du Colombier 		tcb->rcv.wsnt = tcb->rcv.wptr;
275440ef9009SDavid du Colombier 
275540ef9009SDavid du Colombier 		/* put off the next keep alive */
27567dd7cddfSDavid du Colombier 		tcpgo(tpriv, &tcb->katimer);
27573ff48bf5SDavid du Colombier 
27583ff48bf5SDavid du Colombier 		switch(version){
27593ff48bf5SDavid du Colombier 		case V4:
2760a6a9e072SDavid du Colombier 			if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
2761e6c6b7f8SDavid du Colombier 				/* a negative return means no route */
2762e6c6b7f8SDavid du Colombier 				localclose(s, "no route");
2763e6c6b7f8SDavid du Colombier 			}
27643ff48bf5SDavid du Colombier 			break;
27653ff48bf5SDavid du Colombier 		case V6:
2766a6a9e072SDavid du Colombier 			if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
2767e6c6b7f8SDavid du Colombier 				/* a negative return means no route */
2768e6c6b7f8SDavid du Colombier 				localclose(s, "no route");
2769e6c6b7f8SDavid du Colombier 			}
27703ff48bf5SDavid du Colombier 			break;
27713ff48bf5SDavid du Colombier 		default:
27723ff48bf5SDavid du Colombier 			panic("tcpoutput2: version %d", version);
277380ee5cbfSDavid du Colombier 		}
27741ce6c95fSDavid du Colombier 		if((msgs%4) == 3){
27753ff48bf5SDavid du Colombier 			qunlock(s);
277680ee5cbfSDavid du Colombier 			qlock(s);
27773ff48bf5SDavid du Colombier 		}
27787dd7cddfSDavid du Colombier 	}
27797dd7cddfSDavid du Colombier }
27807dd7cddfSDavid du Colombier 
27817dd7cddfSDavid du Colombier /*
27827dd7cddfSDavid du Colombier  *  the BSD convention (hack?) for keep alives.  resend last uchar acked.
27837dd7cddfSDavid du Colombier  */
27841ce6c95fSDavid du Colombier static void
tcpsendka(Conv * s)27857dd7cddfSDavid du Colombier tcpsendka(Conv *s)
27867dd7cddfSDavid du Colombier {
27877dd7cddfSDavid du Colombier 	Tcp seg;
27887dd7cddfSDavid du Colombier 	Tcpctl *tcb;
27897dd7cddfSDavid du Colombier 	Block *hbp,*dbp;
27907dd7cddfSDavid du Colombier 
27917dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
27927dd7cddfSDavid du Colombier 
27937dd7cddfSDavid du Colombier 	dbp = nil;
279410ae047aSDavid du Colombier 	memset(&seg, 0, sizeof seg);
27957dd7cddfSDavid du Colombier 	seg.urg = 0;
27967dd7cddfSDavid du Colombier 	seg.source = s->lport;
27977dd7cddfSDavid du Colombier 	seg.dest = s->rport;
27987dd7cddfSDavid du Colombier 	seg.flags = ACK|PSH;
27997dd7cddfSDavid du Colombier 	seg.mss = 0;
28003f695129SDavid du Colombier 	seg.ws = 0;
28013ff48bf5SDavid du Colombier 	if(tcpporthogdefense)
28023ff48bf5SDavid du Colombier 		seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28033ff48bf5SDavid du Colombier 	else
28047dd7cddfSDavid du Colombier 		seg.seq = tcb->snd.una-1;
28057dd7cddfSDavid du Colombier 	seg.ack = tcb->rcv.nxt;
28061ce6c95fSDavid du Colombier 	tcb->rcv.ackptr = seg.ack;
28071ce6c95fSDavid du Colombier 	tcprcvwin(s);
28087dd7cddfSDavid du Colombier 	seg.wnd = tcb->rcv.wnd;
28097dd7cddfSDavid du Colombier 	if(tcb->state == Finwait2){
28107dd7cddfSDavid du Colombier 		seg.flags |= FIN;
28117dd7cddfSDavid du Colombier 	} else {
28127dd7cddfSDavid du Colombier 		dbp = allocb(1);
28137dd7cddfSDavid du Colombier 		dbp->wp++;
28147dd7cddfSDavid du Colombier 	}
28157dd7cddfSDavid du Colombier 
28163ff48bf5SDavid du Colombier 	if(isv4(s->raddr)) {
28177dd7cddfSDavid du Colombier 		/* Build header, link data and compute cksum */
28183ff48bf5SDavid du Colombier 		tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28193ff48bf5SDavid du Colombier 		hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28207dd7cddfSDavid du Colombier 		if(hbp == nil) {
28217dd7cddfSDavid du Colombier 			freeblist(dbp);
28227dd7cddfSDavid du Colombier 			return;
28237dd7cddfSDavid du Colombier 		}
2824a6a9e072SDavid du Colombier 		ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28253ff48bf5SDavid du Colombier 	}
28263ff48bf5SDavid du Colombier 	else {
28273ff48bf5SDavid du Colombier 		/* Build header, link data and compute cksum */
28283ff48bf5SDavid du Colombier 		tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28293ff48bf5SDavid du Colombier 		hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28303ff48bf5SDavid du Colombier 		if(hbp == nil) {
28313ff48bf5SDavid du Colombier 			freeblist(dbp);
28323ff48bf5SDavid du Colombier 			return;
28333ff48bf5SDavid du Colombier 		}
2834a6a9e072SDavid du Colombier 		ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28353ff48bf5SDavid du Colombier 	}
28367dd7cddfSDavid du Colombier }
28377dd7cddfSDavid du Colombier 
28387dd7cddfSDavid du Colombier /*
283940ef9009SDavid du Colombier  *  set connection to time out after 12 minutes
28407dd7cddfSDavid du Colombier  */
28411ce6c95fSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28427dd7cddfSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28437dd7cddfSDavid du Colombier {
2844e288d156SDavid du Colombier 	tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28457dd7cddfSDavid du Colombier 	if(tcb->kacounter < 3)
28467dd7cddfSDavid du Colombier 		tcb->kacounter = 3;
28477dd7cddfSDavid du Colombier }
284840ef9009SDavid du Colombier 
284940ef9009SDavid du Colombier /*
285040ef9009SDavid du Colombier  *  if we've timed out, close the connection
285140ef9009SDavid du Colombier  *  otherwise, send a keepalive and restart the timer
285240ef9009SDavid du Colombier  */
28531ce6c95fSDavid du Colombier static void
tcpkeepalive(void * v)28547dd7cddfSDavid du Colombier tcpkeepalive(void *v)
28557dd7cddfSDavid du Colombier {
28567dd7cddfSDavid du Colombier 	Tcpctl *tcb;
28577dd7cddfSDavid du Colombier 	Conv *s;
28587dd7cddfSDavid du Colombier 
28597dd7cddfSDavid du Colombier 	s = v;
28607dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
28617dd7cddfSDavid du Colombier 	if(waserror()){
28627dd7cddfSDavid du Colombier 		qunlock(s);
28637dd7cddfSDavid du Colombier 		nexterror();
28647dd7cddfSDavid du Colombier 	}
28657dd7cddfSDavid du Colombier 	qlock(s);
28667dd7cddfSDavid du Colombier 	if(tcb->state != Closed){
28677dd7cddfSDavid du Colombier 		if(--(tcb->kacounter) <= 0) {
28687dd7cddfSDavid du Colombier 			localclose(s, Etimedout);
28697dd7cddfSDavid du Colombier 		} else {
28707dd7cddfSDavid du Colombier 			tcpsendka(s);
28717dd7cddfSDavid du Colombier 			tcpgo(s->p->priv, &tcb->katimer);
28727dd7cddfSDavid du Colombier 		}
28737dd7cddfSDavid du Colombier 	}
28747dd7cddfSDavid du Colombier 	qunlock(s);
28757dd7cddfSDavid du Colombier 	poperror();
28767dd7cddfSDavid du Colombier }
28777dd7cddfSDavid du Colombier 
28787dd7cddfSDavid du Colombier /*
28797dd7cddfSDavid du Colombier  *  start keepalive timer
28807dd7cddfSDavid du Colombier  */
28811ce6c95fSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)28827dd7cddfSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
28837dd7cddfSDavid du Colombier {
28847dd7cddfSDavid du Colombier 	Tcpctl *tcb;
28857dd7cddfSDavid du Colombier 	int x;
28867dd7cddfSDavid du Colombier 
28877dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
288840ef9009SDavid du Colombier 	if(tcb->state != Established)
288940ef9009SDavid du Colombier 		return "connection must be in Establised state";
28907dd7cddfSDavid du Colombier 	if(n > 1){
28917dd7cddfSDavid du Colombier 		x = atoi(f[1]);
28927dd7cddfSDavid du Colombier 		if(x >= MSPTICK)
28937dd7cddfSDavid du Colombier 			tcb->katimer.start = x/MSPTICK;
28947dd7cddfSDavid du Colombier 	}
28957dd7cddfSDavid du Colombier 	tcpsetkacounter(tcb);
28967dd7cddfSDavid du Colombier 	tcpgo(s->p->priv, &tcb->katimer);
28977dd7cddfSDavid du Colombier 
28987dd7cddfSDavid du Colombier 	return nil;
28997dd7cddfSDavid du Colombier }
29007dd7cddfSDavid du Colombier 
290159cc4ca5SDavid du Colombier /*
290259cc4ca5SDavid du Colombier  *  turn checksums on/off
290359cc4ca5SDavid du Colombier  */
29041ce6c95fSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)290559cc4ca5SDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
290659cc4ca5SDavid du Colombier {
290759cc4ca5SDavid du Colombier 	Tcpctl *tcb;
290859cc4ca5SDavid du Colombier 
290959cc4ca5SDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
291059cc4ca5SDavid du Colombier 	tcb->nochecksum = !atoi(f[1]);
291159cc4ca5SDavid du Colombier 
291259cc4ca5SDavid du Colombier 	return nil;
291359cc4ca5SDavid du Colombier }
291459cc4ca5SDavid du Colombier 
29151ce6c95fSDavid du Colombier /*
29161ce6c95fSDavid du Colombier  *  retransmit (at most) one segment at snd.una.
29171ce6c95fSDavid du Colombier  *  preserve cwind & snd.ptr
29181ce6c95fSDavid du Colombier  */
29191ce6c95fSDavid du Colombier static void
tcprxmit(Conv * s)29207dd7cddfSDavid du Colombier tcprxmit(Conv *s)
29217dd7cddfSDavid du Colombier {
29227dd7cddfSDavid du Colombier 	Tcpctl *tcb;
29231ce6c95fSDavid du Colombier 	Tcppriv *tpriv;
29241ce6c95fSDavid du Colombier 	ulong tcwind, tptr;
29257dd7cddfSDavid du Colombier 
29267dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29277dd7cddfSDavid du Colombier 	tcb->flags |= RETRAN|FORCE;
29281ce6c95fSDavid du Colombier 
29291ce6c95fSDavid du Colombier 	tptr = tcb->snd.ptr;
29301ce6c95fSDavid du Colombier 	tcwind = tcb->cwind;
29317dd7cddfSDavid du Colombier 	tcb->snd.ptr = tcb->snd.una;
29327dd7cddfSDavid du Colombier 	tcb->cwind = tcb->mss;
29331ce6c95fSDavid du Colombier 	tcb->snd.retransmit = 1;
29347dd7cddfSDavid du Colombier 	tcpoutput(s);
29351ce6c95fSDavid du Colombier 	tcb->snd.retransmit = 0;
29361ce6c95fSDavid du Colombier 	tcb->cwind = tcwind;
29371ce6c95fSDavid du Colombier 	tcb->snd.ptr = tptr;
29381ce6c95fSDavid du Colombier 
29391ce6c95fSDavid du Colombier 	tpriv = s->p->priv;
29401ce6c95fSDavid du Colombier 	tpriv->stats[RetransSegs]++;
29417dd7cddfSDavid du Colombier }
29427dd7cddfSDavid du Colombier 
29431ce6c95fSDavid du Colombier /*
29441ce6c95fSDavid du Colombier  *  TODO: RFC 4138 F-RTO
29451ce6c95fSDavid du Colombier  */
29461ce6c95fSDavid du Colombier static void
tcptimeout(void * arg)29477dd7cddfSDavid du Colombier tcptimeout(void *arg)
29487dd7cddfSDavid du Colombier {
29497dd7cddfSDavid du Colombier 	Conv *s;
29507dd7cddfSDavid du Colombier 	Tcpctl *tcb;
29517dd7cddfSDavid du Colombier 	int maxback;
29527dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
29537dd7cddfSDavid du Colombier 
29547dd7cddfSDavid du Colombier 	s = (Conv*)arg;
29557dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
29567dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29577dd7cddfSDavid du Colombier 
29587dd7cddfSDavid du Colombier 	if(waserror()){
29597dd7cddfSDavid du Colombier 		qunlock(s);
29607dd7cddfSDavid du Colombier 		nexterror();
29617dd7cddfSDavid du Colombier 	}
29627dd7cddfSDavid du Colombier 	qlock(s);
29637dd7cddfSDavid du Colombier 	switch(tcb->state){
29647dd7cddfSDavid du Colombier 	default:
29657dd7cddfSDavid du Colombier 		tcb->backoff++;
29667dd7cddfSDavid du Colombier 		if(tcb->state == Syn_sent)
29677dd7cddfSDavid du Colombier 			maxback = MAXBACKMS/2;
29687dd7cddfSDavid du Colombier 		else
29697dd7cddfSDavid du Colombier 			maxback = MAXBACKMS;
29707dd7cddfSDavid du Colombier 		tcb->backedoff += tcb->timer.start * MSPTICK;
29717dd7cddfSDavid du Colombier 		if(tcb->backedoff >= maxback) {
29727dd7cddfSDavid du Colombier 			localclose(s, Etimedout);
29737dd7cddfSDavid du Colombier 			break;
29747dd7cddfSDavid du Colombier 		}
29751ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
29761ce6c95fSDavid du Colombier 			tcb->srtt, tcb->mdev, NOW - tcb->time,
29771ce6c95fSDavid du Colombier 			tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
29781ce6c95fSDavid du Colombier 			tcpstates[s->state]);
29799a747e4fSDavid du Colombier 		tcpsettimer(tcb);
29801ce6c95fSDavid du Colombier 		if(tcb->snd.rto == 0)
29811ce6c95fSDavid du Colombier 			tcpcongestion(tcb);
29827dd7cddfSDavid du Colombier 		tcprxmit(s);
29831ce6c95fSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
29841ce6c95fSDavid du Colombier 		tcb->cwind = tcb->mss;
29851ce6c95fSDavid du Colombier 		tcb->snd.rto = 1;
298659cc4ca5SDavid du Colombier 		tpriv->stats[RetransTimeouts]++;
29871ce6c95fSDavid du Colombier 
29881ce6c95fSDavid du Colombier 		if(tcb->snd.recovery){
29891ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;		/* reno rto */
29901ce6c95fSDavid du Colombier 			tcb->snd.recovery = 0;
29911ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryRTO]++;
29921ce6c95fSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
29931ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin,
29941ce6c95fSDavid du Colombier 				"rto recovery rxt @%lud\n", tcb->snd.nxt);
29951ce6c95fSDavid du Colombier 		}
29961ce6c95fSDavid du Colombier 
29971ce6c95fSDavid du Colombier 		tcb->abcbytes = 0;
29987dd7cddfSDavid du Colombier 		break;
29997dd7cddfSDavid du Colombier 	case Time_wait:
30007dd7cddfSDavid du Colombier 		localclose(s, nil);
30017dd7cddfSDavid du Colombier 		break;
30027dd7cddfSDavid du Colombier 	case Closed:
30037dd7cddfSDavid du Colombier 		break;
30047dd7cddfSDavid du Colombier 	}
30057dd7cddfSDavid du Colombier 	qunlock(s);
30067dd7cddfSDavid du Colombier 	poperror();
30077dd7cddfSDavid du Colombier }
30087dd7cddfSDavid du Colombier 
30091ce6c95fSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30107dd7cddfSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30117dd7cddfSDavid du Colombier {
30127dd7cddfSDavid du Colombier 	return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30137dd7cddfSDavid du Colombier }
30147dd7cddfSDavid du Colombier 
30153ff48bf5SDavid du Colombier /*
30163ff48bf5SDavid du Colombier  *  set up state for a received SYN (or SYN ACK) packet
30173ff48bf5SDavid du Colombier  */
30181ce6c95fSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30197dd7cddfSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30207dd7cddfSDavid du Colombier {
30217dd7cddfSDavid du Colombier 	Tcpctl *tcb;
30227ec5746aSDavid du Colombier 	Tcppriv *tpriv;
30237dd7cddfSDavid du Colombier 
30247dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
30257dd7cddfSDavid du Colombier 	tcb->flags |= FORCE;
30267dd7cddfSDavid du Colombier 
30277dd7cddfSDavid du Colombier 	tcb->rcv.nxt = seg->seq + 1;
30281ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
30291ce6c95fSDavid du Colombier 	tcb->rcv.wsnt = 0;
30307dd7cddfSDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
30317dd7cddfSDavid du Colombier 	tcb->irs = seg->seq;
30327dd7cddfSDavid du Colombier 
30333ff48bf5SDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
30347ec5746aSDavid du Colombier 	if(seg->mss != 0 && seg->mss < tcb->mss) {
30357dd7cddfSDavid du Colombier 		tcb->mss = seg->mss;
30367ec5746aSDavid du Colombier 		tpriv = s->p->priv;
30377ec5746aSDavid du Colombier 		tpriv->stats[Mss] = tcb->mss;
30387ec5746aSDavid du Colombier 	}
30397dd7cddfSDavid du Colombier 
30403ff48bf5SDavid du Colombier 	tcb->snd.wnd = seg->wnd;
30411ce6c95fSDavid du Colombier 	initialwindow(tcb);
30427dd7cddfSDavid du Colombier }
30437dd7cddfSDavid du Colombier 
30441ce6c95fSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30451ce6c95fSDavid du Colombier dumpreseq(Tcpctl *tcb)
30467dd7cddfSDavid du Colombier {
30471ce6c95fSDavid du Colombier 	Reseq *r, *next;
30487dd7cddfSDavid du Colombier 
30491ce6c95fSDavid du Colombier 	for(r = tcb->reseq; r != nil; r = next){
30501ce6c95fSDavid du Colombier 		next = r->next;
30511ce6c95fSDavid du Colombier 		freeblist(r->bp);
30521ce6c95fSDavid du Colombier 		free(r);
30531ce6c95fSDavid du Colombier 	}
30541ce6c95fSDavid du Colombier 	tcb->reseq = nil;
30551ce6c95fSDavid du Colombier 	tcb->nreseq = 0;
30561ce6c95fSDavid du Colombier 	tcb->reseqlen = 0;
30571ce6c95fSDavid du Colombier 	return -1;
30581ce6c95fSDavid du Colombier }
30591ce6c95fSDavid du Colombier 
30601ce6c95fSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)30611ce6c95fSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
30621ce6c95fSDavid du Colombier {
30631ce6c95fSDavid du Colombier 	char *s;
30641ce6c95fSDavid du Colombier 
30651ce6c95fSDavid du Colombier 	for(; r != nil; r = r->next){
30661ce6c95fSDavid du Colombier 		s = nil;
30671ce6c95fSDavid du Colombier 		if(r->next == nil && r->seg.seq != n)
30681ce6c95fSDavid du Colombier 			s = "hole/end";
30691ce6c95fSDavid du Colombier 		else if(r->next == nil)
30701ce6c95fSDavid du Colombier 			s = "end";
30711ce6c95fSDavid du Colombier 		else if(r->seg.seq != n)
30721ce6c95fSDavid du Colombier 			s = "hole";
30731ce6c95fSDavid du Colombier 		if(s != nil)
30741ce6c95fSDavid du Colombier 			netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
30751ce6c95fSDavid du Colombier 				n, r->seg.seq, r->seg.seq - n, r->seg.flags);
30761ce6c95fSDavid du Colombier 		n = r->seg.seq + r->seg.len;
30771ce6c95fSDavid du Colombier 	}
30781ce6c95fSDavid du Colombier }
30791ce6c95fSDavid du Colombier 
30801ce6c95fSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)30811ce6c95fSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
30821ce6c95fSDavid du Colombier {
30831ce6c95fSDavid du Colombier 	Reseq *rp, **rr;
30841ce6c95fSDavid du Colombier 	int qmax;
30851ce6c95fSDavid du Colombier 
30861ce6c95fSDavid du Colombier 	rp = malloc(sizeof *rp);
30877dd7cddfSDavid du Colombier 	if(rp == nil){
30881ce6c95fSDavid du Colombier 		freeblist(bp);		/* bp always consumed by addreseq */
30897dd7cddfSDavid du Colombier 		return 0;
30907dd7cddfSDavid du Colombier 	}
30917dd7cddfSDavid du Colombier 
30927dd7cddfSDavid du Colombier 	rp->seg = *seg;
30937dd7cddfSDavid du Colombier 	rp->bp = bp;
30947dd7cddfSDavid du Colombier 	rp->length = length;
30957dd7cddfSDavid du Colombier 
30961ce6c95fSDavid du Colombier 	tcb->reseqlen += length;
30971ce6c95fSDavid du Colombier 	tcb->nreseq++;
30981ce6c95fSDavid du Colombier 
30997dd7cddfSDavid du Colombier 	/* Place on reassembly list sorting by starting seq number */
31001ce6c95fSDavid du Colombier 	for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31011ce6c95fSDavid du Colombier 		if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31021ce6c95fSDavid du Colombier 			rp->next = *rr;
31031ce6c95fSDavid du Colombier 			*rr = rp;
31041ce6c95fSDavid du Colombier 			tpriv->stats[Resequenced]++;
31053ff48bf5SDavid du Colombier 			if(rp->next != nil)
31063ff48bf5SDavid du Colombier 				tpriv->stats[OutOfOrder]++;
31077dd7cddfSDavid du Colombier 			break;
31087dd7cddfSDavid du Colombier 		}
3109d7baab40SDavid du Colombier 
31101ce6c95fSDavid du Colombier 	qmax = tcb->window;
31111ce6c95fSDavid du Colombier 	if(tcb->reseqlen > qmax){
31121ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31131ce6c95fSDavid du Colombier 			tcb->reseqlen, qmax, tcb->nreseq);
31141ce6c95fSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31151ce6c95fSDavid du Colombier 		tpriv->stats[ReseqBytelim]++;
31161ce6c95fSDavid du Colombier 		return dumpreseq(tcb);
3117d7baab40SDavid du Colombier 	}
31181ce6c95fSDavid du Colombier 	qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31191ce6c95fSDavid du Colombier 	if(tcb->nreseq > qmax){
31201ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31211ce6c95fSDavid du Colombier 			tcb->nreseq, qmax, tcb->reseqlen);
31221ce6c95fSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31231ce6c95fSDavid du Colombier 		tpriv->stats[ReseqPktlim]++;
31241ce6c95fSDavid du Colombier 		return dumpreseq(tcb);
31257dd7cddfSDavid du Colombier 	}
31267dd7cddfSDavid du Colombier 	return 0;
31277dd7cddfSDavid du Colombier }
31287dd7cddfSDavid du Colombier 
31291ce6c95fSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31307dd7cddfSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31317dd7cddfSDavid du Colombier {
31327dd7cddfSDavid du Colombier 	Reseq *rp;
31337dd7cddfSDavid du Colombier 
31347dd7cddfSDavid du Colombier 	rp = tcb->reseq;
31357dd7cddfSDavid du Colombier 	if(rp == nil)
31367dd7cddfSDavid du Colombier 		return;
31377dd7cddfSDavid du Colombier 
31387dd7cddfSDavid du Colombier 	tcb->reseq = rp->next;
31397dd7cddfSDavid du Colombier 
31407dd7cddfSDavid du Colombier 	*seg = rp->seg;
31417dd7cddfSDavid du Colombier 	*bp = rp->bp;
31427dd7cddfSDavid du Colombier 	*length = rp->length;
31437dd7cddfSDavid du Colombier 
31441ce6c95fSDavid du Colombier 	tcb->nreseq--;
31451ce6c95fSDavid du Colombier 	tcb->reseqlen -= rp->length;
31461ce6c95fSDavid du Colombier 
31477dd7cddfSDavid du Colombier 	free(rp);
31487dd7cddfSDavid du Colombier }
31497dd7cddfSDavid du Colombier 
31501ce6c95fSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31517dd7cddfSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31527dd7cddfSDavid du Colombier {
31537dd7cddfSDavid du Colombier 	ushort len;
31547dd7cddfSDavid du Colombier 	uchar accept;
31557dd7cddfSDavid du Colombier 	int dupcnt, excess;
31567dd7cddfSDavid du Colombier 
31577dd7cddfSDavid du Colombier 	accept = 0;
31587dd7cddfSDavid du Colombier 	len = *length;
31597dd7cddfSDavid du Colombier 	if(seg->flags & SYN)
31607dd7cddfSDavid du Colombier 		len++;
31617dd7cddfSDavid du Colombier 	if(seg->flags & FIN)
31627dd7cddfSDavid du Colombier 		len++;
31637dd7cddfSDavid du Colombier 
31647dd7cddfSDavid du Colombier 	if(tcb->rcv.wnd == 0) {
31657dd7cddfSDavid du Colombier 		if(len == 0 && seg->seq == tcb->rcv.nxt)
31667dd7cddfSDavid du Colombier 			return 0;
31677dd7cddfSDavid du Colombier 	}
31687dd7cddfSDavid du Colombier 	else {
31697dd7cddfSDavid du Colombier 		/* Some part of the segment should be in the window */
31707dd7cddfSDavid du Colombier 		if(inwindow(tcb,seg->seq))
31717dd7cddfSDavid du Colombier 			accept++;
31727dd7cddfSDavid du Colombier 		else
31737dd7cddfSDavid du Colombier 		if(len != 0) {
31747dd7cddfSDavid du Colombier 			if(inwindow(tcb, seg->seq+len-1) ||
31757dd7cddfSDavid du Colombier 			seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
31767dd7cddfSDavid du Colombier 				accept++;
31777dd7cddfSDavid du Colombier 		}
31787dd7cddfSDavid du Colombier 	}
31797dd7cddfSDavid du Colombier 	if(!accept) {
31807dd7cddfSDavid du Colombier 		freeblist(*bp);
31817dd7cddfSDavid du Colombier 		return -1;
31827dd7cddfSDavid du Colombier 	}
31837dd7cddfSDavid du Colombier 	dupcnt = tcb->rcv.nxt - seg->seq;
31847dd7cddfSDavid du Colombier 	if(dupcnt > 0){
31857dd7cddfSDavid du Colombier 		tcb->rerecv += dupcnt;
31867dd7cddfSDavid du Colombier 		if(seg->flags & SYN){
31877dd7cddfSDavid du Colombier 			seg->flags &= ~SYN;
31887dd7cddfSDavid du Colombier 			seg->seq++;
31897dd7cddfSDavid du Colombier 
31907dd7cddfSDavid du Colombier 			if(seg->urg > 1)
31917dd7cddfSDavid du Colombier 				seg->urg--;
31927dd7cddfSDavid du Colombier 			else
31937dd7cddfSDavid du Colombier 				seg->flags &= ~URG;
31947dd7cddfSDavid du Colombier 			dupcnt--;
31957dd7cddfSDavid du Colombier 		}
31967dd7cddfSDavid du Colombier 		if(dupcnt > 0){
31977dd7cddfSDavid du Colombier 			pullblock(bp, (ushort)dupcnt);
31987dd7cddfSDavid du Colombier 			seg->seq += dupcnt;
31997dd7cddfSDavid du Colombier 			*length -= dupcnt;
32007dd7cddfSDavid du Colombier 
32017dd7cddfSDavid du Colombier 			if(seg->urg > dupcnt)
32027dd7cddfSDavid du Colombier 				seg->urg -= dupcnt;
32037dd7cddfSDavid du Colombier 			else {
32047dd7cddfSDavid du Colombier 				seg->flags &= ~URG;
32057dd7cddfSDavid du Colombier 				seg->urg = 0;
32067dd7cddfSDavid du Colombier 			}
32077dd7cddfSDavid du Colombier 		}
32087dd7cddfSDavid du Colombier 	}
32097dd7cddfSDavid du Colombier 	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32107dd7cddfSDavid du Colombier 	if(excess > 0) {
32117dd7cddfSDavid du Colombier 		tcb->rerecv += excess;
32127dd7cddfSDavid du Colombier 		*length -= excess;
321380ee5cbfSDavid du Colombier 		*bp = trimblock(*bp, 0, *length);
321480ee5cbfSDavid du Colombier 		if(*bp == nil)
321580ee5cbfSDavid du Colombier 			panic("presotto is a boofhead");
32167dd7cddfSDavid du Colombier 		seg->flags &= ~FIN;
32177dd7cddfSDavid du Colombier 	}
32187dd7cddfSDavid du Colombier 	return 0;
32197dd7cddfSDavid du Colombier }
32207dd7cddfSDavid du Colombier 
32211ce6c95fSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32227dd7cddfSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32237dd7cddfSDavid du Colombier {
32243ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
32253ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
32267dd7cddfSDavid du Colombier 	Tcpctl *tcb;
32277dd7cddfSDavid du Colombier 	uchar source[IPaddrlen];
32287dd7cddfSDavid du Colombier 	uchar dest[IPaddrlen];
32297dd7cddfSDavid du Colombier 	ushort psource, pdest;
32307dd7cddfSDavid du Colombier 	Conv *s, **p;
32317dd7cddfSDavid du Colombier 
32323ff48bf5SDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
32333ff48bf5SDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
32347dd7cddfSDavid du Colombier 
32353ff48bf5SDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
32363ff48bf5SDavid du Colombier 		v4tov6(dest, h4->tcpdst);
32373ff48bf5SDavid du Colombier 		v4tov6(source, h4->tcpsrc);
32383ff48bf5SDavid du Colombier 		psource = nhgets(h4->tcpsport);
32393ff48bf5SDavid du Colombier 		pdest = nhgets(h4->tcpdport);
32403ff48bf5SDavid du Colombier 	}
32413ff48bf5SDavid du Colombier 	else {
32423ff48bf5SDavid du Colombier 		ipmove(dest, h6->tcpdst);
32433ff48bf5SDavid du Colombier 		ipmove(source, h6->tcpsrc);
32443ff48bf5SDavid du Colombier 		psource = nhgets(h6->tcpsport);
32453ff48bf5SDavid du Colombier 		pdest = nhgets(h6->tcpdport);
32463ff48bf5SDavid du Colombier 	}
32477dd7cddfSDavid du Colombier 
32487dd7cddfSDavid du Colombier 	/* Look for a connection */
32497dd7cddfSDavid du Colombier 	qlock(tcp);
32507dd7cddfSDavid du Colombier 	for(p = tcp->conv; *p; p++) {
32517dd7cddfSDavid du Colombier 		s = *p;
32527dd7cddfSDavid du Colombier 		tcb = (Tcpctl*)s->ptcl;
32537dd7cddfSDavid du Colombier 		if(s->rport == pdest)
32547dd7cddfSDavid du Colombier 		if(s->lport == psource)
32557dd7cddfSDavid du Colombier 		if(tcb->state != Closed)
32567dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dest) == 0)
32577dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, source) == 0){
32587dd7cddfSDavid du Colombier 			qlock(s);
32597dd7cddfSDavid du Colombier 			qunlock(tcp);
32607dd7cddfSDavid du Colombier 			switch(tcb->state){
32617dd7cddfSDavid du Colombier 			case Syn_sent:
32627dd7cddfSDavid du Colombier 				localclose(s, msg);
32637dd7cddfSDavid du Colombier 				break;
32647dd7cddfSDavid du Colombier 			}
32657dd7cddfSDavid du Colombier 			qunlock(s);
32667dd7cddfSDavid du Colombier 			freeblist(bp);
32677dd7cddfSDavid du Colombier 			return;
32687dd7cddfSDavid du Colombier 		}
32697dd7cddfSDavid du Colombier 	}
32707dd7cddfSDavid du Colombier 	qunlock(tcp);
32717dd7cddfSDavid du Colombier 	freeblist(bp);
32727dd7cddfSDavid du Colombier }
32737dd7cddfSDavid du Colombier 
32743f695129SDavid du Colombier static char*
tcpporthogdefensectl(char * val)32753f695129SDavid du Colombier tcpporthogdefensectl(char *val)
32763f695129SDavid du Colombier {
32773f695129SDavid du Colombier 	if(strcmp(val, "on") == 0)
32783f695129SDavid du Colombier 		tcpporthogdefense = 1;
32793f695129SDavid du Colombier 	else if(strcmp(val, "off") == 0)
32803f695129SDavid du Colombier 		tcpporthogdefense = 0;
32813f695129SDavid du Colombier 	else
32823f695129SDavid du Colombier 		return "unknown value for tcpporthogdefense";
32833f695129SDavid du Colombier 	return nil;
32843f695129SDavid du Colombier }
32853f695129SDavid du Colombier 
328680ee5cbfSDavid du Colombier /* called with c qlocked */
32871ce6c95fSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)32887dd7cddfSDavid du Colombier tcpctl(Conv* c, char** f, int n)
32897dd7cddfSDavid du Colombier {
32907dd7cddfSDavid du Colombier 	if(n == 1 && strcmp(f[0], "hangup") == 0)
32917dd7cddfSDavid du Colombier 		return tcphangup(c);
32927dd7cddfSDavid du Colombier 	if(n >= 1 && strcmp(f[0], "keepalive") == 0)
32937dd7cddfSDavid du Colombier 		return tcpstartka(c, f, n);
329459cc4ca5SDavid du Colombier 	if(n >= 1 && strcmp(f[0], "checksum") == 0)
329559cc4ca5SDavid du Colombier 		return tcpsetchecksum(c, f, n);
32963f695129SDavid du Colombier 	if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
32973f695129SDavid du Colombier 		return tcpporthogdefensectl(f[1]);
32987dd7cddfSDavid du Colombier 	return "unknown control request";
32997dd7cddfSDavid du Colombier }
33007dd7cddfSDavid du Colombier 
33011ce6c95fSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33027dd7cddfSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33037dd7cddfSDavid du Colombier {
330459cc4ca5SDavid du Colombier 	Tcppriv *priv;
330559cc4ca5SDavid du Colombier 	char *p, *e;
330659cc4ca5SDavid du Colombier 	int i;
33077dd7cddfSDavid du Colombier 
330859cc4ca5SDavid du Colombier 	priv = tcp->priv;
330959cc4ca5SDavid du Colombier 	p = buf;
331059cc4ca5SDavid du Colombier 	e = p+len;
331159cc4ca5SDavid du Colombier 	for(i = 0; i < Nstats; i++)
33125e27dea9SDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
331359cc4ca5SDavid du Colombier 	return p - buf;
33147dd7cddfSDavid du Colombier }
33157dd7cddfSDavid du Colombier 
33167dd7cddfSDavid du Colombier /*
33177dd7cddfSDavid du Colombier  *  garbage collect any stale conversations:
33187dd7cddfSDavid du Colombier  *	- SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33197dd7cddfSDavid du Colombier  *	- Finwait2 after 5 minutes
33207dd7cddfSDavid du Colombier  *
33217dd7cddfSDavid du Colombier  *  this is called whenever we run out of channels.  Both checks are
33227dd7cddfSDavid du Colombier  *  of questionable validity so we try to use them only when we're
33237dd7cddfSDavid du Colombier  *  up against the wall.
33247dd7cddfSDavid du Colombier  */
33251ce6c95fSDavid du Colombier static int
tcpgc(Proto * tcp)33267dd7cddfSDavid du Colombier tcpgc(Proto *tcp)
33277dd7cddfSDavid du Colombier {
33287dd7cddfSDavid du Colombier 	Conv *c, **pp, **ep;
33297dd7cddfSDavid du Colombier 	int n;
33307dd7cddfSDavid du Colombier 	Tcpctl *tcb;
33317dd7cddfSDavid du Colombier 
33327dd7cddfSDavid du Colombier 
33337dd7cddfSDavid du Colombier 	n = 0;
33347dd7cddfSDavid du Colombier 	ep = &tcp->conv[tcp->nc];
33357dd7cddfSDavid du Colombier 	for(pp = tcp->conv; pp < ep; pp++) {
33367dd7cddfSDavid du Colombier 		c = *pp;
33377dd7cddfSDavid du Colombier 		if(c == nil)
33387dd7cddfSDavid du Colombier 			break;
33397dd7cddfSDavid du Colombier 		if(!canqlock(c))
33407dd7cddfSDavid du Colombier 			continue;
33417dd7cddfSDavid du Colombier 		tcb = (Tcpctl*)c->ptcl;
33427dd7cddfSDavid du Colombier 		switch(tcb->state){
33437dd7cddfSDavid du Colombier 		case Syn_received:
33443ff48bf5SDavid du Colombier 			if(NOW - tcb->time > 5000){
33451ce6c95fSDavid du Colombier 				localclose(c, Etimedout);
33467dd7cddfSDavid du Colombier 				n++;
33477dd7cddfSDavid du Colombier 			}
33487dd7cddfSDavid du Colombier 			break;
33497dd7cddfSDavid du Colombier 		case Finwait2:
33503ff48bf5SDavid du Colombier 			if(NOW - tcb->time > 5*60*1000){
33511ce6c95fSDavid du Colombier 				localclose(c, Etimedout);
33527dd7cddfSDavid du Colombier 				n++;
33537dd7cddfSDavid du Colombier 			}
33547dd7cddfSDavid du Colombier 			break;
33557dd7cddfSDavid du Colombier 		}
33567dd7cddfSDavid du Colombier 		qunlock(c);
33577dd7cddfSDavid du Colombier 	}
33587dd7cddfSDavid du Colombier 	return n;
33597dd7cddfSDavid du Colombier }
33607dd7cddfSDavid du Colombier 
33611ce6c95fSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)33629a747e4fSDavid du Colombier tcpsettimer(Tcpctl *tcb)
33639a747e4fSDavid du Colombier {
33649a747e4fSDavid du Colombier 	int x;
33659a747e4fSDavid du Colombier 
33661b0f96a1SDavid du Colombier 	/* round trip dependency */
33679a747e4fSDavid du Colombier 	x = backoff(tcb->backoff) *
33689a747e4fSDavid du Colombier 		(tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
33699a747e4fSDavid du Colombier 
33701ce6c95fSDavid du Colombier 	/* bounded twixt 0.3 and 64 seconds */
33711ce6c95fSDavid du Colombier 	if(x < 300/MSPTICK)
33721ce6c95fSDavid du Colombier 		x = 300/MSPTICK;
33731b0f96a1SDavid du Colombier 	else if(x > (64000/MSPTICK))
33741b0f96a1SDavid du Colombier 		x = 64000/MSPTICK;
33759a747e4fSDavid du Colombier 	tcb->timer.start = x;
33769a747e4fSDavid du Colombier }
33779a747e4fSDavid du Colombier 
33789a747e4fSDavid du Colombier void
tcpinit(Fs * fs)33797dd7cddfSDavid du Colombier tcpinit(Fs *fs)
33807dd7cddfSDavid du Colombier {
33817dd7cddfSDavid du Colombier 	Proto *tcp;
33827dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
33837dd7cddfSDavid du Colombier 
33847dd7cddfSDavid du Colombier 	tcp = smalloc(sizeof(Proto));
33857dd7cddfSDavid du Colombier 	tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
33867dd7cddfSDavid du Colombier 	tcp->name = "tcp";
33877dd7cddfSDavid du Colombier 	tcp->connect = tcpconnect;
33887dd7cddfSDavid du Colombier 	tcp->announce = tcpannounce;
33897dd7cddfSDavid du Colombier 	tcp->ctl = tcpctl;
33907dd7cddfSDavid du Colombier 	tcp->state = tcpstate;
33917dd7cddfSDavid du Colombier 	tcp->create = tcpcreate;
33927dd7cddfSDavid du Colombier 	tcp->close = tcpclose;
33937dd7cddfSDavid du Colombier 	tcp->rcv = tcpiput;
33947dd7cddfSDavid du Colombier 	tcp->advise = tcpadvise;
33957dd7cddfSDavid du Colombier 	tcp->stats = tcpstats;
33967dd7cddfSDavid du Colombier 	tcp->inuse = tcpinuse;
33977dd7cddfSDavid du Colombier 	tcp->gc = tcpgc;
33987dd7cddfSDavid du Colombier 	tcp->ipproto = IP_TCPPROTO;
33999a747e4fSDavid du Colombier 	tcp->nc = scalednconv();
34007dd7cddfSDavid du Colombier 	tcp->ptclsize = sizeof(Tcpctl);
34019a747e4fSDavid du Colombier 	tpriv->stats[MaxConn] = tcp->nc;
34027dd7cddfSDavid du Colombier 
34037dd7cddfSDavid du Colombier 	Fsproto(fs, tcp);
34047dd7cddfSDavid du Colombier }
34053f695129SDavid du Colombier 
34061ce6c95fSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34073f695129SDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34083f695129SDavid du Colombier {
34091ce6c95fSDavid du Colombier 	/*
34101ce6c95fSDavid du Colombier 	 * guess at reasonable queue sizes.  there's no current way
34111ce6c95fSDavid du Colombier 	 * to know how many nic receive buffers we can safely tie up in the
34121ce6c95fSDavid du Colombier 	 * tcp stack, and we don't adjust our queues to maximize throughput
34131ce6c95fSDavid du Colombier 	 * and minimize bufferbloat.  n.b. the offer (rcvscale) needs to be
34141ce6c95fSDavid du Colombier 	 * respected, but we still control our own buffer commitment by
34151ce6c95fSDavid du Colombier 	 * keeping a seperate qscale.
34161ce6c95fSDavid du Colombier 	 */
34173f695129SDavid du Colombier 	tcb->rcv.scale = rcvscale & 0xff;
34183f695129SDavid du Colombier 	tcb->snd.scale = sndscale & 0xff;
34191ce6c95fSDavid du Colombier 	tcb->qscale = rcvscale & 0xff;
34201ce6c95fSDavid du Colombier 	if(rcvscale > Maxqscale)
34211ce6c95fSDavid du Colombier 		tcb->qscale = Maxqscale;
34221ce6c95fSDavid du Colombier 
34231ce6c95fSDavid du Colombier 	if(rcvscale != tcb->rcv.scale)
34241ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34251ce6c95fSDavid du Colombier 			"qlen %d >> window %ud lport %d\n",
34261ce6c95fSDavid du Colombier 			tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34271ce6c95fSDavid du Colombier 	tcb->window = QMAX << tcb->qscale;
34281ce6c95fSDavid du Colombier 	tcb->ssthresh = tcb->window;
34291ce6c95fSDavid du Colombier 
34301ce6c95fSDavid du Colombier 	/*
34311ce6c95fSDavid du Colombier 	 * it's important to set wq large enough to cover the full
34321ce6c95fSDavid du Colombier 	 * bandwidth-delay product.  it's possible to be in loss
34331ce6c95fSDavid du Colombier 	 * recovery with a big window, and we need to keep sending
34341ce6c95fSDavid du Colombier 	 * into the inflated window.  the difference can be huge
34351ce6c95fSDavid du Colombier 	 * for even modest (70ms) ping times.
34361ce6c95fSDavid du Colombier 	 */
34373f695129SDavid du Colombier 	qsetlimit(s->rq, tcb->window);
34381ce6c95fSDavid du Colombier 	qsetlimit(s->wq, tcb->window);
34391ce6c95fSDavid du Colombier 	tcprcvwin(s);
34403f695129SDavid du Colombier }
3441