xref: /plan9-contrib/sys/src/9/ip/tcp.c (revision 41aa733568302a52b3e933f0702c3962684de9a2)
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];
1318e860520SDavid 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];
1538e860520SDavid 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 
542ddc91d4cSDavid du Colombier static void
tcpclosestate(Conv * c,Tcpctl * tcb,int state)543ddc91d4cSDavid du Colombier tcpclosestate(Conv *c, Tcpctl *tcb, int state)
544ddc91d4cSDavid du Colombier {
545ddc91d4cSDavid du Colombier 	tcb->flgcnt++;
546ddc91d4cSDavid du Colombier 	tcb->snd.nxt++;
547ddc91d4cSDavid du Colombier 	tcpsetstate(c, state);
548ddc91d4cSDavid du Colombier 	tcpoutput(c);
549ddc91d4cSDavid du Colombier }
550ddc91d4cSDavid du Colombier 
551ddc91d4cSDavid du Colombier /* close the output half of a tcp connection */
552ddc91d4cSDavid du Colombier static char *
tcpxmitclose(Conv * c)553ddc91d4cSDavid du Colombier tcpxmitclose(Conv *c)
554ddc91d4cSDavid du Colombier {
555ddc91d4cSDavid du Colombier 	Tcpctl *tcb;
556ddc91d4cSDavid du Colombier 
557ddc91d4cSDavid du Colombier 	qhangup(c->wq, nil);
558ddc91d4cSDavid du Colombier 
559ddc91d4cSDavid du Colombier 	tcb = (Tcpctl*)c->ptcl;
560ddc91d4cSDavid du Colombier 	switch(tcb->state) {
561ddc91d4cSDavid du Colombier 	case Listen:
562ddc91d4cSDavid du Colombier 		/*
563ddc91d4cSDavid du Colombier 		 *  reset any incoming calls to this listener
564ddc91d4cSDavid du Colombier 		 */
565ddc91d4cSDavid du Colombier 		Fsconnected(c, "Hangup");
566ddc91d4cSDavid du Colombier 		/* fall through */
567ddc91d4cSDavid du Colombier 	case Closed:
568ddc91d4cSDavid du Colombier 	case Syn_sent:
569ddc91d4cSDavid du Colombier 		localclose(c, nil);
570ddc91d4cSDavid du Colombier 		break;
571ddc91d4cSDavid du Colombier 	case Syn_received:
572ddc91d4cSDavid du Colombier 	case Established:
573ddc91d4cSDavid du Colombier 	case Close_wait:
574ddc91d4cSDavid du Colombier 		tcpclosestate(c, tcb, tcb->state);
575ddc91d4cSDavid du Colombier 		break;
576ddc91d4cSDavid du Colombier 	}
577ddc91d4cSDavid du Colombier 	return nil;
578ddc91d4cSDavid du Colombier }
579ddc91d4cSDavid du Colombier 
58080ee5cbfSDavid du Colombier /*
58180ee5cbfSDavid du Colombier  *  tcpclose is always called with the q locked
58280ee5cbfSDavid du Colombier  */
5837dd7cddfSDavid du Colombier static void
tcpclose(Conv * c)5847dd7cddfSDavid du Colombier tcpclose(Conv *c)
5857dd7cddfSDavid du Colombier {
5867dd7cddfSDavid du Colombier 	Tcpctl *tcb;
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)c->ptcl;
5897dd7cddfSDavid du Colombier 
5907dd7cddfSDavid du Colombier 	qhangup(c->rq, nil);
5917dd7cddfSDavid du Colombier 	qhangup(c->wq, nil);
5927dd7cddfSDavid du Colombier 	qhangup(c->eq, nil);
59380ee5cbfSDavid du Colombier 	qflush(c->rq);
5947dd7cddfSDavid du Colombier 
5957dd7cddfSDavid du Colombier 	switch(tcb->state) {
5967dd7cddfSDavid du Colombier 	case Listen:
5977dd7cddfSDavid du Colombier 		/*
5987dd7cddfSDavid du Colombier 		 *  reset any incoming calls to this listener
5997dd7cddfSDavid du Colombier 		 */
6007dd7cddfSDavid du Colombier 		Fsconnected(c, "Hangup");
601ddc91d4cSDavid du Colombier 		/* fall through */
6027dd7cddfSDavid du Colombier 	case Closed:
6037dd7cddfSDavid du Colombier 	case Syn_sent:
6047dd7cddfSDavid du Colombier 		localclose(c, nil);
6057dd7cddfSDavid du Colombier 		break;
6067dd7cddfSDavid du Colombier 	case Syn_received:
6077dd7cddfSDavid du Colombier 	case Established:
608ddc91d4cSDavid du Colombier 		tcpclosestate(c, tcb, Finwait1);
6097dd7cddfSDavid du Colombier 		break;
6107dd7cddfSDavid du Colombier 	case Close_wait:
611ddc91d4cSDavid du Colombier 		tcpclosestate(c, tcb, Last_ack);
6127dd7cddfSDavid du Colombier 		break;
6137dd7cddfSDavid du Colombier 	}
6147dd7cddfSDavid du Colombier }
6157dd7cddfSDavid du Colombier 
6161ce6c95fSDavid du Colombier static void
tcpkick(void * x)6173ff48bf5SDavid du Colombier tcpkick(void *x)
6187dd7cddfSDavid du Colombier {
6193ff48bf5SDavid du Colombier 	Conv *s = x;
6207dd7cddfSDavid du Colombier 	Tcpctl *tcb;
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6237dd7cddfSDavid du Colombier 
62480ee5cbfSDavid du Colombier 	if(waserror()){
62580ee5cbfSDavid du Colombier 		qunlock(s);
62680ee5cbfSDavid du Colombier 		nexterror();
62780ee5cbfSDavid du Colombier 	}
62880ee5cbfSDavid du Colombier 	qlock(s);
6297dd7cddfSDavid du Colombier 
6307dd7cddfSDavid du Colombier 	switch(tcb->state) {
6317dd7cddfSDavid du Colombier 	case Syn_sent:
6327dd7cddfSDavid du Colombier 	case Syn_received:
6337dd7cddfSDavid du Colombier 	case Established:
6347dd7cddfSDavid du Colombier 	case Close_wait:
6357dd7cddfSDavid du Colombier 		/*
6367dd7cddfSDavid du Colombier 		 * Push data
6377dd7cddfSDavid du Colombier 		 */
6387dd7cddfSDavid du Colombier 		tcpoutput(s);
6397dd7cddfSDavid du Colombier 		break;
6407dd7cddfSDavid du Colombier 	default:
6417dd7cddfSDavid du Colombier 		localclose(s, "Hangup");
64280ee5cbfSDavid du Colombier 		break;
6437dd7cddfSDavid du Colombier 	}
64480ee5cbfSDavid du Colombier 
64580ee5cbfSDavid du Colombier 	qunlock(s);
64680ee5cbfSDavid du Colombier 	poperror();
6477dd7cddfSDavid du Colombier }
6487dd7cddfSDavid du Colombier 
6491ce6c95fSDavid du Colombier static int seq_lt(ulong, ulong);
6501ce6c95fSDavid du Colombier 
6511ce6c95fSDavid du Colombier static void
tcprcvwin(Conv * s)6527dd7cddfSDavid du Colombier tcprcvwin(Conv *s)				/* Call with tcb locked */
6537dd7cddfSDavid du Colombier {
6547dd7cddfSDavid du Colombier 	int w;
6557dd7cddfSDavid du Colombier 	Tcpctl *tcb;
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6583f695129SDavid du Colombier 	w = tcb->window - qlen(s->rq);
6597dd7cddfSDavid du Colombier 	if(w < 0)
6607dd7cddfSDavid du Colombier 		w = 0;
6611ce6c95fSDavid du Colombier 	/* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6621ce6c95fSDavid du Colombier 	if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6631ce6c95fSDavid du Colombier 		w = tcb->rcv.wptr - tcb->rcv.nxt;
6641ce6c95fSDavid du Colombier 	if(w != tcb->rcv.wnd)
6651ce6c95fSDavid du Colombier 	if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6667dd7cddfSDavid du Colombier 		tcb->rcv.blocked = 1;
6671ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6681ce6c95fSDavid du Colombier 			tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6691ce6c95fSDavid du Colombier 	}
6701ce6c95fSDavid du Colombier 	tcb->rcv.wnd = w;
6711ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt + w;
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier 
6741ce6c95fSDavid du Colombier static void
tcpacktimer(void * v)6757dd7cddfSDavid du Colombier tcpacktimer(void *v)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier 	Tcpctl *tcb;
6787dd7cddfSDavid du Colombier 	Conv *s;
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier 	s = v;
6817dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier 	if(waserror()){
6847dd7cddfSDavid du Colombier 		qunlock(s);
6857dd7cddfSDavid du Colombier 		nexterror();
6867dd7cddfSDavid du Colombier 	}
6877dd7cddfSDavid du Colombier 	qlock(s);
6887dd7cddfSDavid du Colombier 	if(tcb->state != Closed){
6897dd7cddfSDavid du Colombier 		tcb->flags |= FORCE;
6907dd7cddfSDavid du Colombier 		tcpoutput(s);
6917dd7cddfSDavid du Colombier 	}
6927dd7cddfSDavid du Colombier 	qunlock(s);
6937dd7cddfSDavid du Colombier 	poperror();
6947dd7cddfSDavid du Colombier }
6957dd7cddfSDavid du Colombier 
6967dd7cddfSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6971ce6c95fSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6981ce6c95fSDavid du Colombier {
6991ce6c95fSDavid du Colombier 	ulong inflight;
7001ce6c95fSDavid du Colombier 
7011ce6c95fSDavid du Colombier 	inflight = tcb->snd.nxt - tcb->snd.una;
7021ce6c95fSDavid du Colombier 	if(inflight > tcb->cwind)
7031ce6c95fSDavid du Colombier 		inflight = tcb->cwind;
7041ce6c95fSDavid du Colombier 	tcb->ssthresh = inflight / 2;
7051ce6c95fSDavid du Colombier 	if(tcb->ssthresh < 2*tcb->mss)
7061ce6c95fSDavid du Colombier 		tcb->ssthresh = 2*tcb->mss;
7071ce6c95fSDavid du Colombier }
7081ce6c95fSDavid du Colombier 
7091ce6c95fSDavid du Colombier enum {
7101ce6c95fSDavid du Colombier 	L	= 2,	/* aggressive slow start; legal values ∈ (1.0, 2.0) */
7111ce6c95fSDavid du Colombier };
7121ce6c95fSDavid du Colombier 
7131ce6c95fSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)7141ce6c95fSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
7151ce6c95fSDavid du Colombier {
7161ce6c95fSDavid du Colombier 	uint limit;
7171ce6c95fSDavid du Colombier 
7181ce6c95fSDavid du Colombier 	tcb->abcbytes += acked;
7191ce6c95fSDavid du Colombier 	if(tcb->cwind < tcb->ssthresh){
7201ce6c95fSDavid du Colombier 		/* slow start */
7211ce6c95fSDavid du Colombier 		if(tcb->snd.rto)
7221ce6c95fSDavid du Colombier 			limit = tcb->mss;
7231ce6c95fSDavid du Colombier 		else
7241ce6c95fSDavid du Colombier 			limit = L*tcb->mss;
7251ce6c95fSDavid du Colombier 		tcb->cwind += MIN(tcb->abcbytes, limit);
7261ce6c95fSDavid du Colombier 		tcb->abcbytes = 0;
7271ce6c95fSDavid du Colombier 	} else {
7281ce6c95fSDavid du Colombier 		tcb->snd.rto = 0;
7291ce6c95fSDavid du Colombier 		/* avoidance */
7301ce6c95fSDavid du Colombier 		if(tcb->abcbytes >= tcb->cwind){
7311ce6c95fSDavid du Colombier 			tcb->abcbytes -= tcb->cwind;
7321ce6c95fSDavid du Colombier 			tcb->cwind += tcb->mss;
7331ce6c95fSDavid du Colombier 		}
7341ce6c95fSDavid du Colombier 	}
7351ce6c95fSDavid du Colombier }
7361ce6c95fSDavid du Colombier 
7371ce6c95fSDavid du Colombier static void
tcpcreate(Conv * c)7387dd7cddfSDavid du Colombier tcpcreate(Conv *c)
7397dd7cddfSDavid du Colombier {
7403ff48bf5SDavid du Colombier 	c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7411ce6c95fSDavid du Colombier 	c->wq = qopen(QMAX, Qkick, tcpkick, c);
7427dd7cddfSDavid du Colombier }
7437dd7cddfSDavid du Colombier 
7447dd7cddfSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7459a747e4fSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7467dd7cddfSDavid du Colombier {
7479a747e4fSDavid du Colombier 	if(newstate != TcptimerON){
7489a747e4fSDavid du Colombier 		if(t->state == TcptimerON){
74941dd6b47SDavid du Colombier 			/* unchain */
7507dd7cddfSDavid du Colombier 			if(priv->timers == t){
7517dd7cddfSDavid du Colombier 				priv->timers = t->next;
7527dd7cddfSDavid du Colombier 				if(t->prev != nil)
7537dd7cddfSDavid du Colombier 					panic("timerstate1");
7547dd7cddfSDavid du Colombier 			}
7557dd7cddfSDavid du Colombier 			if(t->next)
7567dd7cddfSDavid du Colombier 				t->next->prev = t->prev;
7577dd7cddfSDavid du Colombier 			if(t->prev)
7587dd7cddfSDavid du Colombier 				t->prev->next = t->next;
7597dd7cddfSDavid du Colombier 			t->next = t->prev = nil;
7607dd7cddfSDavid du Colombier 		}
7617dd7cddfSDavid du Colombier 	} else {
7629a747e4fSDavid du Colombier 		if(t->state != TcptimerON){
76341dd6b47SDavid du Colombier 			/* chain */
7647dd7cddfSDavid du Colombier 			if(t->prev != nil || t->next != nil)
7657dd7cddfSDavid du Colombier 				panic("timerstate2");
7667dd7cddfSDavid du Colombier 			t->prev = nil;
7677dd7cddfSDavid du Colombier 			t->next = priv->timers;
7687dd7cddfSDavid du Colombier 			if(t->next)
7697dd7cddfSDavid du Colombier 				t->next->prev = t;
7707dd7cddfSDavid du Colombier 			priv->timers = t;
7717dd7cddfSDavid du Colombier 		}
7727dd7cddfSDavid du Colombier 	}
7737dd7cddfSDavid du Colombier 	t->state = newstate;
7747dd7cddfSDavid du Colombier }
7757dd7cddfSDavid du Colombier 
7761ce6c95fSDavid du Colombier static void
tcpackproc(void * a)7777dd7cddfSDavid du Colombier tcpackproc(void *a)
7787dd7cddfSDavid du Colombier {
7799a747e4fSDavid du Colombier 	Tcptimer *t, *tp, *timeo;
7807dd7cddfSDavid du Colombier 	Proto *tcp;
7817dd7cddfSDavid du Colombier 	Tcppriv *priv;
7827dd7cddfSDavid du Colombier 	int loop;
7837dd7cddfSDavid du Colombier 
7847dd7cddfSDavid du Colombier 	tcp = a;
7857dd7cddfSDavid du Colombier 	priv = tcp->priv;
7867dd7cddfSDavid du Colombier 
7877dd7cddfSDavid du Colombier 	for(;;) {
788dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, MSPTICK);
7897dd7cddfSDavid du Colombier 
7907dd7cddfSDavid du Colombier 		qlock(&priv->tl);
7917dd7cddfSDavid du Colombier 		timeo = nil;
7927dd7cddfSDavid du Colombier 		loop = 0;
7937dd7cddfSDavid du Colombier 		for(t = priv->timers; t != nil; t = tp) {
7947dd7cddfSDavid du Colombier 			if(loop++ > 10000)
7957dd7cddfSDavid du Colombier 				panic("tcpackproc1");
7967dd7cddfSDavid du Colombier 			tp = t->next;
7979a747e4fSDavid du Colombier 			if(t->state == TcptimerON) {
7987dd7cddfSDavid du Colombier 				t->count--;
7997dd7cddfSDavid du Colombier 				if(t->count == 0) {
8009a747e4fSDavid du Colombier 					timerstate(priv, t, TcptimerDONE);
8017dd7cddfSDavid du Colombier 					t->readynext = timeo;
8027dd7cddfSDavid du Colombier 					timeo = t;
8037dd7cddfSDavid du Colombier 				}
8047dd7cddfSDavid du Colombier 			}
8057dd7cddfSDavid du Colombier 		}
8067dd7cddfSDavid du Colombier 		qunlock(&priv->tl);
8077dd7cddfSDavid du Colombier 
8087dd7cddfSDavid du Colombier 		loop = 0;
8097dd7cddfSDavid du Colombier 		for(t = timeo; t != nil; t = t->readynext) {
8107dd7cddfSDavid du Colombier 			if(loop++ > 10000)
8117dd7cddfSDavid du Colombier 				panic("tcpackproc2");
8129a747e4fSDavid du Colombier 			if(t->state == TcptimerDONE && t->func != nil && !waserror()){
8137dd7cddfSDavid du Colombier 				(*t->func)(t->arg);
8149a747e4fSDavid du Colombier 				poperror();
8159a747e4fSDavid du Colombier 			}
8167dd7cddfSDavid du Colombier 		}
8173ff48bf5SDavid du Colombier 
8183ff48bf5SDavid du Colombier 		limborexmit(tcp);
8197dd7cddfSDavid du Colombier 	}
8207dd7cddfSDavid du Colombier }
8217dd7cddfSDavid du Colombier 
8221ce6c95fSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)8239a747e4fSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
8247dd7cddfSDavid du Colombier {
8257dd7cddfSDavid du Colombier 	if(t == nil || t->start == 0)
8267dd7cddfSDavid du Colombier 		return;
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 	qlock(&priv->tl);
8297dd7cddfSDavid du Colombier 	t->count = t->start;
8309a747e4fSDavid du Colombier 	timerstate(priv, t, TcptimerON);
8317dd7cddfSDavid du Colombier 	qunlock(&priv->tl);
8327dd7cddfSDavid du Colombier }
8337dd7cddfSDavid du Colombier 
8341ce6c95fSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8359a747e4fSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8367dd7cddfSDavid du Colombier {
8377dd7cddfSDavid du Colombier 	if(t == nil)
8387dd7cddfSDavid du Colombier 		return;
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 	qlock(&priv->tl);
8419a747e4fSDavid du Colombier 	timerstate(priv, t, TcptimerOFF);
8427dd7cddfSDavid du Colombier 	qunlock(&priv->tl);
8437dd7cddfSDavid du Colombier }
8447dd7cddfSDavid du Colombier 
8451ce6c95fSDavid du Colombier static int
backoff(int n)8467dd7cddfSDavid du Colombier backoff(int n)
8477dd7cddfSDavid du Colombier {
8487dd7cddfSDavid du Colombier 	return 1 << n;
8497dd7cddfSDavid du Colombier }
8507dd7cddfSDavid du Colombier 
8511ce6c95fSDavid du Colombier static void
localclose(Conv * s,char * reason)8527dd7cddfSDavid du Colombier localclose(Conv *s, char *reason)	/* called with tcb locked */
8537dd7cddfSDavid du Colombier {
8547dd7cddfSDavid du Colombier 	Tcpctl *tcb;
8557dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
8567dd7cddfSDavid du Colombier 
8577dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
8587dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
8597dd7cddfSDavid du Colombier 
86080ee5cbfSDavid du Colombier 	iphtrem(&tpriv->ht, s);
86180ee5cbfSDavid du Colombier 
8627dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->timer);
8637dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
8647dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->acktimer);
8657dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->katimer);
8667dd7cddfSDavid du Colombier 
8677dd7cddfSDavid du Colombier 	/* Flush reassembly queue; nothing more can arrive */
8681ce6c95fSDavid du Colombier 	dumpreseq(tcb);
8697dd7cddfSDavid du Colombier 
8707dd7cddfSDavid du Colombier 	if(tcb->state == Syn_sent)
8717dd7cddfSDavid du Colombier 		Fsconnected(s, reason);
8727dd7cddfSDavid du Colombier 	if(s->state == Announced)
8737dd7cddfSDavid du Colombier 		wakeup(&s->listenr);
8747dd7cddfSDavid du Colombier 
8757dd7cddfSDavid du Colombier 	qhangup(s->rq, reason);
8767dd7cddfSDavid du Colombier 	qhangup(s->wq, reason);
8777dd7cddfSDavid du Colombier 
8787dd7cddfSDavid du Colombier 	tcpsetstate(s, Closed);
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier 
8817dd7cddfSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8821ce6c95fSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8831ce6c95fSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8847dd7cddfSDavid du Colombier {
8857dd7cddfSDavid du Colombier 	Ipifc *ifc;
8867dd7cddfSDavid du Colombier 	int mtu;
8877dd7cddfSDavid du Colombier 
8883ff48bf5SDavid du Colombier 	ifc = findipifc(tcp->f, addr, 0);
8893ff48bf5SDavid du Colombier 	switch(version){
8903ff48bf5SDavid du Colombier 	default:
8913ff48bf5SDavid du Colombier 	case V4:
8927dd7cddfSDavid du Colombier 		mtu = DEF_MSS;
8933ff48bf5SDavid du Colombier 		if(ifc != nil)
894cc057a51SDavid du Colombier 			mtu = ifc->maxtu - ifc->medium->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8953ff48bf5SDavid du Colombier 		break;
8963ff48bf5SDavid du Colombier 	case V6:
8973ff48bf5SDavid du Colombier 		mtu = DEF_MSS6;
8983ff48bf5SDavid du Colombier 		if(ifc != nil)
899cc057a51SDavid du Colombier 			mtu = ifc->maxtu - ifc->medium->hsize - (TCP6_PKT + TCP6_HDRSIZE);
9003ff48bf5SDavid du Colombier 		break;
9013ff48bf5SDavid du Colombier 	}
9021ce6c95fSDavid du Colombier 	/*
9031ce6c95fSDavid du Colombier 	 * set the ws.  it doesn't commit us to anything.
9041ce6c95fSDavid du Colombier 	 * ws is the ultimate limit to the bandwidth-delay product.
9051ce6c95fSDavid du Colombier 	 */
9061ce6c95fSDavid du Colombier 	*scale = Defadvscale;
9073ff48bf5SDavid du Colombier 
9083ff48bf5SDavid du Colombier 	return mtu;
9097dd7cddfSDavid du Colombier }
9107dd7cddfSDavid du Colombier 
9111ce6c95fSDavid du Colombier static void
inittcpctl(Conv * s,int mode)91280ee5cbfSDavid du Colombier inittcpctl(Conv *s, int mode)
9137dd7cddfSDavid du Colombier {
9147dd7cddfSDavid du Colombier 	Tcpctl *tcb;
9153ff48bf5SDavid du Colombier 	Tcp4hdr* h4;
9163ff48bf5SDavid du Colombier 	Tcp6hdr* h6;
9177ec5746aSDavid du Colombier 	Tcppriv *tpriv;
9183f695129SDavid du Colombier 	int mss;
9197dd7cddfSDavid du Colombier 
9207dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
9217dd7cddfSDavid du Colombier 
9227dd7cddfSDavid du Colombier 	memset(tcb, 0, sizeof(Tcpctl));
9237dd7cddfSDavid du Colombier 
9241ce6c95fSDavid du Colombier 	tcb->ssthresh = QMAX;			/* reset by tcpsetscale() */
92580ee5cbfSDavid du Colombier 	tcb->srtt = tcp_irtt<<LOGAGAIN;
92680ee5cbfSDavid du Colombier 	tcb->mdev = 0;
9277dd7cddfSDavid du Colombier 
92880ee5cbfSDavid du Colombier 	/* setup timers */
9297dd7cddfSDavid du Colombier 	tcb->timer.start = tcp_irtt / MSPTICK;
9307dd7cddfSDavid du Colombier 	tcb->timer.func = tcptimeout;
9317dd7cddfSDavid du Colombier 	tcb->timer.arg = s;
9327dd7cddfSDavid du Colombier 	tcb->rtt_timer.start = MAX_TIME;
9337dd7cddfSDavid du Colombier 	tcb->acktimer.start = TCP_ACK / MSPTICK;
9347dd7cddfSDavid du Colombier 	tcb->acktimer.func = tcpacktimer;
9357dd7cddfSDavid du Colombier 	tcb->acktimer.arg = s;
9367dd7cddfSDavid du Colombier 	tcb->katimer.start = DEF_KAT / MSPTICK;
9377dd7cddfSDavid du Colombier 	tcb->katimer.func = tcpkeepalive;
9387dd7cddfSDavid du Colombier 	tcb->katimer.arg = s;
9397dd7cddfSDavid du Colombier 
9403f695129SDavid du Colombier 	mss = DEF_MSS;
9413f695129SDavid du Colombier 
9427dd7cddfSDavid du Colombier 	/* create a prototype(pseudo) header */
9433ff48bf5SDavid du Colombier 	if(mode != TCP_LISTEN){
9447dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, IPnoaddr) == 0)
9457dd7cddfSDavid du Colombier 			findlocalip(s->p->f, s->laddr, s->raddr);
9467dd7cddfSDavid du Colombier 
9473ff48bf5SDavid du Colombier 		switch(s->ipversion){
9483ff48bf5SDavid du Colombier 		case V4:
9493ff48bf5SDavid du Colombier 			h4 = &tcb->protohdr.tcp4hdr;
9503ff48bf5SDavid du Colombier 			memset(h4, 0, sizeof(*h4));
9513ff48bf5SDavid du Colombier 			h4->proto = IP_TCPPROTO;
9523ff48bf5SDavid du Colombier 			hnputs(h4->tcpsport, s->lport);
9533ff48bf5SDavid du Colombier 			hnputs(h4->tcpdport, s->rport);
9543ff48bf5SDavid du Colombier 			v6tov4(h4->tcpsrc, s->laddr);
9553ff48bf5SDavid du Colombier 			v6tov4(h4->tcpdst, s->raddr);
9563ff48bf5SDavid du Colombier 			break;
9573ff48bf5SDavid du Colombier 		case V6:
9583ff48bf5SDavid du Colombier 			h6 = &tcb->protohdr.tcp6hdr;
9593ff48bf5SDavid du Colombier 			memset(h6, 0, sizeof(*h6));
9603ff48bf5SDavid du Colombier 			h6->proto = IP_TCPPROTO;
9613ff48bf5SDavid du Colombier 			hnputs(h6->tcpsport, s->lport);
9623ff48bf5SDavid du Colombier 			hnputs(h6->tcpdport, s->rport);
9633ff48bf5SDavid du Colombier 			ipmove(h6->tcpsrc, s->laddr);
9643ff48bf5SDavid du Colombier 			ipmove(h6->tcpdst, s->raddr);
9653f695129SDavid du Colombier 			mss = DEF_MSS6;
9663ff48bf5SDavid du Colombier 			break;
9673ff48bf5SDavid du Colombier 		default:
9683ff48bf5SDavid du Colombier 			panic("inittcpctl: version %d", s->ipversion);
9693ff48bf5SDavid du Colombier 		}
9703ff48bf5SDavid du Colombier 	}
9713ff48bf5SDavid du Colombier 
9723f695129SDavid du Colombier 	tcb->mss = tcb->cwind = mss;
9731ce6c95fSDavid du Colombier 	tcb->abcbytes = 0;
9747ec5746aSDavid du Colombier 	tpriv = s->p->priv;
9757ec5746aSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
9763f695129SDavid du Colombier 
9773f695129SDavid du Colombier 	/* default is no window scaling */
9781ce6c95fSDavid du Colombier 	tcpsetscale(s, tcb, 0, 0);
9797dd7cddfSDavid du Colombier }
9807dd7cddfSDavid du Colombier 
98180ee5cbfSDavid du Colombier /*
98280ee5cbfSDavid du Colombier  *  called with s qlocked
98380ee5cbfSDavid du Colombier  */
9841ce6c95fSDavid du Colombier static void
tcpstart(Conv * s,int mode)9853f695129SDavid du Colombier tcpstart(Conv *s, int mode)
9867dd7cddfSDavid du Colombier {
9877dd7cddfSDavid du Colombier 	Tcpctl *tcb;
9887dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
9899a747e4fSDavid du Colombier 	char kpname[KNAMELEN];
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
9927dd7cddfSDavid du Colombier 
9937dd7cddfSDavid du Colombier 	if(tpriv->ackprocstarted == 0){
9947dd7cddfSDavid du Colombier 		qlock(&tpriv->apl);
9957dd7cddfSDavid du Colombier 		if(tpriv->ackprocstarted == 0){
9961ce6c95fSDavid du Colombier 			snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9977dd7cddfSDavid du Colombier 			kproc(kpname, tcpackproc, s->p);
9987dd7cddfSDavid du Colombier 			tpriv->ackprocstarted = 1;
9997dd7cddfSDavid du Colombier 		}
10007dd7cddfSDavid du Colombier 		qunlock(&tpriv->apl);
10017dd7cddfSDavid du Colombier 	}
10027dd7cddfSDavid du Colombier 
10037dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
10047dd7cddfSDavid du Colombier 
100580ee5cbfSDavid du Colombier 	inittcpctl(s, mode);
10067dd7cddfSDavid du Colombier 
100780ee5cbfSDavid du Colombier 	iphtadd(&tpriv->ht, s);
10087dd7cddfSDavid du Colombier 	switch(mode) {
10097dd7cddfSDavid du Colombier 	case TCP_LISTEN:
101059cc4ca5SDavid du Colombier 		tpriv->stats[PassiveOpens]++;
10117dd7cddfSDavid du Colombier 		tcb->flags |= CLONE;
10127dd7cddfSDavid du Colombier 		tcpsetstate(s, Listen);
10137dd7cddfSDavid du Colombier 		break;
10147dd7cddfSDavid du Colombier 
10157dd7cddfSDavid du Colombier 	case TCP_CONNECT:
101659cc4ca5SDavid du Colombier 		tpriv->stats[ActiveOpens]++;
10177dd7cddfSDavid du Colombier 		tcb->flags |= ACTIVE;
10183f695129SDavid du Colombier 		tcpsndsyn(s, tcb);
10197dd7cddfSDavid du Colombier 		tcpsetstate(s, Syn_sent);
10207dd7cddfSDavid du Colombier 		tcpoutput(s);
10217dd7cddfSDavid du Colombier 		break;
10227dd7cddfSDavid du Colombier 	}
10237dd7cddfSDavid du Colombier }
10247dd7cddfSDavid du Colombier 
10257dd7cddfSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)10261ce6c95fSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
10277dd7cddfSDavid du Colombier {
10281ce6c95fSDavid du Colombier 	char *p;
10297dd7cddfSDavid du Colombier 
10301ce6c95fSDavid du Colombier 	p = seprint(buf, e, "%d", flag>>10);	/* Head len */
10317dd7cddfSDavid du Colombier 	if(flag & URG)
10321ce6c95fSDavid du Colombier 		p = seprint(p, e, " URG");
10337dd7cddfSDavid du Colombier 	if(flag & ACK)
10341ce6c95fSDavid du Colombier 		p = seprint(p, e, " ACK");
10357dd7cddfSDavid du Colombier 	if(flag & PSH)
10361ce6c95fSDavid du Colombier 		p = seprint(p, e, " PSH");
10377dd7cddfSDavid du Colombier 	if(flag & RST)
10381ce6c95fSDavid du Colombier 		p = seprint(p, e, " RST");
10397dd7cddfSDavid du Colombier 	if(flag & SYN)
10401ce6c95fSDavid du Colombier 		p = seprint(p, e, " SYN");
10417dd7cddfSDavid du Colombier 	if(flag & FIN)
10421ce6c95fSDavid du Colombier 		p = seprint(p, e, " FIN");
10431ce6c95fSDavid du Colombier 	USED(p);
10447dd7cddfSDavid du Colombier 	return buf;
10457dd7cddfSDavid du Colombier }
10467dd7cddfSDavid du Colombier 
10471ce6c95fSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10483ff48bf5SDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10497dd7cddfSDavid du Colombier {
10507dd7cddfSDavid du Colombier 	int dlen;
10513ff48bf5SDavid du Colombier 	Tcp6hdr *h;
10527dd7cddfSDavid du Colombier 	ushort csum;
10533f695129SDavid du Colombier 	ushort hdrlen, optpad = 0;
10543f695129SDavid du Colombier 	uchar *opt;
10557dd7cddfSDavid du Colombier 
10563ff48bf5SDavid du Colombier 	hdrlen = TCP6_HDRSIZE;
10573f695129SDavid du Colombier 	if(tcph->flags & SYN){
10587dd7cddfSDavid du Colombier 		if(tcph->mss)
10597dd7cddfSDavid du Colombier 			hdrlen += MSS_LENGTH;
10603f695129SDavid du Colombier 		if(tcph->ws)
10613f695129SDavid du Colombier 			hdrlen += WS_LENGTH;
10623f695129SDavid du Colombier 		optpad = hdrlen & 3;
10633f695129SDavid du Colombier 		if(optpad)
10643f695129SDavid du Colombier 			optpad = 4 - optpad;
10653f695129SDavid du Colombier 		hdrlen += optpad;
10663f695129SDavid du Colombier 	}
10677dd7cddfSDavid du Colombier 
10687dd7cddfSDavid du Colombier 	if(data) {
10697dd7cddfSDavid du Colombier 		dlen = blocklen(data);
10703ff48bf5SDavid du Colombier 		data = padblock(data, hdrlen + TCP6_PKT);
10717dd7cddfSDavid du Colombier 		if(data == nil)
10727dd7cddfSDavid du Colombier 			return nil;
10737dd7cddfSDavid du Colombier 	}
10747dd7cddfSDavid du Colombier 	else {
10757dd7cddfSDavid du Colombier 		dlen = 0;
10763ff48bf5SDavid du Colombier 		data = allocb(hdrlen + TCP6_PKT + 64);	/* the 64 pad is to meet mintu's */
10777dd7cddfSDavid du Colombier 		if(data == nil)
10787dd7cddfSDavid du Colombier 			return nil;
10793ff48bf5SDavid du Colombier 		data->wp += hdrlen + TCP6_PKT;
10807dd7cddfSDavid du Colombier 	}
10817dd7cddfSDavid du Colombier 
10827dd7cddfSDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
10833ff48bf5SDavid du Colombier 	h = (Tcp6hdr *)(data->rp);
10843ff48bf5SDavid du Colombier 	memmove(h, ph, TCP6_TCBPHDRSZ);
10853ff48bf5SDavid du Colombier 
10863ff48bf5SDavid du Colombier 	/* compose pseudo tcp header, do cksum calculation */
10873ff48bf5SDavid du Colombier 	hnputl(h->vcf, hdrlen + dlen);
10883ff48bf5SDavid du Colombier 	h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10893ff48bf5SDavid du Colombier 	h->ttl = ph->proto;
10903ff48bf5SDavid du Colombier 
10913ff48bf5SDavid du Colombier 	/* copy in variable bits */
10923ff48bf5SDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
10933ff48bf5SDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
10943ff48bf5SDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10953f695129SDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10963ff48bf5SDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
10973ff48bf5SDavid du Colombier 
10983f695129SDavid du Colombier 	if(tcph->flags & SYN){
10993f695129SDavid du Colombier 		opt = h->tcpopt;
11003ff48bf5SDavid du Colombier 		if(tcph->mss != 0){
11013f695129SDavid du Colombier 			*opt++ = MSSOPT;
11023f695129SDavid du Colombier 			*opt++ = MSS_LENGTH;
11033f695129SDavid du Colombier 			hnputs(opt, tcph->mss);
11043f695129SDavid du Colombier 			opt += 2;
11053ff48bf5SDavid du Colombier 		}
11063f695129SDavid du Colombier 		if(tcph->ws != 0){
11073f695129SDavid du Colombier 			*opt++ = WSOPT;
11083f695129SDavid du Colombier 			*opt++ = WS_LENGTH;
11093f695129SDavid du Colombier 			*opt++ = tcph->ws;
11103f695129SDavid du Colombier 		}
11113f695129SDavid du Colombier 		while(optpad-- > 0)
11123f695129SDavid du Colombier 			*opt++ = NOOPOPT;
11133f695129SDavid du Colombier 	}
11143f695129SDavid du Colombier 
11153ff48bf5SDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
11163ff48bf5SDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
11173ff48bf5SDavid du Colombier 	} else {
11183ff48bf5SDavid du Colombier 		csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
11193ff48bf5SDavid du Colombier 		hnputs(h->tcpcksum, csum);
11203ff48bf5SDavid du Colombier 	}
11213ff48bf5SDavid du Colombier 
11223ff48bf5SDavid du Colombier 	/* move from pseudo header back to normal ip header */
11233ff48bf5SDavid du Colombier 	memset(h->vcf, 0, 4);
11243ff48bf5SDavid du Colombier 	h->vcf[0] = IP_VER6;
11253ff48bf5SDavid du Colombier 	hnputs(h->ploadlen, hdrlen+dlen);
11263ff48bf5SDavid du Colombier 	h->proto = ph->proto;
11273ff48bf5SDavid du Colombier 
11283ff48bf5SDavid du Colombier 	return data;
11293ff48bf5SDavid du Colombier }
11303ff48bf5SDavid du Colombier 
11311ce6c95fSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11323ff48bf5SDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11333ff48bf5SDavid du Colombier {
11343ff48bf5SDavid du Colombier 	int dlen;
11353ff48bf5SDavid du Colombier 	Tcp4hdr *h;
11363ff48bf5SDavid du Colombier 	ushort csum;
11373f695129SDavid du Colombier 	ushort hdrlen, optpad = 0;
11383f695129SDavid du Colombier 	uchar *opt;
11393ff48bf5SDavid du Colombier 
11403ff48bf5SDavid du Colombier 	hdrlen = TCP4_HDRSIZE;
11413f695129SDavid du Colombier 	if(tcph->flags & SYN){
11423ff48bf5SDavid du Colombier 		if(tcph->mss)
11433ff48bf5SDavid du Colombier 			hdrlen += MSS_LENGTH;
11441ce6c95fSDavid du Colombier 		if(1)
11453f695129SDavid du Colombier 			hdrlen += WS_LENGTH;
11463f695129SDavid du Colombier 		optpad = hdrlen & 3;
11473f695129SDavid du Colombier 		if(optpad)
11483f695129SDavid du Colombier 			optpad = 4 - optpad;
11493f695129SDavid du Colombier 		hdrlen += optpad;
11503f695129SDavid du Colombier 	}
11513ff48bf5SDavid du Colombier 
11523ff48bf5SDavid du Colombier 	if(data) {
11533ff48bf5SDavid du Colombier 		dlen = blocklen(data);
11543ff48bf5SDavid du Colombier 		data = padblock(data, hdrlen + TCP4_PKT);
11553ff48bf5SDavid du Colombier 		if(data == nil)
11563ff48bf5SDavid du Colombier 			return nil;
11573ff48bf5SDavid du Colombier 	}
11583ff48bf5SDavid du Colombier 	else {
11593ff48bf5SDavid du Colombier 		dlen = 0;
11603ff48bf5SDavid du Colombier 		data = allocb(hdrlen + TCP4_PKT + 64);	/* the 64 pad is to meet mintu's */
11613ff48bf5SDavid du Colombier 		if(data == nil)
11623ff48bf5SDavid du Colombier 			return nil;
11633ff48bf5SDavid du Colombier 		data->wp += hdrlen + TCP4_PKT;
11643ff48bf5SDavid du Colombier 	}
11653ff48bf5SDavid du Colombier 
11663ff48bf5SDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
11673ff48bf5SDavid du Colombier 	h = (Tcp4hdr *)(data->rp);
11683ff48bf5SDavid du Colombier 	memmove(h, ph, TCP4_TCBPHDRSZ);
11697dd7cddfSDavid du Colombier 
11707dd7cddfSDavid du Colombier 	/* copy in variable bits */
11717dd7cddfSDavid du Colombier 	hnputs(h->tcplen, hdrlen + dlen);
11727dd7cddfSDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
11737dd7cddfSDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
11747dd7cddfSDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11753f695129SDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11767dd7cddfSDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
11777dd7cddfSDavid du Colombier 
11783f695129SDavid du Colombier 	if(tcph->flags & SYN){
11793f695129SDavid du Colombier 		opt = h->tcpopt;
11807dd7cddfSDavid du Colombier 		if(tcph->mss != 0){
11813f695129SDavid du Colombier 			*opt++ = MSSOPT;
11823f695129SDavid du Colombier 			*opt++ = MSS_LENGTH;
11833f695129SDavid du Colombier 			hnputs(opt, tcph->mss);
11843f695129SDavid du Colombier 			opt += 2;
11857dd7cddfSDavid du Colombier 		}
11861ce6c95fSDavid du Colombier 		/* always offer.  rfc1323 §2.2 */
11871ce6c95fSDavid du Colombier 		if(1){
11883f695129SDavid du Colombier 			*opt++ = WSOPT;
11893f695129SDavid du Colombier 			*opt++ = WS_LENGTH;
11903f695129SDavid du Colombier 			*opt++ = tcph->ws;
11913f695129SDavid du Colombier 		}
11923f695129SDavid du Colombier 		while(optpad-- > 0)
11933f695129SDavid du Colombier 			*opt++ = NOOPOPT;
11943f695129SDavid du Colombier 	}
11953f695129SDavid du Colombier 
119659cc4ca5SDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
119759cc4ca5SDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
119859cc4ca5SDavid du Colombier 	} else {
11993ff48bf5SDavid du Colombier 		csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
12007dd7cddfSDavid du Colombier 		hnputs(h->tcpcksum, csum);
120159cc4ca5SDavid du Colombier 	}
12027dd7cddfSDavid du Colombier 
12037dd7cddfSDavid du Colombier 	return data;
12047dd7cddfSDavid du Colombier }
12057dd7cddfSDavid du Colombier 
12061ce6c95fSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)12073ff48bf5SDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
12087dd7cddfSDavid du Colombier {
12093ff48bf5SDavid du Colombier 	Tcp6hdr *h;
12107dd7cddfSDavid du Colombier 	uchar *optr;
12117dd7cddfSDavid du Colombier 	ushort hdrlen;
12127dd7cddfSDavid du Colombier 	ushort optlen;
12137dd7cddfSDavid du Colombier 	int n;
12147dd7cddfSDavid du Colombier 
12153ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
12167dd7cddfSDavid du Colombier 	if(*bpp == nil)
12177dd7cddfSDavid du Colombier 		return -1;
12187dd7cddfSDavid du Colombier 
12193ff48bf5SDavid du Colombier 	h = (Tcp6hdr *)((*bpp)->rp);
12207dd7cddfSDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
12217dd7cddfSDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
12227dd7cddfSDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
12237dd7cddfSDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
12243f695129SDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
12253ff48bf5SDavid du Colombier 	if(hdrlen < TCP6_HDRSIZE) {
12267dd7cddfSDavid du Colombier 		freeblist(*bpp);
12277dd7cddfSDavid du Colombier 		return -1;
12287dd7cddfSDavid du Colombier 	}
12297dd7cddfSDavid du Colombier 
12307dd7cddfSDavid du Colombier 	tcph->flags = h->tcpflag[1];
12317dd7cddfSDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12327dd7cddfSDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12337dd7cddfSDavid du Colombier 	tcph->mss = 0;
12343f695129SDavid du Colombier 	tcph->ws = 0;
12351ce6c95fSDavid du Colombier 	tcph->update = 0;
12363ff48bf5SDavid du Colombier 	tcph->len = nhgets(h->ploadlen) - hdrlen;
12377dd7cddfSDavid du Colombier 
12383ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12397dd7cddfSDavid du Colombier 	if(*bpp == nil)
12407dd7cddfSDavid du Colombier 		return -1;
12417dd7cddfSDavid du Colombier 
12427dd7cddfSDavid du Colombier 	optr = h->tcpopt;
12433ff48bf5SDavid du Colombier 	n = hdrlen - TCP6_HDRSIZE;
12447dd7cddfSDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
12457dd7cddfSDavid du Colombier 		if(*optr == NOOPOPT) {
12467dd7cddfSDavid du Colombier 			n--;
12477dd7cddfSDavid du Colombier 			optr++;
12487dd7cddfSDavid du Colombier 			continue;
12497dd7cddfSDavid du Colombier 		}
12507dd7cddfSDavid du Colombier 		optlen = optr[1];
12517dd7cddfSDavid du Colombier 		if(optlen < 2 || optlen > n)
12527dd7cddfSDavid du Colombier 			break;
12537dd7cddfSDavid du Colombier 		switch(*optr) {
12547dd7cddfSDavid du Colombier 		case MSSOPT:
12557dd7cddfSDavid du Colombier 			if(optlen == MSS_LENGTH)
12567dd7cddfSDavid du Colombier 				tcph->mss = nhgets(optr+2);
12577dd7cddfSDavid du Colombier 			break;
12583f695129SDavid du Colombier 		case WSOPT:
12593f695129SDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
12601ce6c95fSDavid du Colombier 				tcph->ws = *(optr+2);
12613f695129SDavid du Colombier 			break;
12627dd7cddfSDavid du Colombier 		}
12637dd7cddfSDavid du Colombier 		n -= optlen;
12647dd7cddfSDavid du Colombier 		optr += optlen;
12657dd7cddfSDavid du Colombier 	}
12667dd7cddfSDavid du Colombier 	return hdrlen;
12677dd7cddfSDavid du Colombier }
12687dd7cddfSDavid du Colombier 
12691ce6c95fSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12703ff48bf5SDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12713ff48bf5SDavid du Colombier {
12723ff48bf5SDavid du Colombier 	Tcp4hdr *h;
12733ff48bf5SDavid du Colombier 	uchar *optr;
12743ff48bf5SDavid du Colombier 	ushort hdrlen;
12753ff48bf5SDavid du Colombier 	ushort optlen;
12763ff48bf5SDavid du Colombier 	int n;
12773ff48bf5SDavid du Colombier 
12783ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12793ff48bf5SDavid du Colombier 	if(*bpp == nil)
12803ff48bf5SDavid du Colombier 		return -1;
12813ff48bf5SDavid du Colombier 
12823ff48bf5SDavid du Colombier 	h = (Tcp4hdr *)((*bpp)->rp);
12833ff48bf5SDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
12843ff48bf5SDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
12853ff48bf5SDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
12863ff48bf5SDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
12873ff48bf5SDavid du Colombier 
12883f695129SDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
12893ff48bf5SDavid du Colombier 	if(hdrlen < TCP4_HDRSIZE) {
12903ff48bf5SDavid du Colombier 		freeblist(*bpp);
12913ff48bf5SDavid du Colombier 		return -1;
12923ff48bf5SDavid du Colombier 	}
12933ff48bf5SDavid du Colombier 
12943ff48bf5SDavid du Colombier 	tcph->flags = h->tcpflag[1];
12953ff48bf5SDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12963ff48bf5SDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12973ff48bf5SDavid du Colombier 	tcph->mss = 0;
12983f695129SDavid du Colombier 	tcph->ws = 0;
12991ce6c95fSDavid du Colombier 	tcph->update = 0;
13003ff48bf5SDavid du Colombier 	tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
13013ff48bf5SDavid du Colombier 
13023ff48bf5SDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
13033ff48bf5SDavid du Colombier 	if(*bpp == nil)
13043ff48bf5SDavid du Colombier 		return -1;
13053ff48bf5SDavid du Colombier 
13063ff48bf5SDavid du Colombier 	optr = h->tcpopt;
13073ff48bf5SDavid du Colombier 	n = hdrlen - TCP4_HDRSIZE;
13083ff48bf5SDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
13093ff48bf5SDavid du Colombier 		if(*optr == NOOPOPT) {
13103ff48bf5SDavid du Colombier 			n--;
13113ff48bf5SDavid du Colombier 			optr++;
13123ff48bf5SDavid du Colombier 			continue;
13133ff48bf5SDavid du Colombier 		}
13143ff48bf5SDavid du Colombier 		optlen = optr[1];
13153ff48bf5SDavid du Colombier 		if(optlen < 2 || optlen > n)
13163ff48bf5SDavid du Colombier 			break;
13173ff48bf5SDavid du Colombier 		switch(*optr) {
13183ff48bf5SDavid du Colombier 		case MSSOPT:
13191ce6c95fSDavid du Colombier 			if(optlen == MSS_LENGTH)
13203ff48bf5SDavid du Colombier 				tcph->mss = nhgets(optr+2);
13213ff48bf5SDavid du Colombier 			break;
13223f695129SDavid du Colombier 		case WSOPT:
13233f695129SDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
13241ce6c95fSDavid du Colombier 				tcph->ws = *(optr+2);
13253f695129SDavid du Colombier 			break;
13263ff48bf5SDavid du Colombier 		}
13273ff48bf5SDavid du Colombier 		n -= optlen;
13283ff48bf5SDavid du Colombier 		optr += optlen;
13293ff48bf5SDavid du Colombier 	}
13303ff48bf5SDavid du Colombier 	return hdrlen;
13313ff48bf5SDavid du Colombier }
13323ff48bf5SDavid du Colombier 
13333ff48bf5SDavid du Colombier /*
13341ce6c95fSDavid du Colombier  *  For outgoing calls, generate an initial sequence
13353ff48bf5SDavid du Colombier  *  number and put a SYN on the send queue
13363ff48bf5SDavid du Colombier  */
13371ce6c95fSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13383f695129SDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13397dd7cddfSDavid du Colombier {
13407ec5746aSDavid du Colombier 	Tcppriv *tpriv;
13417ec5746aSDavid du Colombier 
13427dd7cddfSDavid du Colombier 	tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13437dd7cddfSDavid du Colombier 	tcb->rttseq = tcb->iss;
13447dd7cddfSDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
13457dd7cddfSDavid du Colombier 	tcb->snd.una = tcb->iss;
13461ce6c95fSDavid du Colombier 	tcb->snd.rxt = tcb->iss;
13477dd7cddfSDavid du Colombier 	tcb->snd.ptr = tcb->rttseq;
13487dd7cddfSDavid du Colombier 	tcb->snd.nxt = tcb->rttseq;
134980ee5cbfSDavid du Colombier 	tcb->flgcnt++;
13507dd7cddfSDavid du Colombier 	tcb->flags |= FORCE;
13513ff48bf5SDavid du Colombier 	tcb->sndsyntime = NOW;
13523f695129SDavid du Colombier 
13533f695129SDavid du Colombier 	/* set desired mss and scale */
13543f695129SDavid du Colombier 	tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13557ec5746aSDavid du Colombier 	tpriv = s->p->priv;
13567ec5746aSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
13577dd7cddfSDavid du Colombier }
13587dd7cddfSDavid du Colombier 
13597dd7cddfSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13603ff48bf5SDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13617dd7cddfSDavid du Colombier {
13627dd7cddfSDavid du Colombier 	Block *hbp;
13637dd7cddfSDavid du Colombier 	uchar rflags;
13647dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
13653ff48bf5SDavid du Colombier 	Tcp4hdr ph4;
13663ff48bf5SDavid du Colombier 	Tcp6hdr ph6;
13673ff48bf5SDavid du Colombier 
136884363d68SDavid du Colombier 	netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13697dd7cddfSDavid du Colombier 
13707dd7cddfSDavid du Colombier 	tpriv = tcp->priv;
13717dd7cddfSDavid du Colombier 
13727dd7cddfSDavid du Colombier 	if(seg->flags & RST)
13737dd7cddfSDavid du Colombier 		return;
13747dd7cddfSDavid du Colombier 
13757dd7cddfSDavid du Colombier 	/* make pseudo header */
13763ff48bf5SDavid du Colombier 	switch(version) {
13773ff48bf5SDavid du Colombier 	case V4:
13783ff48bf5SDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
13793ff48bf5SDavid du Colombier 		ph4.vihl = IP_VER4;
13803ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpsrc, dest);
13813ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpdst, source);
13823ff48bf5SDavid du Colombier 		ph4.proto = IP_TCPPROTO;
13833ff48bf5SDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
13843ff48bf5SDavid du Colombier 		hnputs(ph4.tcpsport, seg->dest);
13853ff48bf5SDavid du Colombier 		hnputs(ph4.tcpdport, seg->source);
13863ff48bf5SDavid du Colombier 		break;
13873ff48bf5SDavid du Colombier 	case V6:
13883ff48bf5SDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
13893ff48bf5SDavid du Colombier 		ph6.vcf[0] = IP_VER6;
13903ff48bf5SDavid du Colombier 		ipmove(ph6.tcpsrc, dest);
13913ff48bf5SDavid du Colombier 		ipmove(ph6.tcpdst, source);
13923ff48bf5SDavid du Colombier 		ph6.proto = IP_TCPPROTO;
13933ff48bf5SDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13943ff48bf5SDavid du Colombier 		hnputs(ph6.tcpsport, seg->dest);
13953ff48bf5SDavid du Colombier 		hnputs(ph6.tcpdport, seg->source);
13963ff48bf5SDavid du Colombier 		break;
13973ff48bf5SDavid du Colombier 	default:
13983ff48bf5SDavid du Colombier 		panic("sndrst: version %d", version);
13993ff48bf5SDavid du Colombier 	}
14007dd7cddfSDavid du Colombier 
140159cc4ca5SDavid du Colombier 	tpriv->stats[OutRsts]++;
14027dd7cddfSDavid du Colombier 	rflags = RST;
14037dd7cddfSDavid du Colombier 
14047dd7cddfSDavid du Colombier 	/* convince the other end that this reset is in band */
14057dd7cddfSDavid du Colombier 	if(seg->flags & ACK) {
14067dd7cddfSDavid du Colombier 		seg->seq = seg->ack;
14077dd7cddfSDavid du Colombier 		seg->ack = 0;
14087dd7cddfSDavid du Colombier 	}
14097dd7cddfSDavid du Colombier 	else {
14107dd7cddfSDavid du Colombier 		rflags |= ACK;
14117dd7cddfSDavid du Colombier 		seg->ack = seg->seq;
14127dd7cddfSDavid du Colombier 		seg->seq = 0;
14137dd7cddfSDavid du Colombier 		if(seg->flags & SYN)
14147dd7cddfSDavid du Colombier 			seg->ack++;
14157dd7cddfSDavid du Colombier 		seg->ack += length;
14167dd7cddfSDavid du Colombier 		if(seg->flags & FIN)
14177dd7cddfSDavid du Colombier 			seg->ack++;
14187dd7cddfSDavid du Colombier 	}
14197dd7cddfSDavid du Colombier 	seg->flags = rflags;
14207dd7cddfSDavid du Colombier 	seg->wnd = 0;
14217dd7cddfSDavid du Colombier 	seg->urg = 0;
14227dd7cddfSDavid du Colombier 	seg->mss = 0;
14233f695129SDavid du Colombier 	seg->ws = 0;
14243ff48bf5SDavid du Colombier 	switch(version) {
14253ff48bf5SDavid du Colombier 	case V4:
14263ff48bf5SDavid du Colombier 		hbp = htontcp4(seg, nil, &ph4, nil);
14277dd7cddfSDavid du Colombier 		if(hbp == nil)
14287dd7cddfSDavid du Colombier 			return;
1429a6a9e072SDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14303ff48bf5SDavid du Colombier 		break;
14313ff48bf5SDavid du Colombier 	case V6:
14323ff48bf5SDavid du Colombier 		hbp = htontcp6(seg, nil, &ph6, nil);
14333ff48bf5SDavid du Colombier 		if(hbp == nil)
14343ff48bf5SDavid du Colombier 			return;
1435a6a9e072SDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14363ff48bf5SDavid du Colombier 		break;
14373ff48bf5SDavid du Colombier 	default:
14383ff48bf5SDavid du Colombier 		panic("sndrst2: version %d", version);
14393ff48bf5SDavid du Colombier 	}
14407dd7cddfSDavid du Colombier }
14417dd7cddfSDavid du Colombier 
14427dd7cddfSDavid du Colombier /*
1443*41aa7335SDavid du Colombier  * close the conversation
1444*41aa7335SDavid du Colombier  */
1445*41aa7335SDavid du Colombier static char*
tcpclose2(Conv * s)1446*41aa7335SDavid du Colombier tcpclose2(Conv *s)
1447*41aa7335SDavid du Colombier {
1448*41aa7335SDavid du Colombier 	tcpclose(s);
1449*41aa7335SDavid du Colombier 	return nil;
1450*41aa7335SDavid du Colombier }
1451*41aa7335SDavid du Colombier 
1452*41aa7335SDavid du Colombier /*
14537dd7cddfSDavid du Colombier  *  send a reset to the remote side and close the conversation
145480ee5cbfSDavid du Colombier  *  called with s qlocked
14557dd7cddfSDavid du Colombier  */
14561ce6c95fSDavid du Colombier static char*
tcphangup(Conv * s)14577dd7cddfSDavid du Colombier tcphangup(Conv *s)
14587dd7cddfSDavid du Colombier {
14597dd7cddfSDavid du Colombier 	Tcp seg;
14607dd7cddfSDavid du Colombier 	Tcpctl *tcb;
14617dd7cddfSDavid du Colombier 	Block *hbp;
14627dd7cddfSDavid du Colombier 
14637dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
146480ee5cbfSDavid du Colombier 	if(waserror())
14657dd7cddfSDavid du Colombier 		return commonerror();
1466c02046c4SDavid du Colombier 	if(ipcmp(s->raddr, IPnoaddr) != 0) {
14675fab9909SDavid du Colombier 		if(!waserror()){
146810ae047aSDavid du Colombier 			memset(&seg, 0, sizeof seg);
14697dd7cddfSDavid du Colombier 			seg.flags = RST | ACK;
14707dd7cddfSDavid du Colombier 			seg.ack = tcb->rcv.nxt;
14711ce6c95fSDavid du Colombier 			tcb->rcv.ackptr = seg.ack;
14727dd7cddfSDavid du Colombier 			seg.seq = tcb->snd.ptr;
14737dd7cddfSDavid du Colombier 			seg.wnd = 0;
14747dd7cddfSDavid du Colombier 			seg.urg = 0;
14757dd7cddfSDavid du Colombier 			seg.mss = 0;
14763f695129SDavid du Colombier 			seg.ws = 0;
14773ff48bf5SDavid du Colombier 			switch(s->ipversion) {
14783ff48bf5SDavid du Colombier 			case V4:
14793ff48bf5SDavid du Colombier 				tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14803ff48bf5SDavid du Colombier 				hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
1481a6a9e072SDavid du Colombier 				ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14823ff48bf5SDavid du Colombier 				break;
14833ff48bf5SDavid du Colombier 			case V6:
14843ff48bf5SDavid du Colombier 				tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14853ff48bf5SDavid du Colombier 				hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
1486a6a9e072SDavid du Colombier 				ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14873ff48bf5SDavid du Colombier 				break;
14883ff48bf5SDavid du Colombier 			default:
14893ff48bf5SDavid du Colombier 				panic("tcphangup: version %d", s->ipversion);
14903ff48bf5SDavid du Colombier 			}
14915fab9909SDavid du Colombier 			poperror();
14925fab9909SDavid du Colombier 		}
14937dd7cddfSDavid du Colombier 	}
14947dd7cddfSDavid du Colombier 	localclose(s, nil);
14957dd7cddfSDavid du Colombier 	poperror();
14967dd7cddfSDavid du Colombier 	return nil;
14977dd7cddfSDavid du Colombier }
14987dd7cddfSDavid du Colombier 
14993ff48bf5SDavid du Colombier /*
15003ff48bf5SDavid du Colombier  *  (re)send a SYN ACK
15013ff48bf5SDavid du Colombier  */
15021ce6c95fSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)15033ff48bf5SDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
15043ff48bf5SDavid du Colombier {
15053ff48bf5SDavid du Colombier 	Block *hbp;
15063ff48bf5SDavid du Colombier 	Tcp4hdr ph4;
15073ff48bf5SDavid du Colombier 	Tcp6hdr ph6;
15083ff48bf5SDavid du Colombier 	Tcp seg;
15091ce6c95fSDavid du Colombier 	uint scale;
15103ff48bf5SDavid du Colombier 
15113ff48bf5SDavid du Colombier 	/* make pseudo header */
15123ff48bf5SDavid du Colombier 	switch(lp->version) {
15133ff48bf5SDavid du Colombier 	case V4:
15143ff48bf5SDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
15153ff48bf5SDavid du Colombier 		ph4.vihl = IP_VER4;
15163ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpsrc, lp->laddr);
15173ff48bf5SDavid du Colombier 		v6tov4(ph4.tcpdst, lp->raddr);
15183ff48bf5SDavid du Colombier 		ph4.proto = IP_TCPPROTO;
15193ff48bf5SDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
15203ff48bf5SDavid du Colombier 		hnputs(ph4.tcpsport, lp->lport);
15213ff48bf5SDavid du Colombier 		hnputs(ph4.tcpdport, lp->rport);
15223ff48bf5SDavid du Colombier 		break;
15233ff48bf5SDavid du Colombier 	case V6:
15243ff48bf5SDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
15253ff48bf5SDavid du Colombier 		ph6.vcf[0] = IP_VER6;
15263ff48bf5SDavid du Colombier 		ipmove(ph6.tcpsrc, lp->laddr);
15273ff48bf5SDavid du Colombier 		ipmove(ph6.tcpdst, lp->raddr);
15283ff48bf5SDavid du Colombier 		ph6.proto = IP_TCPPROTO;
15293ff48bf5SDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
15303ff48bf5SDavid du Colombier 		hnputs(ph6.tcpsport, lp->lport);
15313ff48bf5SDavid du Colombier 		hnputs(ph6.tcpdport, lp->rport);
15323ff48bf5SDavid du Colombier 		break;
15333ff48bf5SDavid du Colombier 	default:
15343ff48bf5SDavid du Colombier 		panic("sndrst: version %d", lp->version);
15353ff48bf5SDavid du Colombier 	}
15363ff48bf5SDavid du Colombier 
153710ae047aSDavid du Colombier 	memset(&seg, 0, sizeof seg);
15383ff48bf5SDavid du Colombier 	seg.seq = lp->iss;
15393ff48bf5SDavid du Colombier 	seg.ack = lp->irs+1;
15403ff48bf5SDavid du Colombier 	seg.flags = SYN|ACK;
15413ff48bf5SDavid du Colombier 	seg.urg = 0;
15423f695129SDavid du Colombier 	seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15433ff48bf5SDavid du Colombier 	seg.wnd = QMAX;
15443ff48bf5SDavid du Colombier 
15453f695129SDavid du Colombier 	/* if the other side set scale, we should too */
15463f695129SDavid du Colombier 	if(lp->rcvscale){
15473f695129SDavid du Colombier 		seg.ws = scale;
15483f695129SDavid du Colombier 		lp->sndscale = scale;
15493f695129SDavid du Colombier 	} else {
15503f695129SDavid du Colombier 		seg.ws = 0;
15513f695129SDavid du Colombier 		lp->sndscale = 0;
15523f695129SDavid du Colombier 	}
15533f695129SDavid du Colombier 
15543ff48bf5SDavid du Colombier 	switch(lp->version) {
15553ff48bf5SDavid du Colombier 	case V4:
15563ff48bf5SDavid du Colombier 		hbp = htontcp4(&seg, nil, &ph4, nil);
15573ff48bf5SDavid du Colombier 		if(hbp == nil)
15583ff48bf5SDavid du Colombier 			return -1;
1559a6a9e072SDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15603ff48bf5SDavid du Colombier 		break;
15613ff48bf5SDavid du Colombier 	case V6:
15623ff48bf5SDavid du Colombier 		hbp = htontcp6(&seg, nil, &ph6, nil);
15633ff48bf5SDavid du Colombier 		if(hbp == nil)
15643ff48bf5SDavid du Colombier 			return -1;
1565a6a9e072SDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15663ff48bf5SDavid du Colombier 		break;
15673ff48bf5SDavid du Colombier 	default:
15683ff48bf5SDavid du Colombier 		panic("sndsnack: version %d", lp->version);
15693ff48bf5SDavid du Colombier 	}
15703ff48bf5SDavid du Colombier 	lp->lastsend = NOW;
15713ff48bf5SDavid du Colombier 	return 0;
15723ff48bf5SDavid du Colombier }
15733ff48bf5SDavid du Colombier 
15743ff48bf5SDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15753ff48bf5SDavid du Colombier 
15763ff48bf5SDavid du Colombier /*
15773ff48bf5SDavid du Colombier  *  put a call into limbo and respond with a SYN ACK
15783ff48bf5SDavid du Colombier  *
15793ff48bf5SDavid du Colombier  *  called with proto locked
15803ff48bf5SDavid du Colombier  */
15813ff48bf5SDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15823ff48bf5SDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15833ff48bf5SDavid du Colombier {
15843ff48bf5SDavid du Colombier 	Limbo *lp, **l;
15853ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
15863ff48bf5SDavid du Colombier 	int h;
15873ff48bf5SDavid du Colombier 
15883ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
15893ff48bf5SDavid du Colombier 	h = hashipa(source, seg->source);
15903ff48bf5SDavid du Colombier 
15913ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15923ff48bf5SDavid du Colombier 		lp = *l;
15933ff48bf5SDavid du Colombier 		if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15943ff48bf5SDavid du Colombier 			continue;
15953ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, source) != 0)
15963ff48bf5SDavid du Colombier 			continue;
15973ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dest) != 0)
15983ff48bf5SDavid du Colombier 			continue;
15993ff48bf5SDavid du Colombier 
1600ef9eff0bSDavid du Colombier 		/* each new SYN restarts the retransmits */
16013ff48bf5SDavid du Colombier 		lp->irs = seg->seq;
16023ff48bf5SDavid du Colombier 		break;
16033ff48bf5SDavid du Colombier 	}
16043ff48bf5SDavid du Colombier 	lp = *l;
16053ff48bf5SDavid du Colombier 	if(lp == nil){
16063ff48bf5SDavid du Colombier 		if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
16073ff48bf5SDavid du Colombier 			lp = tpriv->lht[h];
16083ff48bf5SDavid du Colombier 			tpriv->lht[h] = lp->next;
16093ff48bf5SDavid du Colombier 			lp->next = nil;
16103ff48bf5SDavid du Colombier 		} else {
16113ff48bf5SDavid du Colombier 			lp = malloc(sizeof(*lp));
16123ff48bf5SDavid du Colombier 			if(lp == nil)
16133ff48bf5SDavid du Colombier 				return;
16143ff48bf5SDavid du Colombier 			tpriv->nlimbo++;
16153ff48bf5SDavid du Colombier 		}
16163ff48bf5SDavid du Colombier 		*l = lp;
16173ff48bf5SDavid du Colombier 		lp->version = version;
16183ff48bf5SDavid du Colombier 		ipmove(lp->laddr, dest);
16193ff48bf5SDavid du Colombier 		ipmove(lp->raddr, source);
16203ff48bf5SDavid du Colombier 		lp->lport = seg->dest;
16213ff48bf5SDavid du Colombier 		lp->rport = seg->source;
16223ff48bf5SDavid du Colombier 		lp->mss = seg->mss;
16233f695129SDavid du Colombier 		lp->rcvscale = seg->ws;
16243ff48bf5SDavid du Colombier 		lp->irs = seg->seq;
16253ff48bf5SDavid du Colombier 		lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
16263ff48bf5SDavid du Colombier 	}
16273ff48bf5SDavid du Colombier 
16283ff48bf5SDavid du Colombier 	if(sndsynack(s->p, lp) < 0){
16293ff48bf5SDavid du Colombier 		*l = lp->next;
16303ff48bf5SDavid du Colombier 		tpriv->nlimbo--;
16313ff48bf5SDavid du Colombier 		free(lp);
16323ff48bf5SDavid du Colombier 	}
16333ff48bf5SDavid du Colombier }
16343ff48bf5SDavid du Colombier 
16353ff48bf5SDavid du Colombier /*
16363ff48bf5SDavid du Colombier  *  resend SYN ACK's once every SYNACK_RXTIMER ms.
16373ff48bf5SDavid du Colombier  */
16383ff48bf5SDavid du Colombier static void
limborexmit(Proto * tcp)16393ff48bf5SDavid du Colombier limborexmit(Proto *tcp)
16403ff48bf5SDavid du Colombier {
16413ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
16423ff48bf5SDavid du Colombier 	Limbo **l, *lp;
16433ff48bf5SDavid du Colombier 	int h;
16443ff48bf5SDavid du Colombier 	int seen;
16453ff48bf5SDavid du Colombier 	ulong now;
16463ff48bf5SDavid du Colombier 
16473ff48bf5SDavid du Colombier 	tpriv = tcp->priv;
16483ff48bf5SDavid du Colombier 
16493ff48bf5SDavid du Colombier 	if(!canqlock(tcp))
16503ff48bf5SDavid du Colombier 		return;
16513ff48bf5SDavid du Colombier 	seen = 0;
16523ff48bf5SDavid du Colombier 	now = NOW;
16533ff48bf5SDavid du Colombier 	for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16543ff48bf5SDavid du Colombier 		for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16553ff48bf5SDavid du Colombier 			lp = *l;
16563ff48bf5SDavid du Colombier 			seen++;
16573ff48bf5SDavid du Colombier 			if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16583ff48bf5SDavid du Colombier 				continue;
16593ff48bf5SDavid du Colombier 
16603ff48bf5SDavid du Colombier 			/* time it out after 1 second */
16613ff48bf5SDavid du Colombier 			if(++(lp->rexmits) > 5){
16623ff48bf5SDavid du Colombier 				tpriv->nlimbo--;
16633ff48bf5SDavid du Colombier 				*l = lp->next;
16643ff48bf5SDavid du Colombier 				free(lp);
16653ff48bf5SDavid du Colombier 				continue;
16663ff48bf5SDavid du Colombier 			}
16673ff48bf5SDavid du Colombier 
16683ff48bf5SDavid du Colombier 			/* if we're being attacked, don't bother resending SYN ACK's */
16693ff48bf5SDavid du Colombier 			if(tpriv->nlimbo > 100)
16703ff48bf5SDavid du Colombier 				continue;
16713ff48bf5SDavid du Colombier 
16723ff48bf5SDavid du Colombier 			if(sndsynack(tcp, lp) < 0){
16733ff48bf5SDavid du Colombier 				tpriv->nlimbo--;
16743ff48bf5SDavid du Colombier 				*l = lp->next;
16753ff48bf5SDavid du Colombier 				free(lp);
16763ff48bf5SDavid du Colombier 				continue;
16773ff48bf5SDavid du Colombier 			}
16783ff48bf5SDavid du Colombier 
16793ff48bf5SDavid du Colombier 			l = &lp->next;
16803ff48bf5SDavid du Colombier 		}
16813ff48bf5SDavid du Colombier 	}
16823ff48bf5SDavid du Colombier 	qunlock(tcp);
16833ff48bf5SDavid du Colombier }
16843ff48bf5SDavid du Colombier 
16853ff48bf5SDavid du Colombier /*
16863ff48bf5SDavid du Colombier  *  lookup call in limbo.  if found, throw it out.
16873ff48bf5SDavid du Colombier  *
16883ff48bf5SDavid du Colombier  *  called with proto locked
16893ff48bf5SDavid du Colombier  */
16903ff48bf5SDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16913ff48bf5SDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16923ff48bf5SDavid du Colombier {
16933ff48bf5SDavid du Colombier 	Limbo *lp, **l;
16943ff48bf5SDavid du Colombier 	int h;
16953ff48bf5SDavid du Colombier 	Tcppriv *tpriv;
16963ff48bf5SDavid du Colombier 
16973ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
16983ff48bf5SDavid du Colombier 
16993ff48bf5SDavid du Colombier 	/* find a call in limbo */
17003ff48bf5SDavid du Colombier 	h = hashipa(src, segp->source);
17013ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
17023ff48bf5SDavid du Colombier 		lp = *l;
17033ff48bf5SDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17043ff48bf5SDavid du Colombier 			continue;
17053ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
17063ff48bf5SDavid du Colombier 			continue;
17073ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
17083ff48bf5SDavid du Colombier 			continue;
17093ff48bf5SDavid du Colombier 
17103ff48bf5SDavid du Colombier 		/* RST can only follow the SYN */
17113ff48bf5SDavid du Colombier 		if(segp->seq == lp->irs+1){
17123ff48bf5SDavid du Colombier 			tpriv->nlimbo--;
17133ff48bf5SDavid du Colombier 			*l = lp->next;
17143ff48bf5SDavid du Colombier 			free(lp);
17153ff48bf5SDavid du Colombier 		}
17163ff48bf5SDavid du Colombier 		break;
17173ff48bf5SDavid du Colombier 	}
17183ff48bf5SDavid du Colombier }
17193ff48bf5SDavid du Colombier 
17201ce6c95fSDavid du Colombier static void
initialwindow(Tcpctl * tcb)17211ce6c95fSDavid du Colombier initialwindow(Tcpctl *tcb)
17221ce6c95fSDavid du Colombier {
17231ce6c95fSDavid du Colombier 	/* RFC 3390 initial window */
17241ce6c95fSDavid du Colombier 	if(tcb->mss < 1095)
17251ce6c95fSDavid du Colombier 		tcb->cwind = 4*tcb->mss;
17261ce6c95fSDavid du Colombier 	else if(tcb->mss < 2190)
17271ce6c95fSDavid du Colombier 		tcb->cwind = 2*2190;
17281ce6c95fSDavid du Colombier 	else
17291ce6c95fSDavid du Colombier 		tcb->cwind = 2*tcb->mss;
17301ce6c95fSDavid du Colombier }
17311ce6c95fSDavid du Colombier 
17323ff48bf5SDavid du Colombier /*
17333f695129SDavid du Colombier  *  come here when we finally get an ACK to our SYN-ACK.
17343ff48bf5SDavid du Colombier  *  lookup call in limbo.  if found, create a new conversation
17353ff48bf5SDavid du Colombier  *
17363ff48bf5SDavid du Colombier  *  called with proto locked
17373ff48bf5SDavid du Colombier  */
17383ff48bf5SDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)17393ff48bf5SDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17407dd7cddfSDavid du Colombier {
17417dd7cddfSDavid du Colombier 	Conv *new;
17427dd7cddfSDavid du Colombier 	Tcpctl *tcb;
174380ee5cbfSDavid du Colombier 	Tcppriv *tpriv;
17443ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
17453ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
17463ff48bf5SDavid du Colombier 	Limbo *lp, **l;
17473ff48bf5SDavid du Colombier 	int h;
17487dd7cddfSDavid du Colombier 
17493ff48bf5SDavid du Colombier 	/* unless it's just an ack, it can't be someone coming out of limbo */
17503ff48bf5SDavid du Colombier 	if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17513ff48bf5SDavid du Colombier 		return nil;
17523ff48bf5SDavid du Colombier 
17533ff48bf5SDavid du Colombier 	tpriv = s->p->priv;
17543ff48bf5SDavid du Colombier 
17553ff48bf5SDavid du Colombier 	/* find a call in limbo */
17563ff48bf5SDavid du Colombier 	h = hashipa(src, segp->source);
17573ff48bf5SDavid du Colombier 	for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17587133e0eeSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17593ff48bf5SDavid du Colombier 			src, segp->source, lp->raddr, lp->rport,
17603ff48bf5SDavid du Colombier 			dst, segp->dest, lp->laddr, lp->lport,
17613ff48bf5SDavid du Colombier 			version, lp->version
17623ff48bf5SDavid du Colombier 		);
17633ff48bf5SDavid du Colombier 
17643ff48bf5SDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17653ff48bf5SDavid du Colombier 			continue;
17663ff48bf5SDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
17673ff48bf5SDavid du Colombier 			continue;
17683ff48bf5SDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
17693ff48bf5SDavid du Colombier 			continue;
17703ff48bf5SDavid du Colombier 
17713ff48bf5SDavid du Colombier 		/* we're assuming no data with the initial SYN */
17723ff48bf5SDavid du Colombier 		if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
177384363d68SDavid du Colombier 			netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17743ff48bf5SDavid du Colombier 				segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17753ff48bf5SDavid du Colombier 			lp = nil;
17763ff48bf5SDavid du Colombier 		} else {
17773ff48bf5SDavid du Colombier 			tpriv->nlimbo--;
17783ff48bf5SDavid du Colombier 			*l = lp->next;
17793ff48bf5SDavid du Colombier 		}
17803ff48bf5SDavid du Colombier 		break;
17813ff48bf5SDavid du Colombier 	}
17823ff48bf5SDavid du Colombier 	if(lp == nil)
17833ff48bf5SDavid du Colombier 		return nil;
17843ff48bf5SDavid du Colombier 
17853ff48bf5SDavid du Colombier 	new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17867dd7cddfSDavid du Colombier 	if(new == nil)
17877dd7cddfSDavid du Colombier 		return nil;
17887dd7cddfSDavid du Colombier 
17897dd7cddfSDavid du Colombier 	memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17907dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)new->ptcl;
17917dd7cddfSDavid du Colombier 	tcb->flags &= ~CLONE;
17927dd7cddfSDavid du Colombier 	tcb->timer.arg = new;
17939a747e4fSDavid du Colombier 	tcb->timer.state = TcptimerOFF;
17947dd7cddfSDavid du Colombier 	tcb->acktimer.arg = new;
17959a747e4fSDavid du Colombier 	tcb->acktimer.state = TcptimerOFF;
17967dd7cddfSDavid du Colombier 	tcb->katimer.arg = new;
17979a747e4fSDavid du Colombier 	tcb->katimer.state = TcptimerOFF;
17987dd7cddfSDavid du Colombier 	tcb->rtt_timer.arg = new;
17999a747e4fSDavid du Colombier 	tcb->rtt_timer.state = TcptimerOFF;
18007dd7cddfSDavid du Colombier 
18013ff48bf5SDavid du Colombier 	tcb->irs = lp->irs;
18023ff48bf5SDavid du Colombier 	tcb->rcv.nxt = tcb->irs+1;
18031ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
18041ce6c95fSDavid du Colombier 	tcb->rcv.wsnt = 0;
18053ff48bf5SDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
18067dd7cddfSDavid du Colombier 
18073ff48bf5SDavid du Colombier 	tcb->iss = lp->iss;
18083ff48bf5SDavid du Colombier 	tcb->rttseq = tcb->iss;
18093ff48bf5SDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
18103ff48bf5SDavid du Colombier 	tcb->snd.una = tcb->iss+1;
18113ff48bf5SDavid du Colombier 	tcb->snd.ptr = tcb->iss+1;
18123ff48bf5SDavid du Colombier 	tcb->snd.nxt = tcb->iss+1;
18131ce6c95fSDavid du Colombier 	tcb->snd.rxt = tcb->iss+1;
18143ff48bf5SDavid du Colombier 	tcb->flgcnt = 0;
18153ff48bf5SDavid du Colombier 	tcb->flags |= SYNACK;
18163ff48bf5SDavid du Colombier 
18176d6d0683SDavid du Colombier 	/* set desired mss and scale */
18186d6d0683SDavid du Colombier 	tcb->mss = tcpmtu(s->p, dst, s->ipversion, &tcb->scale);
18196d6d0683SDavid du Colombier 
18203ff48bf5SDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
18216d6d0683SDavid du Colombier 	if(lp->mss != 0 && lp->mss < tcb->mss)
18223ff48bf5SDavid du Colombier 		tcb->mss = lp->mss;
18237ec5746aSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
18243ff48bf5SDavid du Colombier 
18253f695129SDavid du Colombier 	/* window scaling */
18263f695129SDavid du Colombier 	tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
18273f695129SDavid du Colombier 
18281ce6c95fSDavid du Colombier 	/* congestion window */
18293ff48bf5SDavid du Colombier 	tcb->snd.wnd = segp->wnd;
18301ce6c95fSDavid du Colombier 	initialwindow(tcb);
18313ff48bf5SDavid du Colombier 
18323ff48bf5SDavid du Colombier 	/* set initial round trip time */
18333ff48bf5SDavid du Colombier 	tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
18343ff48bf5SDavid du Colombier 	tcpsynackrtt(new);
18353ff48bf5SDavid du Colombier 
18363ff48bf5SDavid du Colombier 	free(lp);
18373ff48bf5SDavid du Colombier 
18383ff48bf5SDavid du Colombier 	/* set up proto header */
18393ff48bf5SDavid du Colombier 	switch(version){
18403ff48bf5SDavid du Colombier 	case V4:
18413ff48bf5SDavid du Colombier 		h4 = &tcb->protohdr.tcp4hdr;
18423ff48bf5SDavid du Colombier 		memset(h4, 0, sizeof(*h4));
18433ff48bf5SDavid du Colombier 		h4->proto = IP_TCPPROTO;
18443ff48bf5SDavid du Colombier 		hnputs(h4->tcpsport, new->lport);
18453ff48bf5SDavid du Colombier 		hnputs(h4->tcpdport, new->rport);
18463ff48bf5SDavid du Colombier 		v6tov4(h4->tcpsrc, dst);
18473ff48bf5SDavid du Colombier 		v6tov4(h4->tcpdst, src);
18483ff48bf5SDavid du Colombier 		break;
18493ff48bf5SDavid du Colombier 	case V6:
18503ff48bf5SDavid du Colombier 		h6 = &tcb->protohdr.tcp6hdr;
18513ff48bf5SDavid du Colombier 		memset(h6, 0, sizeof(*h6));
18523ff48bf5SDavid du Colombier 		h6->proto = IP_TCPPROTO;
18533ff48bf5SDavid du Colombier 		hnputs(h6->tcpsport, new->lport);
18543ff48bf5SDavid du Colombier 		hnputs(h6->tcpdport, new->rport);
18553ff48bf5SDavid du Colombier 		ipmove(h6->tcpsrc, dst);
18563ff48bf5SDavid du Colombier 		ipmove(h6->tcpdst, src);
18573ff48bf5SDavid du Colombier 		break;
18583ff48bf5SDavid du Colombier 	default:
18593ff48bf5SDavid du Colombier 		panic("tcpincoming: version %d", new->ipversion);
18603ff48bf5SDavid du Colombier 	}
18613ff48bf5SDavid du Colombier 
18623ff48bf5SDavid du Colombier 	tcpsetstate(new, Established);
18633ff48bf5SDavid du Colombier 
186480ee5cbfSDavid du Colombier 	iphtadd(&tpriv->ht, new);
186580ee5cbfSDavid du Colombier 
18667dd7cddfSDavid du Colombier 	return new;
18677dd7cddfSDavid du Colombier }
18687dd7cddfSDavid du Colombier 
18691ce6c95fSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18707dd7cddfSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18717dd7cddfSDavid du Colombier {
18727dd7cddfSDavid du Colombier 	if(low <= high){
18737dd7cddfSDavid du Colombier 		if(low <= x && x <= high)
18747dd7cddfSDavid du Colombier 			return 1;
18757dd7cddfSDavid du Colombier 	}
18767dd7cddfSDavid du Colombier 	else {
18777dd7cddfSDavid du Colombier 		if(x >= low || x <= high)
18787dd7cddfSDavid du Colombier 			return 1;
18797dd7cddfSDavid du Colombier 	}
18807dd7cddfSDavid du Colombier 	return 0;
18817dd7cddfSDavid du Colombier }
18827dd7cddfSDavid du Colombier 
18831ce6c95fSDavid du Colombier static int
seq_lt(ulong x,ulong y)18847dd7cddfSDavid du Colombier seq_lt(ulong x, ulong y)
18857dd7cddfSDavid du Colombier {
18867dd7cddfSDavid du Colombier 	return (int)(x-y) < 0;
18877dd7cddfSDavid du Colombier }
18887dd7cddfSDavid du Colombier 
18891ce6c95fSDavid du Colombier static int
seq_le(ulong x,ulong y)18907dd7cddfSDavid du Colombier seq_le(ulong x, ulong y)
18917dd7cddfSDavid du Colombier {
18927dd7cddfSDavid du Colombier 	return (int)(x-y) <= 0;
18937dd7cddfSDavid du Colombier }
18947dd7cddfSDavid du Colombier 
18951ce6c95fSDavid du Colombier static int
seq_gt(ulong x,ulong y)18967dd7cddfSDavid du Colombier seq_gt(ulong x, ulong y)
18977dd7cddfSDavid du Colombier {
18987dd7cddfSDavid du Colombier 	return (int)(x-y) > 0;
18997dd7cddfSDavid du Colombier }
19007dd7cddfSDavid du Colombier 
19011ce6c95fSDavid du Colombier static int
seq_ge(ulong x,ulong y)19027dd7cddfSDavid du Colombier seq_ge(ulong x, ulong y)
19037dd7cddfSDavid du Colombier {
19047dd7cddfSDavid du Colombier 	return (int)(x-y) >= 0;
19057dd7cddfSDavid du Colombier }
19067dd7cddfSDavid du Colombier 
19077dd7cddfSDavid du Colombier /*
19087dd7cddfSDavid du Colombier  *  use the time between the first SYN and it's ack as the
19097dd7cddfSDavid du Colombier  *  initial round trip time
19107dd7cddfSDavid du Colombier  */
19111ce6c95fSDavid du Colombier static void
tcpsynackrtt(Conv * s)19127dd7cddfSDavid du Colombier tcpsynackrtt(Conv *s)
19137dd7cddfSDavid du Colombier {
19147dd7cddfSDavid du Colombier 	Tcpctl *tcb;
19157dd7cddfSDavid du Colombier 	int delta;
19167dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
19177dd7cddfSDavid du Colombier 
19187dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
19197dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
19207dd7cddfSDavid du Colombier 
19213ff48bf5SDavid du Colombier 	delta = NOW - tcb->sndsyntime;
19227dd7cddfSDavid du Colombier 	tcb->srtt = delta<<LOGAGAIN;
19237dd7cddfSDavid du Colombier 	tcb->mdev = delta<<LOGDGAIN;
19247dd7cddfSDavid du Colombier 
19257dd7cddfSDavid du Colombier 	/* halt round trip timer */
19267dd7cddfSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
19277dd7cddfSDavid du Colombier }
19287dd7cddfSDavid du Colombier 
19291ce6c95fSDavid du Colombier static void
update(Conv * s,Tcp * seg)19307dd7cddfSDavid du Colombier update(Conv *s, Tcp *seg)
19317dd7cddfSDavid du Colombier {
19327dd7cddfSDavid du Colombier 	int rtt, delta;
19337dd7cddfSDavid du Colombier 	Tcpctl *tcb;
19343f695129SDavid du Colombier 	ulong acked;
19357dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
19367dd7cddfSDavid du Colombier 
19371ce6c95fSDavid du Colombier 	if(seg->update)
19381ce6c95fSDavid du Colombier 		return;
19391ce6c95fSDavid du Colombier 	seg->update = 1;
19401ce6c95fSDavid du Colombier 
19417dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
19427dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
19437dd7cddfSDavid du Colombier 
19441ce6c95fSDavid du Colombier 	/* catch zero-window updates, update window & recover */
19451ce6c95fSDavid du Colombier 	if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19461ce6c95fSDavid du Colombier 	    seq_lt(seg->ack, tcb->snd.ptr)){
19471ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19481ce6c95fSDavid du Colombier 			seg->ack,  tcb->snd.una, tcb->snd.ptr, seg->wnd);
19491ce6c95fSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19501ce6c95fSDavid du Colombier 		goto recovery;
19517dd7cddfSDavid du Colombier 	}
19527dd7cddfSDavid du Colombier 
19531ce6c95fSDavid du Colombier 	/* newreno fast retransmit */
19541ce6c95fSDavid du Colombier 	if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19551ce6c95fSDavid du Colombier 	    ++tcb->snd.dupacks == 3){		/* was TCPREXMTTHRESH */
19561ce6c95fSDavid du Colombier recovery:
19571ce6c95fSDavid du Colombier 		if(tcb->snd.recovery){
19581ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryCwind]++;
19591ce6c95fSDavid du Colombier 			tcb->cwind += tcb->mss;
19601ce6c95fSDavid du Colombier 		}else if(seq_le(tcb->snd.rxt, seg->ack)){
19611ce6c95fSDavid du Colombier 			tpriv->stats[Recovery]++;
19621ce6c95fSDavid du Colombier 			tcb->abcbytes = 0;
19637dd7cddfSDavid du Colombier 			tcb->snd.recovery = 1;
19641ce6c95fSDavid du Colombier 			tcb->snd.partialack = 0;
19657dd7cddfSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
19661ce6c95fSDavid du Colombier 			tcpcongestion(tcb);
19671ce6c95fSDavid du Colombier 			tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19681ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19691ce6c95fSDavid du Colombier 				tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19707dd7cddfSDavid du Colombier 			tcprxmit(s);
19717dd7cddfSDavid du Colombier 		}else{
19721ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryNoSeq]++;
19731ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19741ce6c95fSDavid du Colombier 				tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19751ce6c95fSDavid du Colombier 			/* don't enter fast retransmit, don't change ssthresh */
19767dd7cddfSDavid du Colombier 		}
19771ce6c95fSDavid du Colombier 	}else if(tcb->snd.recovery){
19781ce6c95fSDavid du Colombier 		tpriv->stats[RecoveryCwind]++;
19791ce6c95fSDavid du Colombier 		tcb->cwind += tcb->mss;
19807dd7cddfSDavid du Colombier 	}
19817dd7cddfSDavid du Colombier 
198280ee5cbfSDavid du Colombier 	/*
198380ee5cbfSDavid du Colombier 	 *  update window
198480ee5cbfSDavid du Colombier 	 */
198580ee5cbfSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.wl2)
198680ee5cbfSDavid du Colombier 	|| (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19871ce6c95fSDavid du Colombier 		/* clear dupack if we advance wl2 */
19881ce6c95fSDavid du Colombier 		if(tcb->snd.wl2 != seg->ack)
19891ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;
19907dd7cddfSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19917dd7cddfSDavid du Colombier 		tcb->snd.wl2 = seg->ack;
19927dd7cddfSDavid du Colombier 	}
19937dd7cddfSDavid du Colombier 
199480ee5cbfSDavid du Colombier 	if(!seq_gt(seg->ack, tcb->snd.una)){
199580ee5cbfSDavid du Colombier 		/*
199680ee5cbfSDavid du Colombier 		 *  don't let us hangup if sending into a closed window and
199780ee5cbfSDavid du Colombier 		 *  we're still getting acks
199880ee5cbfSDavid du Colombier 		 */
19991ce6c95fSDavid du Colombier 		if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
200080ee5cbfSDavid du Colombier 			tcb->backedoff = MAXBACKMS/4;
20017dd7cddfSDavid du Colombier 		return;
200280ee5cbfSDavid du Colombier 	}
20037dd7cddfSDavid du Colombier 
20047dd7cddfSDavid du Colombier 	/* Compute the new send window size */
20057dd7cddfSDavid du Colombier 	acked = seg->ack - tcb->snd.una;
20067dd7cddfSDavid du Colombier 
20077dd7cddfSDavid du Colombier 	/* avoid slow start and timers for SYN acks */
20087dd7cddfSDavid du Colombier 	if((tcb->flags & SYNACK) == 0) {
20097dd7cddfSDavid du Colombier 		tcb->flags |= SYNACK;
20107dd7cddfSDavid du Colombier 		acked--;
201180ee5cbfSDavid du Colombier 		tcb->flgcnt--;
20127dd7cddfSDavid du Colombier 		goto done;
20137dd7cddfSDavid du Colombier 	}
20147dd7cddfSDavid du Colombier 
20151ce6c95fSDavid du Colombier 	/*
20161ce6c95fSDavid du Colombier 	 * congestion control
20171ce6c95fSDavid du Colombier 	 */
20181ce6c95fSDavid du Colombier 	if(tcb->snd.recovery){
20191ce6c95fSDavid du Colombier 		if(seq_ge(seg->ack, tcb->snd.rxt)){
20201ce6c95fSDavid du Colombier 			/* recovery finished; deflate window */
20211ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryDone]++;
20221ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;
20231ce6c95fSDavid du Colombier 			tcb->snd.recovery = 0;
20241ce6c95fSDavid du Colombier 			tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
20251ce6c95fSDavid du Colombier 			if(tcb->ssthresh < tcb->cwind)
20261ce6c95fSDavid du Colombier 				tcb->cwind = tcb->ssthresh;
20271ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
20281ce6c95fSDavid du Colombier 				tcb->cwind, tcb->ssthresh);
20291ce6c95fSDavid du Colombier 		} else {
20301ce6c95fSDavid du Colombier 			/* partial ack; we lost more than one segment */
20311ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryPA]++;
20321ce6c95fSDavid du Colombier 			if(tcb->cwind > acked)
20331ce6c95fSDavid du Colombier 				tcb->cwind -= acked;
20341ce6c95fSDavid du Colombier 			else{
20351ce6c95fSDavid du Colombier 				netlog(s->p->f, Logtcpwin, "partial ack neg\n");
20361ce6c95fSDavid du Colombier 				tcb->cwind = tcb->mss;
20377dd7cddfSDavid du Colombier 			}
20381ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
20391ce6c95fSDavid du Colombier 				acked, tcb->snd.rxt - seg->ack, tcb->cwind);
20407dd7cddfSDavid du Colombier 
20411ce6c95fSDavid du Colombier 			if(acked >= tcb->mss)
20421ce6c95fSDavid du Colombier 				tcb->cwind += tcb->mss;
20431ce6c95fSDavid du Colombier 			tcb->snd.partialack++;
20447dd7cddfSDavid du Colombier 		}
20451ce6c95fSDavid du Colombier 	} else
20461ce6c95fSDavid du Colombier 		tcpabcincr(tcb, acked);
20477dd7cddfSDavid du Colombier 
20487dd7cddfSDavid du Colombier 	/* Adjust the timers according to the round trip time */
20491ce6c95fSDavid du Colombier 	/* TODO: fix sloppy treatment of overflow cases here. */
20509a747e4fSDavid du Colombier 	if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20517dd7cddfSDavid du Colombier 		tcphalt(tpriv, &tcb->rtt_timer);
20527dd7cddfSDavid du Colombier 		if((tcb->flags&RETRAN) == 0) {
20537dd7cddfSDavid du Colombier 			tcb->backoff = 0;
20547dd7cddfSDavid du Colombier 			tcb->backedoff = 0;
20557dd7cddfSDavid du Colombier 			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20567dd7cddfSDavid du Colombier 			if(rtt == 0)
20571ce6c95fSDavid du Colombier 				rtt = 1; /* else all close sys's will rexmit in 0 time */
20587dd7cddfSDavid du Colombier 			rtt *= MSPTICK;
20597dd7cddfSDavid du Colombier 			if(tcb->srtt == 0) {
20607dd7cddfSDavid du Colombier 				tcb->srtt = rtt << LOGAGAIN;
20617dd7cddfSDavid du Colombier 				tcb->mdev = rtt << LOGDGAIN;
20627dd7cddfSDavid du Colombier 			} else {
20637dd7cddfSDavid du Colombier 				delta = rtt - (tcb->srtt>>LOGAGAIN);
20647dd7cddfSDavid du Colombier 				tcb->srtt += delta;
20657dd7cddfSDavid du Colombier 				if(tcb->srtt <= 0)
20667dd7cddfSDavid du Colombier 					tcb->srtt = 1;
20677dd7cddfSDavid du Colombier 
20687dd7cddfSDavid du Colombier 				delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20697dd7cddfSDavid du Colombier 				tcb->mdev += delta;
20707dd7cddfSDavid du Colombier 				if(tcb->mdev <= 0)
20717dd7cddfSDavid du Colombier 					tcb->mdev = 1;
20727dd7cddfSDavid du Colombier 			}
20739a747e4fSDavid du Colombier 			tcpsettimer(tcb);
20747dd7cddfSDavid du Colombier 		}
20757dd7cddfSDavid du Colombier 	}
20767dd7cddfSDavid du Colombier 
20777dd7cddfSDavid du Colombier done:
207880ee5cbfSDavid du Colombier 	if(qdiscard(s->wq, acked) < acked)
207980ee5cbfSDavid du Colombier 		tcb->flgcnt--;
20807dd7cddfSDavid du Colombier 	tcb->snd.una = seg->ack;
20811ce6c95fSDavid du Colombier 
20821ce6c95fSDavid du Colombier 	/* newreno fast recovery */
20831ce6c95fSDavid du Colombier 	if(tcb->snd.recovery)
20841ce6c95fSDavid du Colombier 		tcprxmit(s);
20851ce6c95fSDavid du Colombier 
20867dd7cddfSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.urg))
20877dd7cddfSDavid du Colombier 		tcb->snd.urg = seg->ack;
20887dd7cddfSDavid du Colombier 
20891ce6c95fSDavid du Colombier 	if(tcb->snd.una != tcb->snd.nxt){
20901ce6c95fSDavid du Colombier 		/* `impatient' variant */
20911ce6c95fSDavid du Colombier 		if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20921ce6c95fSDavid du Colombier 			tcb->time = NOW;
20931ce6c95fSDavid du Colombier 			tcb->timeuna = tcb->snd.una;
20947dd7cddfSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
20951ce6c95fSDavid du Colombier 		}
20961ce6c95fSDavid du Colombier 	} else
20973ff48bf5SDavid du Colombier 		tcphalt(tpriv, &tcb->timer);
20987dd7cddfSDavid du Colombier 
20997dd7cddfSDavid du Colombier 	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
21007dd7cddfSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
21017dd7cddfSDavid du Colombier 
21021ce6c95fSDavid du Colombier 	if(!tcb->snd.recovery)
21037dd7cddfSDavid du Colombier 		tcb->flags &= ~RETRAN;
21047dd7cddfSDavid du Colombier 	tcb->backoff = 0;
21057dd7cddfSDavid du Colombier 	tcb->backedoff = 0;
21067dd7cddfSDavid du Colombier }
21077dd7cddfSDavid du Colombier 
21081ce6c95fSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)21099a747e4fSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
21107dd7cddfSDavid du Colombier {
21117dd7cddfSDavid du Colombier 	Tcp seg;
21123ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
21133ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
21147dd7cddfSDavid du Colombier 	int hdrlen;
21157dd7cddfSDavid du Colombier 	Tcpctl *tcb;
211684363d68SDavid du Colombier 	ushort length, csum;
21177dd7cddfSDavid du Colombier 	uchar source[IPaddrlen], dest[IPaddrlen];
211880ee5cbfSDavid du Colombier 	Conv *s;
21197dd7cddfSDavid du Colombier 	Fs *f;
21207dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
21213ff48bf5SDavid du Colombier 	uchar version;
21227dd7cddfSDavid du Colombier 
21237dd7cddfSDavid du Colombier 	f = tcp->f;
21247dd7cddfSDavid du Colombier 	tpriv = tcp->priv;
21257dd7cddfSDavid du Colombier 
212659cc4ca5SDavid du Colombier 	tpriv->stats[InSegs]++;
21277dd7cddfSDavid du Colombier 
21283ff48bf5SDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
21293ff48bf5SDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
21307dd7cddfSDavid du Colombier 
21313ff48bf5SDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
21323ff48bf5SDavid du Colombier 		version = V4;
21333ff48bf5SDavid du Colombier 		length = nhgets(h4->length);
21343ff48bf5SDavid du Colombier 		v4tov6(dest, h4->tcpdst);
21353ff48bf5SDavid du Colombier 		v4tov6(source, h4->tcpsrc);
21367dd7cddfSDavid du Colombier 
21373ff48bf5SDavid du Colombier 		h4->Unused = 0;
21383ff48bf5SDavid du Colombier 		hnputs(h4->tcplen, length-TCP4_PKT);
21396b6b9ac8SDavid du Colombier 		if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
21403ff48bf5SDavid du Colombier 			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
214159cc4ca5SDavid du Colombier 			tpriv->stats[CsumErrs]++;
214259cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21437dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "bad tcp proto cksum\n");
21447dd7cddfSDavid du Colombier 			freeblist(bp);
21457dd7cddfSDavid du Colombier 			return;
21467dd7cddfSDavid du Colombier 		}
21477dd7cddfSDavid du Colombier 
21483ff48bf5SDavid du Colombier 		hdrlen = ntohtcp4(&seg, &bp);
21497dd7cddfSDavid du Colombier 		if(hdrlen < 0){
215059cc4ca5SDavid du Colombier 			tpriv->stats[HlenErrs]++;
215159cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21527dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "bad tcp hdr len\n");
21537dd7cddfSDavid du Colombier 			return;
21547dd7cddfSDavid du Colombier 		}
21557dd7cddfSDavid du Colombier 
21567dd7cddfSDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
21573ff48bf5SDavid du Colombier 		length -= hdrlen+TCP4_PKT;
21583ff48bf5SDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21597dd7cddfSDavid du Colombier 		if(bp == nil){
216059cc4ca5SDavid du Colombier 			tpriv->stats[LenErrs]++;
216159cc4ca5SDavid du Colombier 			tpriv->stats[InErrs]++;
21627dd7cddfSDavid du Colombier 			netlog(f, Logtcp, "tcp len < 0 after trim\n");
21637dd7cddfSDavid du Colombier 			return;
21647dd7cddfSDavid du Colombier 		}
21653ff48bf5SDavid du Colombier 	}
21663ff48bf5SDavid du Colombier 	else {
21673ff48bf5SDavid du Colombier 		int ttl = h6->ttl;
21683ff48bf5SDavid du Colombier 		int proto = h6->proto;
21693ff48bf5SDavid du Colombier 
21703ff48bf5SDavid du Colombier 		version = V6;
21713ff48bf5SDavid du Colombier 		length = nhgets(h6->ploadlen);
21723ff48bf5SDavid du Colombier 		ipmove(dest, h6->tcpdst);
21733ff48bf5SDavid du Colombier 		ipmove(source, h6->tcpsrc);
21743ff48bf5SDavid du Colombier 
21753ff48bf5SDavid du Colombier 		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21763ff48bf5SDavid du Colombier 		h6->ttl = proto;
21773ff48bf5SDavid du Colombier 		hnputl(h6->vcf, length);
21783ff48bf5SDavid du Colombier 		if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
217984363d68SDavid du Colombier 		    (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21803ff48bf5SDavid du Colombier 			tpriv->stats[CsumErrs]++;
21813ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
218284363d68SDavid du Colombier 			netlog(f, Logtcp,
218384363d68SDavid du Colombier 			    "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
218484363d68SDavid du Colombier 				h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21853ff48bf5SDavid du Colombier 			freeblist(bp);
21863ff48bf5SDavid du Colombier 			return;
21873ff48bf5SDavid du Colombier 		}
21883ff48bf5SDavid du Colombier 		h6->ttl = ttl;
21893ff48bf5SDavid du Colombier 		h6->proto = proto;
21903ff48bf5SDavid du Colombier 		hnputs(h6->ploadlen, length);
21913ff48bf5SDavid du Colombier 
21923ff48bf5SDavid du Colombier 		hdrlen = ntohtcp6(&seg, &bp);
21933ff48bf5SDavid du Colombier 		if(hdrlen < 0){
21943ff48bf5SDavid du Colombier 			tpriv->stats[HlenErrs]++;
21953ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
219684363d68SDavid du Colombier 			netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21973ff48bf5SDavid du Colombier 			return;
21983ff48bf5SDavid du Colombier 		}
21993ff48bf5SDavid du Colombier 
22003ff48bf5SDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
22013ff48bf5SDavid du Colombier 		length -= hdrlen;
22023ff48bf5SDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP6_PKT, length);
22033ff48bf5SDavid du Colombier 		if(bp == nil){
22043ff48bf5SDavid du Colombier 			tpriv->stats[LenErrs]++;
22053ff48bf5SDavid du Colombier 			tpriv->stats[InErrs]++;
220684363d68SDavid du Colombier 			netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
22073ff48bf5SDavid du Colombier 			return;
22083ff48bf5SDavid du Colombier 		}
22093ff48bf5SDavid du Colombier 	}
22107dd7cddfSDavid du Colombier 
22117dd7cddfSDavid du Colombier 	/* lock protocol while searching for a conversation */
22127dd7cddfSDavid du Colombier 	qlock(tcp);
22137dd7cddfSDavid du Colombier 
221480ee5cbfSDavid du Colombier 	/* Look for a matching conversation */
221580ee5cbfSDavid du Colombier 	s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
221680ee5cbfSDavid du Colombier 	if(s == nil){
22177133e0eeSDavid du Colombier 		netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
22187133e0eeSDavid du Colombier 			source, seg.source, dest, seg.dest);
221980ee5cbfSDavid du Colombier reset:
22207dd7cddfSDavid du Colombier 		qunlock(tcp);
22213ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "no conversation");
22227dd7cddfSDavid du Colombier 		freeblist(bp);
22237dd7cddfSDavid du Colombier 		return;
22247dd7cddfSDavid du Colombier 	}
222580ee5cbfSDavid du Colombier 
222680ee5cbfSDavid du Colombier 	/* if it's a listener, look for the right flags and get a new conv */
222780ee5cbfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
222880ee5cbfSDavid du Colombier 	if(tcb->state == Listen){
22297dd7cddfSDavid du Colombier 		if(seg.flags & RST){
22303ff48bf5SDavid du Colombier 			limborst(s, &seg, source, dest, version);
22317dd7cddfSDavid du Colombier 			qunlock(tcp);
22327dd7cddfSDavid du Colombier 			freeblist(bp);
22337dd7cddfSDavid du Colombier 			return;
22347dd7cddfSDavid du Colombier 		}
22357dd7cddfSDavid du Colombier 
22363ff48bf5SDavid du Colombier 		/* if this is a new SYN, put the call into limbo */
22373ff48bf5SDavid du Colombier 		if((seg.flags & SYN) && (seg.flags & ACK) == 0){
22383ff48bf5SDavid du Colombier 			limbo(s, source, dest, &seg, version);
22393ff48bf5SDavid du Colombier 			qunlock(tcp);
22403ff48bf5SDavid du Colombier 			freeblist(bp);
22413ff48bf5SDavid du Colombier 			return;
22423ff48bf5SDavid du Colombier 		}
22433ff48bf5SDavid du Colombier 
22443ff48bf5SDavid du Colombier 		/*
22453ff48bf5SDavid du Colombier 		 *  if there's a matching call in limbo, tcpincoming will
22463ff48bf5SDavid du Colombier 		 *  return it in state Syn_received
22473ff48bf5SDavid du Colombier 		 */
22483ff48bf5SDavid du Colombier 		s = tcpincoming(s, &seg, source, dest, version);
224980ee5cbfSDavid du Colombier 		if(s == nil)
225080ee5cbfSDavid du Colombier 			goto reset;
22517dd7cddfSDavid du Colombier 	}
22527dd7cddfSDavid du Colombier 
22537dd7cddfSDavid du Colombier 	/* The rest of the input state machine is run with the control block
22547dd7cddfSDavid du Colombier 	 * locked and implements the state machine directly out of the RFC.
22557dd7cddfSDavid du Colombier 	 * Out-of-band data is ignored - it was always a bad idea.
22567dd7cddfSDavid du Colombier 	 */
22577dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
22587dd7cddfSDavid du Colombier 	if(waserror()){
22597dd7cddfSDavid du Colombier 		qunlock(s);
22607dd7cddfSDavid du Colombier 		nexterror();
22617dd7cddfSDavid du Colombier 	}
22627dd7cddfSDavid du Colombier 	qlock(s);
22637dd7cddfSDavid du Colombier 	qunlock(tcp);
22647dd7cddfSDavid du Colombier 
22653f695129SDavid du Colombier 	/* fix up window */
22663f695129SDavid du Colombier 	seg.wnd <<= tcb->rcv.scale;
22673f695129SDavid du Colombier 
226840ef9009SDavid du Colombier 	/* every input packet in puts off the keep alive time out */
2269e288d156SDavid du Colombier 	tcpsetkacounter(tcb);
22707dd7cddfSDavid du Colombier 
22717dd7cddfSDavid du Colombier 	switch(tcb->state) {
22727dd7cddfSDavid du Colombier 	case Closed:
22733ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22747dd7cddfSDavid du Colombier 		goto raise;
22757dd7cddfSDavid du Colombier 	case Syn_sent:
22767dd7cddfSDavid du Colombier 		if(seg.flags & ACK) {
22777dd7cddfSDavid du Colombier 			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22783ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
22793ff48bf5SDavid du Colombier 					 "bad seq in Syn_sent");
22807dd7cddfSDavid du Colombier 				goto raise;
22817dd7cddfSDavid du Colombier 			}
22827dd7cddfSDavid du Colombier 		}
22837dd7cddfSDavid du Colombier 		if(seg.flags & RST) {
22847dd7cddfSDavid du Colombier 			if(seg.flags & ACK)
22857dd7cddfSDavid du Colombier 				localclose(s, Econrefused);
22867dd7cddfSDavid du Colombier 			goto raise;
22877dd7cddfSDavid du Colombier 		}
22887dd7cddfSDavid du Colombier 
22897dd7cddfSDavid du Colombier 		if(seg.flags & SYN) {
22907dd7cddfSDavid du Colombier 			procsyn(s, &seg);
22917dd7cddfSDavid du Colombier 			if(seg.flags & ACK){
22927dd7cddfSDavid du Colombier 				update(s, &seg);
22937dd7cddfSDavid du Colombier 				tcpsynackrtt(s);
22947dd7cddfSDavid du Colombier 				tcpsetstate(s, Established);
22953f695129SDavid du Colombier 				tcpsetscale(s, tcb, seg.ws, tcb->scale);
22967dd7cddfSDavid du Colombier 			}
22977dd7cddfSDavid du Colombier 			else {
22983ff48bf5SDavid du Colombier 				tcb->time = NOW;
22993f695129SDavid du Colombier 				tcpsetstate(s, Syn_received);	/* DLP - shouldn't this be a reset? */
23007dd7cddfSDavid du Colombier 			}
23017dd7cddfSDavid du Colombier 
23027dd7cddfSDavid du Colombier 			if(length != 0 || (seg.flags & FIN))
23037dd7cddfSDavid du Colombier 				break;
23047dd7cddfSDavid du Colombier 
23057dd7cddfSDavid du Colombier 			freeblist(bp);
23067dd7cddfSDavid du Colombier 			goto output;
23077dd7cddfSDavid du Colombier 		}
23087dd7cddfSDavid du Colombier 		else
23097dd7cddfSDavid du Colombier 			freeblist(bp);
23107dd7cddfSDavid du Colombier 
23117dd7cddfSDavid du Colombier 		qunlock(s);
23127dd7cddfSDavid du Colombier 		poperror();
23137dd7cddfSDavid du Colombier 		return;
23147dd7cddfSDavid du Colombier 	case Syn_received:
23157dd7cddfSDavid du Colombier 		/* doesn't matter if it's the correct ack, we're just trying to set timing */
23167dd7cddfSDavid du Colombier 		if(seg.flags & ACK)
23177dd7cddfSDavid du Colombier 			tcpsynackrtt(s);
23187dd7cddfSDavid du Colombier 		break;
23197dd7cddfSDavid du Colombier 	}
23207dd7cddfSDavid du Colombier 
23213ff48bf5SDavid du Colombier 	/*
23223ff48bf5SDavid du Colombier 	 *  One DOS attack is to open connections to us and then forget about them,
23233ff48bf5SDavid du Colombier 	 *  thereby tying up a conv at no long term cost to the attacker.
23243ff48bf5SDavid du Colombier 	 *  This is an attempt to defeat these stateless DOS attacks.  See
23253ff48bf5SDavid du Colombier 	 *  corresponding code in tcpsendka().
23263ff48bf5SDavid du Colombier 	 */
23273ff48bf5SDavid du Colombier 	if(tcb->state != Syn_received && (seg.flags & RST) == 0){
23283ff48bf5SDavid du Colombier 		if(tcpporthogdefense
23293ff48bf5SDavid du Colombier 		&& seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
23303ff48bf5SDavid du Colombier 			print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
23313ff48bf5SDavid du Colombier 				source, seg.source, dest, seg.dest, seg.flags,
23323ff48bf5SDavid du Colombier 				tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
23333ff48bf5SDavid du Colombier 			localclose(s, "stateless hog");
23343ff48bf5SDavid du Colombier 		}
23353ff48bf5SDavid du Colombier 	}
23363ff48bf5SDavid du Colombier 
23377dd7cddfSDavid du Colombier 	/* Cut the data to fit the receive window */
23381ce6c95fSDavid du Colombier 	tcprcvwin(s);
23397dd7cddfSDavid du Colombier 	if(tcptrim(tcb, &seg, &bp, &length) == -1) {
23401ce6c95fSDavid du Colombier 		if(seg.seq+1 != tcb->rcv.nxt || length != 1)
23411ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23421ce6c95fSDavid du Colombier 			"%lud-%lud l %d from %I\n", seg.seq,
23431ce6c95fSDavid du Colombier 			seg.seq + length - 1, tcb->rcv.nxt,
23441ce6c95fSDavid du Colombier 			tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23457dd7cddfSDavid du Colombier 		update(s, &seg);
234680ee5cbfSDavid du Colombier 		if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23477dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->rtt_timer);
23487dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->acktimer);
23497dd7cddfSDavid du Colombier 			tcphalt(tpriv, &tcb->katimer);
23507dd7cddfSDavid du Colombier 			tcpsetstate(s, Time_wait);
23517dd7cddfSDavid du Colombier 			tcb->timer.start = MSL2*(1000 / MSPTICK);
23527dd7cddfSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
23537dd7cddfSDavid du Colombier 		}
23547dd7cddfSDavid du Colombier 		if(!(seg.flags & RST)) {
23557dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
23567dd7cddfSDavid du Colombier 			goto output;
23577dd7cddfSDavid du Colombier 		}
23587dd7cddfSDavid du Colombier 		qunlock(s);
23597dd7cddfSDavid du Colombier 		poperror();
23607dd7cddfSDavid du Colombier 		return;
23617dd7cddfSDavid du Colombier 	}
23627dd7cddfSDavid du Colombier 
23637dd7cddfSDavid du Colombier 	/* Cannot accept so answer with a rst */
23647dd7cddfSDavid du Colombier 	if(length && tcb->state == Closed) {
23653ff48bf5SDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23667dd7cddfSDavid du Colombier 		goto raise;
23677dd7cddfSDavid du Colombier 	}
23687dd7cddfSDavid du Colombier 
23697dd7cddfSDavid du Colombier 	/* The segment is beyond the current receive pointer so
23707dd7cddfSDavid du Colombier 	 * queue the data in the resequence queue
23717dd7cddfSDavid du Colombier 	 */
23727dd7cddfSDavid du Colombier 	if(seg.seq != tcb->rcv.nxt)
23737dd7cddfSDavid du Colombier 	if(length != 0 || (seg.flags & (SYN|FIN))) {
23747dd7cddfSDavid du Colombier 		update(s, &seg);
23751ce6c95fSDavid du Colombier 		if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23761ce6c95fSDavid du Colombier 			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23771ce6c95fSDavid du Colombier 				s->laddr, s->lport);
23781ce6c95fSDavid du Colombier 		tcb->flags |= FORCE;	/* force duplicate ack; RFC 5681 §3.2 */
23797dd7cddfSDavid du Colombier 		goto output;
23807dd7cddfSDavid du Colombier 	}
23817dd7cddfSDavid du Colombier 
23821ce6c95fSDavid du Colombier 	if(tcb->nreseq > 0)
23831ce6c95fSDavid du Colombier 		tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23841ce6c95fSDavid du Colombier 
23857dd7cddfSDavid du Colombier 	/*
23867dd7cddfSDavid du Colombier 	 *  keep looping till we've processed this packet plus any
23877dd7cddfSDavid du Colombier 	 *  adjacent packets in the resequence queue
23887dd7cddfSDavid du Colombier 	 */
23897dd7cddfSDavid du Colombier 	for(;;) {
23907dd7cddfSDavid du Colombier 		if(seg.flags & RST) {
239151711cb6SDavid du Colombier 			if(tcb->state == Established) {
239259cc4ca5SDavid du Colombier 				tpriv->stats[EstabResets]++;
239351711cb6SDavid du Colombier 				if(tcb->rcv.nxt != seg.seq)
23946b7c5dceSDavid du Colombier 					netlog(f, Logtcp, "out of order RST "
23956b7c5dceSDavid du Colombier 						"rcvd: %I.%d -> %I.%d, rcv.nxt "
23966b7c5dceSDavid du Colombier 						"%lux seq %lux\n",
23971ce6c95fSDavid du Colombier 						s->raddr, s->rport, s->laddr,
23981ce6c95fSDavid du Colombier 						s->lport, tcb->rcv.nxt, seg.seq);
239951711cb6SDavid du Colombier 			}
24007dd7cddfSDavid du Colombier 			localclose(s, Econrefused);
24017dd7cddfSDavid du Colombier 			goto raise;
24027dd7cddfSDavid du Colombier 		}
24037dd7cddfSDavid du Colombier 
24047dd7cddfSDavid du Colombier 		if((seg.flags&ACK) == 0)
24057dd7cddfSDavid du Colombier 			goto raise;
24067dd7cddfSDavid du Colombier 
24077dd7cddfSDavid du Colombier 		switch(tcb->state) {
24087dd7cddfSDavid du Colombier 		case Syn_received:
24097dd7cddfSDavid du Colombier 			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
24103ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
24113ff48bf5SDavid du Colombier 					"bad seq in Syn_received");
24127dd7cddfSDavid du Colombier 				goto raise;
24137dd7cddfSDavid du Colombier 			}
24147dd7cddfSDavid du Colombier 			update(s, &seg);
24157dd7cddfSDavid du Colombier 			tcpsetstate(s, Established);
24167dd7cddfSDavid du Colombier 		case Established:
24177dd7cddfSDavid du Colombier 		case Close_wait:
24187dd7cddfSDavid du Colombier 			update(s, &seg);
24197dd7cddfSDavid du Colombier 			break;
24207dd7cddfSDavid du Colombier 		case Finwait1:
24217dd7cddfSDavid du Colombier 			update(s, &seg);
242280ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0){
24237dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
24247dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
24257dd7cddfSDavid du Colombier 				tcpsetkacounter(tcb);
24263ff48bf5SDavid du Colombier 				tcb->time = NOW;
24277dd7cddfSDavid du Colombier 				tcpsetstate(s, Finwait2);
24287dd7cddfSDavid du Colombier 				tcb->katimer.start = MSL2 * (1000 / MSPTICK);
24297dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->katimer);
24307dd7cddfSDavid du Colombier 			}
24317dd7cddfSDavid du Colombier 			break;
24327dd7cddfSDavid du Colombier 		case Finwait2:
24337dd7cddfSDavid du Colombier 			update(s, &seg);
24347dd7cddfSDavid du Colombier 			break;
24357dd7cddfSDavid du Colombier 		case Closing:
24367dd7cddfSDavid du Colombier 			update(s, &seg);
243780ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
24387dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
24397dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
24407dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
24417dd7cddfSDavid du Colombier 				tcpsetstate(s, Time_wait);
24427dd7cddfSDavid du Colombier 				tcb->timer.start = MSL2*(1000 / MSPTICK);
24437dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24447dd7cddfSDavid du Colombier 			}
24457dd7cddfSDavid du Colombier 			break;
24467dd7cddfSDavid du Colombier 		case Last_ack:
24477dd7cddfSDavid du Colombier 			update(s, &seg);
244880ee5cbfSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
24497dd7cddfSDavid du Colombier 				localclose(s, nil);
24507dd7cddfSDavid du Colombier 				goto raise;
24517dd7cddfSDavid du Colombier 			}
24527dd7cddfSDavid du Colombier 		case Time_wait:
24537dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
24549a747e4fSDavid du Colombier 			if(tcb->timer.state != TcptimerON)
24557dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24567dd7cddfSDavid du Colombier 		}
24577dd7cddfSDavid du Colombier 
24587dd7cddfSDavid du Colombier 		if((seg.flags&URG) && seg.urg) {
24597dd7cddfSDavid du Colombier 			if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24607dd7cddfSDavid du Colombier 				tcb->rcv.urg = seg.urg + seg.seq;
24617dd7cddfSDavid du Colombier 				pullblock(&bp, seg.urg);
24627dd7cddfSDavid du Colombier 			}
24637dd7cddfSDavid du Colombier 		}
24647dd7cddfSDavid du Colombier 		else
24657dd7cddfSDavid du Colombier 		if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24667dd7cddfSDavid du Colombier 			tcb->rcv.urg = tcb->rcv.nxt;
24677dd7cddfSDavid du Colombier 
24687dd7cddfSDavid du Colombier 		if(length == 0) {
24697dd7cddfSDavid du Colombier 			if(bp != nil)
24707dd7cddfSDavid du Colombier 				freeblist(bp);
24717dd7cddfSDavid du Colombier 		}
24727dd7cddfSDavid du Colombier 		else {
24737dd7cddfSDavid du Colombier 			switch(tcb->state){
24747dd7cddfSDavid du Colombier 			default:
24757dd7cddfSDavid du Colombier 				/* Ignore segment text */
24767dd7cddfSDavid du Colombier 				if(bp != nil)
24777dd7cddfSDavid du Colombier 					freeblist(bp);
24787dd7cddfSDavid du Colombier 				break;
24797dd7cddfSDavid du Colombier 
24807dd7cddfSDavid du Colombier 			case Syn_received:
24817dd7cddfSDavid du Colombier 			case Established:
24827dd7cddfSDavid du Colombier 			case Finwait1:
24837dd7cddfSDavid du Colombier 				/* If we still have some data place on
24847dd7cddfSDavid du Colombier 				 * receive queue
24857dd7cddfSDavid du Colombier 				 */
24867dd7cddfSDavid du Colombier 				if(bp) {
24877dd7cddfSDavid du Colombier 					bp = packblock(bp);
24887dd7cddfSDavid du Colombier 					if(bp == nil)
24897dd7cddfSDavid du Colombier 						panic("tcp packblock");
24907dd7cddfSDavid du Colombier 					qpassnolim(s->rq, bp);
24917dd7cddfSDavid du Colombier 					bp = nil;
24927dd7cddfSDavid du Colombier 				}
24937dd7cddfSDavid du Colombier 				tcb->rcv.nxt += length;
249480ee5cbfSDavid du Colombier 
249580ee5cbfSDavid du Colombier 				/*
249680ee5cbfSDavid du Colombier 				 *  turn on the acktimer if there's something
249780ee5cbfSDavid du Colombier 				 *  to ack
249880ee5cbfSDavid du Colombier 				 */
24999a747e4fSDavid du Colombier 				if(tcb->acktimer.state != TcptimerON)
25007dd7cddfSDavid du Colombier 					tcpgo(tpriv, &tcb->acktimer);
25017dd7cddfSDavid du Colombier 
25027dd7cddfSDavid du Colombier 				break;
25037dd7cddfSDavid du Colombier 			case Finwait2:
25047dd7cddfSDavid du Colombier 				/* no process to read the data, send a reset */
25057dd7cddfSDavid du Colombier 				if(bp != nil)
25067dd7cddfSDavid du Colombier 					freeblist(bp);
25073ff48bf5SDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
25083ff48bf5SDavid du Colombier 					"send to Finwait2");
25097dd7cddfSDavid du Colombier 				qunlock(s);
25107dd7cddfSDavid du Colombier 				poperror();
25117dd7cddfSDavid du Colombier 				return;
25127dd7cddfSDavid du Colombier 			}
25137dd7cddfSDavid du Colombier 		}
25147dd7cddfSDavid du Colombier 
25157dd7cddfSDavid du Colombier 		if(seg.flags & FIN) {
25167dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
25177dd7cddfSDavid du Colombier 
25187dd7cddfSDavid du Colombier 			switch(tcb->state) {
25197dd7cddfSDavid du Colombier 			case Syn_received:
25207dd7cddfSDavid du Colombier 			case Established:
25217dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
25227dd7cddfSDavid du Colombier 				tcpsetstate(s, Close_wait);
25237dd7cddfSDavid du Colombier 				break;
25247dd7cddfSDavid du Colombier 			case Finwait1:
25257dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
252680ee5cbfSDavid du Colombier 				if(qlen(s->wq)+tcb->flgcnt == 0) {
25277dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->rtt_timer);
25287dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->acktimer);
25297dd7cddfSDavid du Colombier 					tcphalt(tpriv, &tcb->katimer);
25307dd7cddfSDavid du Colombier 					tcpsetstate(s, Time_wait);
25317dd7cddfSDavid du Colombier 					tcb->timer.start = MSL2*(1000/MSPTICK);
25327dd7cddfSDavid du Colombier 					tcpgo(tpriv, &tcb->timer);
25337dd7cddfSDavid du Colombier 				}
25347dd7cddfSDavid du Colombier 				else
25357dd7cddfSDavid du Colombier 					tcpsetstate(s, Closing);
25367dd7cddfSDavid du Colombier 				break;
25377dd7cddfSDavid du Colombier 			case Finwait2:
25387dd7cddfSDavid du Colombier 				tcb->rcv.nxt++;
25397dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
25407dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
25417dd7cddfSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
25427dd7cddfSDavid du Colombier 				tcpsetstate(s, Time_wait);
25437dd7cddfSDavid du Colombier 				tcb->timer.start = MSL2 * (1000/MSPTICK);
25447dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25457dd7cddfSDavid du Colombier 				break;
25467dd7cddfSDavid du Colombier 			case Close_wait:
25477dd7cddfSDavid du Colombier 			case Closing:
25487dd7cddfSDavid du Colombier 			case Last_ack:
25497dd7cddfSDavid du Colombier 				break;
25507dd7cddfSDavid du Colombier 			case Time_wait:
25517dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25527dd7cddfSDavid du Colombier 				break;
25537dd7cddfSDavid du Colombier 			}
25547dd7cddfSDavid du Colombier 		}
25557dd7cddfSDavid du Colombier 
25567dd7cddfSDavid du Colombier 		/*
25577dd7cddfSDavid du Colombier 		 *  get next adjacent segment from the resequence queue.
25587dd7cddfSDavid du Colombier 		 *  dump/trim any overlapping segments
25597dd7cddfSDavid du Colombier 		 */
25607dd7cddfSDavid du Colombier 		for(;;) {
25617dd7cddfSDavid du Colombier 			if(tcb->reseq == nil)
25627dd7cddfSDavid du Colombier 				goto output;
25637dd7cddfSDavid du Colombier 
25647dd7cddfSDavid du Colombier 			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25657dd7cddfSDavid du Colombier 				goto output;
25667dd7cddfSDavid du Colombier 
25677dd7cddfSDavid du Colombier 			getreseq(tcb, &seg, &bp, &length);
25687dd7cddfSDavid du Colombier 
25691ce6c95fSDavid du Colombier 			tcprcvwin(s);
25701ce6c95fSDavid du Colombier 			if(tcptrim(tcb, &seg, &bp, &length) == 0){
25711ce6c95fSDavid du Colombier 				tcb->flags |= FORCE;
25727dd7cddfSDavid du Colombier 				break;
25737dd7cddfSDavid du Colombier 			}
25747dd7cddfSDavid du Colombier 		}
25751ce6c95fSDavid du Colombier 	}
25767dd7cddfSDavid du Colombier output:
25777dd7cddfSDavid du Colombier 	tcpoutput(s);
25787dd7cddfSDavid du Colombier 	qunlock(s);
25797dd7cddfSDavid du Colombier 	poperror();
25807dd7cddfSDavid du Colombier 	return;
25817dd7cddfSDavid du Colombier raise:
25827dd7cddfSDavid du Colombier 	qunlock(s);
25837dd7cddfSDavid du Colombier 	poperror();
25847dd7cddfSDavid du Colombier 	freeblist(bp);
258580ee5cbfSDavid du Colombier 	tcpkick(s);
25867dd7cddfSDavid du Colombier }
25877dd7cddfSDavid du Colombier 
25887dd7cddfSDavid du Colombier /*
258980ee5cbfSDavid du Colombier  *  always enters and exits with the s locked.  We drop
259080ee5cbfSDavid du Colombier  *  the lock to ipoput the packet so some care has to be
259180ee5cbfSDavid du Colombier  *  taken by callers.
25927dd7cddfSDavid du Colombier  */
25931ce6c95fSDavid du Colombier static void
tcpoutput(Conv * s)25947dd7cddfSDavid du Colombier tcpoutput(Conv *s)
25957dd7cddfSDavid du Colombier {
25967dd7cddfSDavid du Colombier 	Tcp seg;
25971ce6c95fSDavid du Colombier 	uint msgs;
25987dd7cddfSDavid du Colombier 	Tcpctl *tcb;
25997dd7cddfSDavid du Colombier 	Block *hbp, *bp;
26001ce6c95fSDavid du Colombier 	int sndcnt;
26011ce6c95fSDavid du Colombier 	ulong ssize, dsize, sent;
26027dd7cddfSDavid du Colombier 	Fs *f;
26037dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
26043ff48bf5SDavid du Colombier 	uchar version;
26057dd7cddfSDavid du Colombier 
26067dd7cddfSDavid du Colombier 	f = s->p->f;
26077dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
26083ff48bf5SDavid du Colombier 	version = s->ipversion;
26097dd7cddfSDavid du Colombier 
26107dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
26117dd7cddfSDavid du Colombier 
26121ce6c95fSDavid du Colombier 	/* force ack every 2*mss */
26131ce6c95fSDavid du Colombier 	if((tcb->flags & FORCE) == 0 &&
26141ce6c95fSDavid du Colombier 	    tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
26151ce6c95fSDavid du Colombier 		tpriv->stats[Delayack]++;
26161ce6c95fSDavid du Colombier 		tcb->flags |= FORCE;
26171ce6c95fSDavid du Colombier 	}
26181ce6c95fSDavid du Colombier 
26191ce6c95fSDavid du Colombier 	/* force ack if window opening */
26201ce6c95fSDavid du Colombier 	if((tcb->flags & FORCE) == 0){
26211ce6c95fSDavid du Colombier 		tcprcvwin(s);
26221ce6c95fSDavid du Colombier 		if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
26231ce6c95fSDavid du Colombier 			tpriv->stats[Wopenack]++;
26241ce6c95fSDavid du Colombier 			tcb->flags |= FORCE;
26251ce6c95fSDavid du Colombier 		}
26261ce6c95fSDavid du Colombier 	}
26271ce6c95fSDavid du Colombier 
26281ce6c95fSDavid du Colombier 	for(msgs = 0; msgs < 100; msgs++) {
26297dd7cddfSDavid du Colombier 		switch(tcb->state) {
26307dd7cddfSDavid du Colombier 		case Listen:
26317dd7cddfSDavid du Colombier 		case Closed:
26327dd7cddfSDavid du Colombier 		case Finwait2:
2633e6c6b7f8SDavid du Colombier 			return;
26347dd7cddfSDavid du Colombier 		}
26357dd7cddfSDavid du Colombier 
26361ce6c95fSDavid du Colombier 		/* Don't send anything else until our SYN has been acked */
26371ce6c95fSDavid du Colombier 		if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
26381ce6c95fSDavid du Colombier 			break;
26391ce6c95fSDavid du Colombier 
26407dd7cddfSDavid du Colombier 		/* force an ack when a window has opened up */
26411ce6c95fSDavid du Colombier 		tcprcvwin(s);
26427dd7cddfSDavid du Colombier 		if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26437dd7cddfSDavid du Colombier 			tcb->rcv.blocked = 0;
26447dd7cddfSDavid du Colombier 			tcb->flags |= FORCE;
26457dd7cddfSDavid du Colombier 		}
26467dd7cddfSDavid du Colombier 
264780ee5cbfSDavid du Colombier 		sndcnt = qlen(s->wq)+tcb->flgcnt;
26487dd7cddfSDavid du Colombier 		sent = tcb->snd.ptr - tcb->snd.una;
26491ce6c95fSDavid du Colombier 		ssize = sndcnt;
26507dd7cddfSDavid du Colombier 		if(tcb->snd.wnd == 0){
26511ce6c95fSDavid du Colombier 			/* zero window probe */
26521ce6c95fSDavid du Colombier 			if(sent > 0 && !(tcb->flags & FORCE))
26531ce6c95fSDavid du Colombier 				break;	/* already probing, rto re-probes */
26541ce6c95fSDavid du Colombier 			if(ssize < sent)
26551ce6c95fSDavid du Colombier 				ssize = 0;
26567dd7cddfSDavid du Colombier 			else{
26571ce6c95fSDavid du Colombier 				ssize -= sent;
26581ce6c95fSDavid du Colombier 				if(ssize > 0)
26591ce6c95fSDavid du Colombier 					ssize = 1;
26607dd7cddfSDavid du Colombier 			}
26611ce6c95fSDavid du Colombier 		} else {
26621ce6c95fSDavid du Colombier 			/* calculate usable segment size */
26631ce6c95fSDavid du Colombier 			if(ssize > tcb->cwind)
26641ce6c95fSDavid du Colombier 				ssize = tcb->cwind;
26651ce6c95fSDavid du Colombier 			if(ssize > tcb->snd.wnd)
26661ce6c95fSDavid du Colombier 				ssize = tcb->snd.wnd;
26671ce6c95fSDavid du Colombier 
26681ce6c95fSDavid du Colombier 			if(ssize < sent)
26691ce6c95fSDavid du Colombier 				ssize = 0;
26701ce6c95fSDavid du Colombier 			else {
26711ce6c95fSDavid du Colombier 				ssize -= sent;
26721ce6c95fSDavid du Colombier 				if(ssize > tcb->mss)
26737dd7cddfSDavid du Colombier 					ssize = tcb->mss;
26741ce6c95fSDavid du Colombier 			}
26751ce6c95fSDavid du Colombier 		}
26761ce6c95fSDavid du Colombier 
26777dd7cddfSDavid du Colombier 		dsize = ssize;
26787dd7cddfSDavid du Colombier 		seg.urg = 0;
26797dd7cddfSDavid du Colombier 
26801ce6c95fSDavid du Colombier 		if(!(tcb->flags & FORCE))
26811ce6c95fSDavid du Colombier 			if(ssize == 0 ||
26821ce6c95fSDavid du Colombier 			    ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26831ce6c95fSDavid du Colombier 			    sent > TCPREXMTTHRESH * tcb->mss)
26847dd7cddfSDavid du Colombier 				break;
26857dd7cddfSDavid du Colombier 
26867dd7cddfSDavid du Colombier 		tcb->flags &= ~FORCE;
26877dd7cddfSDavid du Colombier 
26887dd7cddfSDavid du Colombier 		/* By default we will generate an ack */
26893f695129SDavid du Colombier 		tcphalt(tpriv, &tcb->acktimer);
26907dd7cddfSDavid du Colombier 		seg.source = s->lport;
26917dd7cddfSDavid du Colombier 		seg.dest = s->rport;
26927dd7cddfSDavid du Colombier 		seg.flags = ACK;
26937dd7cddfSDavid du Colombier 		seg.mss = 0;
26943f695129SDavid du Colombier 		seg.ws = 0;
26951ce6c95fSDavid du Colombier 		seg.update = 0;
26967dd7cddfSDavid du Colombier 		switch(tcb->state){
26977dd7cddfSDavid du Colombier 		case Syn_sent:
26987dd7cddfSDavid du Colombier 			seg.flags = 0;
26997dd7cddfSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
27007dd7cddfSDavid du Colombier 				seg.flags |= SYN;
27017dd7cddfSDavid du Colombier 				dsize--;
27023f695129SDavid du Colombier 				seg.mss = tcb->mss;
27033f695129SDavid du Colombier 				seg.ws = tcb->scale;
27047dd7cddfSDavid du Colombier 			}
27057dd7cddfSDavid du Colombier 			break;
27067dd7cddfSDavid du Colombier 		case Syn_received:
27077dd7cddfSDavid du Colombier 			/*
27087dd7cddfSDavid du Colombier 			 *  don't send any data with a SYN/ACK packet
27097dd7cddfSDavid du Colombier 			 *  because Linux rejects the packet in its
27107dd7cddfSDavid du Colombier 			 *  attempt to solve the SYN attack problem
27117dd7cddfSDavid du Colombier 			 */
27127dd7cddfSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
27137dd7cddfSDavid du Colombier 				seg.flags |= SYN;
27147dd7cddfSDavid du Colombier 				dsize = 0;
27157dd7cddfSDavid du Colombier 				ssize = 1;
27163f695129SDavid du Colombier 				seg.mss = tcb->mss;
27173f695129SDavid du Colombier 				seg.ws = tcb->scale;
27187dd7cddfSDavid du Colombier 			}
27197dd7cddfSDavid du Colombier 			break;
27207dd7cddfSDavid du Colombier 		}
27217dd7cddfSDavid du Colombier 		seg.seq = tcb->snd.ptr;
27227dd7cddfSDavid du Colombier 		seg.ack = tcb->rcv.nxt;
27237dd7cddfSDavid du Colombier 		seg.wnd = tcb->rcv.wnd;
27247dd7cddfSDavid du Colombier 
27257dd7cddfSDavid du Colombier 		/* Pull out data to send */
27267dd7cddfSDavid du Colombier 		bp = nil;
27277dd7cddfSDavid du Colombier 		if(dsize != 0) {
27287dd7cddfSDavid du Colombier 			bp = qcopy(s->wq, dsize, sent);
27297dd7cddfSDavid du Colombier 			if(BLEN(bp) != dsize) {
27307dd7cddfSDavid du Colombier 				seg.flags |= FIN;
27317dd7cddfSDavid du Colombier 				dsize--;
27327dd7cddfSDavid du Colombier 			}
27337dd7cddfSDavid du Colombier 		}
27347dd7cddfSDavid du Colombier 
27351ce6c95fSDavid du Colombier 		if(sent+dsize == sndcnt && dsize)
27367dd7cddfSDavid du Colombier 			seg.flags |= PSH;
27377dd7cddfSDavid du Colombier 
27387dd7cddfSDavid du Colombier 		tcb->snd.ptr += ssize;
27397dd7cddfSDavid du Colombier 
27407dd7cddfSDavid du Colombier 		/* Pull up the send pointer so we can accept acks
27417dd7cddfSDavid du Colombier 		 * for this window
27427dd7cddfSDavid du Colombier 		 */
27437dd7cddfSDavid du Colombier 		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27447dd7cddfSDavid du Colombier 			tcb->snd.nxt = tcb->snd.ptr;
27457dd7cddfSDavid du Colombier 
27467dd7cddfSDavid du Colombier 		/* Build header, link data and compute cksum */
27473ff48bf5SDavid du Colombier 		switch(version){
27483ff48bf5SDavid du Colombier 		case V4:
27493ff48bf5SDavid du Colombier 			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27503ff48bf5SDavid du Colombier 			hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27517dd7cddfSDavid du Colombier 			if(hbp == nil) {
27527dd7cddfSDavid du Colombier 				freeblist(bp);
2753e6c6b7f8SDavid du Colombier 				return;
27547dd7cddfSDavid du Colombier 			}
27553ff48bf5SDavid du Colombier 			break;
27563ff48bf5SDavid du Colombier 		case V6:
27573ff48bf5SDavid du Colombier 			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27583ff48bf5SDavid du Colombier 			hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27593ff48bf5SDavid du Colombier 			if(hbp == nil) {
27603ff48bf5SDavid du Colombier 				freeblist(bp);
2761e6c6b7f8SDavid du Colombier 				return;
27623ff48bf5SDavid du Colombier 			}
27633ff48bf5SDavid du Colombier 			break;
27643ff48bf5SDavid du Colombier 		default:
27653ff48bf5SDavid du Colombier 			hbp = nil;	/* to suppress a warning */
27663ff48bf5SDavid du Colombier 			panic("tcpoutput: version %d", version);
27673ff48bf5SDavid du Colombier 		}
27687dd7cddfSDavid du Colombier 
27697dd7cddfSDavid du Colombier 		/* Start the transmission timers if there is new data and we
27707dd7cddfSDavid du Colombier 		 * expect acknowledges
27717dd7cddfSDavid du Colombier 		 */
27727dd7cddfSDavid du Colombier 		if(ssize != 0){
27731ce6c95fSDavid du Colombier 			if(tcb->timer.state != TcptimerON){
27741ce6c95fSDavid du Colombier 				tcb->time = NOW;
27751ce6c95fSDavid du Colombier 				tcb->timeuna = tcb->snd.una;
27767dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
27771ce6c95fSDavid du Colombier 			}
27787dd7cddfSDavid du Colombier 
27797dd7cddfSDavid du Colombier 			/*  If round trip timer isn't running, start it.
27807dd7cddfSDavid du Colombier 			 *  measure the longest packet only in case the
27817dd7cddfSDavid du Colombier 			 *  transmission time dominates RTT
27827dd7cddfSDavid du Colombier 			 */
27831ce6c95fSDavid du Colombier 			if(tcb->snd.retransmit == 0)
27849a747e4fSDavid du Colombier 			if(tcb->rtt_timer.state != TcptimerON)
27857dd7cddfSDavid du Colombier 			if(ssize == tcb->mss) {
27867dd7cddfSDavid du Colombier 				tcpgo(tpriv, &tcb->rtt_timer);
27877dd7cddfSDavid du Colombier 				tcb->rttseq = tcb->snd.ptr;
27887dd7cddfSDavid du Colombier 			}
27897dd7cddfSDavid du Colombier 		}
27907dd7cddfSDavid du Colombier 
279159cc4ca5SDavid du Colombier 		tpriv->stats[OutSegs]++;
27921ce6c95fSDavid du Colombier 		if(tcb->snd.retransmit)
27931ce6c95fSDavid du Colombier 			tpriv->stats[RetransSegsSent]++;
27941ce6c95fSDavid du Colombier 		tcb->rcv.ackptr = seg.ack;
27951ce6c95fSDavid du Colombier 		tcb->rcv.wsnt = tcb->rcv.wptr;
279640ef9009SDavid du Colombier 
279740ef9009SDavid du Colombier 		/* put off the next keep alive */
27987dd7cddfSDavid du Colombier 		tcpgo(tpriv, &tcb->katimer);
27993ff48bf5SDavid du Colombier 
28003ff48bf5SDavid du Colombier 		switch(version){
28013ff48bf5SDavid du Colombier 		case V4:
2802a6a9e072SDavid du Colombier 			if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
2803e6c6b7f8SDavid du Colombier 				/* a negative return means no route */
2804e6c6b7f8SDavid du Colombier 				localclose(s, "no route");
2805e6c6b7f8SDavid du Colombier 			}
28063ff48bf5SDavid du Colombier 			break;
28073ff48bf5SDavid du Colombier 		case V6:
2808a6a9e072SDavid du Colombier 			if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
2809e6c6b7f8SDavid du Colombier 				/* a negative return means no route */
2810e6c6b7f8SDavid du Colombier 				localclose(s, "no route");
2811e6c6b7f8SDavid du Colombier 			}
28123ff48bf5SDavid du Colombier 			break;
28133ff48bf5SDavid du Colombier 		default:
28143ff48bf5SDavid du Colombier 			panic("tcpoutput2: version %d", version);
281580ee5cbfSDavid du Colombier 		}
28161ce6c95fSDavid du Colombier 		if((msgs%4) == 3){
28173ff48bf5SDavid du Colombier 			qunlock(s);
281880ee5cbfSDavid du Colombier 			qlock(s);
28193ff48bf5SDavid du Colombier 		}
28207dd7cddfSDavid du Colombier 	}
28217dd7cddfSDavid du Colombier }
28227dd7cddfSDavid du Colombier 
28237dd7cddfSDavid du Colombier /*
28247dd7cddfSDavid du Colombier  *  the BSD convention (hack?) for keep alives.  resend last uchar acked.
28257dd7cddfSDavid du Colombier  */
28261ce6c95fSDavid du Colombier static void
tcpsendka(Conv * s)28277dd7cddfSDavid du Colombier tcpsendka(Conv *s)
28287dd7cddfSDavid du Colombier {
28297dd7cddfSDavid du Colombier 	Tcp seg;
28307dd7cddfSDavid du Colombier 	Tcpctl *tcb;
28317dd7cddfSDavid du Colombier 	Block *hbp,*dbp;
28327dd7cddfSDavid du Colombier 
28337dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
28347dd7cddfSDavid du Colombier 
28357dd7cddfSDavid du Colombier 	dbp = nil;
283610ae047aSDavid du Colombier 	memset(&seg, 0, sizeof seg);
28377dd7cddfSDavid du Colombier 	seg.urg = 0;
28387dd7cddfSDavid du Colombier 	seg.source = s->lport;
28397dd7cddfSDavid du Colombier 	seg.dest = s->rport;
28407dd7cddfSDavid du Colombier 	seg.flags = ACK|PSH;
28417dd7cddfSDavid du Colombier 	seg.mss = 0;
28423f695129SDavid du Colombier 	seg.ws = 0;
28433ff48bf5SDavid du Colombier 	if(tcpporthogdefense)
28443ff48bf5SDavid du Colombier 		seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28453ff48bf5SDavid du Colombier 	else
28467dd7cddfSDavid du Colombier 		seg.seq = tcb->snd.una-1;
28477dd7cddfSDavid du Colombier 	seg.ack = tcb->rcv.nxt;
28481ce6c95fSDavid du Colombier 	tcb->rcv.ackptr = seg.ack;
28491ce6c95fSDavid du Colombier 	tcprcvwin(s);
28507dd7cddfSDavid du Colombier 	seg.wnd = tcb->rcv.wnd;
28517dd7cddfSDavid du Colombier 	if(tcb->state == Finwait2){
28527dd7cddfSDavid du Colombier 		seg.flags |= FIN;
28537dd7cddfSDavid du Colombier 	} else {
28547dd7cddfSDavid du Colombier 		dbp = allocb(1);
28557dd7cddfSDavid du Colombier 		dbp->wp++;
28567dd7cddfSDavid du Colombier 	}
28577dd7cddfSDavid du Colombier 
28583ff48bf5SDavid du Colombier 	if(isv4(s->raddr)) {
28597dd7cddfSDavid du Colombier 		/* Build header, link data and compute cksum */
28603ff48bf5SDavid du Colombier 		tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28613ff48bf5SDavid du Colombier 		hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28627dd7cddfSDavid du Colombier 		if(hbp == nil) {
28637dd7cddfSDavid du Colombier 			freeblist(dbp);
28647dd7cddfSDavid du Colombier 			return;
28657dd7cddfSDavid du Colombier 		}
2866a6a9e072SDavid du Colombier 		ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28673ff48bf5SDavid du Colombier 	}
28683ff48bf5SDavid du Colombier 	else {
28693ff48bf5SDavid du Colombier 		/* Build header, link data and compute cksum */
28703ff48bf5SDavid du Colombier 		tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28713ff48bf5SDavid du Colombier 		hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28723ff48bf5SDavid du Colombier 		if(hbp == nil) {
28733ff48bf5SDavid du Colombier 			freeblist(dbp);
28743ff48bf5SDavid du Colombier 			return;
28753ff48bf5SDavid du Colombier 		}
2876a6a9e072SDavid du Colombier 		ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28773ff48bf5SDavid du Colombier 	}
28787dd7cddfSDavid du Colombier }
28797dd7cddfSDavid du Colombier 
28807dd7cddfSDavid du Colombier /*
288140ef9009SDavid du Colombier  *  set connection to time out after 12 minutes
28827dd7cddfSDavid du Colombier  */
28831ce6c95fSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28847dd7cddfSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28857dd7cddfSDavid du Colombier {
2886e288d156SDavid du Colombier 	tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28877dd7cddfSDavid du Colombier 	if(tcb->kacounter < 3)
28887dd7cddfSDavid du Colombier 		tcb->kacounter = 3;
28897dd7cddfSDavid du Colombier }
289040ef9009SDavid du Colombier 
289140ef9009SDavid du Colombier /*
289240ef9009SDavid du Colombier  *  if we've timed out, close the connection
289340ef9009SDavid du Colombier  *  otherwise, send a keepalive and restart the timer
289440ef9009SDavid du Colombier  */
28951ce6c95fSDavid du Colombier static void
tcpkeepalive(void * v)28967dd7cddfSDavid du Colombier tcpkeepalive(void *v)
28977dd7cddfSDavid du Colombier {
28987dd7cddfSDavid du Colombier 	Tcpctl *tcb;
28997dd7cddfSDavid du Colombier 	Conv *s;
29007dd7cddfSDavid du Colombier 
29017dd7cddfSDavid du Colombier 	s = v;
29027dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29037dd7cddfSDavid du Colombier 	if(waserror()){
29047dd7cddfSDavid du Colombier 		qunlock(s);
29057dd7cddfSDavid du Colombier 		nexterror();
29067dd7cddfSDavid du Colombier 	}
29077dd7cddfSDavid du Colombier 	qlock(s);
29087dd7cddfSDavid du Colombier 	if(tcb->state != Closed){
29097dd7cddfSDavid du Colombier 		if(--(tcb->kacounter) <= 0) {
29107dd7cddfSDavid du Colombier 			localclose(s, Etimedout);
29117dd7cddfSDavid du Colombier 		} else {
29127dd7cddfSDavid du Colombier 			tcpsendka(s);
29137dd7cddfSDavid du Colombier 			tcpgo(s->p->priv, &tcb->katimer);
29147dd7cddfSDavid du Colombier 		}
29157dd7cddfSDavid du Colombier 	}
29167dd7cddfSDavid du Colombier 	qunlock(s);
29177dd7cddfSDavid du Colombier 	poperror();
29187dd7cddfSDavid du Colombier }
29197dd7cddfSDavid du Colombier 
29207dd7cddfSDavid du Colombier /*
29217dd7cddfSDavid du Colombier  *  start keepalive timer
29227dd7cddfSDavid du Colombier  */
29231ce6c95fSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)29247dd7cddfSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
29257dd7cddfSDavid du Colombier {
29267dd7cddfSDavid du Colombier 	Tcpctl *tcb;
29277dd7cddfSDavid du Colombier 	int x;
29287dd7cddfSDavid du Colombier 
29297dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
293040ef9009SDavid du Colombier 	if(tcb->state != Established)
293140ef9009SDavid du Colombier 		return "connection must be in Establised state";
29327dd7cddfSDavid du Colombier 	if(n > 1){
29337dd7cddfSDavid du Colombier 		x = atoi(f[1]);
29347dd7cddfSDavid du Colombier 		if(x >= MSPTICK)
29357dd7cddfSDavid du Colombier 			tcb->katimer.start = x/MSPTICK;
29367dd7cddfSDavid du Colombier 	}
29377dd7cddfSDavid du Colombier 	tcpsetkacounter(tcb);
29387dd7cddfSDavid du Colombier 	tcpgo(s->p->priv, &tcb->katimer);
29397dd7cddfSDavid du Colombier 
29407dd7cddfSDavid du Colombier 	return nil;
29417dd7cddfSDavid du Colombier }
29427dd7cddfSDavid du Colombier 
294359cc4ca5SDavid du Colombier /*
294459cc4ca5SDavid du Colombier  *  turn checksums on/off
294559cc4ca5SDavid du Colombier  */
29461ce6c95fSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)294759cc4ca5SDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
294859cc4ca5SDavid du Colombier {
294959cc4ca5SDavid du Colombier 	Tcpctl *tcb;
295059cc4ca5SDavid du Colombier 
295159cc4ca5SDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
295259cc4ca5SDavid du Colombier 	tcb->nochecksum = !atoi(f[1]);
295359cc4ca5SDavid du Colombier 
295459cc4ca5SDavid du Colombier 	return nil;
295559cc4ca5SDavid du Colombier }
295659cc4ca5SDavid du Colombier 
29571ce6c95fSDavid du Colombier /*
29581ce6c95fSDavid du Colombier  *  retransmit (at most) one segment at snd.una.
29591ce6c95fSDavid du Colombier  *  preserve cwind & snd.ptr
29601ce6c95fSDavid du Colombier  */
29611ce6c95fSDavid du Colombier static void
tcprxmit(Conv * s)29627dd7cddfSDavid du Colombier tcprxmit(Conv *s)
29637dd7cddfSDavid du Colombier {
29647dd7cddfSDavid du Colombier 	Tcpctl *tcb;
29651ce6c95fSDavid du Colombier 	Tcppriv *tpriv;
29661ce6c95fSDavid du Colombier 	ulong tcwind, tptr;
29677dd7cddfSDavid du Colombier 
29687dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29697dd7cddfSDavid du Colombier 	tcb->flags |= RETRAN|FORCE;
29701ce6c95fSDavid du Colombier 
29711ce6c95fSDavid du Colombier 	tptr = tcb->snd.ptr;
29721ce6c95fSDavid du Colombier 	tcwind = tcb->cwind;
29737dd7cddfSDavid du Colombier 	tcb->snd.ptr = tcb->snd.una;
29747dd7cddfSDavid du Colombier 	tcb->cwind = tcb->mss;
29751ce6c95fSDavid du Colombier 	tcb->snd.retransmit = 1;
29767dd7cddfSDavid du Colombier 	tcpoutput(s);
29771ce6c95fSDavid du Colombier 	tcb->snd.retransmit = 0;
29781ce6c95fSDavid du Colombier 	tcb->cwind = tcwind;
29791ce6c95fSDavid du Colombier 	tcb->snd.ptr = tptr;
29801ce6c95fSDavid du Colombier 
29811ce6c95fSDavid du Colombier 	tpriv = s->p->priv;
29821ce6c95fSDavid du Colombier 	tpriv->stats[RetransSegs]++;
29837dd7cddfSDavid du Colombier }
29847dd7cddfSDavid du Colombier 
29851ce6c95fSDavid du Colombier /*
29861ce6c95fSDavid du Colombier  *  TODO: RFC 4138 F-RTO
29871ce6c95fSDavid du Colombier  */
29881ce6c95fSDavid du Colombier static void
tcptimeout(void * arg)29897dd7cddfSDavid du Colombier tcptimeout(void *arg)
29907dd7cddfSDavid du Colombier {
29917dd7cddfSDavid du Colombier 	Conv *s;
29927dd7cddfSDavid du Colombier 	Tcpctl *tcb;
29937dd7cddfSDavid du Colombier 	int maxback;
29947dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
29957dd7cddfSDavid du Colombier 
29967dd7cddfSDavid du Colombier 	s = (Conv*)arg;
29977dd7cddfSDavid du Colombier 	tpriv = s->p->priv;
29987dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29997dd7cddfSDavid du Colombier 
30007dd7cddfSDavid du Colombier 	if(waserror()){
30017dd7cddfSDavid du Colombier 		qunlock(s);
30027dd7cddfSDavid du Colombier 		nexterror();
30037dd7cddfSDavid du Colombier 	}
30047dd7cddfSDavid du Colombier 	qlock(s);
30057dd7cddfSDavid du Colombier 	switch(tcb->state){
30067dd7cddfSDavid du Colombier 	default:
30077dd7cddfSDavid du Colombier 		tcb->backoff++;
30087dd7cddfSDavid du Colombier 		if(tcb->state == Syn_sent)
30097dd7cddfSDavid du Colombier 			maxback = MAXBACKMS/2;
30107dd7cddfSDavid du Colombier 		else
30117dd7cddfSDavid du Colombier 			maxback = MAXBACKMS;
30127dd7cddfSDavid du Colombier 		tcb->backedoff += tcb->timer.start * MSPTICK;
30137dd7cddfSDavid du Colombier 		if(tcb->backedoff >= maxback) {
30147dd7cddfSDavid du Colombier 			localclose(s, Etimedout);
30157dd7cddfSDavid du Colombier 			break;
30167dd7cddfSDavid du Colombier 		}
30171ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
30181ce6c95fSDavid du Colombier 			tcb->srtt, tcb->mdev, NOW - tcb->time,
30191ce6c95fSDavid du Colombier 			tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
30201ce6c95fSDavid du Colombier 			tcpstates[s->state]);
30219a747e4fSDavid du Colombier 		tcpsettimer(tcb);
30221ce6c95fSDavid du Colombier 		if(tcb->snd.rto == 0)
30231ce6c95fSDavid du Colombier 			tcpcongestion(tcb);
30247dd7cddfSDavid du Colombier 		tcprxmit(s);
30251ce6c95fSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
30261ce6c95fSDavid du Colombier 		tcb->cwind = tcb->mss;
30271ce6c95fSDavid du Colombier 		tcb->snd.rto = 1;
302859cc4ca5SDavid du Colombier 		tpriv->stats[RetransTimeouts]++;
30291ce6c95fSDavid du Colombier 
30301ce6c95fSDavid du Colombier 		if(tcb->snd.recovery){
30311ce6c95fSDavid du Colombier 			tcb->snd.dupacks = 0;		/* reno rto */
30321ce6c95fSDavid du Colombier 			tcb->snd.recovery = 0;
30331ce6c95fSDavid du Colombier 			tpriv->stats[RecoveryRTO]++;
30341ce6c95fSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
30351ce6c95fSDavid du Colombier 			netlog(s->p->f, Logtcpwin,
30361ce6c95fSDavid du Colombier 				"rto recovery rxt @%lud\n", tcb->snd.nxt);
30371ce6c95fSDavid du Colombier 		}
30381ce6c95fSDavid du Colombier 
30391ce6c95fSDavid du Colombier 		tcb->abcbytes = 0;
30407dd7cddfSDavid du Colombier 		break;
30417dd7cddfSDavid du Colombier 	case Time_wait:
30427dd7cddfSDavid du Colombier 		localclose(s, nil);
30437dd7cddfSDavid du Colombier 		break;
30447dd7cddfSDavid du Colombier 	case Closed:
30457dd7cddfSDavid du Colombier 		break;
30467dd7cddfSDavid du Colombier 	}
30477dd7cddfSDavid du Colombier 	qunlock(s);
30487dd7cddfSDavid du Colombier 	poperror();
30497dd7cddfSDavid du Colombier }
30507dd7cddfSDavid du Colombier 
30511ce6c95fSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30527dd7cddfSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30537dd7cddfSDavid du Colombier {
30547dd7cddfSDavid du Colombier 	return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30557dd7cddfSDavid du Colombier }
30567dd7cddfSDavid du Colombier 
30573ff48bf5SDavid du Colombier /*
30583ff48bf5SDavid du Colombier  *  set up state for a received SYN (or SYN ACK) packet
30593ff48bf5SDavid du Colombier  */
30601ce6c95fSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30617dd7cddfSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30627dd7cddfSDavid du Colombier {
30637dd7cddfSDavid du Colombier 	Tcpctl *tcb;
30647ec5746aSDavid du Colombier 	Tcppriv *tpriv;
30657dd7cddfSDavid du Colombier 
30667dd7cddfSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
30677dd7cddfSDavid du Colombier 	tcb->flags |= FORCE;
30687dd7cddfSDavid du Colombier 
30697dd7cddfSDavid du Colombier 	tcb->rcv.nxt = seg->seq + 1;
30701ce6c95fSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
30711ce6c95fSDavid du Colombier 	tcb->rcv.wsnt = 0;
30727dd7cddfSDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
30737dd7cddfSDavid du Colombier 	tcb->irs = seg->seq;
30747dd7cddfSDavid du Colombier 
30753ff48bf5SDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
30767ec5746aSDavid du Colombier 	if(seg->mss != 0 && seg->mss < tcb->mss) {
30777dd7cddfSDavid du Colombier 		tcb->mss = seg->mss;
30787ec5746aSDavid du Colombier 		tpriv = s->p->priv;
30797ec5746aSDavid du Colombier 		tpriv->stats[Mss] = tcb->mss;
30807ec5746aSDavid du Colombier 	}
30817dd7cddfSDavid du Colombier 
30823ff48bf5SDavid du Colombier 	tcb->snd.wnd = seg->wnd;
30831ce6c95fSDavid du Colombier 	initialwindow(tcb);
30847dd7cddfSDavid du Colombier }
30857dd7cddfSDavid du Colombier 
30861ce6c95fSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30871ce6c95fSDavid du Colombier dumpreseq(Tcpctl *tcb)
30887dd7cddfSDavid du Colombier {
30891ce6c95fSDavid du Colombier 	Reseq *r, *next;
30907dd7cddfSDavid du Colombier 
30911ce6c95fSDavid du Colombier 	for(r = tcb->reseq; r != nil; r = next){
30921ce6c95fSDavid du Colombier 		next = r->next;
30931ce6c95fSDavid du Colombier 		freeblist(r->bp);
30941ce6c95fSDavid du Colombier 		free(r);
30951ce6c95fSDavid du Colombier 	}
30961ce6c95fSDavid du Colombier 	tcb->reseq = nil;
30971ce6c95fSDavid du Colombier 	tcb->nreseq = 0;
30981ce6c95fSDavid du Colombier 	tcb->reseqlen = 0;
30991ce6c95fSDavid du Colombier 	return -1;
31001ce6c95fSDavid du Colombier }
31011ce6c95fSDavid du Colombier 
31021ce6c95fSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)31031ce6c95fSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
31041ce6c95fSDavid du Colombier {
31051ce6c95fSDavid du Colombier 	char *s;
31061ce6c95fSDavid du Colombier 
31071ce6c95fSDavid du Colombier 	for(; r != nil; r = r->next){
31081ce6c95fSDavid du Colombier 		s = nil;
31091ce6c95fSDavid du Colombier 		if(r->next == nil && r->seg.seq != n)
31101ce6c95fSDavid du Colombier 			s = "hole/end";
31111ce6c95fSDavid du Colombier 		else if(r->next == nil)
31121ce6c95fSDavid du Colombier 			s = "end";
31131ce6c95fSDavid du Colombier 		else if(r->seg.seq != n)
31141ce6c95fSDavid du Colombier 			s = "hole";
31151ce6c95fSDavid du Colombier 		if(s != nil)
31161ce6c95fSDavid du Colombier 			netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
31171ce6c95fSDavid du Colombier 				n, r->seg.seq, r->seg.seq - n, r->seg.flags);
31181ce6c95fSDavid du Colombier 		n = r->seg.seq + r->seg.len;
31191ce6c95fSDavid du Colombier 	}
31201ce6c95fSDavid du Colombier }
31211ce6c95fSDavid du Colombier 
31221ce6c95fSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)31231ce6c95fSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
31241ce6c95fSDavid du Colombier {
31251ce6c95fSDavid du Colombier 	Reseq *rp, **rr;
31261ce6c95fSDavid du Colombier 	int qmax;
31271ce6c95fSDavid du Colombier 
31281ce6c95fSDavid du Colombier 	rp = malloc(sizeof *rp);
31297dd7cddfSDavid du Colombier 	if(rp == nil){
31301ce6c95fSDavid du Colombier 		freeblist(bp);		/* bp always consumed by addreseq */
31317dd7cddfSDavid du Colombier 		return 0;
31327dd7cddfSDavid du Colombier 	}
31337dd7cddfSDavid du Colombier 
31347dd7cddfSDavid du Colombier 	rp->seg = *seg;
31357dd7cddfSDavid du Colombier 	rp->bp = bp;
31367dd7cddfSDavid du Colombier 	rp->length = length;
31377dd7cddfSDavid du Colombier 
31381ce6c95fSDavid du Colombier 	tcb->reseqlen += length;
31391ce6c95fSDavid du Colombier 	tcb->nreseq++;
31401ce6c95fSDavid du Colombier 
31417dd7cddfSDavid du Colombier 	/* Place on reassembly list sorting by starting seq number */
31421ce6c95fSDavid du Colombier 	for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31431ce6c95fSDavid du Colombier 		if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31441ce6c95fSDavid du Colombier 			rp->next = *rr;
31451ce6c95fSDavid du Colombier 			*rr = rp;
31461ce6c95fSDavid du Colombier 			tpriv->stats[Resequenced]++;
31473ff48bf5SDavid du Colombier 			if(rp->next != nil)
31483ff48bf5SDavid du Colombier 				tpriv->stats[OutOfOrder]++;
31497dd7cddfSDavid du Colombier 			break;
31507dd7cddfSDavid du Colombier 		}
3151d7baab40SDavid du Colombier 
31521ce6c95fSDavid du Colombier 	qmax = tcb->window;
31531ce6c95fSDavid du Colombier 	if(tcb->reseqlen > qmax){
31541ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31551ce6c95fSDavid du Colombier 			tcb->reseqlen, qmax, tcb->nreseq);
31561ce6c95fSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31571ce6c95fSDavid du Colombier 		tpriv->stats[ReseqBytelim]++;
31581ce6c95fSDavid du Colombier 		return dumpreseq(tcb);
3159d7baab40SDavid du Colombier 	}
31601ce6c95fSDavid du Colombier 	qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31611ce6c95fSDavid du Colombier 	if(tcb->nreseq > qmax){
31621ce6c95fSDavid du Colombier 		netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31631ce6c95fSDavid du Colombier 			tcb->nreseq, qmax, tcb->reseqlen);
31641ce6c95fSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31651ce6c95fSDavid du Colombier 		tpriv->stats[ReseqPktlim]++;
31661ce6c95fSDavid du Colombier 		return dumpreseq(tcb);
31677dd7cddfSDavid du Colombier 	}
31687dd7cddfSDavid du Colombier 	return 0;
31697dd7cddfSDavid du Colombier }
31707dd7cddfSDavid du Colombier 
31711ce6c95fSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31727dd7cddfSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31737dd7cddfSDavid du Colombier {
31747dd7cddfSDavid du Colombier 	Reseq *rp;
31757dd7cddfSDavid du Colombier 
31767dd7cddfSDavid du Colombier 	rp = tcb->reseq;
31777dd7cddfSDavid du Colombier 	if(rp == nil)
31787dd7cddfSDavid du Colombier 		return;
31797dd7cddfSDavid du Colombier 
31807dd7cddfSDavid du Colombier 	tcb->reseq = rp->next;
31817dd7cddfSDavid du Colombier 
31827dd7cddfSDavid du Colombier 	*seg = rp->seg;
31837dd7cddfSDavid du Colombier 	*bp = rp->bp;
31847dd7cddfSDavid du Colombier 	*length = rp->length;
31857dd7cddfSDavid du Colombier 
31861ce6c95fSDavid du Colombier 	tcb->nreseq--;
31871ce6c95fSDavid du Colombier 	tcb->reseqlen -= rp->length;
31881ce6c95fSDavid du Colombier 
31897dd7cddfSDavid du Colombier 	free(rp);
31907dd7cddfSDavid du Colombier }
31917dd7cddfSDavid du Colombier 
31921ce6c95fSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31937dd7cddfSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31947dd7cddfSDavid du Colombier {
31957dd7cddfSDavid du Colombier 	ushort len;
31967dd7cddfSDavid du Colombier 	uchar accept;
31977dd7cddfSDavid du Colombier 	int dupcnt, excess;
31987dd7cddfSDavid du Colombier 
31997dd7cddfSDavid du Colombier 	accept = 0;
32007dd7cddfSDavid du Colombier 	len = *length;
32017dd7cddfSDavid du Colombier 	if(seg->flags & SYN)
32027dd7cddfSDavid du Colombier 		len++;
32037dd7cddfSDavid du Colombier 	if(seg->flags & FIN)
32047dd7cddfSDavid du Colombier 		len++;
32057dd7cddfSDavid du Colombier 
32067dd7cddfSDavid du Colombier 	if(tcb->rcv.wnd == 0) {
32077dd7cddfSDavid du Colombier 		if(len == 0 && seg->seq == tcb->rcv.nxt)
32087dd7cddfSDavid du Colombier 			return 0;
32097dd7cddfSDavid du Colombier 	}
32107dd7cddfSDavid du Colombier 	else {
32117dd7cddfSDavid du Colombier 		/* Some part of the segment should be in the window */
32127dd7cddfSDavid du Colombier 		if(inwindow(tcb,seg->seq))
32137dd7cddfSDavid du Colombier 			accept++;
32147dd7cddfSDavid du Colombier 		else
32157dd7cddfSDavid du Colombier 		if(len != 0) {
32167dd7cddfSDavid du Colombier 			if(inwindow(tcb, seg->seq+len-1) ||
32177dd7cddfSDavid du Colombier 			seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
32187dd7cddfSDavid du Colombier 				accept++;
32197dd7cddfSDavid du Colombier 		}
32207dd7cddfSDavid du Colombier 	}
32217dd7cddfSDavid du Colombier 	if(!accept) {
32227dd7cddfSDavid du Colombier 		freeblist(*bp);
32237dd7cddfSDavid du Colombier 		return -1;
32247dd7cddfSDavid du Colombier 	}
32257dd7cddfSDavid du Colombier 	dupcnt = tcb->rcv.nxt - seg->seq;
32267dd7cddfSDavid du Colombier 	if(dupcnt > 0){
32277dd7cddfSDavid du Colombier 		tcb->rerecv += dupcnt;
32287dd7cddfSDavid du Colombier 		if(seg->flags & SYN){
32297dd7cddfSDavid du Colombier 			seg->flags &= ~SYN;
32307dd7cddfSDavid du Colombier 			seg->seq++;
32317dd7cddfSDavid du Colombier 
32327dd7cddfSDavid du Colombier 			if(seg->urg > 1)
32337dd7cddfSDavid du Colombier 				seg->urg--;
32347dd7cddfSDavid du Colombier 			else
32357dd7cddfSDavid du Colombier 				seg->flags &= ~URG;
32367dd7cddfSDavid du Colombier 			dupcnt--;
32377dd7cddfSDavid du Colombier 		}
32387dd7cddfSDavid du Colombier 		if(dupcnt > 0){
32397dd7cddfSDavid du Colombier 			pullblock(bp, (ushort)dupcnt);
32407dd7cddfSDavid du Colombier 			seg->seq += dupcnt;
32417dd7cddfSDavid du Colombier 			*length -= dupcnt;
32427dd7cddfSDavid du Colombier 
32437dd7cddfSDavid du Colombier 			if(seg->urg > dupcnt)
32447dd7cddfSDavid du Colombier 				seg->urg -= dupcnt;
32457dd7cddfSDavid du Colombier 			else {
32467dd7cddfSDavid du Colombier 				seg->flags &= ~URG;
32477dd7cddfSDavid du Colombier 				seg->urg = 0;
32487dd7cddfSDavid du Colombier 			}
32497dd7cddfSDavid du Colombier 		}
32507dd7cddfSDavid du Colombier 	}
32517dd7cddfSDavid du Colombier 	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32527dd7cddfSDavid du Colombier 	if(excess > 0) {
32537dd7cddfSDavid du Colombier 		tcb->rerecv += excess;
32547dd7cddfSDavid du Colombier 		*length -= excess;
325580ee5cbfSDavid du Colombier 		*bp = trimblock(*bp, 0, *length);
325680ee5cbfSDavid du Colombier 		if(*bp == nil)
325780ee5cbfSDavid du Colombier 			panic("presotto is a boofhead");
32587dd7cddfSDavid du Colombier 		seg->flags &= ~FIN;
32597dd7cddfSDavid du Colombier 	}
32607dd7cddfSDavid du Colombier 	return 0;
32617dd7cddfSDavid du Colombier }
32627dd7cddfSDavid du Colombier 
32631ce6c95fSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32647dd7cddfSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32657dd7cddfSDavid du Colombier {
32663ff48bf5SDavid du Colombier 	Tcp4hdr *h4;
32673ff48bf5SDavid du Colombier 	Tcp6hdr *h6;
32687dd7cddfSDavid du Colombier 	Tcpctl *tcb;
32697dd7cddfSDavid du Colombier 	uchar source[IPaddrlen];
32707dd7cddfSDavid du Colombier 	uchar dest[IPaddrlen];
32717dd7cddfSDavid du Colombier 	ushort psource, pdest;
32727dd7cddfSDavid du Colombier 	Conv *s, **p;
32737dd7cddfSDavid du Colombier 
32743ff48bf5SDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
32753ff48bf5SDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
32767dd7cddfSDavid du Colombier 
32773ff48bf5SDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
32783ff48bf5SDavid du Colombier 		v4tov6(dest, h4->tcpdst);
32793ff48bf5SDavid du Colombier 		v4tov6(source, h4->tcpsrc);
32803ff48bf5SDavid du Colombier 		psource = nhgets(h4->tcpsport);
32813ff48bf5SDavid du Colombier 		pdest = nhgets(h4->tcpdport);
32823ff48bf5SDavid du Colombier 	}
32833ff48bf5SDavid du Colombier 	else {
32843ff48bf5SDavid du Colombier 		ipmove(dest, h6->tcpdst);
32853ff48bf5SDavid du Colombier 		ipmove(source, h6->tcpsrc);
32863ff48bf5SDavid du Colombier 		psource = nhgets(h6->tcpsport);
32873ff48bf5SDavid du Colombier 		pdest = nhgets(h6->tcpdport);
32883ff48bf5SDavid du Colombier 	}
32897dd7cddfSDavid du Colombier 
32907dd7cddfSDavid du Colombier 	/* Look for a connection */
32917dd7cddfSDavid du Colombier 	qlock(tcp);
32927dd7cddfSDavid du Colombier 	for(p = tcp->conv; *p; p++) {
32937dd7cddfSDavid du Colombier 		s = *p;
32947dd7cddfSDavid du Colombier 		tcb = (Tcpctl*)s->ptcl;
32957dd7cddfSDavid du Colombier 		if(s->rport == pdest)
32967dd7cddfSDavid du Colombier 		if(s->lport == psource)
32977dd7cddfSDavid du Colombier 		if(tcb->state != Closed)
32987dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dest) == 0)
32997dd7cddfSDavid du Colombier 		if(ipcmp(s->laddr, source) == 0){
33007dd7cddfSDavid du Colombier 			qlock(s);
33017dd7cddfSDavid du Colombier 			qunlock(tcp);
33027dd7cddfSDavid du Colombier 			switch(tcb->state){
33037dd7cddfSDavid du Colombier 			case Syn_sent:
33047dd7cddfSDavid du Colombier 				localclose(s, msg);
33057dd7cddfSDavid du Colombier 				break;
33067dd7cddfSDavid du Colombier 			}
33077dd7cddfSDavid du Colombier 			qunlock(s);
33087dd7cddfSDavid du Colombier 			freeblist(bp);
33097dd7cddfSDavid du Colombier 			return;
33107dd7cddfSDavid du Colombier 		}
33117dd7cddfSDavid du Colombier 	}
33127dd7cddfSDavid du Colombier 	qunlock(tcp);
33137dd7cddfSDavid du Colombier 	freeblist(bp);
33147dd7cddfSDavid du Colombier }
33157dd7cddfSDavid du Colombier 
33163f695129SDavid du Colombier static char*
tcpporthogdefensectl(char * val)33173f695129SDavid du Colombier tcpporthogdefensectl(char *val)
33183f695129SDavid du Colombier {
33193f695129SDavid du Colombier 	if(strcmp(val, "on") == 0)
33203f695129SDavid du Colombier 		tcpporthogdefense = 1;
33213f695129SDavid du Colombier 	else if(strcmp(val, "off") == 0)
33223f695129SDavid du Colombier 		tcpporthogdefense = 0;
33233f695129SDavid du Colombier 	else
33243f695129SDavid du Colombier 		return "unknown value for tcpporthogdefense";
33253f695129SDavid du Colombier 	return nil;
33263f695129SDavid du Colombier }
33273f695129SDavid du Colombier 
332880ee5cbfSDavid du Colombier /* called with c qlocked */
33291ce6c95fSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)33307dd7cddfSDavid du Colombier tcpctl(Conv* c, char** f, int n)
33317dd7cddfSDavid du Colombier {
3332*41aa7335SDavid du Colombier 	if(n == 1 && strcmp(f[0], "close") == 0)
3333*41aa7335SDavid du Colombier 		return tcpclose2(c);
33347dd7cddfSDavid du Colombier 	if(n == 1 && strcmp(f[0], "hangup") == 0)
33357dd7cddfSDavid du Colombier 		return tcphangup(c);
3336ddc91d4cSDavid du Colombier 	if(n == 1 && strcmp(f[0], "hangupxmit") == 0)
3337ddc91d4cSDavid du Colombier 		return tcpxmitclose(c);
33387dd7cddfSDavid du Colombier 	if(n >= 1 && strcmp(f[0], "keepalive") == 0)
33397dd7cddfSDavid du Colombier 		return tcpstartka(c, f, n);
334059cc4ca5SDavid du Colombier 	if(n >= 1 && strcmp(f[0], "checksum") == 0)
334159cc4ca5SDavid du Colombier 		return tcpsetchecksum(c, f, n);
33423f695129SDavid du Colombier 	if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
33433f695129SDavid du Colombier 		return tcpporthogdefensectl(f[1]);
33447dd7cddfSDavid du Colombier 	return "unknown control request";
33457dd7cddfSDavid du Colombier }
33467dd7cddfSDavid du Colombier 
33471ce6c95fSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33487dd7cddfSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33497dd7cddfSDavid du Colombier {
335059cc4ca5SDavid du Colombier 	Tcppriv *priv;
335159cc4ca5SDavid du Colombier 	char *p, *e;
335259cc4ca5SDavid du Colombier 	int i;
33537dd7cddfSDavid du Colombier 
335459cc4ca5SDavid du Colombier 	priv = tcp->priv;
335559cc4ca5SDavid du Colombier 	p = buf;
335659cc4ca5SDavid du Colombier 	e = p+len;
335759cc4ca5SDavid du Colombier 	for(i = 0; i < Nstats; i++)
33585e27dea9SDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
335959cc4ca5SDavid du Colombier 	return p - buf;
33607dd7cddfSDavid du Colombier }
33617dd7cddfSDavid du Colombier 
33627dd7cddfSDavid du Colombier /*
33637dd7cddfSDavid du Colombier  *  garbage collect any stale conversations:
33647dd7cddfSDavid du Colombier  *	- SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33657dd7cddfSDavid du Colombier  *	- Finwait2 after 5 minutes
33667dd7cddfSDavid du Colombier  *
33677dd7cddfSDavid du Colombier  *  this is called whenever we run out of channels.  Both checks are
33687dd7cddfSDavid du Colombier  *  of questionable validity so we try to use them only when we're
33697dd7cddfSDavid du Colombier  *  up against the wall.
33707dd7cddfSDavid du Colombier  */
33711ce6c95fSDavid du Colombier static int
tcpgc(Proto * tcp)33727dd7cddfSDavid du Colombier tcpgc(Proto *tcp)
33737dd7cddfSDavid du Colombier {
33747dd7cddfSDavid du Colombier 	Conv *c, **pp, **ep;
33757dd7cddfSDavid du Colombier 	int n;
33767dd7cddfSDavid du Colombier 	Tcpctl *tcb;
33777dd7cddfSDavid du Colombier 
33787dd7cddfSDavid du Colombier 
33797dd7cddfSDavid du Colombier 	n = 0;
33807dd7cddfSDavid du Colombier 	ep = &tcp->conv[tcp->nc];
33817dd7cddfSDavid du Colombier 	for(pp = tcp->conv; pp < ep; pp++) {
33827dd7cddfSDavid du Colombier 		c = *pp;
33837dd7cddfSDavid du Colombier 		if(c == nil)
33847dd7cddfSDavid du Colombier 			break;
33857dd7cddfSDavid du Colombier 		if(!canqlock(c))
33867dd7cddfSDavid du Colombier 			continue;
33877dd7cddfSDavid du Colombier 		tcb = (Tcpctl*)c->ptcl;
33887dd7cddfSDavid du Colombier 		switch(tcb->state){
33897dd7cddfSDavid du Colombier 		case Syn_received:
33903ff48bf5SDavid du Colombier 			if(NOW - tcb->time > 5000){
33911ce6c95fSDavid du Colombier 				localclose(c, Etimedout);
33927dd7cddfSDavid du Colombier 				n++;
33937dd7cddfSDavid du Colombier 			}
33947dd7cddfSDavid du Colombier 			break;
33957dd7cddfSDavid du Colombier 		case Finwait2:
33963ff48bf5SDavid du Colombier 			if(NOW - tcb->time > 5*60*1000){
33971ce6c95fSDavid du Colombier 				localclose(c, Etimedout);
33987dd7cddfSDavid du Colombier 				n++;
33997dd7cddfSDavid du Colombier 			}
34007dd7cddfSDavid du Colombier 			break;
34017dd7cddfSDavid du Colombier 		}
34027dd7cddfSDavid du Colombier 		qunlock(c);
34037dd7cddfSDavid du Colombier 	}
34047dd7cddfSDavid du Colombier 	return n;
34057dd7cddfSDavid du Colombier }
34067dd7cddfSDavid du Colombier 
34071ce6c95fSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)34089a747e4fSDavid du Colombier tcpsettimer(Tcpctl *tcb)
34099a747e4fSDavid du Colombier {
34109a747e4fSDavid du Colombier 	int x;
34119a747e4fSDavid du Colombier 
34121b0f96a1SDavid du Colombier 	/* round trip dependency */
34139a747e4fSDavid du Colombier 	x = backoff(tcb->backoff) *
34149a747e4fSDavid du Colombier 		(tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
34159a747e4fSDavid du Colombier 
34161ce6c95fSDavid du Colombier 	/* bounded twixt 0.3 and 64 seconds */
34171ce6c95fSDavid du Colombier 	if(x < 300/MSPTICK)
34181ce6c95fSDavid du Colombier 		x = 300/MSPTICK;
34191b0f96a1SDavid du Colombier 	else if(x > (64000/MSPTICK))
34201b0f96a1SDavid du Colombier 		x = 64000/MSPTICK;
34219a747e4fSDavid du Colombier 	tcb->timer.start = x;
34229a747e4fSDavid du Colombier }
34239a747e4fSDavid du Colombier 
34249a747e4fSDavid du Colombier void
tcpinit(Fs * fs)34257dd7cddfSDavid du Colombier tcpinit(Fs *fs)
34267dd7cddfSDavid du Colombier {
34277dd7cddfSDavid du Colombier 	Proto *tcp;
34287dd7cddfSDavid du Colombier 	Tcppriv *tpriv;
34297dd7cddfSDavid du Colombier 
34307dd7cddfSDavid du Colombier 	tcp = smalloc(sizeof(Proto));
34317dd7cddfSDavid du Colombier 	tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
34327dd7cddfSDavid du Colombier 	tcp->name = "tcp";
34337dd7cddfSDavid du Colombier 	tcp->connect = tcpconnect;
34347dd7cddfSDavid du Colombier 	tcp->announce = tcpannounce;
34357dd7cddfSDavid du Colombier 	tcp->ctl = tcpctl;
34367dd7cddfSDavid du Colombier 	tcp->state = tcpstate;
34377dd7cddfSDavid du Colombier 	tcp->create = tcpcreate;
34387dd7cddfSDavid du Colombier 	tcp->close = tcpclose;
34397dd7cddfSDavid du Colombier 	tcp->rcv = tcpiput;
34407dd7cddfSDavid du Colombier 	tcp->advise = tcpadvise;
34417dd7cddfSDavid du Colombier 	tcp->stats = tcpstats;
34427dd7cddfSDavid du Colombier 	tcp->inuse = tcpinuse;
34437dd7cddfSDavid du Colombier 	tcp->gc = tcpgc;
34447dd7cddfSDavid du Colombier 	tcp->ipproto = IP_TCPPROTO;
34459a747e4fSDavid du Colombier 	tcp->nc = scalednconv();
34467dd7cddfSDavid du Colombier 	tcp->ptclsize = sizeof(Tcpctl);
34479a747e4fSDavid du Colombier 	tpriv->stats[MaxConn] = tcp->nc;
34487dd7cddfSDavid du Colombier 
34497dd7cddfSDavid du Colombier 	Fsproto(fs, tcp);
34507dd7cddfSDavid du Colombier }
34513f695129SDavid du Colombier 
34521ce6c95fSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34533f695129SDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34543f695129SDavid du Colombier {
34551ce6c95fSDavid du Colombier 	/*
34561ce6c95fSDavid du Colombier 	 * guess at reasonable queue sizes.  there's no current way
34571ce6c95fSDavid du Colombier 	 * to know how many nic receive buffers we can safely tie up in the
34581ce6c95fSDavid du Colombier 	 * tcp stack, and we don't adjust our queues to maximize throughput
34591ce6c95fSDavid du Colombier 	 * and minimize bufferbloat.  n.b. the offer (rcvscale) needs to be
34601ce6c95fSDavid du Colombier 	 * respected, but we still control our own buffer commitment by
34611ce6c95fSDavid du Colombier 	 * keeping a seperate qscale.
34621ce6c95fSDavid du Colombier 	 */
34633f695129SDavid du Colombier 	tcb->rcv.scale = rcvscale & 0xff;
34643f695129SDavid du Colombier 	tcb->snd.scale = sndscale & 0xff;
34651ce6c95fSDavid du Colombier 	tcb->qscale = rcvscale & 0xff;
34661ce6c95fSDavid du Colombier 	if(rcvscale > Maxqscale)
34671ce6c95fSDavid du Colombier 		tcb->qscale = Maxqscale;
34681ce6c95fSDavid du Colombier 
34691ce6c95fSDavid du Colombier 	if(rcvscale != tcb->rcv.scale)
34701ce6c95fSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34711ce6c95fSDavid du Colombier 			"qlen %d >> window %ud lport %d\n",
34721ce6c95fSDavid du Colombier 			tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34731ce6c95fSDavid du Colombier 	tcb->window = QMAX << tcb->qscale;
34741ce6c95fSDavid du Colombier 	tcb->ssthresh = tcb->window;
34751ce6c95fSDavid du Colombier 
34761ce6c95fSDavid du Colombier 	/*
34771ce6c95fSDavid du Colombier 	 * it's important to set wq large enough to cover the full
34781ce6c95fSDavid du Colombier 	 * bandwidth-delay product.  it's possible to be in loss
34791ce6c95fSDavid du Colombier 	 * recovery with a big window, and we need to keep sending
34801ce6c95fSDavid du Colombier 	 * into the inflated window.  the difference can be huge
34811ce6c95fSDavid du Colombier 	 * for even modest (70ms) ping times.
34821ce6c95fSDavid du Colombier 	 */
34833f695129SDavid du Colombier 	qsetlimit(s->rq, tcb->window);
34841ce6c95fSDavid du Colombier 	qsetlimit(s->wq, tcb->window);
34851ce6c95fSDavid du Colombier 	tcprcvwin(s);
34863f695129SDavid du Colombier }
3487