xref: /plan9-contrib/sys/src/9k/ip/tcp.c (revision 41165bba57fea051ea0ad3129955d848d8699516)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier #include	"../port/error.h"
79ef1f84bSDavid du Colombier 
89ef1f84bSDavid du Colombier #include	"ip.h"
99ef1f84bSDavid du Colombier 
109ef1f84bSDavid du Colombier enum
119ef1f84bSDavid du Colombier {
129ef1f84bSDavid du Colombier 	QMAX		= 64*1024-1,
139ef1f84bSDavid du Colombier 	IP_TCPPROTO	= 6,
149ef1f84bSDavid du Colombier 
159ef1f84bSDavid du Colombier 	TCP4_IPLEN	= 8,
169ef1f84bSDavid du Colombier 	TCP4_PHDRSIZE	= 12,
179ef1f84bSDavid du Colombier 	TCP4_HDRSIZE	= 20,
189ef1f84bSDavid du Colombier 	TCP4_TCBPHDRSZ	= 40,
199ef1f84bSDavid du Colombier 	TCP4_PKT	= TCP4_IPLEN+TCP4_PHDRSIZE,
209ef1f84bSDavid du Colombier 
219ef1f84bSDavid du Colombier 	TCP6_IPLEN	= 0,
229ef1f84bSDavid du Colombier 	TCP6_PHDRSIZE	= 40,
239ef1f84bSDavid du Colombier 	TCP6_HDRSIZE	= 20,
249ef1f84bSDavid du Colombier 	TCP6_TCBPHDRSZ	= 60,
259ef1f84bSDavid du Colombier 	TCP6_PKT	= TCP6_IPLEN+TCP6_PHDRSIZE,
269ef1f84bSDavid du Colombier 
279ef1f84bSDavid du Colombier 	TcptimerOFF	= 0,
289ef1f84bSDavid du Colombier 	TcptimerON	= 1,
299ef1f84bSDavid du Colombier 	TcptimerDONE	= 2,
309ef1f84bSDavid du Colombier 	MAX_TIME 	= (1<<20),	/* Forever */
319ef1f84bSDavid du Colombier 	TCP_ACK		= 50,		/* Timed ack sequence in ms */
329ef1f84bSDavid du Colombier 	MAXBACKMS	= 9*60*1000,	/* longest backoff time (ms) before hangup */
339ef1f84bSDavid du Colombier 
349ef1f84bSDavid du Colombier 	URG		= 0x20,		/* Data marked urgent */
359ef1f84bSDavid du Colombier 	ACK		= 0x10,		/* Acknowledge is valid */
369ef1f84bSDavid du Colombier 	PSH		= 0x08,		/* Whole data pipe is pushed */
379ef1f84bSDavid du Colombier 	RST		= 0x04,		/* Reset connection */
389ef1f84bSDavid du Colombier 	SYN		= 0x02,		/* Pkt. is synchronise */
399ef1f84bSDavid du Colombier 	FIN		= 0x01,		/* Start close down */
409ef1f84bSDavid du Colombier 
419ef1f84bSDavid du Colombier 	EOLOPT		= 0,
429ef1f84bSDavid du Colombier 	NOOPOPT		= 1,
439ef1f84bSDavid du Colombier 	MSSOPT		= 2,
449ef1f84bSDavid du Colombier 	MSS_LENGTH	= 4,		/* Maximum segment size */
459ef1f84bSDavid du Colombier 	WSOPT		= 3,
469ef1f84bSDavid du Colombier 	WS_LENGTH	= 3,		/* Bits to scale window size by */
479ef1f84bSDavid du Colombier 	MSL2		= 10,
489ef1f84bSDavid du Colombier 	MSPTICK		= 50,		/* Milliseconds per timer tick */
499ef1f84bSDavid du Colombier 	DEF_MSS		= 1460,		/* Default maximum segment */
509ef1f84bSDavid du Colombier 	DEF_MSS6	= 1280,		/* Default maximum segment (min) for v6 */
519ef1f84bSDavid du Colombier 	DEF_RTT		= 500,		/* Default round trip */
529ef1f84bSDavid du Colombier 	DEF_KAT		= 120000,	/* Default time (ms) between keep alives */
539ef1f84bSDavid du Colombier 	TCP_LISTEN	= 0,		/* Listen connection */
549ef1f84bSDavid du Colombier 	TCP_CONNECT	= 1,		/* Outgoing connection */
559ef1f84bSDavid du Colombier 	SYNACK_RXTIMER	= 250,		/* ms between SYNACK retransmits */
569ef1f84bSDavid du Colombier 
579ef1f84bSDavid du Colombier 	TCPREXMTTHRESH	= 3,		/* dupack threshhold for rxt */
589ef1f84bSDavid du Colombier 
599ef1f84bSDavid du Colombier 	FORCE		= 1,
609ef1f84bSDavid du Colombier 	CLONE		= 2,
619ef1f84bSDavid du Colombier 	RETRAN		= 4,
629ef1f84bSDavid du Colombier 	ACTIVE		= 8,
639ef1f84bSDavid du Colombier 	SYNACK		= 16,
649ef1f84bSDavid du Colombier 
659ef1f84bSDavid du Colombier 	LOGAGAIN	= 3,
669ef1f84bSDavid du Colombier 	LOGDGAIN	= 2,
679ef1f84bSDavid du Colombier 
689ef1f84bSDavid du Colombier 	Closed		= 0,		/* Connection states */
699ef1f84bSDavid du Colombier 	Listen,
709ef1f84bSDavid du Colombier 	Syn_sent,
719ef1f84bSDavid du Colombier 	Syn_received,
729ef1f84bSDavid du Colombier 	Established,
739ef1f84bSDavid du Colombier 	Finwait1,
749ef1f84bSDavid du Colombier 	Finwait2,
759ef1f84bSDavid du Colombier 	Close_wait,
769ef1f84bSDavid du Colombier 	Closing,
779ef1f84bSDavid du Colombier 	Last_ack,
789ef1f84bSDavid du Colombier 	Time_wait,
799ef1f84bSDavid du Colombier 
809ef1f84bSDavid du Colombier 	Maxlimbo	= 1000,		/* maximum procs waiting for response to SYN ACK */
819ef1f84bSDavid du Colombier 	NLHT		= 256,		/* hash table size, must be a power of 2 */
829ef1f84bSDavid du Colombier 	LHTMASK		= NLHT-1,
839ef1f84bSDavid du Colombier 
849ef1f84bSDavid du Colombier 	/*
859ef1f84bSDavid du Colombier 	 * window is 64kb * 2ⁿ
869ef1f84bSDavid du Colombier 	 * these factors determine the ultimate bandwidth-delay product.
879ef1f84bSDavid du Colombier 	 * 64kb * 2⁵ = 2mb, or 2× overkill for 100mbps * 70ms.
889ef1f84bSDavid du Colombier 	 */
899ef1f84bSDavid du Colombier 	Maxqscale	= 4,		/* maximum queuing scale */
909ef1f84bSDavid du Colombier 	Defadvscale	= 4,		/* default advertisement */
919ef1f84bSDavid du Colombier };
929ef1f84bSDavid du Colombier 
939ef1f84bSDavid du Colombier /* Must correspond to the enumeration above */
949ef1f84bSDavid du Colombier char *tcpstates[] =
959ef1f84bSDavid du Colombier {
969ef1f84bSDavid du Colombier 	"Closed", 	"Listen", 	"Syn_sent", "Syn_received",
979ef1f84bSDavid du Colombier 	"Established", 	"Finwait1",	"Finwait2", "Close_wait",
989ef1f84bSDavid du Colombier 	"Closing", 	"Last_ack", 	"Time_wait"
999ef1f84bSDavid du Colombier };
1009ef1f84bSDavid du Colombier 
1019ef1f84bSDavid du Colombier typedef struct Tcptimer Tcptimer;
1029ef1f84bSDavid du Colombier struct Tcptimer
1039ef1f84bSDavid du Colombier {
1049ef1f84bSDavid du Colombier 	Tcptimer	*next;
1059ef1f84bSDavid du Colombier 	Tcptimer	*prev;
1069ef1f84bSDavid du Colombier 	Tcptimer	*readynext;
1079ef1f84bSDavid du Colombier 	int	state;
1089ef1f84bSDavid du Colombier 	int	start;
1099ef1f84bSDavid du Colombier 	int	count;
1109ef1f84bSDavid du Colombier 	void	(*func)(void*);
1119ef1f84bSDavid du Colombier 	void	*arg;
1129ef1f84bSDavid du Colombier };
1139ef1f84bSDavid du Colombier 
1149ef1f84bSDavid du Colombier /*
1159ef1f84bSDavid du Colombier  *  v4 and v6 pseudo headers used for
1169ef1f84bSDavid du Colombier  *  checksuming tcp
1179ef1f84bSDavid du Colombier  */
1189ef1f84bSDavid du Colombier typedef struct Tcp4hdr Tcp4hdr;
1199ef1f84bSDavid du Colombier struct Tcp4hdr
1209ef1f84bSDavid du Colombier {
1219ef1f84bSDavid du Colombier 	uchar	vihl;		/* Version and header length */
1229ef1f84bSDavid du Colombier 	uchar	tos;		/* Type of service */
1239ef1f84bSDavid du Colombier 	uchar	length[2];	/* packet length */
1249ef1f84bSDavid du Colombier 	uchar	id[2];		/* Identification */
1259ef1f84bSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
1269ef1f84bSDavid du Colombier 	uchar	Unused;
1279ef1f84bSDavid du Colombier 	uchar	proto;
1289ef1f84bSDavid du Colombier 	uchar	tcplen[2];
1299ef1f84bSDavid du Colombier 	uchar	tcpsrc[4];
1309ef1f84bSDavid du Colombier 	uchar	tcpdst[4];
1319ef1f84bSDavid du Colombier 	uchar	tcpsport[2];
1329ef1f84bSDavid du Colombier 	uchar	tcpdport[2];
1339ef1f84bSDavid du Colombier 	uchar	tcpseq[4];
1349ef1f84bSDavid du Colombier 	uchar	tcpack[4];
1359ef1f84bSDavid du Colombier 	uchar	tcpflag[2];
1369ef1f84bSDavid du Colombier 	uchar	tcpwin[2];
1379ef1f84bSDavid du Colombier 	uchar	tcpcksum[2];
1389ef1f84bSDavid du Colombier 	uchar	tcpurg[2];
1399ef1f84bSDavid du Colombier 	/* Options segment */
1409ef1f84bSDavid du Colombier 	uchar	tcpopt[1];
1419ef1f84bSDavid du Colombier };
1429ef1f84bSDavid du Colombier 
1439ef1f84bSDavid du Colombier typedef struct Tcp6hdr Tcp6hdr;
1449ef1f84bSDavid du Colombier struct Tcp6hdr
1459ef1f84bSDavid du Colombier {
1469ef1f84bSDavid du Colombier 	uchar	vcf[4];
1479ef1f84bSDavid du Colombier 	uchar	ploadlen[2];
1489ef1f84bSDavid du Colombier 	uchar	proto;
1499ef1f84bSDavid du Colombier 	uchar	ttl;
1509ef1f84bSDavid du Colombier 	uchar	tcpsrc[IPaddrlen];
1519ef1f84bSDavid du Colombier 	uchar	tcpdst[IPaddrlen];
1529ef1f84bSDavid du Colombier 	uchar	tcpsport[2];
1539ef1f84bSDavid du Colombier 	uchar	tcpdport[2];
1549ef1f84bSDavid du Colombier 	uchar	tcpseq[4];
1559ef1f84bSDavid du Colombier 	uchar	tcpack[4];
1569ef1f84bSDavid du Colombier 	uchar	tcpflag[2];
1579ef1f84bSDavid du Colombier 	uchar	tcpwin[2];
1589ef1f84bSDavid du Colombier 	uchar	tcpcksum[2];
1599ef1f84bSDavid du Colombier 	uchar	tcpurg[2];
1609ef1f84bSDavid du Colombier 	/* Options segment */
1619ef1f84bSDavid du Colombier 	uchar	tcpopt[1];
1629ef1f84bSDavid du Colombier };
1639ef1f84bSDavid du Colombier 
1649ef1f84bSDavid du Colombier /*
1659ef1f84bSDavid du Colombier  *  this represents the control info
1669ef1f84bSDavid du Colombier  *  for a single packet.  It is derived from
1679ef1f84bSDavid du Colombier  *  a packet in ntohtcp{4,6}() and stuck into
1689ef1f84bSDavid du Colombier  *  a packet in htontcp{4,6}().
1699ef1f84bSDavid du Colombier  */
1709ef1f84bSDavid du Colombier typedef struct Tcp Tcp;
1719ef1f84bSDavid du Colombier struct	Tcp
1729ef1f84bSDavid du Colombier {
1739ef1f84bSDavid du Colombier 	ushort	source;
1749ef1f84bSDavid du Colombier 	ushort	dest;
1759ef1f84bSDavid du Colombier 	ulong	seq;
1769ef1f84bSDavid du Colombier 	ulong	ack;
1779ef1f84bSDavid du Colombier 	uchar	flags;
1789ef1f84bSDavid du Colombier 	uchar	update;
1799ef1f84bSDavid du Colombier 	ushort	ws;	/* window scale option */
1809ef1f84bSDavid du Colombier 	ulong	wnd;	/* prescaled window*/
1819ef1f84bSDavid du Colombier 	ushort	urg;
1829ef1f84bSDavid du Colombier 	ushort	mss;	/* max segment size option (if not zero) */
1839ef1f84bSDavid du Colombier 	ushort	len;	/* size of data */
1849ef1f84bSDavid du Colombier };
1859ef1f84bSDavid du Colombier 
1869ef1f84bSDavid du Colombier /*
1879ef1f84bSDavid du Colombier  *  this header is malloc'd to thread together fragments
1889ef1f84bSDavid du Colombier  *  waiting to be coalesced
1899ef1f84bSDavid du Colombier  */
1909ef1f84bSDavid du Colombier typedef struct Reseq Reseq;
1919ef1f84bSDavid du Colombier struct Reseq
1929ef1f84bSDavid du Colombier {
1939ef1f84bSDavid du Colombier 	Reseq	*next;
1949ef1f84bSDavid du Colombier 	Tcp	seg;
1959ef1f84bSDavid du Colombier 	Block	*bp;
1969ef1f84bSDavid du Colombier 	ushort	length;
1979ef1f84bSDavid du Colombier };
1989ef1f84bSDavid du Colombier 
1999ef1f84bSDavid du Colombier /*
2009ef1f84bSDavid du Colombier  *  the qlock in the Conv locks this structure
2019ef1f84bSDavid du Colombier  */
2029ef1f84bSDavid du Colombier typedef struct Tcpctl Tcpctl;
2039ef1f84bSDavid du Colombier struct Tcpctl
2049ef1f84bSDavid du Colombier {
2059ef1f84bSDavid du Colombier 	uchar	state;			/* Connection state */
2069ef1f84bSDavid du Colombier 	uchar	type;			/* Listening or active connection */
2079ef1f84bSDavid du Colombier 	uchar	code;			/* Icmp code */
2089ef1f84bSDavid du Colombier 	struct {
2099ef1f84bSDavid du Colombier 		ulong	una;		/* Unacked data pointer */
2109ef1f84bSDavid du Colombier 		ulong	nxt;		/* Next sequence expected */
2119ef1f84bSDavid du Colombier 		ulong	ptr;		/* Data pointer */
2129ef1f84bSDavid du Colombier 		ulong	wnd;		/* Tcp send window */
2139ef1f84bSDavid du Colombier 		ulong	urg;		/* Urgent data pointer */
2149ef1f84bSDavid du Colombier 		ulong	wl2;
2159ef1f84bSDavid du Colombier 		uint	scale;		/* how much to right shift window */
2169ef1f84bSDavid du Colombier 					/* in xmitted packets */
2179ef1f84bSDavid du Colombier 		/* to implement tahoe and reno TCP */
2189ef1f84bSDavid du Colombier 		ulong	dupacks;	/* number of duplicate acks rcvd */
2199ef1f84bSDavid du Colombier 		ulong	partialack;
2209ef1f84bSDavid du Colombier 		int	recovery;	/* loss recovery flag */
2219ef1f84bSDavid du Colombier 		int	retransmit;	/* retransmit 1 packet @ una flag */
2229ef1f84bSDavid du Colombier 		int	rto;
2239ef1f84bSDavid du Colombier 		ulong	rxt;		/* right window marker for recovery */
2249ef1f84bSDavid du Colombier 					/* "recover" rfc3782 */
2259ef1f84bSDavid du Colombier 	} snd;
2269ef1f84bSDavid du Colombier 	struct {
2279ef1f84bSDavid du Colombier 		ulong	nxt;		/* Receive pointer to next uchar slot */
2289ef1f84bSDavid du Colombier 		ulong	wnd;		/* Receive window incoming */
2299ef1f84bSDavid du Colombier 		ulong	wsnt;		/* Last wptr sent.  important to */
2309ef1f84bSDavid du Colombier 					/* track for large bdp */
2319ef1f84bSDavid du Colombier 		ulong	wptr;
2329ef1f84bSDavid du Colombier 		ulong	urg;		/* Urgent pointer */
2339ef1f84bSDavid du Colombier 		ulong	ackptr;		/* last acked sequence */
2349ef1f84bSDavid du Colombier 		int	blocked;
2359ef1f84bSDavid du Colombier 		uint	scale;		/* how much to left shift window in */
2369ef1f84bSDavid du Colombier 					/* rcv'd packets */
2379ef1f84bSDavid du Colombier 	} rcv;
2389ef1f84bSDavid du Colombier 	ulong	iss;			/* Initial sequence number */
2399ef1f84bSDavid du Colombier 	ulong	cwind;			/* Congestion window */
2409ef1f84bSDavid du Colombier 	ulong	abcbytes;		/* appropriate byte counting rfc 3465 */
2419ef1f84bSDavid du Colombier 	uint	scale;			/* desired snd.scale */
2429ef1f84bSDavid du Colombier 	ulong	ssthresh;		/* Slow start threshold */
2439ef1f84bSDavid du Colombier 	int	resent;			/* Bytes just resent */
2449ef1f84bSDavid du Colombier 	int	irs;			/* Initial received squence */
2459ef1f84bSDavid du Colombier 	ushort	mss;			/* Maximum segment size */
2469ef1f84bSDavid du Colombier 	int	rerecv;			/* Overlap of data rerecevived */
2479ef1f84bSDavid du Colombier 	ulong	window;			/* Our receive window (queue) */
2489ef1f84bSDavid du Colombier 	uint	qscale;			/* Log2 of our receive window (queue) */
2499ef1f84bSDavid du Colombier 	uchar	backoff;		/* Exponential backoff counter */
2509ef1f84bSDavid du Colombier 	int	backedoff;		/* ms we've backed off for rexmits */
2519ef1f84bSDavid du Colombier 	uchar	flags;			/* State flags */
2529ef1f84bSDavid du Colombier 	Reseq	*reseq;			/* Resequencing queue */
2539ef1f84bSDavid du Colombier 	int	nreseq;
2549ef1f84bSDavid du Colombier 	int	reseqlen;
2559ef1f84bSDavid du Colombier 	Tcptimer	timer;			/* Activity timer */
2569ef1f84bSDavid du Colombier 	Tcptimer	acktimer;		/* Acknowledge timer */
2579ef1f84bSDavid du Colombier 	Tcptimer	rtt_timer;		/* Round trip timer */
2589ef1f84bSDavid du Colombier 	Tcptimer	katimer;		/* keep alive timer */
2599ef1f84bSDavid du Colombier 	ulong	rttseq;			/* Round trip sequence */
2609ef1f84bSDavid du Colombier 	int	srtt;			/* Smoothed round trip */
2619ef1f84bSDavid du Colombier 	int	mdev;			/* Mean deviation of round trip */
2629ef1f84bSDavid du Colombier 	int	kacounter;		/* count down for keep alive */
2639ef1f84bSDavid du Colombier 	uint	sndsyntime;		/* time syn sent */
2649ef1f84bSDavid du Colombier 	ulong	time;			/* time Finwait2 or Syn_received was sent */
2659ef1f84bSDavid du Colombier 	ulong	timeuna;		/* snd.una when time was set */
2669ef1f84bSDavid du Colombier 	int	nochecksum;		/* non-zero means don't send checksums */
2679ef1f84bSDavid du Colombier 	int	flgcnt;			/* number of flags in the sequence (FIN,SEQ) */
2689ef1f84bSDavid du Colombier 
2699ef1f84bSDavid du Colombier 	union {
2709ef1f84bSDavid du Colombier 		Tcp4hdr	tcp4hdr;
2719ef1f84bSDavid du Colombier 		Tcp6hdr	tcp6hdr;
2729ef1f84bSDavid du Colombier 	} protohdr;		/* prototype header */
2739ef1f84bSDavid du Colombier };
2749ef1f84bSDavid du Colombier 
2759ef1f84bSDavid du Colombier /*
2769ef1f84bSDavid du Colombier  *  New calls are put in limbo rather than having a conversation structure
2779ef1f84bSDavid du Colombier  *  allocated.  Thus, a SYN attack results in lots of limbo'd calls but not
2789ef1f84bSDavid du Colombier  *  any real Conv structures mucking things up.  Calls in limbo rexmit their
2799ef1f84bSDavid du Colombier  *  SYN ACK every SYNACK_RXTIMER ms up to 4 times, i.e., they disappear after 1 second.
2809ef1f84bSDavid du Colombier  *
2819ef1f84bSDavid du Colombier  *  In particular they aren't on a listener's queue so that they don't figure
2829ef1f84bSDavid du Colombier  *  in the input queue limit.
2839ef1f84bSDavid du Colombier  *
2849ef1f84bSDavid du Colombier  *  If 1/2 of a T3 was attacking SYN packets, we'ld have a permanent queue
2859ef1f84bSDavid du Colombier  *  of 70000 limbo'd calls.  Not great for a linear list but doable.  Therefore
2869ef1f84bSDavid du Colombier  *  there is no hashing of this list.
2879ef1f84bSDavid du Colombier  */
2889ef1f84bSDavid du Colombier typedef struct Limbo Limbo;
2899ef1f84bSDavid du Colombier struct Limbo
2909ef1f84bSDavid du Colombier {
2919ef1f84bSDavid du Colombier 	Limbo	*next;
2929ef1f84bSDavid du Colombier 
2939ef1f84bSDavid du Colombier 	uchar	laddr[IPaddrlen];
2949ef1f84bSDavid du Colombier 	uchar	raddr[IPaddrlen];
2959ef1f84bSDavid du Colombier 	ushort	lport;
2969ef1f84bSDavid du Colombier 	ushort	rport;
2979ef1f84bSDavid du Colombier 	ulong	irs;		/* initial received sequence */
2989ef1f84bSDavid du Colombier 	ulong	iss;		/* initial sent sequence */
2999ef1f84bSDavid du Colombier 	ushort	mss;		/* mss from the other end */
3009ef1f84bSDavid du Colombier 	ushort	rcvscale;	/* how much to scale rcvd windows */
3019ef1f84bSDavid du Colombier 	ushort	sndscale;	/* how much to scale sent windows */
3029ef1f84bSDavid du Colombier 	ulong	lastsend;	/* last time we sent a synack */
3039ef1f84bSDavid du Colombier 	uchar	version;	/* v4 or v6 */
3049ef1f84bSDavid du Colombier 	uchar	rexmits;	/* number of retransmissions */
3059ef1f84bSDavid du Colombier };
3069ef1f84bSDavid du Colombier 
3079ef1f84bSDavid du Colombier int	tcp_irtt = DEF_RTT;	/* Initial guess at round trip time */
3089ef1f84bSDavid du Colombier 
3099ef1f84bSDavid du Colombier enum {
3109ef1f84bSDavid du Colombier 	/* MIB stats */
3119ef1f84bSDavid du Colombier 	MaxConn,
3129ef1f84bSDavid du Colombier 	Mss,
3139ef1f84bSDavid du Colombier 	ActiveOpens,
3149ef1f84bSDavid du Colombier 	PassiveOpens,
3159ef1f84bSDavid du Colombier 	EstabResets,
3169ef1f84bSDavid du Colombier 	CurrEstab,
3179ef1f84bSDavid du Colombier 	InSegs,
3189ef1f84bSDavid du Colombier 	OutSegs,
3199ef1f84bSDavid du Colombier 	RetransSegs,
3209ef1f84bSDavid du Colombier 	RetransSegsSent,
3219ef1f84bSDavid du Colombier 	RetransTimeouts,
3229ef1f84bSDavid du Colombier 	InErrs,
3239ef1f84bSDavid du Colombier 	OutRsts,
3249ef1f84bSDavid du Colombier 
3259ef1f84bSDavid du Colombier 	/* non-MIB stats */
3269ef1f84bSDavid du Colombier 	CsumErrs,
3279ef1f84bSDavid du Colombier 	HlenErrs,
3289ef1f84bSDavid du Colombier 	LenErrs,
3299ef1f84bSDavid du Colombier 	Resequenced,
3309ef1f84bSDavid du Colombier 	OutOfOrder,
3319ef1f84bSDavid du Colombier 	ReseqBytelim,
3329ef1f84bSDavid du Colombier 	ReseqPktlim,
3339ef1f84bSDavid du Colombier 	Delayack,
3349ef1f84bSDavid du Colombier 	Wopenack,
3359ef1f84bSDavid du Colombier 
3369ef1f84bSDavid du Colombier 	Recovery,
3379ef1f84bSDavid du Colombier 	RecoveryDone,
3389ef1f84bSDavid du Colombier 	RecoveryRTO,
3399ef1f84bSDavid du Colombier 	RecoveryNoSeq,
3409ef1f84bSDavid du Colombier 	RecoveryCwind,
3419ef1f84bSDavid du Colombier 	RecoveryPA,
3429ef1f84bSDavid du Colombier 
3439ef1f84bSDavid du Colombier 	Nstats
3449ef1f84bSDavid du Colombier };
3459ef1f84bSDavid du Colombier 
3469ef1f84bSDavid du Colombier static char *statnames[Nstats] =
3479ef1f84bSDavid du Colombier {
3489ef1f84bSDavid du Colombier [MaxConn]	"MaxConn",
3499ef1f84bSDavid du Colombier [Mss]		"MaxSegment",
3509ef1f84bSDavid du Colombier [ActiveOpens]	"ActiveOpens",
3519ef1f84bSDavid du Colombier [PassiveOpens]	"PassiveOpens",
3529ef1f84bSDavid du Colombier [EstabResets]	"EstabResets",
3539ef1f84bSDavid du Colombier [CurrEstab]	"CurrEstab",
3549ef1f84bSDavid du Colombier [InSegs]	"InSegs",
3559ef1f84bSDavid du Colombier [OutSegs]	"OutSegs",
3569ef1f84bSDavid du Colombier [RetransSegs]	"RetransSegs",
3579ef1f84bSDavid du Colombier [RetransSegsSent]	"RetransSegsSent",
3589ef1f84bSDavid du Colombier [RetransTimeouts]	"RetransTimeouts",
3599ef1f84bSDavid du Colombier [InErrs]	"InErrs",
3609ef1f84bSDavid du Colombier [OutRsts]	"OutRsts",
3619ef1f84bSDavid du Colombier [CsumErrs]	"CsumErrs",
3629ef1f84bSDavid du Colombier [HlenErrs]	"HlenErrs",
3639ef1f84bSDavid du Colombier [LenErrs]	"LenErrs",
3649ef1f84bSDavid du Colombier [OutOfOrder]	"OutOfOrder",
3659ef1f84bSDavid du Colombier [Resequenced]	"Resequenced",
3669ef1f84bSDavid du Colombier [ReseqBytelim]	"ReseqBytelim",
3679ef1f84bSDavid du Colombier [ReseqPktlim]	"ReseqPktlim",
3689ef1f84bSDavid du Colombier [Delayack]	"Delayack",
3699ef1f84bSDavid du Colombier [Wopenack]	"Wopenack",
3709ef1f84bSDavid du Colombier 
3719ef1f84bSDavid du Colombier [Recovery]	"Recovery",
3729ef1f84bSDavid du Colombier [RecoveryDone]	"RecoveryDone",
3739ef1f84bSDavid du Colombier [RecoveryRTO]	"RecoveryRTO",
3749ef1f84bSDavid du Colombier 
3759ef1f84bSDavid du Colombier [RecoveryNoSeq]	"RecoveryNoSeq",
3769ef1f84bSDavid du Colombier [RecoveryCwind]	"RecoveryCwind",
3779ef1f84bSDavid du Colombier [RecoveryPA]	"RecoveryPA",
3789ef1f84bSDavid du Colombier };
3799ef1f84bSDavid du Colombier 
3809ef1f84bSDavid du Colombier typedef struct Tcppriv Tcppriv;
3819ef1f84bSDavid du Colombier struct Tcppriv
3829ef1f84bSDavid du Colombier {
3839ef1f84bSDavid du Colombier 	/* List of active timers */
3849ef1f84bSDavid du Colombier 	QLock 	tl;
3859ef1f84bSDavid du Colombier 	Tcptimer *timers;
3869ef1f84bSDavid du Colombier 
3879ef1f84bSDavid du Colombier 	/* hash table for matching conversations */
3889ef1f84bSDavid du Colombier 	Ipht	ht;
3899ef1f84bSDavid du Colombier 
3909ef1f84bSDavid du Colombier 	/* calls in limbo waiting for an ACK to our SYN ACK */
3919ef1f84bSDavid du Colombier 	int	nlimbo;
3929ef1f84bSDavid du Colombier 	Limbo	*lht[NLHT];
3939ef1f84bSDavid du Colombier 
3949ef1f84bSDavid du Colombier 	/* for keeping track of tcpackproc */
3959ef1f84bSDavid du Colombier 	QLock	apl;
3969ef1f84bSDavid du Colombier 	int	ackprocstarted;
3979ef1f84bSDavid du Colombier 
3989ef1f84bSDavid du Colombier 	uvlong	stats[Nstats];
3999ef1f84bSDavid du Colombier };
4009ef1f84bSDavid du Colombier 
4019ef1f84bSDavid du Colombier /*
4029ef1f84bSDavid du Colombier  *  Setting tcpporthogdefense to non-zero enables Dong Lin's
4039ef1f84bSDavid du Colombier  *  solution to hijacked systems staking out port's as a form
4049ef1f84bSDavid du Colombier  *  of DoS attack.
4059ef1f84bSDavid du Colombier  *
4069ef1f84bSDavid du Colombier  *  To avoid stateless Conv hogs, we pick a sequence number at random.  If
4079ef1f84bSDavid du Colombier  *  that number gets acked by the other end, we shut down the connection.
4089ef1f84bSDavid du Colombier  *  Look for tcpporthogdefense in the code.
4099ef1f84bSDavid du Colombier  */
4109ef1f84bSDavid du Colombier int tcpporthogdefense = 0;
4119ef1f84bSDavid du Colombier 
4129ef1f84bSDavid du Colombier static	int	addreseq(Fs*, Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
4139ef1f84bSDavid du Colombier static	int	dumpreseq(Tcpctl*);
4149ef1f84bSDavid du Colombier static	void	getreseq(Tcpctl*, Tcp*, Block**, ushort*);
4159ef1f84bSDavid du Colombier static	void	limbo(Conv*, uchar*, uchar*, Tcp*, int);
4169ef1f84bSDavid du Colombier static	void	limborexmit(Proto*);
4179ef1f84bSDavid du Colombier static	void	localclose(Conv*, char*);
4189ef1f84bSDavid du Colombier static	void	procsyn(Conv*, Tcp*);
4199ef1f84bSDavid du Colombier static	void	tcpacktimer(void*);
4209ef1f84bSDavid du Colombier static	void	tcpiput(Proto*, Ipifc*, Block*);
4219ef1f84bSDavid du Colombier static	void	tcpkeepalive(void*);
4229ef1f84bSDavid du Colombier static	void	tcpoutput(Conv*);
4239ef1f84bSDavid du Colombier static	void	tcprcvwin(Conv*);
4249ef1f84bSDavid du Colombier static	void	tcprxmit(Conv*);
4259ef1f84bSDavid du Colombier static	void	tcpsetkacounter(Tcpctl*);
4269ef1f84bSDavid du Colombier static	void	tcpsetscale(Conv*, Tcpctl*, ushort, ushort);
4279ef1f84bSDavid du Colombier static	void	tcpsettimer(Tcpctl*);
4289ef1f84bSDavid du Colombier static	void	tcpsndsyn(Conv*, Tcpctl*);
4299ef1f84bSDavid du Colombier static	void	tcpstart(Conv*, int);
4309ef1f84bSDavid du Colombier static	void	tcpsynackrtt(Conv*);
4319ef1f84bSDavid du Colombier static	void	tcptimeout(void*);
4329ef1f84bSDavid du Colombier static	int	tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
4339ef1f84bSDavid du Colombier 
4349ef1f84bSDavid du Colombier static void
tcpsetstate(Conv * s,uchar newstate)4359ef1f84bSDavid du Colombier tcpsetstate(Conv *s, uchar newstate)
4369ef1f84bSDavid du Colombier {
4379ef1f84bSDavid du Colombier 	Tcpctl *tcb;
4389ef1f84bSDavid du Colombier 	uchar oldstate;
4399ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
4409ef1f84bSDavid du Colombier 
4419ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
4429ef1f84bSDavid du Colombier 
4439ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
4449ef1f84bSDavid du Colombier 
4459ef1f84bSDavid du Colombier 	oldstate = tcb->state;
4469ef1f84bSDavid du Colombier 	if(oldstate == newstate)
4479ef1f84bSDavid du Colombier 		return;
4489ef1f84bSDavid du Colombier 
4499ef1f84bSDavid du Colombier 	if(oldstate == Established)
4509ef1f84bSDavid du Colombier 		tpriv->stats[CurrEstab]--;
4519ef1f84bSDavid du Colombier 	if(newstate == Established)
4529ef1f84bSDavid du Colombier 		tpriv->stats[CurrEstab]++;
4539ef1f84bSDavid du Colombier 
4549ef1f84bSDavid du Colombier 	switch(newstate) {
4559ef1f84bSDavid du Colombier 	case Closed:
4569ef1f84bSDavid du Colombier 		qclose(s->rq);
4579ef1f84bSDavid du Colombier 		qclose(s->wq);
4589ef1f84bSDavid du Colombier 		qclose(s->eq);
4599ef1f84bSDavid du Colombier 		break;
4609ef1f84bSDavid du Colombier 
4619ef1f84bSDavid du Colombier 	case Close_wait:		/* Remote closes */
4629ef1f84bSDavid du Colombier 		qhangup(s->rq, nil);
4639ef1f84bSDavid du Colombier 		break;
4649ef1f84bSDavid du Colombier 	}
4659ef1f84bSDavid du Colombier 
4669ef1f84bSDavid du Colombier 	tcb->state = newstate;
4679ef1f84bSDavid du Colombier 
4689ef1f84bSDavid du Colombier 	if(oldstate == Syn_sent && newstate != Closed)
4699ef1f84bSDavid du Colombier 		Fsconnected(s, nil);
4709ef1f84bSDavid du Colombier }
4719ef1f84bSDavid du Colombier 
4729ef1f84bSDavid du Colombier static char*
tcpconnect(Conv * c,char ** argv,int argc)4739ef1f84bSDavid du Colombier tcpconnect(Conv *c, char **argv, int argc)
4749ef1f84bSDavid du Colombier {
4759ef1f84bSDavid du Colombier 	char *e;
4769ef1f84bSDavid du Colombier 	Tcpctl *tcb;
4779ef1f84bSDavid du Colombier 
4789ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)(c->ptcl);
4799ef1f84bSDavid du Colombier 	if(tcb->state != Closed)
4809ef1f84bSDavid du Colombier 		return Econinuse;
4819ef1f84bSDavid du Colombier 
4829ef1f84bSDavid du Colombier 	e = Fsstdconnect(c, argv, argc);
4839ef1f84bSDavid du Colombier 	if(e != nil)
4849ef1f84bSDavid du Colombier 		return e;
4859ef1f84bSDavid du Colombier 	tcpstart(c, TCP_CONNECT);
4869ef1f84bSDavid du Colombier 
4879ef1f84bSDavid du Colombier 	return nil;
4889ef1f84bSDavid du Colombier }
4899ef1f84bSDavid du Colombier 
4909ef1f84bSDavid du Colombier static int
tcpstate(Conv * c,char * state,int n)4919ef1f84bSDavid du Colombier tcpstate(Conv *c, char *state, int n)
4929ef1f84bSDavid du Colombier {
4939ef1f84bSDavid du Colombier 	Tcpctl *s;
4949ef1f84bSDavid du Colombier 
4959ef1f84bSDavid du Colombier 	s = (Tcpctl*)(c->ptcl);
4969ef1f84bSDavid du Colombier 
4979ef1f84bSDavid du Colombier 	return snprint(state, n,
4989ef1f84bSDavid du Colombier 		"%s qin %d qout %d rq %d.%d srtt %d mdev %d sst %lud cwin %lud "
4999ef1f84bSDavid du Colombier 		"swin %lud>>%d rwin %lud>>%d qscale %d timer.start %d "
5009ef1f84bSDavid du Colombier 		"timer.count %d rerecv %d katimer.start %d katimer.count %d\n",
5019ef1f84bSDavid du Colombier 		tcpstates[s->state],
5029ef1f84bSDavid du Colombier 		c->rq ? qlen(c->rq) : 0,
5039ef1f84bSDavid du Colombier 		c->wq ? qlen(c->wq) : 0,
5049ef1f84bSDavid du Colombier 		s->nreseq, s->reseqlen,
5059ef1f84bSDavid du Colombier 		s->srtt, s->mdev, s->ssthresh,
5069ef1f84bSDavid du Colombier 		s->cwind, s->snd.wnd, s->rcv.scale, s->rcv.wnd, s->snd.scale,
5079ef1f84bSDavid du Colombier 		s->qscale,
5089ef1f84bSDavid du Colombier 		s->timer.start, s->timer.count, s->rerecv,
5099ef1f84bSDavid du Colombier 		s->katimer.start, s->katimer.count);
5109ef1f84bSDavid du Colombier }
5119ef1f84bSDavid du Colombier 
5129ef1f84bSDavid du Colombier static int
tcpinuse(Conv * c)5139ef1f84bSDavid du Colombier tcpinuse(Conv *c)
5149ef1f84bSDavid du Colombier {
5159ef1f84bSDavid du Colombier 	Tcpctl *s;
5169ef1f84bSDavid du Colombier 
5179ef1f84bSDavid du Colombier 	s = (Tcpctl*)(c->ptcl);
5189ef1f84bSDavid du Colombier 	return s->state != Closed;
5199ef1f84bSDavid du Colombier }
5209ef1f84bSDavid du Colombier 
5219ef1f84bSDavid du Colombier static char*
tcpannounce(Conv * c,char ** argv,int argc)5229ef1f84bSDavid du Colombier tcpannounce(Conv *c, char **argv, int argc)
5239ef1f84bSDavid du Colombier {
5249ef1f84bSDavid du Colombier 	char *e;
5259ef1f84bSDavid du Colombier 	Tcpctl *tcb;
5269ef1f84bSDavid du Colombier 
5279ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)(c->ptcl);
5289ef1f84bSDavid du Colombier 	if(tcb->state != Closed)
5299ef1f84bSDavid du Colombier 		return Econinuse;
5309ef1f84bSDavid du Colombier 
5319ef1f84bSDavid du Colombier 	e = Fsstdannounce(c, argv, argc);
5329ef1f84bSDavid du Colombier 	if(e != nil)
5339ef1f84bSDavid du Colombier 		return e;
5349ef1f84bSDavid du Colombier 	tcpstart(c, TCP_LISTEN);
5359ef1f84bSDavid du Colombier 	Fsconnected(c, nil);
5369ef1f84bSDavid du Colombier 
5379ef1f84bSDavid du Colombier 	return nil;
5389ef1f84bSDavid du Colombier }
5399ef1f84bSDavid du Colombier 
54040398a43SDavid du Colombier static void
tcpclosestate(Conv * c,Tcpctl * tcb,int state)54140398a43SDavid du Colombier tcpclosestate(Conv *c, Tcpctl *tcb, int state)
54240398a43SDavid du Colombier {
54340398a43SDavid du Colombier 	tcb->flgcnt++;
54440398a43SDavid du Colombier 	tcb->snd.nxt++;
54540398a43SDavid du Colombier 	tcpsetstate(c, state);
54640398a43SDavid du Colombier 	tcpoutput(c);
54740398a43SDavid du Colombier }
54840398a43SDavid du Colombier 
54940398a43SDavid du Colombier /* close the output half of a tcp connection */
55040398a43SDavid du Colombier static char *
tcpxmitclose(Conv * c)55140398a43SDavid du Colombier tcpxmitclose(Conv *c)
55240398a43SDavid du Colombier {
55340398a43SDavid du Colombier 	Tcpctl *tcb;
55440398a43SDavid du Colombier 
55540398a43SDavid du Colombier 	qhangup(c->wq, nil);
55640398a43SDavid du Colombier 
55740398a43SDavid du Colombier 	tcb = (Tcpctl*)c->ptcl;
55840398a43SDavid du Colombier 	switch(tcb->state) {
55940398a43SDavid du Colombier 	case Listen:
56040398a43SDavid du Colombier 		/*
56140398a43SDavid du Colombier 		 *  reset any incoming calls to this listener
56240398a43SDavid du Colombier 		 */
56340398a43SDavid du Colombier 		Fsconnected(c, "Hangup");
56440398a43SDavid du Colombier 		/* fall through */
56540398a43SDavid du Colombier 	case Closed:
56640398a43SDavid du Colombier 	case Syn_sent:
56740398a43SDavid du Colombier 		localclose(c, nil);
56840398a43SDavid du Colombier 		break;
56940398a43SDavid du Colombier 	case Syn_received:
57040398a43SDavid du Colombier 	case Established:
57140398a43SDavid du Colombier 	case Close_wait:
57240398a43SDavid du Colombier 		tcpclosestate(c, tcb, tcb->state);
57340398a43SDavid du Colombier 		break;
57440398a43SDavid du Colombier 	}
57540398a43SDavid du Colombier 	return nil;
57640398a43SDavid du Colombier }
57740398a43SDavid du Colombier 
5789ef1f84bSDavid du Colombier /*
5799ef1f84bSDavid du Colombier  *  tcpclose is always called with the q locked
5809ef1f84bSDavid du Colombier  */
5819ef1f84bSDavid du Colombier static void
tcpclose(Conv * c)5829ef1f84bSDavid du Colombier tcpclose(Conv *c)
5839ef1f84bSDavid du Colombier {
5849ef1f84bSDavid du Colombier 	Tcpctl *tcb;
5859ef1f84bSDavid du Colombier 
5869ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)c->ptcl;
5879ef1f84bSDavid du Colombier 
5889ef1f84bSDavid du Colombier 	qhangup(c->rq, nil);
5899ef1f84bSDavid du Colombier 	qhangup(c->wq, nil);
5909ef1f84bSDavid du Colombier 	qhangup(c->eq, nil);
5919ef1f84bSDavid du Colombier 	qflush(c->rq);
5929ef1f84bSDavid du Colombier 
5939ef1f84bSDavid du Colombier 	switch(tcb->state) {
5949ef1f84bSDavid du Colombier 	case Listen:
5959ef1f84bSDavid du Colombier 		/*
5969ef1f84bSDavid du Colombier 		 *  reset any incoming calls to this listener
5979ef1f84bSDavid du Colombier 		 */
5989ef1f84bSDavid du Colombier 		Fsconnected(c, "Hangup");
59940398a43SDavid du Colombier 		/* fall through */
6009ef1f84bSDavid du Colombier 	case Closed:
6019ef1f84bSDavid du Colombier 	case Syn_sent:
6029ef1f84bSDavid du Colombier 		localclose(c, nil);
6039ef1f84bSDavid du Colombier 		break;
6049ef1f84bSDavid du Colombier 	case Syn_received:
6059ef1f84bSDavid du Colombier 	case Established:
60640398a43SDavid du Colombier 		tcpclosestate(c, tcb, Finwait1);
6079ef1f84bSDavid du Colombier 		break;
6089ef1f84bSDavid du Colombier 	case Close_wait:
60940398a43SDavid du Colombier 		tcpclosestate(c, tcb, Last_ack);
6109ef1f84bSDavid du Colombier 		break;
6119ef1f84bSDavid du Colombier 	}
6129ef1f84bSDavid du Colombier }
6139ef1f84bSDavid du Colombier 
6149ef1f84bSDavid du Colombier static void
tcpkick(void * x)6159ef1f84bSDavid du Colombier tcpkick(void *x)
6169ef1f84bSDavid du Colombier {
6179ef1f84bSDavid du Colombier 	Conv *s = x;
6189ef1f84bSDavid du Colombier 	Tcpctl *tcb;
6199ef1f84bSDavid du Colombier 
6209ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6219ef1f84bSDavid du Colombier 
6229ef1f84bSDavid du Colombier 	if(waserror()){
6239ef1f84bSDavid du Colombier 		qunlock(s);
6249ef1f84bSDavid du Colombier 		nexterror();
6259ef1f84bSDavid du Colombier 	}
6269ef1f84bSDavid du Colombier 	qlock(s);
6279ef1f84bSDavid du Colombier 
6289ef1f84bSDavid du Colombier 	switch(tcb->state) {
6299ef1f84bSDavid du Colombier 	case Syn_sent:
6309ef1f84bSDavid du Colombier 	case Syn_received:
6319ef1f84bSDavid du Colombier 	case Established:
6329ef1f84bSDavid du Colombier 	case Close_wait:
6339ef1f84bSDavid du Colombier 		/*
6349ef1f84bSDavid du Colombier 		 * Push data
6359ef1f84bSDavid du Colombier 		 */
6369ef1f84bSDavid du Colombier 		tcpoutput(s);
6379ef1f84bSDavid du Colombier 		break;
6389ef1f84bSDavid du Colombier 	default:
6399ef1f84bSDavid du Colombier 		localclose(s, "Hangup");
6409ef1f84bSDavid du Colombier 		break;
6419ef1f84bSDavid du Colombier 	}
6429ef1f84bSDavid du Colombier 
6439ef1f84bSDavid du Colombier 	qunlock(s);
6449ef1f84bSDavid du Colombier 	poperror();
6459ef1f84bSDavid du Colombier }
6469ef1f84bSDavid du Colombier 
6479ef1f84bSDavid du Colombier static int seq_lt(ulong, ulong);
6489ef1f84bSDavid du Colombier 
6499ef1f84bSDavid du Colombier static void
tcprcvwin(Conv * s)6509ef1f84bSDavid du Colombier tcprcvwin(Conv *s)				/* Call with tcb locked */
6519ef1f84bSDavid du Colombier {
6529ef1f84bSDavid du Colombier 	int w;
6539ef1f84bSDavid du Colombier 	Tcpctl *tcb;
6549ef1f84bSDavid du Colombier 
6559ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6569ef1f84bSDavid du Colombier 	w = tcb->window - qlen(s->rq);
6579ef1f84bSDavid du Colombier 	if(w < 0)
6589ef1f84bSDavid du Colombier 		w = 0;
6599ef1f84bSDavid du Colombier 	/* RFC 1122 § 4.2.2.17 do not move right edge of window left */
6609ef1f84bSDavid du Colombier 	if(seq_lt(tcb->rcv.nxt + w, tcb->rcv.wptr))
6619ef1f84bSDavid du Colombier 		w = tcb->rcv.wptr - tcb->rcv.nxt;
6629ef1f84bSDavid du Colombier 	if(w != tcb->rcv.wnd)
6639ef1f84bSDavid du Colombier 	if(w>>tcb->rcv.scale == 0 || tcb->window > 4*tcb->mss && w < tcb->mss/4){
6649ef1f84bSDavid du Colombier 		tcb->rcv.blocked = 1;
6659ef1f84bSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcprcvwin: window %lud qlen %d ws %ud lport %d\n",
6669ef1f84bSDavid du Colombier 			tcb->window, qlen(s->rq), tcb->rcv.scale, s->lport);
6679ef1f84bSDavid du Colombier 	}
6689ef1f84bSDavid du Colombier 	tcb->rcv.wnd = w;
6699ef1f84bSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt + w;
6709ef1f84bSDavid du Colombier }
6719ef1f84bSDavid du Colombier 
6729ef1f84bSDavid du Colombier static void
tcpacktimer(void * v)6739ef1f84bSDavid du Colombier tcpacktimer(void *v)
6749ef1f84bSDavid du Colombier {
6759ef1f84bSDavid du Colombier 	Tcpctl *tcb;
6769ef1f84bSDavid du Colombier 	Conv *s;
6779ef1f84bSDavid du Colombier 
6789ef1f84bSDavid du Colombier 	s = v;
6799ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
6809ef1f84bSDavid du Colombier 
6819ef1f84bSDavid du Colombier 	if(waserror()){
6829ef1f84bSDavid du Colombier 		qunlock(s);
6839ef1f84bSDavid du Colombier 		nexterror();
6849ef1f84bSDavid du Colombier 	}
6859ef1f84bSDavid du Colombier 	qlock(s);
6869ef1f84bSDavid du Colombier 	if(tcb->state != Closed){
6879ef1f84bSDavid du Colombier 		tcb->flags |= FORCE;
6889ef1f84bSDavid du Colombier 		tcpoutput(s);
6899ef1f84bSDavid du Colombier 	}
6909ef1f84bSDavid du Colombier 	qunlock(s);
6919ef1f84bSDavid du Colombier 	poperror();
6929ef1f84bSDavid du Colombier }
6939ef1f84bSDavid du Colombier 
6949ef1f84bSDavid du Colombier static void
tcpcongestion(Tcpctl * tcb)6959ef1f84bSDavid du Colombier tcpcongestion(Tcpctl *tcb)
6969ef1f84bSDavid du Colombier {
6979ef1f84bSDavid du Colombier 	ulong inflight;
6989ef1f84bSDavid du Colombier 
6999ef1f84bSDavid du Colombier 	inflight = tcb->snd.nxt - tcb->snd.una;
7009ef1f84bSDavid du Colombier 	if(inflight > tcb->cwind)
7019ef1f84bSDavid du Colombier 		inflight = tcb->cwind;
7029ef1f84bSDavid du Colombier 	tcb->ssthresh = inflight / 2;
7039ef1f84bSDavid du Colombier 	if(tcb->ssthresh < 2*tcb->mss)
7049ef1f84bSDavid du Colombier 		tcb->ssthresh = 2*tcb->mss;
7059ef1f84bSDavid du Colombier }
7069ef1f84bSDavid du Colombier 
7079ef1f84bSDavid du Colombier enum {
7089ef1f84bSDavid du Colombier 	L	= 2,	/* aggressive slow start; legal values ∈ (1.0, 2.0) */
7099ef1f84bSDavid du Colombier };
7109ef1f84bSDavid du Colombier 
7119ef1f84bSDavid du Colombier static void
tcpabcincr(Tcpctl * tcb,uint acked)7129ef1f84bSDavid du Colombier tcpabcincr(Tcpctl *tcb, uint acked)
7139ef1f84bSDavid du Colombier {
7149ef1f84bSDavid du Colombier 	uint limit;
7159ef1f84bSDavid du Colombier 
7169ef1f84bSDavid du Colombier 	tcb->abcbytes += acked;
7179ef1f84bSDavid du Colombier 	if(tcb->cwind < tcb->ssthresh){
7189ef1f84bSDavid du Colombier 		/* slow start */
7199ef1f84bSDavid du Colombier 		if(tcb->snd.rto)
7209ef1f84bSDavid du Colombier 			limit = tcb->mss;
7219ef1f84bSDavid du Colombier 		else
7229ef1f84bSDavid du Colombier 			limit = L*tcb->mss;
7239ef1f84bSDavid du Colombier 		tcb->cwind += MIN(tcb->abcbytes, limit);
7249ef1f84bSDavid du Colombier 		tcb->abcbytes = 0;
7259ef1f84bSDavid du Colombier 	} else {
7269ef1f84bSDavid du Colombier 		tcb->snd.rto = 0;
7279ef1f84bSDavid du Colombier 		/* avoidance */
7289ef1f84bSDavid du Colombier 		if(tcb->abcbytes >= tcb->cwind){
7299ef1f84bSDavid du Colombier 			tcb->abcbytes -= tcb->cwind;
7309ef1f84bSDavid du Colombier 			tcb->cwind += tcb->mss;
7319ef1f84bSDavid du Colombier 		}
7329ef1f84bSDavid du Colombier 	}
7339ef1f84bSDavid du Colombier }
7349ef1f84bSDavid du Colombier 
7359ef1f84bSDavid du Colombier static void
tcpcreate(Conv * c)7369ef1f84bSDavid du Colombier tcpcreate(Conv *c)
7379ef1f84bSDavid du Colombier {
7389ef1f84bSDavid du Colombier 	c->rq = qopen(QMAX, Qcoalesce, tcpacktimer, c);
7399ef1f84bSDavid du Colombier 	c->wq = qopen(QMAX, Qkick, tcpkick, c);
7409ef1f84bSDavid du Colombier }
7419ef1f84bSDavid du Colombier 
7429ef1f84bSDavid du Colombier static void
timerstate(Tcppriv * priv,Tcptimer * t,int newstate)7439ef1f84bSDavid du Colombier timerstate(Tcppriv *priv, Tcptimer *t, int newstate)
7449ef1f84bSDavid du Colombier {
7459ef1f84bSDavid du Colombier 	if(newstate != TcptimerON){
7469ef1f84bSDavid du Colombier 		if(t->state == TcptimerON){
7479ef1f84bSDavid du Colombier 			/* unchain */
7489ef1f84bSDavid du Colombier 			if(priv->timers == t){
7499ef1f84bSDavid du Colombier 				priv->timers = t->next;
7509ef1f84bSDavid du Colombier 				if(t->prev != nil)
7519ef1f84bSDavid du Colombier 					panic("timerstate1");
7529ef1f84bSDavid du Colombier 			}
7539ef1f84bSDavid du Colombier 			if(t->next)
7549ef1f84bSDavid du Colombier 				t->next->prev = t->prev;
7559ef1f84bSDavid du Colombier 			if(t->prev)
7569ef1f84bSDavid du Colombier 				t->prev->next = t->next;
7579ef1f84bSDavid du Colombier 			t->next = t->prev = nil;
7589ef1f84bSDavid du Colombier 		}
7599ef1f84bSDavid du Colombier 	} else {
7609ef1f84bSDavid du Colombier 		if(t->state != TcptimerON){
7619ef1f84bSDavid du Colombier 			/* chain */
7629ef1f84bSDavid du Colombier 			if(t->prev != nil || t->next != nil)
7639ef1f84bSDavid du Colombier 				panic("timerstate2");
7649ef1f84bSDavid du Colombier 			t->prev = nil;
7659ef1f84bSDavid du Colombier 			t->next = priv->timers;
7669ef1f84bSDavid du Colombier 			if(t->next)
7679ef1f84bSDavid du Colombier 				t->next->prev = t;
7689ef1f84bSDavid du Colombier 			priv->timers = t;
7699ef1f84bSDavid du Colombier 		}
7709ef1f84bSDavid du Colombier 	}
7719ef1f84bSDavid du Colombier 	t->state = newstate;
7729ef1f84bSDavid du Colombier }
7739ef1f84bSDavid du Colombier 
7749ef1f84bSDavid du Colombier static void
tcpackproc(void * a)7759ef1f84bSDavid du Colombier tcpackproc(void *a)
7769ef1f84bSDavid du Colombier {
7779ef1f84bSDavid du Colombier 	Tcptimer *t, *tp, *timeo;
7789ef1f84bSDavid du Colombier 	Proto *tcp;
7799ef1f84bSDavid du Colombier 	Tcppriv *priv;
7809ef1f84bSDavid du Colombier 	int loop;
7819ef1f84bSDavid du Colombier 
7829ef1f84bSDavid du Colombier 	tcp = a;
7839ef1f84bSDavid du Colombier 	priv = tcp->priv;
7849ef1f84bSDavid du Colombier 
7859ef1f84bSDavid du Colombier 	for(;;) {
7869ef1f84bSDavid du Colombier 		tsleep(&up->sleep, return0, 0, MSPTICK);
7879ef1f84bSDavid du Colombier 
7889ef1f84bSDavid du Colombier 		qlock(&priv->tl);
7899ef1f84bSDavid du Colombier 		timeo = nil;
7909ef1f84bSDavid du Colombier 		loop = 0;
7919ef1f84bSDavid du Colombier 		for(t = priv->timers; t != nil; t = tp) {
7929ef1f84bSDavid du Colombier 			if(loop++ > 10000)
7939ef1f84bSDavid du Colombier 				panic("tcpackproc1");
7949ef1f84bSDavid du Colombier 			tp = t->next;
7959ef1f84bSDavid du Colombier  			if(t->state == TcptimerON) {
7969ef1f84bSDavid du Colombier 				t->count--;
7979ef1f84bSDavid du Colombier 				if(t->count == 0) {
7989ef1f84bSDavid du Colombier 					timerstate(priv, t, TcptimerDONE);
7999ef1f84bSDavid du Colombier 					t->readynext = timeo;
8009ef1f84bSDavid du Colombier 					timeo = t;
8019ef1f84bSDavid du Colombier 				}
8029ef1f84bSDavid du Colombier 			}
8039ef1f84bSDavid du Colombier 		}
8049ef1f84bSDavid du Colombier 		qunlock(&priv->tl);
8059ef1f84bSDavid du Colombier 
8069ef1f84bSDavid du Colombier 		loop = 0;
8079ef1f84bSDavid du Colombier 		for(t = timeo; t != nil; t = t->readynext) {
8089ef1f84bSDavid du Colombier 			if(loop++ > 10000)
8099ef1f84bSDavid du Colombier 				panic("tcpackproc2");
8109ef1f84bSDavid du Colombier 			if(t->state == TcptimerDONE && t->func != nil && !waserror()){
8119ef1f84bSDavid du Colombier 				(*t->func)(t->arg);
8129ef1f84bSDavid du Colombier 				poperror();
8139ef1f84bSDavid du Colombier 			}
8149ef1f84bSDavid du Colombier 		}
8159ef1f84bSDavid du Colombier 
8169ef1f84bSDavid du Colombier 		limborexmit(tcp);
8179ef1f84bSDavid du Colombier 	}
8189ef1f84bSDavid du Colombier }
8199ef1f84bSDavid du Colombier 
8209ef1f84bSDavid du Colombier static void
tcpgo(Tcppriv * priv,Tcptimer * t)8219ef1f84bSDavid du Colombier tcpgo(Tcppriv *priv, Tcptimer *t)
8229ef1f84bSDavid du Colombier {
8239ef1f84bSDavid du Colombier 	if(t == nil || t->start == 0)
8249ef1f84bSDavid du Colombier 		return;
8259ef1f84bSDavid du Colombier 
8269ef1f84bSDavid du Colombier 	qlock(&priv->tl);
8279ef1f84bSDavid du Colombier 	t->count = t->start;
8289ef1f84bSDavid du Colombier 	timerstate(priv, t, TcptimerON);
8299ef1f84bSDavid du Colombier 	qunlock(&priv->tl);
8309ef1f84bSDavid du Colombier }
8319ef1f84bSDavid du Colombier 
8329ef1f84bSDavid du Colombier static void
tcphalt(Tcppriv * priv,Tcptimer * t)8339ef1f84bSDavid du Colombier tcphalt(Tcppriv *priv, Tcptimer *t)
8349ef1f84bSDavid du Colombier {
8359ef1f84bSDavid du Colombier 	if(t == nil)
8369ef1f84bSDavid du Colombier 		return;
8379ef1f84bSDavid du Colombier 
8389ef1f84bSDavid du Colombier 	qlock(&priv->tl);
8399ef1f84bSDavid du Colombier 	timerstate(priv, t, TcptimerOFF);
8409ef1f84bSDavid du Colombier 	qunlock(&priv->tl);
8419ef1f84bSDavid du Colombier }
8429ef1f84bSDavid du Colombier 
8439ef1f84bSDavid du Colombier static int
backoff(int n)8449ef1f84bSDavid du Colombier backoff(int n)
8459ef1f84bSDavid du Colombier {
8469ef1f84bSDavid du Colombier 	return 1 << n;
8479ef1f84bSDavid du Colombier }
8489ef1f84bSDavid du Colombier 
8499ef1f84bSDavid du Colombier static void
localclose(Conv * s,char * reason)8509ef1f84bSDavid du Colombier localclose(Conv *s, char *reason)	/* called with tcb locked */
8519ef1f84bSDavid du Colombier {
8529ef1f84bSDavid du Colombier 	Tcpctl *tcb;
8539ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
8549ef1f84bSDavid du Colombier 
8559ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
8569ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
8579ef1f84bSDavid du Colombier 
8589ef1f84bSDavid du Colombier 	iphtrem(&tpriv->ht, s);
8599ef1f84bSDavid du Colombier 
8609ef1f84bSDavid du Colombier 	tcphalt(tpriv, &tcb->timer);
8619ef1f84bSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
8629ef1f84bSDavid du Colombier 	tcphalt(tpriv, &tcb->acktimer);
8639ef1f84bSDavid du Colombier 	tcphalt(tpriv, &tcb->katimer);
8649ef1f84bSDavid du Colombier 
8659ef1f84bSDavid du Colombier 	/* Flush reassembly queue; nothing more can arrive */
8669ef1f84bSDavid du Colombier 	dumpreseq(tcb);
8679ef1f84bSDavid du Colombier 
8689ef1f84bSDavid du Colombier 	if(tcb->state == Syn_sent)
8699ef1f84bSDavid du Colombier 		Fsconnected(s, reason);
8709ef1f84bSDavid du Colombier 	if(s->state == Announced)
8719ef1f84bSDavid du Colombier 		wakeup(&s->listenr);
8729ef1f84bSDavid du Colombier 
8739ef1f84bSDavid du Colombier 	qhangup(s->rq, reason);
8749ef1f84bSDavid du Colombier 	qhangup(s->wq, reason);
8759ef1f84bSDavid du Colombier 
8769ef1f84bSDavid du Colombier 	tcpsetstate(s, Closed);
8779ef1f84bSDavid du Colombier }
8789ef1f84bSDavid du Colombier 
8799ef1f84bSDavid du Colombier /* mtu (- TCP + IP hdr len) of 1st hop */
8809ef1f84bSDavid du Colombier static int
tcpmtu(Proto * tcp,uchar * addr,int version,uint * scale)8819ef1f84bSDavid du Colombier tcpmtu(Proto *tcp, uchar *addr, int version, uint *scale)
8829ef1f84bSDavid du Colombier {
8839ef1f84bSDavid du Colombier 	Ipifc *ifc;
8849ef1f84bSDavid du Colombier 	int mtu;
8859ef1f84bSDavid du Colombier 
8869ef1f84bSDavid du Colombier 	ifc = findipifc(tcp->f, addr, 0);
8879ef1f84bSDavid du Colombier 	switch(version){
8889ef1f84bSDavid du Colombier 	default:
8899ef1f84bSDavid du Colombier 	case V4:
8909ef1f84bSDavid du Colombier 		mtu = DEF_MSS;
8919ef1f84bSDavid du Colombier 		if(ifc != nil)
8929ef1f84bSDavid du Colombier 			mtu = ifc->maxtu - ifc->medium->hsize - (TCP4_PKT + TCP4_HDRSIZE);
8939ef1f84bSDavid du Colombier 		break;
8949ef1f84bSDavid du Colombier 	case V6:
8959ef1f84bSDavid du Colombier 		mtu = DEF_MSS6;
8969ef1f84bSDavid du Colombier 		if(ifc != nil)
8979ef1f84bSDavid du Colombier 			mtu = ifc->maxtu - ifc->medium->hsize - (TCP6_PKT + TCP6_HDRSIZE);
8989ef1f84bSDavid du Colombier 		break;
8999ef1f84bSDavid du Colombier 	}
9009ef1f84bSDavid du Colombier 	/*
9019ef1f84bSDavid du Colombier 	 * set the ws.  it doesn't commit us to anything.
9029ef1f84bSDavid du Colombier 	 * ws is the ultimate limit to the bandwidth-delay product.
9039ef1f84bSDavid du Colombier 	 */
9049ef1f84bSDavid du Colombier 	*scale = Defadvscale;
9059ef1f84bSDavid du Colombier 
9069ef1f84bSDavid du Colombier 	return mtu;
9079ef1f84bSDavid du Colombier }
9089ef1f84bSDavid du Colombier 
9099ef1f84bSDavid du Colombier static void
inittcpctl(Conv * s,int mode)9109ef1f84bSDavid du Colombier inittcpctl(Conv *s, int mode)
9119ef1f84bSDavid du Colombier {
9129ef1f84bSDavid du Colombier 	Tcpctl *tcb;
9139ef1f84bSDavid du Colombier 	Tcp4hdr* h4;
9149ef1f84bSDavid du Colombier 	Tcp6hdr* h6;
9159ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
9169ef1f84bSDavid du Colombier 	int mss;
9179ef1f84bSDavid du Colombier 
9189ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
9199ef1f84bSDavid du Colombier 
9209ef1f84bSDavid du Colombier 	memset(tcb, 0, sizeof(Tcpctl));
9219ef1f84bSDavid du Colombier 
9229ef1f84bSDavid du Colombier 	tcb->ssthresh = QMAX;			/* reset by tcpsetscale() */
9239ef1f84bSDavid du Colombier 	tcb->srtt = tcp_irtt<<LOGAGAIN;
9249ef1f84bSDavid du Colombier 	tcb->mdev = 0;
9259ef1f84bSDavid du Colombier 
9269ef1f84bSDavid du Colombier 	/* setup timers */
9279ef1f84bSDavid du Colombier 	tcb->timer.start = tcp_irtt / MSPTICK;
9289ef1f84bSDavid du Colombier 	tcb->timer.func = tcptimeout;
9299ef1f84bSDavid du Colombier 	tcb->timer.arg = s;
9309ef1f84bSDavid du Colombier 	tcb->rtt_timer.start = MAX_TIME;
9319ef1f84bSDavid du Colombier 	tcb->acktimer.start = TCP_ACK / MSPTICK;
9329ef1f84bSDavid du Colombier 	tcb->acktimer.func = tcpacktimer;
9339ef1f84bSDavid du Colombier 	tcb->acktimer.arg = s;
9349ef1f84bSDavid du Colombier 	tcb->katimer.start = DEF_KAT / MSPTICK;
9359ef1f84bSDavid du Colombier 	tcb->katimer.func = tcpkeepalive;
9369ef1f84bSDavid du Colombier 	tcb->katimer.arg = s;
9379ef1f84bSDavid du Colombier 
9389ef1f84bSDavid du Colombier 	mss = DEF_MSS;
9399ef1f84bSDavid du Colombier 
9409ef1f84bSDavid du Colombier 	/* create a prototype(pseudo) header */
9419ef1f84bSDavid du Colombier 	if(mode != TCP_LISTEN){
9429ef1f84bSDavid du Colombier 		if(ipcmp(s->laddr, IPnoaddr) == 0)
9439ef1f84bSDavid du Colombier 			findlocalip(s->p->f, s->laddr, s->raddr);
9449ef1f84bSDavid du Colombier 
9459ef1f84bSDavid du Colombier 		switch(s->ipversion){
9469ef1f84bSDavid du Colombier 		case V4:
9479ef1f84bSDavid du Colombier 			h4 = &tcb->protohdr.tcp4hdr;
9489ef1f84bSDavid du Colombier 			memset(h4, 0, sizeof(*h4));
9499ef1f84bSDavid du Colombier 			h4->proto = IP_TCPPROTO;
9509ef1f84bSDavid du Colombier 			hnputs(h4->tcpsport, s->lport);
9519ef1f84bSDavid du Colombier 			hnputs(h4->tcpdport, s->rport);
9529ef1f84bSDavid du Colombier 			v6tov4(h4->tcpsrc, s->laddr);
9539ef1f84bSDavid du Colombier 			v6tov4(h4->tcpdst, s->raddr);
9549ef1f84bSDavid du Colombier 			break;
9559ef1f84bSDavid du Colombier 		case V6:
9569ef1f84bSDavid du Colombier 			h6 = &tcb->protohdr.tcp6hdr;
9579ef1f84bSDavid du Colombier 			memset(h6, 0, sizeof(*h6));
9589ef1f84bSDavid du Colombier 			h6->proto = IP_TCPPROTO;
9599ef1f84bSDavid du Colombier 			hnputs(h6->tcpsport, s->lport);
9609ef1f84bSDavid du Colombier 			hnputs(h6->tcpdport, s->rport);
9619ef1f84bSDavid du Colombier 			ipmove(h6->tcpsrc, s->laddr);
9629ef1f84bSDavid du Colombier 			ipmove(h6->tcpdst, s->raddr);
9639ef1f84bSDavid du Colombier 			mss = DEF_MSS6;
9649ef1f84bSDavid du Colombier 			break;
9659ef1f84bSDavid du Colombier 		default:
9669ef1f84bSDavid du Colombier 			panic("inittcpctl: version %d", s->ipversion);
9679ef1f84bSDavid du Colombier 		}
9689ef1f84bSDavid du Colombier 	}
9699ef1f84bSDavid du Colombier 
9709ef1f84bSDavid du Colombier 	tcb->mss = tcb->cwind = mss;
9719ef1f84bSDavid du Colombier 	tcb->abcbytes = 0;
9729ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
9739ef1f84bSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
9749ef1f84bSDavid du Colombier 
9759ef1f84bSDavid du Colombier 	/* default is no window scaling */
9769ef1f84bSDavid du Colombier 	tcpsetscale(s, tcb, 0, 0);
9779ef1f84bSDavid du Colombier }
9789ef1f84bSDavid du Colombier 
9799ef1f84bSDavid du Colombier /*
9809ef1f84bSDavid du Colombier  *  called with s qlocked
9819ef1f84bSDavid du Colombier  */
9829ef1f84bSDavid du Colombier static void
tcpstart(Conv * s,int mode)9839ef1f84bSDavid du Colombier tcpstart(Conv *s, int mode)
9849ef1f84bSDavid du Colombier {
9859ef1f84bSDavid du Colombier 	Tcpctl *tcb;
9869ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
9879ef1f84bSDavid du Colombier 	char kpname[KNAMELEN];
9889ef1f84bSDavid du Colombier 
9899ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
9909ef1f84bSDavid du Colombier 
9919ef1f84bSDavid du Colombier 	if(tpriv->ackprocstarted == 0){
9929ef1f84bSDavid du Colombier 		qlock(&tpriv->apl);
9939ef1f84bSDavid du Colombier 		if(tpriv->ackprocstarted == 0){
9949ef1f84bSDavid du Colombier 			snprint(kpname, sizeof kpname, "#I%dtcpack", s->p->f->dev);
9959ef1f84bSDavid du Colombier 			kproc(kpname, tcpackproc, s->p);
9969ef1f84bSDavid du Colombier 			tpriv->ackprocstarted = 1;
9979ef1f84bSDavid du Colombier 		}
9989ef1f84bSDavid du Colombier 		qunlock(&tpriv->apl);
9999ef1f84bSDavid du Colombier 	}
10009ef1f84bSDavid du Colombier 
10019ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
10029ef1f84bSDavid du Colombier 
10039ef1f84bSDavid du Colombier 	inittcpctl(s, mode);
10049ef1f84bSDavid du Colombier 
10059ef1f84bSDavid du Colombier 	iphtadd(&tpriv->ht, s);
10069ef1f84bSDavid du Colombier 	switch(mode) {
10079ef1f84bSDavid du Colombier 	case TCP_LISTEN:
10089ef1f84bSDavid du Colombier 		tpriv->stats[PassiveOpens]++;
10099ef1f84bSDavid du Colombier 		tcb->flags |= CLONE;
10109ef1f84bSDavid du Colombier 		tcpsetstate(s, Listen);
10119ef1f84bSDavid du Colombier 		break;
10129ef1f84bSDavid du Colombier 
10139ef1f84bSDavid du Colombier 	case TCP_CONNECT:
10149ef1f84bSDavid du Colombier 		tpriv->stats[ActiveOpens]++;
10159ef1f84bSDavid du Colombier 		tcb->flags |= ACTIVE;
10169ef1f84bSDavid du Colombier 		tcpsndsyn(s, tcb);
10179ef1f84bSDavid du Colombier 		tcpsetstate(s, Syn_sent);
10189ef1f84bSDavid du Colombier 		tcpoutput(s);
10199ef1f84bSDavid du Colombier 		break;
10209ef1f84bSDavid du Colombier 	}
10219ef1f84bSDavid du Colombier }
10229ef1f84bSDavid du Colombier 
10239ef1f84bSDavid du Colombier static char*
tcpflag(char * buf,char * e,ushort flag)10249ef1f84bSDavid du Colombier tcpflag(char *buf, char *e, ushort flag)
10259ef1f84bSDavid du Colombier {
10269ef1f84bSDavid du Colombier 	char *p;
10279ef1f84bSDavid du Colombier 
10289ef1f84bSDavid du Colombier 	p = seprint(buf, e, "%d", flag>>10);	/* Head len */
10299ef1f84bSDavid du Colombier 	if(flag & URG)
10309ef1f84bSDavid du Colombier 		p = seprint(p, e, " URG");
10319ef1f84bSDavid du Colombier 	if(flag & ACK)
10329ef1f84bSDavid du Colombier 		p = seprint(p, e, " ACK");
10339ef1f84bSDavid du Colombier 	if(flag & PSH)
10349ef1f84bSDavid du Colombier 		p = seprint(p, e, " PSH");
10359ef1f84bSDavid du Colombier 	if(flag & RST)
10369ef1f84bSDavid du Colombier 		p = seprint(p, e, " RST");
10379ef1f84bSDavid du Colombier 	if(flag & SYN)
10389ef1f84bSDavid du Colombier 		p = seprint(p, e, " SYN");
10399ef1f84bSDavid du Colombier 	if(flag & FIN)
10409ef1f84bSDavid du Colombier 		p = seprint(p, e, " FIN");
10419ef1f84bSDavid du Colombier 	USED(p);
10429ef1f84bSDavid du Colombier 	return buf;
10439ef1f84bSDavid du Colombier }
10449ef1f84bSDavid du Colombier 
10459ef1f84bSDavid du Colombier static Block*
htontcp6(Tcp * tcph,Block * data,Tcp6hdr * ph,Tcpctl * tcb)10469ef1f84bSDavid du Colombier htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
10479ef1f84bSDavid du Colombier {
10489ef1f84bSDavid du Colombier 	int dlen;
10499ef1f84bSDavid du Colombier 	Tcp6hdr *h;
10509ef1f84bSDavid du Colombier 	ushort csum;
10519ef1f84bSDavid du Colombier 	ushort hdrlen, optpad = 0;
10529ef1f84bSDavid du Colombier 	uchar *opt;
10539ef1f84bSDavid du Colombier 
10549ef1f84bSDavid du Colombier 	hdrlen = TCP6_HDRSIZE;
10559ef1f84bSDavid du Colombier 	if(tcph->flags & SYN){
10569ef1f84bSDavid du Colombier 		if(tcph->mss)
10579ef1f84bSDavid du Colombier 			hdrlen += MSS_LENGTH;
10589ef1f84bSDavid du Colombier 		if(tcph->ws)
10599ef1f84bSDavid du Colombier 			hdrlen += WS_LENGTH;
10609ef1f84bSDavid du Colombier 		optpad = hdrlen & 3;
10619ef1f84bSDavid du Colombier 		if(optpad)
10629ef1f84bSDavid du Colombier 			optpad = 4 - optpad;
10639ef1f84bSDavid du Colombier 		hdrlen += optpad;
10649ef1f84bSDavid du Colombier 	}
10659ef1f84bSDavid du Colombier 
10669ef1f84bSDavid du Colombier 	if(data) {
10679ef1f84bSDavid du Colombier 		dlen = blocklen(data);
10689ef1f84bSDavid du Colombier 		data = padblock(data, hdrlen + TCP6_PKT);
10699ef1f84bSDavid du Colombier 		if(data == nil)
10709ef1f84bSDavid du Colombier 			return nil;
10719ef1f84bSDavid du Colombier 	}
10729ef1f84bSDavid du Colombier 	else {
10739ef1f84bSDavid du Colombier 		dlen = 0;
10749ef1f84bSDavid du Colombier 		data = allocb(hdrlen + TCP6_PKT + 64);	/* the 64 pad is to meet mintu's */
10759ef1f84bSDavid du Colombier 		if(data == nil)
10769ef1f84bSDavid du Colombier 			return nil;
10779ef1f84bSDavid du Colombier 		data->wp += hdrlen + TCP6_PKT;
10789ef1f84bSDavid du Colombier 	}
10799ef1f84bSDavid du Colombier 
10809ef1f84bSDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
10819ef1f84bSDavid du Colombier 	h = (Tcp6hdr *)(data->rp);
10829ef1f84bSDavid du Colombier 	memmove(h, ph, TCP6_TCBPHDRSZ);
10839ef1f84bSDavid du Colombier 
10849ef1f84bSDavid du Colombier 	/* compose pseudo tcp header, do cksum calculation */
10859ef1f84bSDavid du Colombier 	hnputl(h->vcf, hdrlen + dlen);
10869ef1f84bSDavid du Colombier 	h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
10879ef1f84bSDavid du Colombier 	h->ttl = ph->proto;
10889ef1f84bSDavid du Colombier 
10899ef1f84bSDavid du Colombier 	/* copy in variable bits */
10909ef1f84bSDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
10919ef1f84bSDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
10929ef1f84bSDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
10939ef1f84bSDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
10949ef1f84bSDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
10959ef1f84bSDavid du Colombier 
10969ef1f84bSDavid du Colombier 	if(tcph->flags & SYN){
10979ef1f84bSDavid du Colombier 		opt = h->tcpopt;
10989ef1f84bSDavid du Colombier 		if(tcph->mss != 0){
10999ef1f84bSDavid du Colombier 			*opt++ = MSSOPT;
11009ef1f84bSDavid du Colombier 			*opt++ = MSS_LENGTH;
11019ef1f84bSDavid du Colombier 			hnputs(opt, tcph->mss);
11029ef1f84bSDavid du Colombier 			opt += 2;
11039ef1f84bSDavid du Colombier 		}
11049ef1f84bSDavid du Colombier 		if(tcph->ws != 0){
11059ef1f84bSDavid du Colombier 			*opt++ = WSOPT;
11069ef1f84bSDavid du Colombier 			*opt++ = WS_LENGTH;
11079ef1f84bSDavid du Colombier 			*opt++ = tcph->ws;
11089ef1f84bSDavid du Colombier 		}
11099ef1f84bSDavid du Colombier 		while(optpad-- > 0)
11109ef1f84bSDavid du Colombier 			*opt++ = NOOPOPT;
11119ef1f84bSDavid du Colombier 	}
11129ef1f84bSDavid du Colombier 
11139ef1f84bSDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
11149ef1f84bSDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
11159ef1f84bSDavid du Colombier 	} else {
11169ef1f84bSDavid du Colombier 		csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
11179ef1f84bSDavid du Colombier 		hnputs(h->tcpcksum, csum);
11189ef1f84bSDavid du Colombier 	}
11199ef1f84bSDavid du Colombier 
11209ef1f84bSDavid du Colombier 	/* move from pseudo header back to normal ip header */
11219ef1f84bSDavid du Colombier 	memset(h->vcf, 0, 4);
11229ef1f84bSDavid du Colombier 	h->vcf[0] = IP_VER6;
11239ef1f84bSDavid du Colombier 	hnputs(h->ploadlen, hdrlen+dlen);
11249ef1f84bSDavid du Colombier 	h->proto = ph->proto;
11259ef1f84bSDavid du Colombier 
11269ef1f84bSDavid du Colombier 	return data;
11279ef1f84bSDavid du Colombier }
11289ef1f84bSDavid du Colombier 
11299ef1f84bSDavid du Colombier static Block*
htontcp4(Tcp * tcph,Block * data,Tcp4hdr * ph,Tcpctl * tcb)11309ef1f84bSDavid du Colombier htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
11319ef1f84bSDavid du Colombier {
11329ef1f84bSDavid du Colombier 	int dlen;
11339ef1f84bSDavid du Colombier 	Tcp4hdr *h;
11349ef1f84bSDavid du Colombier 	ushort csum;
11359ef1f84bSDavid du Colombier 	ushort hdrlen, optpad = 0;
11369ef1f84bSDavid du Colombier 	uchar *opt;
11379ef1f84bSDavid du Colombier 
11389ef1f84bSDavid du Colombier 	hdrlen = TCP4_HDRSIZE;
11399ef1f84bSDavid du Colombier 	if(tcph->flags & SYN){
11409ef1f84bSDavid du Colombier 		if(tcph->mss)
11419ef1f84bSDavid du Colombier 			hdrlen += MSS_LENGTH;
11429ef1f84bSDavid du Colombier 		if(1)
11439ef1f84bSDavid du Colombier 			hdrlen += WS_LENGTH;
11449ef1f84bSDavid du Colombier 		optpad = hdrlen & 3;
11459ef1f84bSDavid du Colombier 		if(optpad)
11469ef1f84bSDavid du Colombier 			optpad = 4 - optpad;
11479ef1f84bSDavid du Colombier 		hdrlen += optpad;
11489ef1f84bSDavid du Colombier 	}
11499ef1f84bSDavid du Colombier 
11509ef1f84bSDavid du Colombier 	if(data) {
11519ef1f84bSDavid du Colombier 		dlen = blocklen(data);
11529ef1f84bSDavid du Colombier 		data = padblock(data, hdrlen + TCP4_PKT);
11539ef1f84bSDavid du Colombier 		if(data == nil)
11549ef1f84bSDavid du Colombier 			return nil;
11559ef1f84bSDavid du Colombier 	}
11569ef1f84bSDavid du Colombier 	else {
11579ef1f84bSDavid du Colombier 		dlen = 0;
11589ef1f84bSDavid du Colombier 		data = allocb(hdrlen + TCP4_PKT + 64);	/* the 64 pad is to meet mintu's */
11599ef1f84bSDavid du Colombier 		if(data == nil)
11609ef1f84bSDavid du Colombier 			return nil;
11619ef1f84bSDavid du Colombier 		data->wp += hdrlen + TCP4_PKT;
11629ef1f84bSDavid du Colombier 	}
11639ef1f84bSDavid du Colombier 
11649ef1f84bSDavid du Colombier 	/* copy in pseudo ip header plus port numbers */
11659ef1f84bSDavid du Colombier 	h = (Tcp4hdr *)(data->rp);
11669ef1f84bSDavid du Colombier 	memmove(h, ph, TCP4_TCBPHDRSZ);
11679ef1f84bSDavid du Colombier 
11689ef1f84bSDavid du Colombier 	/* copy in variable bits */
11699ef1f84bSDavid du Colombier 	hnputs(h->tcplen, hdrlen + dlen);
11709ef1f84bSDavid du Colombier 	hnputl(h->tcpseq, tcph->seq);
11719ef1f84bSDavid du Colombier 	hnputl(h->tcpack, tcph->ack);
11729ef1f84bSDavid du Colombier 	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
11739ef1f84bSDavid du Colombier 	hnputs(h->tcpwin, tcph->wnd>>(tcb != nil ? tcb->snd.scale : 0));
11749ef1f84bSDavid du Colombier 	hnputs(h->tcpurg, tcph->urg);
11759ef1f84bSDavid du Colombier 
11769ef1f84bSDavid du Colombier 	if(tcph->flags & SYN){
11779ef1f84bSDavid du Colombier 		opt = h->tcpopt;
11789ef1f84bSDavid du Colombier 		if(tcph->mss != 0){
11799ef1f84bSDavid du Colombier 			*opt++ = MSSOPT;
11809ef1f84bSDavid du Colombier 			*opt++ = MSS_LENGTH;
11819ef1f84bSDavid du Colombier 			hnputs(opt, tcph->mss);
11829ef1f84bSDavid du Colombier 			opt += 2;
11839ef1f84bSDavid du Colombier 		}
11849ef1f84bSDavid du Colombier 		/* always offer.  rfc1323 §2.2 */
11859ef1f84bSDavid du Colombier 		if(1){
11869ef1f84bSDavid du Colombier 			*opt++ = WSOPT;
11879ef1f84bSDavid du Colombier 			*opt++ = WS_LENGTH;
11889ef1f84bSDavid du Colombier 			*opt++ = tcph->ws;
11899ef1f84bSDavid du Colombier 		}
11909ef1f84bSDavid du Colombier 		while(optpad-- > 0)
11919ef1f84bSDavid du Colombier 			*opt++ = NOOPOPT;
11929ef1f84bSDavid du Colombier 	}
11939ef1f84bSDavid du Colombier 
11949ef1f84bSDavid du Colombier 	if(tcb != nil && tcb->nochecksum){
11959ef1f84bSDavid du Colombier 		h->tcpcksum[0] = h->tcpcksum[1] = 0;
11969ef1f84bSDavid du Colombier 	} else {
11979ef1f84bSDavid du Colombier 		csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
11989ef1f84bSDavid du Colombier 		hnputs(h->tcpcksum, csum);
11999ef1f84bSDavid du Colombier 	}
12009ef1f84bSDavid du Colombier 
12019ef1f84bSDavid du Colombier 	return data;
12029ef1f84bSDavid du Colombier }
12039ef1f84bSDavid du Colombier 
12049ef1f84bSDavid du Colombier static int
ntohtcp6(Tcp * tcph,Block ** bpp)12059ef1f84bSDavid du Colombier ntohtcp6(Tcp *tcph, Block **bpp)
12069ef1f84bSDavid du Colombier {
12079ef1f84bSDavid du Colombier 	Tcp6hdr *h;
12089ef1f84bSDavid du Colombier 	uchar *optr;
12099ef1f84bSDavid du Colombier 	ushort hdrlen;
12109ef1f84bSDavid du Colombier 	ushort optlen;
12119ef1f84bSDavid du Colombier 	int n;
12129ef1f84bSDavid du Colombier 
12139ef1f84bSDavid du Colombier 	*bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
12149ef1f84bSDavid du Colombier 	if(*bpp == nil)
12159ef1f84bSDavid du Colombier 		return -1;
12169ef1f84bSDavid du Colombier 
12179ef1f84bSDavid du Colombier 	h = (Tcp6hdr *)((*bpp)->rp);
12189ef1f84bSDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
12199ef1f84bSDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
12209ef1f84bSDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
12219ef1f84bSDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
12229ef1f84bSDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
12239ef1f84bSDavid du Colombier 	if(hdrlen < TCP6_HDRSIZE) {
12249ef1f84bSDavid du Colombier 		freeblist(*bpp);
12259ef1f84bSDavid du Colombier 		return -1;
12269ef1f84bSDavid du Colombier 	}
12279ef1f84bSDavid du Colombier 
12289ef1f84bSDavid du Colombier 	tcph->flags = h->tcpflag[1];
12299ef1f84bSDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12309ef1f84bSDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12319ef1f84bSDavid du Colombier 	tcph->mss = 0;
12329ef1f84bSDavid du Colombier 	tcph->ws = 0;
12339ef1f84bSDavid du Colombier 	tcph->update = 0;
12349ef1f84bSDavid du Colombier 	tcph->len = nhgets(h->ploadlen) - hdrlen;
12359ef1f84bSDavid du Colombier 
12369ef1f84bSDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
12379ef1f84bSDavid du Colombier 	if(*bpp == nil)
12389ef1f84bSDavid du Colombier 		return -1;
12399ef1f84bSDavid du Colombier 
12409ef1f84bSDavid du Colombier 	optr = h->tcpopt;
12419ef1f84bSDavid du Colombier 	n = hdrlen - TCP6_HDRSIZE;
12429ef1f84bSDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
12439ef1f84bSDavid du Colombier 		if(*optr == NOOPOPT) {
12449ef1f84bSDavid du Colombier 			n--;
12459ef1f84bSDavid du Colombier 			optr++;
12469ef1f84bSDavid du Colombier 			continue;
12479ef1f84bSDavid du Colombier 		}
12489ef1f84bSDavid du Colombier 		optlen = optr[1];
12499ef1f84bSDavid du Colombier 		if(optlen < 2 || optlen > n)
12509ef1f84bSDavid du Colombier 			break;
12519ef1f84bSDavid du Colombier 		switch(*optr) {
12529ef1f84bSDavid du Colombier 		case MSSOPT:
12539ef1f84bSDavid du Colombier 			if(optlen == MSS_LENGTH)
12549ef1f84bSDavid du Colombier 				tcph->mss = nhgets(optr+2);
12559ef1f84bSDavid du Colombier 			break;
12569ef1f84bSDavid du Colombier 		case WSOPT:
12579ef1f84bSDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
12589ef1f84bSDavid du Colombier 				tcph->ws = *(optr+2);
12599ef1f84bSDavid du Colombier 			break;
12609ef1f84bSDavid du Colombier 		}
12619ef1f84bSDavid du Colombier 		n -= optlen;
12629ef1f84bSDavid du Colombier 		optr += optlen;
12639ef1f84bSDavid du Colombier 	}
12649ef1f84bSDavid du Colombier 	return hdrlen;
12659ef1f84bSDavid du Colombier }
12669ef1f84bSDavid du Colombier 
12679ef1f84bSDavid du Colombier static int
ntohtcp4(Tcp * tcph,Block ** bpp)12689ef1f84bSDavid du Colombier ntohtcp4(Tcp *tcph, Block **bpp)
12699ef1f84bSDavid du Colombier {
12709ef1f84bSDavid du Colombier 	Tcp4hdr *h;
12719ef1f84bSDavid du Colombier 	uchar *optr;
12729ef1f84bSDavid du Colombier 	ushort hdrlen;
12739ef1f84bSDavid du Colombier 	ushort optlen;
12749ef1f84bSDavid du Colombier 	int n;
12759ef1f84bSDavid du Colombier 
12769ef1f84bSDavid du Colombier 	*bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
12779ef1f84bSDavid du Colombier 	if(*bpp == nil)
12789ef1f84bSDavid du Colombier 		return -1;
12799ef1f84bSDavid du Colombier 
12809ef1f84bSDavid du Colombier 	h = (Tcp4hdr *)((*bpp)->rp);
12819ef1f84bSDavid du Colombier 	tcph->source = nhgets(h->tcpsport);
12829ef1f84bSDavid du Colombier 	tcph->dest = nhgets(h->tcpdport);
12839ef1f84bSDavid du Colombier 	tcph->seq = nhgetl(h->tcpseq);
12849ef1f84bSDavid du Colombier 	tcph->ack = nhgetl(h->tcpack);
12859ef1f84bSDavid du Colombier 
12869ef1f84bSDavid du Colombier 	hdrlen = (h->tcpflag[0]>>2) & ~3;
12879ef1f84bSDavid du Colombier 	if(hdrlen < TCP4_HDRSIZE) {
12889ef1f84bSDavid du Colombier 		freeblist(*bpp);
12899ef1f84bSDavid du Colombier 		return -1;
12909ef1f84bSDavid du Colombier 	}
12919ef1f84bSDavid du Colombier 
12929ef1f84bSDavid du Colombier 	tcph->flags = h->tcpflag[1];
12939ef1f84bSDavid du Colombier 	tcph->wnd = nhgets(h->tcpwin);
12949ef1f84bSDavid du Colombier 	tcph->urg = nhgets(h->tcpurg);
12959ef1f84bSDavid du Colombier 	tcph->mss = 0;
12969ef1f84bSDavid du Colombier 	tcph->ws = 0;
12979ef1f84bSDavid du Colombier 	tcph->update = 0;
12989ef1f84bSDavid du Colombier 	tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);
12999ef1f84bSDavid du Colombier 
13009ef1f84bSDavid du Colombier 	*bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
13019ef1f84bSDavid du Colombier 	if(*bpp == nil)
13029ef1f84bSDavid du Colombier 		return -1;
13039ef1f84bSDavid du Colombier 
13049ef1f84bSDavid du Colombier 	optr = h->tcpopt;
13059ef1f84bSDavid du Colombier 	n = hdrlen - TCP4_HDRSIZE;
13069ef1f84bSDavid du Colombier 	while(n > 0 && *optr != EOLOPT) {
13079ef1f84bSDavid du Colombier 		if(*optr == NOOPOPT) {
13089ef1f84bSDavid du Colombier 			n--;
13099ef1f84bSDavid du Colombier 			optr++;
13109ef1f84bSDavid du Colombier 			continue;
13119ef1f84bSDavid du Colombier 		}
13129ef1f84bSDavid du Colombier 		optlen = optr[1];
13139ef1f84bSDavid du Colombier 		if(optlen < 2 || optlen > n)
13149ef1f84bSDavid du Colombier 			break;
13159ef1f84bSDavid du Colombier 		switch(*optr) {
13169ef1f84bSDavid du Colombier 		case MSSOPT:
13179ef1f84bSDavid du Colombier 			if(optlen == MSS_LENGTH)
13189ef1f84bSDavid du Colombier 				tcph->mss = nhgets(optr+2);
13199ef1f84bSDavid du Colombier 			break;
13209ef1f84bSDavid du Colombier 		case WSOPT:
13219ef1f84bSDavid du Colombier 			if(optlen == WS_LENGTH && *(optr+2) <= 14)
13229ef1f84bSDavid du Colombier 				tcph->ws = *(optr+2);
13239ef1f84bSDavid du Colombier 			break;
13249ef1f84bSDavid du Colombier 		}
13259ef1f84bSDavid du Colombier 		n -= optlen;
13269ef1f84bSDavid du Colombier 		optr += optlen;
13279ef1f84bSDavid du Colombier 	}
13289ef1f84bSDavid du Colombier 	return hdrlen;
13299ef1f84bSDavid du Colombier }
13309ef1f84bSDavid du Colombier 
13319ef1f84bSDavid du Colombier /*
13329ef1f84bSDavid du Colombier  *  For outgoing calls, generate an initial sequence
13339ef1f84bSDavid du Colombier  *  number and put a SYN on the send queue
13349ef1f84bSDavid du Colombier  */
13359ef1f84bSDavid du Colombier static void
tcpsndsyn(Conv * s,Tcpctl * tcb)13369ef1f84bSDavid du Colombier tcpsndsyn(Conv *s, Tcpctl *tcb)
13379ef1f84bSDavid du Colombier {
13389ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
13399ef1f84bSDavid du Colombier 
13409ef1f84bSDavid du Colombier 	tcb->iss = (nrand(1<<16)<<16)|nrand(1<<16);
13419ef1f84bSDavid du Colombier 	tcb->rttseq = tcb->iss;
13429ef1f84bSDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
13439ef1f84bSDavid du Colombier 	tcb->snd.una = tcb->iss;
13449ef1f84bSDavid du Colombier 	tcb->snd.rxt = tcb->iss;
13459ef1f84bSDavid du Colombier 	tcb->snd.ptr = tcb->rttseq;
13469ef1f84bSDavid du Colombier 	tcb->snd.nxt = tcb->rttseq;
13479ef1f84bSDavid du Colombier 	tcb->flgcnt++;
13489ef1f84bSDavid du Colombier 	tcb->flags |= FORCE;
13499ef1f84bSDavid du Colombier 	tcb->sndsyntime = NOW;
13509ef1f84bSDavid du Colombier 
13519ef1f84bSDavid du Colombier 	/* set desired mss and scale */
13529ef1f84bSDavid du Colombier 	tcb->mss = tcpmtu(s->p, s->laddr, s->ipversion, &tcb->scale);
13539ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
13549ef1f84bSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
13559ef1f84bSDavid du Colombier }
13569ef1f84bSDavid du Colombier 
13579ef1f84bSDavid du Colombier void
sndrst(Proto * tcp,uchar * source,uchar * dest,ushort length,Tcp * seg,uchar version,char * reason)13589ef1f84bSDavid du Colombier sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, uchar version, char *reason)
13599ef1f84bSDavid du Colombier {
13609ef1f84bSDavid du Colombier 	Block *hbp;
13619ef1f84bSDavid du Colombier 	uchar rflags;
13629ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
13639ef1f84bSDavid du Colombier 	Tcp4hdr ph4;
13649ef1f84bSDavid du Colombier 	Tcp6hdr ph6;
13659ef1f84bSDavid du Colombier 
13669ef1f84bSDavid du Colombier 	netlog(tcp->f, Logtcp, "sndrst: %s\n", reason);
13679ef1f84bSDavid du Colombier 
13689ef1f84bSDavid du Colombier 	tpriv = tcp->priv;
13699ef1f84bSDavid du Colombier 
13709ef1f84bSDavid du Colombier 	if(seg->flags & RST)
13719ef1f84bSDavid du Colombier 		return;
13729ef1f84bSDavid du Colombier 
13739ef1f84bSDavid du Colombier 	/* make pseudo header */
13749ef1f84bSDavid du Colombier 	switch(version) {
13759ef1f84bSDavid du Colombier 	case V4:
13769ef1f84bSDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
13779ef1f84bSDavid du Colombier 		ph4.vihl = IP_VER4;
13789ef1f84bSDavid du Colombier 		v6tov4(ph4.tcpsrc, dest);
13799ef1f84bSDavid du Colombier 		v6tov4(ph4.tcpdst, source);
13809ef1f84bSDavid du Colombier 		ph4.proto = IP_TCPPROTO;
13819ef1f84bSDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
13829ef1f84bSDavid du Colombier 		hnputs(ph4.tcpsport, seg->dest);
13839ef1f84bSDavid du Colombier 		hnputs(ph4.tcpdport, seg->source);
13849ef1f84bSDavid du Colombier 		break;
13859ef1f84bSDavid du Colombier 	case V6:
13869ef1f84bSDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
13879ef1f84bSDavid du Colombier 		ph6.vcf[0] = IP_VER6;
13889ef1f84bSDavid du Colombier 		ipmove(ph6.tcpsrc, dest);
13899ef1f84bSDavid du Colombier 		ipmove(ph6.tcpdst, source);
13909ef1f84bSDavid du Colombier 		ph6.proto = IP_TCPPROTO;
13919ef1f84bSDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
13929ef1f84bSDavid du Colombier 		hnputs(ph6.tcpsport, seg->dest);
13939ef1f84bSDavid du Colombier 		hnputs(ph6.tcpdport, seg->source);
13949ef1f84bSDavid du Colombier 		break;
13959ef1f84bSDavid du Colombier 	default:
13969ef1f84bSDavid du Colombier 		panic("sndrst: version %d", version);
13979ef1f84bSDavid du Colombier 	}
13989ef1f84bSDavid du Colombier 
13999ef1f84bSDavid du Colombier 	tpriv->stats[OutRsts]++;
14009ef1f84bSDavid du Colombier 	rflags = RST;
14019ef1f84bSDavid du Colombier 
14029ef1f84bSDavid du Colombier 	/* convince the other end that this reset is in band */
14039ef1f84bSDavid du Colombier 	if(seg->flags & ACK) {
14049ef1f84bSDavid du Colombier 		seg->seq = seg->ack;
14059ef1f84bSDavid du Colombier 		seg->ack = 0;
14069ef1f84bSDavid du Colombier 	}
14079ef1f84bSDavid du Colombier 	else {
14089ef1f84bSDavid du Colombier 		rflags |= ACK;
14099ef1f84bSDavid du Colombier 		seg->ack = seg->seq;
14109ef1f84bSDavid du Colombier 		seg->seq = 0;
14119ef1f84bSDavid du Colombier 		if(seg->flags & SYN)
14129ef1f84bSDavid du Colombier 			seg->ack++;
14139ef1f84bSDavid du Colombier 		seg->ack += length;
14149ef1f84bSDavid du Colombier 		if(seg->flags & FIN)
14159ef1f84bSDavid du Colombier 			seg->ack++;
14169ef1f84bSDavid du Colombier 	}
14179ef1f84bSDavid du Colombier 	seg->flags = rflags;
14189ef1f84bSDavid du Colombier 	seg->wnd = 0;
14199ef1f84bSDavid du Colombier 	seg->urg = 0;
14209ef1f84bSDavid du Colombier 	seg->mss = 0;
14219ef1f84bSDavid du Colombier 	seg->ws = 0;
14229ef1f84bSDavid du Colombier 	switch(version) {
14239ef1f84bSDavid du Colombier 	case V4:
14249ef1f84bSDavid du Colombier 		hbp = htontcp4(seg, nil, &ph4, nil);
14259ef1f84bSDavid du Colombier 		if(hbp == nil)
14269ef1f84bSDavid du Colombier 			return;
14279ef1f84bSDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14289ef1f84bSDavid du Colombier 		break;
14299ef1f84bSDavid du Colombier 	case V6:
14309ef1f84bSDavid du Colombier 		hbp = htontcp6(seg, nil, &ph6, nil);
14319ef1f84bSDavid du Colombier 		if(hbp == nil)
14329ef1f84bSDavid du Colombier 			return;
14339ef1f84bSDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
14349ef1f84bSDavid du Colombier 		break;
14359ef1f84bSDavid du Colombier 	default:
14369ef1f84bSDavid du Colombier 		panic("sndrst2: version %d", version);
14379ef1f84bSDavid du Colombier 	}
14389ef1f84bSDavid du Colombier }
14399ef1f84bSDavid du Colombier 
14409ef1f84bSDavid du Colombier /*
1441*41165bbaSDavid du Colombier  * close the conversation
1442*41165bbaSDavid du Colombier  */
1443*41165bbaSDavid du Colombier static char*
tcpclose2(Conv * s)1444*41165bbaSDavid du Colombier tcpclose2(Conv *s)
1445*41165bbaSDavid du Colombier {
1446*41165bbaSDavid du Colombier 	tcpclose(s);
1447*41165bbaSDavid du Colombier 	return nil;
1448*41165bbaSDavid du Colombier }
1449*41165bbaSDavid du Colombier 
1450*41165bbaSDavid du Colombier /*
14519ef1f84bSDavid du Colombier  *  send a reset to the remote side and close the conversation
14529ef1f84bSDavid du Colombier  *  called with s qlocked
14539ef1f84bSDavid du Colombier  */
14549ef1f84bSDavid du Colombier static char*
tcphangup(Conv * s)14559ef1f84bSDavid du Colombier tcphangup(Conv *s)
14569ef1f84bSDavid du Colombier {
14579ef1f84bSDavid du Colombier 	Tcp seg;
14589ef1f84bSDavid du Colombier 	Tcpctl *tcb;
14599ef1f84bSDavid du Colombier 	Block *hbp;
14609ef1f84bSDavid du Colombier 
14619ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
14629ef1f84bSDavid du Colombier 	if(waserror())
14639ef1f84bSDavid du Colombier 		return up->errstr;
14649ef1f84bSDavid du Colombier 	if(ipcmp(s->raddr, IPnoaddr) != 0) {
14659ef1f84bSDavid du Colombier 		if(!waserror()){
14669ef1f84bSDavid du Colombier 			memset(&seg, 0, sizeof seg);
14679ef1f84bSDavid du Colombier 			seg.flags = RST | ACK;
14689ef1f84bSDavid du Colombier 			seg.ack = tcb->rcv.nxt;
14699ef1f84bSDavid du Colombier 			tcb->rcv.ackptr = seg.ack;
14709ef1f84bSDavid du Colombier 			seg.seq = tcb->snd.ptr;
14719ef1f84bSDavid du Colombier 			seg.wnd = 0;
14729ef1f84bSDavid du Colombier 			seg.urg = 0;
14739ef1f84bSDavid du Colombier 			seg.mss = 0;
14749ef1f84bSDavid du Colombier 			seg.ws = 0;
14759ef1f84bSDavid du Colombier 			switch(s->ipversion) {
14769ef1f84bSDavid du Colombier 			case V4:
14779ef1f84bSDavid du Colombier 				tcb->protohdr.tcp4hdr.vihl = IP_VER4;
14789ef1f84bSDavid du Colombier 				hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
14799ef1f84bSDavid du Colombier 				ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
14809ef1f84bSDavid du Colombier 				break;
14819ef1f84bSDavid du Colombier 			case V6:
14829ef1f84bSDavid du Colombier 				tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
14839ef1f84bSDavid du Colombier 				hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
14849ef1f84bSDavid du Colombier 				ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
14859ef1f84bSDavid du Colombier 				break;
14869ef1f84bSDavid du Colombier 			default:
14879ef1f84bSDavid du Colombier 				panic("tcphangup: version %d", s->ipversion);
14889ef1f84bSDavid du Colombier 			}
14899ef1f84bSDavid du Colombier 			poperror();
14909ef1f84bSDavid du Colombier 		}
14919ef1f84bSDavid du Colombier 	}
14929ef1f84bSDavid du Colombier 	localclose(s, nil);
14939ef1f84bSDavid du Colombier 	poperror();
14949ef1f84bSDavid du Colombier 	return nil;
14959ef1f84bSDavid du Colombier }
14969ef1f84bSDavid du Colombier 
14979ef1f84bSDavid du Colombier /*
14989ef1f84bSDavid du Colombier  *  (re)send a SYN ACK
14999ef1f84bSDavid du Colombier  */
15009ef1f84bSDavid du Colombier static int
sndsynack(Proto * tcp,Limbo * lp)15019ef1f84bSDavid du Colombier sndsynack(Proto *tcp, Limbo *lp)
15029ef1f84bSDavid du Colombier {
15039ef1f84bSDavid du Colombier 	Block *hbp;
15049ef1f84bSDavid du Colombier 	Tcp4hdr ph4;
15059ef1f84bSDavid du Colombier 	Tcp6hdr ph6;
15069ef1f84bSDavid du Colombier 	Tcp seg;
15079ef1f84bSDavid du Colombier 	uint scale;
15089ef1f84bSDavid du Colombier 
15099ef1f84bSDavid du Colombier 	/* make pseudo header */
15109ef1f84bSDavid du Colombier 	switch(lp->version) {
15119ef1f84bSDavid du Colombier 	case V4:
15129ef1f84bSDavid du Colombier 		memset(&ph4, 0, sizeof(ph4));
15139ef1f84bSDavid du Colombier 		ph4.vihl = IP_VER4;
15149ef1f84bSDavid du Colombier 		v6tov4(ph4.tcpsrc, lp->laddr);
15159ef1f84bSDavid du Colombier 		v6tov4(ph4.tcpdst, lp->raddr);
15169ef1f84bSDavid du Colombier 		ph4.proto = IP_TCPPROTO;
15179ef1f84bSDavid du Colombier 		hnputs(ph4.tcplen, TCP4_HDRSIZE);
15189ef1f84bSDavid du Colombier 		hnputs(ph4.tcpsport, lp->lport);
15199ef1f84bSDavid du Colombier 		hnputs(ph4.tcpdport, lp->rport);
15209ef1f84bSDavid du Colombier 		break;
15219ef1f84bSDavid du Colombier 	case V6:
15229ef1f84bSDavid du Colombier 		memset(&ph6, 0, sizeof(ph6));
15239ef1f84bSDavid du Colombier 		ph6.vcf[0] = IP_VER6;
15249ef1f84bSDavid du Colombier 		ipmove(ph6.tcpsrc, lp->laddr);
15259ef1f84bSDavid du Colombier 		ipmove(ph6.tcpdst, lp->raddr);
15269ef1f84bSDavid du Colombier 		ph6.proto = IP_TCPPROTO;
15279ef1f84bSDavid du Colombier 		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
15289ef1f84bSDavid du Colombier 		hnputs(ph6.tcpsport, lp->lport);
15299ef1f84bSDavid du Colombier 		hnputs(ph6.tcpdport, lp->rport);
15309ef1f84bSDavid du Colombier 		break;
15319ef1f84bSDavid du Colombier 	default:
15329ef1f84bSDavid du Colombier 		panic("sndrst: version %d", lp->version);
15339ef1f84bSDavid du Colombier 	}
15349ef1f84bSDavid du Colombier 
15359ef1f84bSDavid du Colombier 	memset(&seg, 0, sizeof seg);
15369ef1f84bSDavid du Colombier 	seg.seq = lp->iss;
15379ef1f84bSDavid du Colombier 	seg.ack = lp->irs+1;
15389ef1f84bSDavid du Colombier 	seg.flags = SYN|ACK;
15399ef1f84bSDavid du Colombier 	seg.urg = 0;
15409ef1f84bSDavid du Colombier 	seg.mss = tcpmtu(tcp, lp->laddr, lp->version, &scale);
15419ef1f84bSDavid du Colombier 	seg.wnd = QMAX;
15429ef1f84bSDavid du Colombier 
15439ef1f84bSDavid du Colombier 	/* if the other side set scale, we should too */
15449ef1f84bSDavid du Colombier 	if(lp->rcvscale){
15459ef1f84bSDavid du Colombier 		seg.ws = scale;
15469ef1f84bSDavid du Colombier 		lp->sndscale = scale;
15479ef1f84bSDavid du Colombier 	} else {
15489ef1f84bSDavid du Colombier 		seg.ws = 0;
15499ef1f84bSDavid du Colombier 		lp->sndscale = 0;
15509ef1f84bSDavid du Colombier 	}
15519ef1f84bSDavid du Colombier 
15529ef1f84bSDavid du Colombier 	switch(lp->version) {
15539ef1f84bSDavid du Colombier 	case V4:
15549ef1f84bSDavid du Colombier 		hbp = htontcp4(&seg, nil, &ph4, nil);
15559ef1f84bSDavid du Colombier 		if(hbp == nil)
15569ef1f84bSDavid du Colombier 			return -1;
15579ef1f84bSDavid du Colombier 		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15589ef1f84bSDavid du Colombier 		break;
15599ef1f84bSDavid du Colombier 	case V6:
15609ef1f84bSDavid du Colombier 		hbp = htontcp6(&seg, nil, &ph6, nil);
15619ef1f84bSDavid du Colombier 		if(hbp == nil)
15629ef1f84bSDavid du Colombier 			return -1;
15639ef1f84bSDavid du Colombier 		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS, nil);
15649ef1f84bSDavid du Colombier 		break;
15659ef1f84bSDavid du Colombier 	default:
15669ef1f84bSDavid du Colombier 		panic("sndsnack: version %d", lp->version);
15679ef1f84bSDavid du Colombier 	}
15689ef1f84bSDavid du Colombier 	lp->lastsend = NOW;
15699ef1f84bSDavid du Colombier 	return 0;
15709ef1f84bSDavid du Colombier }
15719ef1f84bSDavid du Colombier 
15729ef1f84bSDavid du Colombier #define hashipa(a, p) ( ( (a)[IPaddrlen-2] + (a)[IPaddrlen-1] + p )&LHTMASK )
15739ef1f84bSDavid du Colombier 
15749ef1f84bSDavid du Colombier /*
15759ef1f84bSDavid du Colombier  *  put a call into limbo and respond with a SYN ACK
15769ef1f84bSDavid du Colombier  *
15779ef1f84bSDavid du Colombier  *  called with proto locked
15789ef1f84bSDavid du Colombier  */
15799ef1f84bSDavid du Colombier static void
limbo(Conv * s,uchar * source,uchar * dest,Tcp * seg,int version)15809ef1f84bSDavid du Colombier limbo(Conv *s, uchar *source, uchar *dest, Tcp *seg, int version)
15819ef1f84bSDavid du Colombier {
15829ef1f84bSDavid du Colombier 	Limbo *lp, **l;
15839ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
15849ef1f84bSDavid du Colombier 	int h;
15859ef1f84bSDavid du Colombier 
15869ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
15879ef1f84bSDavid du Colombier 	h = hashipa(source, seg->source);
15889ef1f84bSDavid du Colombier 
15899ef1f84bSDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
15909ef1f84bSDavid du Colombier 		lp = *l;
15919ef1f84bSDavid du Colombier 		if(lp->lport != seg->dest || lp->rport != seg->source || lp->version != version)
15929ef1f84bSDavid du Colombier 			continue;
15939ef1f84bSDavid du Colombier 		if(ipcmp(lp->raddr, source) != 0)
15949ef1f84bSDavid du Colombier 			continue;
15959ef1f84bSDavid du Colombier 		if(ipcmp(lp->laddr, dest) != 0)
15969ef1f84bSDavid du Colombier 			continue;
15979ef1f84bSDavid du Colombier 
15989ef1f84bSDavid du Colombier 		/* each new SYN restarts the retransmits */
15999ef1f84bSDavid du Colombier 		lp->irs = seg->seq;
16009ef1f84bSDavid du Colombier 		break;
16019ef1f84bSDavid du Colombier 	}
16029ef1f84bSDavid du Colombier 	lp = *l;
16039ef1f84bSDavid du Colombier 	if(lp == nil){
16049ef1f84bSDavid du Colombier 		if(tpriv->nlimbo >= Maxlimbo && tpriv->lht[h]){
16059ef1f84bSDavid du Colombier 			lp = tpriv->lht[h];
16069ef1f84bSDavid du Colombier 			tpriv->lht[h] = lp->next;
16079ef1f84bSDavid du Colombier 			lp->next = nil;
16089ef1f84bSDavid du Colombier 		} else {
16099ef1f84bSDavid du Colombier 			lp = malloc(sizeof(*lp));
16109ef1f84bSDavid du Colombier 			if(lp == nil)
16119ef1f84bSDavid du Colombier 				return;
16129ef1f84bSDavid du Colombier 			tpriv->nlimbo++;
16139ef1f84bSDavid du Colombier 		}
16149ef1f84bSDavid du Colombier 		*l = lp;
16159ef1f84bSDavid du Colombier 		lp->version = version;
16169ef1f84bSDavid du Colombier 		ipmove(lp->laddr, dest);
16179ef1f84bSDavid du Colombier 		ipmove(lp->raddr, source);
16189ef1f84bSDavid du Colombier 		lp->lport = seg->dest;
16199ef1f84bSDavid du Colombier 		lp->rport = seg->source;
16209ef1f84bSDavid du Colombier 		lp->mss = seg->mss;
16219ef1f84bSDavid du Colombier 		lp->rcvscale = seg->ws;
16229ef1f84bSDavid du Colombier 		lp->irs = seg->seq;
16239ef1f84bSDavid du Colombier 		lp->iss = (nrand(1<<16)<<16)|nrand(1<<16);
16249ef1f84bSDavid du Colombier 	}
16259ef1f84bSDavid du Colombier 
16269ef1f84bSDavid du Colombier 	if(sndsynack(s->p, lp) < 0){
16279ef1f84bSDavid du Colombier 		*l = lp->next;
16289ef1f84bSDavid du Colombier 		tpriv->nlimbo--;
16299ef1f84bSDavid du Colombier 		free(lp);
16309ef1f84bSDavid du Colombier 	}
16319ef1f84bSDavid du Colombier }
16329ef1f84bSDavid du Colombier 
16339ef1f84bSDavid du Colombier /*
16349ef1f84bSDavid du Colombier  *  resend SYN ACK's once every SYNACK_RXTIMER ms.
16359ef1f84bSDavid du Colombier  */
16369ef1f84bSDavid du Colombier static void
limborexmit(Proto * tcp)16379ef1f84bSDavid du Colombier limborexmit(Proto *tcp)
16389ef1f84bSDavid du Colombier {
16399ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
16409ef1f84bSDavid du Colombier 	Limbo **l, *lp;
16419ef1f84bSDavid du Colombier 	int h;
16429ef1f84bSDavid du Colombier 	int seen;
16439ef1f84bSDavid du Colombier 	ulong now;
16449ef1f84bSDavid du Colombier 
16459ef1f84bSDavid du Colombier 	tpriv = tcp->priv;
16469ef1f84bSDavid du Colombier 
16479ef1f84bSDavid du Colombier 	if(!canqlock(tcp))
16489ef1f84bSDavid du Colombier 		return;
16499ef1f84bSDavid du Colombier 	seen = 0;
16509ef1f84bSDavid du Colombier 	now = NOW;
16519ef1f84bSDavid du Colombier 	for(h = 0; h < NLHT && seen < tpriv->nlimbo; h++){
16529ef1f84bSDavid du Colombier 		for(l = &tpriv->lht[h]; *l != nil && seen < tpriv->nlimbo; ){
16539ef1f84bSDavid du Colombier 			lp = *l;
16549ef1f84bSDavid du Colombier 			seen++;
16559ef1f84bSDavid du Colombier 			if(now - lp->lastsend < (lp->rexmits+1)*SYNACK_RXTIMER)
16569ef1f84bSDavid du Colombier 				continue;
16579ef1f84bSDavid du Colombier 
16589ef1f84bSDavid du Colombier 			/* time it out after 1 second */
16599ef1f84bSDavid du Colombier 			if(++(lp->rexmits) > 5){
16609ef1f84bSDavid du Colombier 				tpriv->nlimbo--;
16619ef1f84bSDavid du Colombier 				*l = lp->next;
16629ef1f84bSDavid du Colombier 				free(lp);
16639ef1f84bSDavid du Colombier 				continue;
16649ef1f84bSDavid du Colombier 			}
16659ef1f84bSDavid du Colombier 
16669ef1f84bSDavid du Colombier 			/* if we're being attacked, don't bother resending SYN ACK's */
16679ef1f84bSDavid du Colombier 			if(tpriv->nlimbo > 100)
16689ef1f84bSDavid du Colombier 				continue;
16699ef1f84bSDavid du Colombier 
16709ef1f84bSDavid du Colombier 			if(sndsynack(tcp, lp) < 0){
16719ef1f84bSDavid du Colombier 				tpriv->nlimbo--;
16729ef1f84bSDavid du Colombier 				*l = lp->next;
16739ef1f84bSDavid du Colombier 				free(lp);
16749ef1f84bSDavid du Colombier 				continue;
16759ef1f84bSDavid du Colombier 			}
16769ef1f84bSDavid du Colombier 
16779ef1f84bSDavid du Colombier 			l = &lp->next;
16789ef1f84bSDavid du Colombier 		}
16799ef1f84bSDavid du Colombier 	}
16809ef1f84bSDavid du Colombier 	qunlock(tcp);
16819ef1f84bSDavid du Colombier }
16829ef1f84bSDavid du Colombier 
16839ef1f84bSDavid du Colombier /*
16849ef1f84bSDavid du Colombier  *  lookup call in limbo.  if found, throw it out.
16859ef1f84bSDavid du Colombier  *
16869ef1f84bSDavid du Colombier  *  called with proto locked
16879ef1f84bSDavid du Colombier  */
16889ef1f84bSDavid du Colombier static void
limborst(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)16899ef1f84bSDavid du Colombier limborst(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
16909ef1f84bSDavid du Colombier {
16919ef1f84bSDavid du Colombier 	Limbo *lp, **l;
16929ef1f84bSDavid du Colombier 	int h;
16939ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
16949ef1f84bSDavid du Colombier 
16959ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
16969ef1f84bSDavid du Colombier 
16979ef1f84bSDavid du Colombier 	/* find a call in limbo */
16989ef1f84bSDavid du Colombier 	h = hashipa(src, segp->source);
16999ef1f84bSDavid du Colombier 	for(l = &tpriv->lht[h]; *l != nil; l = &lp->next){
17009ef1f84bSDavid du Colombier 		lp = *l;
17019ef1f84bSDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17029ef1f84bSDavid du Colombier 			continue;
17039ef1f84bSDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
17049ef1f84bSDavid du Colombier 			continue;
17059ef1f84bSDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
17069ef1f84bSDavid du Colombier 			continue;
17079ef1f84bSDavid du Colombier 
17089ef1f84bSDavid du Colombier 		/* RST can only follow the SYN */
17099ef1f84bSDavid du Colombier 		if(segp->seq == lp->irs+1){
17109ef1f84bSDavid du Colombier 			tpriv->nlimbo--;
17119ef1f84bSDavid du Colombier 			*l = lp->next;
17129ef1f84bSDavid du Colombier 			free(lp);
17139ef1f84bSDavid du Colombier 		}
17149ef1f84bSDavid du Colombier 		break;
17159ef1f84bSDavid du Colombier 	}
17169ef1f84bSDavid du Colombier }
17179ef1f84bSDavid du Colombier 
17189ef1f84bSDavid du Colombier static void
initialwindow(Tcpctl * tcb)17199ef1f84bSDavid du Colombier initialwindow(Tcpctl *tcb)
17209ef1f84bSDavid du Colombier {
17219ef1f84bSDavid du Colombier 	/* RFC 3390 initial window */
17229ef1f84bSDavid du Colombier 	if(tcb->mss < 1095)
17239ef1f84bSDavid du Colombier 		tcb->cwind = 4*tcb->mss;
17249ef1f84bSDavid du Colombier 	else if(tcb->mss < 2190)
17259ef1f84bSDavid du Colombier 		tcb->cwind = 2*2190;
17269ef1f84bSDavid du Colombier 	else
17279ef1f84bSDavid du Colombier 		tcb->cwind = 2*tcb->mss;
17289ef1f84bSDavid du Colombier }
17299ef1f84bSDavid du Colombier 
17309ef1f84bSDavid du Colombier /*
17319ef1f84bSDavid du Colombier  *  come here when we finally get an ACK to our SYN-ACK.
17329ef1f84bSDavid du Colombier  *  lookup call in limbo.  if found, create a new conversation
17339ef1f84bSDavid du Colombier  *
17349ef1f84bSDavid du Colombier  *  called with proto locked
17359ef1f84bSDavid du Colombier  */
17369ef1f84bSDavid du Colombier static Conv*
tcpincoming(Conv * s,Tcp * segp,uchar * src,uchar * dst,uchar version)17379ef1f84bSDavid du Colombier tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst, uchar version)
17389ef1f84bSDavid du Colombier {
17399ef1f84bSDavid du Colombier 	Conv *new;
17409ef1f84bSDavid du Colombier 	Tcpctl *tcb;
17419ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
17429ef1f84bSDavid du Colombier 	Tcp4hdr *h4;
17439ef1f84bSDavid du Colombier 	Tcp6hdr *h6;
17449ef1f84bSDavid du Colombier 	Limbo *lp, **l;
17459ef1f84bSDavid du Colombier 	int h;
17469ef1f84bSDavid du Colombier 
17479ef1f84bSDavid du Colombier 	/* unless it's just an ack, it can't be someone coming out of limbo */
17489ef1f84bSDavid du Colombier 	if((segp->flags & SYN) || (segp->flags & ACK) == 0)
17499ef1f84bSDavid du Colombier 		return nil;
17509ef1f84bSDavid du Colombier 
17519ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
17529ef1f84bSDavid du Colombier 
17539ef1f84bSDavid du Colombier 	/* find a call in limbo */
17549ef1f84bSDavid du Colombier 	h = hashipa(src, segp->source);
17559ef1f84bSDavid du Colombier 	for(l = &tpriv->lht[h]; (lp = *l) != nil; l = &lp->next){
17569ef1f84bSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpincoming s %I!%ud/%I!%ud d %I!%ud/%I!%ud v %d/%d\n",
17579ef1f84bSDavid du Colombier 			src, segp->source, lp->raddr, lp->rport,
17589ef1f84bSDavid du Colombier 			dst, segp->dest, lp->laddr, lp->lport,
17599ef1f84bSDavid du Colombier 			version, lp->version
17609ef1f84bSDavid du Colombier  		);
17619ef1f84bSDavid du Colombier 
17629ef1f84bSDavid du Colombier 		if(lp->lport != segp->dest || lp->rport != segp->source || lp->version != version)
17639ef1f84bSDavid du Colombier 			continue;
17649ef1f84bSDavid du Colombier 		if(ipcmp(lp->laddr, dst) != 0)
17659ef1f84bSDavid du Colombier 			continue;
17669ef1f84bSDavid du Colombier 		if(ipcmp(lp->raddr, src) != 0)
17679ef1f84bSDavid du Colombier 			continue;
17689ef1f84bSDavid du Colombier 
17699ef1f84bSDavid du Colombier 		/* we're assuming no data with the initial SYN */
17709ef1f84bSDavid du Colombier 		if(segp->seq != lp->irs+1 || segp->ack != lp->iss+1){
17719ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcp, "tcpincoming s %lux/%lux a %lux %lux\n",
17729ef1f84bSDavid du Colombier 				segp->seq, lp->irs+1, segp->ack, lp->iss+1);
17739ef1f84bSDavid du Colombier 			lp = nil;
17749ef1f84bSDavid du Colombier 		} else {
17759ef1f84bSDavid du Colombier 			tpriv->nlimbo--;
17769ef1f84bSDavid du Colombier 			*l = lp->next;
17779ef1f84bSDavid du Colombier 		}
17789ef1f84bSDavid du Colombier 		break;
17799ef1f84bSDavid du Colombier 	}
17809ef1f84bSDavid du Colombier 	if(lp == nil)
17819ef1f84bSDavid du Colombier 		return nil;
17829ef1f84bSDavid du Colombier 
17839ef1f84bSDavid du Colombier 	new = Fsnewcall(s, src, segp->source, dst, segp->dest, version);
17849ef1f84bSDavid du Colombier 	if(new == nil)
17859ef1f84bSDavid du Colombier 		return nil;
17869ef1f84bSDavid du Colombier 
17879ef1f84bSDavid du Colombier 	memmove(new->ptcl, s->ptcl, sizeof(Tcpctl));
17889ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)new->ptcl;
17899ef1f84bSDavid du Colombier 	tcb->flags &= ~CLONE;
17909ef1f84bSDavid du Colombier 	tcb->timer.arg = new;
17919ef1f84bSDavid du Colombier 	tcb->timer.state = TcptimerOFF;
17929ef1f84bSDavid du Colombier 	tcb->acktimer.arg = new;
17939ef1f84bSDavid du Colombier 	tcb->acktimer.state = TcptimerOFF;
17949ef1f84bSDavid du Colombier 	tcb->katimer.arg = new;
17959ef1f84bSDavid du Colombier 	tcb->katimer.state = TcptimerOFF;
17969ef1f84bSDavid du Colombier 	tcb->rtt_timer.arg = new;
17979ef1f84bSDavid du Colombier 	tcb->rtt_timer.state = TcptimerOFF;
17989ef1f84bSDavid du Colombier 
17999ef1f84bSDavid du Colombier 	tcb->irs = lp->irs;
18009ef1f84bSDavid du Colombier 	tcb->rcv.nxt = tcb->irs+1;
18019ef1f84bSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
18029ef1f84bSDavid du Colombier 	tcb->rcv.wsnt = 0;
18039ef1f84bSDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
18049ef1f84bSDavid du Colombier 
18059ef1f84bSDavid du Colombier 	tcb->iss = lp->iss;
18069ef1f84bSDavid du Colombier 	tcb->rttseq = tcb->iss;
18079ef1f84bSDavid du Colombier 	tcb->snd.wl2 = tcb->iss;
18089ef1f84bSDavid du Colombier 	tcb->snd.una = tcb->iss+1;
18099ef1f84bSDavid du Colombier 	tcb->snd.ptr = tcb->iss+1;
18109ef1f84bSDavid du Colombier 	tcb->snd.nxt = tcb->iss+1;
18119ef1f84bSDavid du Colombier 	tcb->snd.rxt = tcb->iss+1;
18129ef1f84bSDavid du Colombier 	tcb->flgcnt = 0;
18139ef1f84bSDavid du Colombier 	tcb->flags |= SYNACK;
18149ef1f84bSDavid du Colombier 
181588bae168SDavid du Colombier 	/* set desired mss and scale */
181688bae168SDavid du Colombier 	tcb->mss = tcpmtu(s->p, dst, s->ipversion, &tcb->scale);
181788bae168SDavid du Colombier 
18189ef1f84bSDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
181988bae168SDavid du Colombier 	if(lp->mss != 0 && lp->mss < tcb->mss)
18209ef1f84bSDavid du Colombier 		tcb->mss = lp->mss;
18219ef1f84bSDavid du Colombier 	tpriv->stats[Mss] = tcb->mss;
18229ef1f84bSDavid du Colombier 
18239ef1f84bSDavid du Colombier 	/* window scaling */
18249ef1f84bSDavid du Colombier 	tcpsetscale(new, tcb, lp->rcvscale, lp->sndscale);
18259ef1f84bSDavid du Colombier 
18269ef1f84bSDavid du Colombier 	/* congestion window */
18279ef1f84bSDavid du Colombier 	tcb->snd.wnd = segp->wnd;
18289ef1f84bSDavid du Colombier 	initialwindow(tcb);
18299ef1f84bSDavid du Colombier 
18309ef1f84bSDavid du Colombier 	/* set initial round trip time */
18319ef1f84bSDavid du Colombier 	tcb->sndsyntime = lp->lastsend+lp->rexmits*SYNACK_RXTIMER;
18329ef1f84bSDavid du Colombier 	tcpsynackrtt(new);
18339ef1f84bSDavid du Colombier 
18349ef1f84bSDavid du Colombier 	free(lp);
18359ef1f84bSDavid du Colombier 
18369ef1f84bSDavid du Colombier 	/* set up proto header */
18379ef1f84bSDavid du Colombier 	switch(version){
18389ef1f84bSDavid du Colombier 	case V4:
18399ef1f84bSDavid du Colombier 		h4 = &tcb->protohdr.tcp4hdr;
18409ef1f84bSDavid du Colombier 		memset(h4, 0, sizeof(*h4));
18419ef1f84bSDavid du Colombier 		h4->proto = IP_TCPPROTO;
18429ef1f84bSDavid du Colombier 		hnputs(h4->tcpsport, new->lport);
18439ef1f84bSDavid du Colombier 		hnputs(h4->tcpdport, new->rport);
18449ef1f84bSDavid du Colombier 		v6tov4(h4->tcpsrc, dst);
18459ef1f84bSDavid du Colombier 		v6tov4(h4->tcpdst, src);
18469ef1f84bSDavid du Colombier 		break;
18479ef1f84bSDavid du Colombier 	case V6:
18489ef1f84bSDavid du Colombier 		h6 = &tcb->protohdr.tcp6hdr;
18499ef1f84bSDavid du Colombier 		memset(h6, 0, sizeof(*h6));
18509ef1f84bSDavid du Colombier 		h6->proto = IP_TCPPROTO;
18519ef1f84bSDavid du Colombier 		hnputs(h6->tcpsport, new->lport);
18529ef1f84bSDavid du Colombier 		hnputs(h6->tcpdport, new->rport);
18539ef1f84bSDavid du Colombier 		ipmove(h6->tcpsrc, dst);
18549ef1f84bSDavid du Colombier 		ipmove(h6->tcpdst, src);
18559ef1f84bSDavid du Colombier 		break;
18569ef1f84bSDavid du Colombier 	default:
18579ef1f84bSDavid du Colombier 		panic("tcpincoming: version %d", new->ipversion);
18589ef1f84bSDavid du Colombier 	}
18599ef1f84bSDavid du Colombier 
18609ef1f84bSDavid du Colombier 	tcpsetstate(new, Established);
18619ef1f84bSDavid du Colombier 
18629ef1f84bSDavid du Colombier 	iphtadd(&tpriv->ht, new);
18639ef1f84bSDavid du Colombier 
18649ef1f84bSDavid du Colombier 	return new;
18659ef1f84bSDavid du Colombier }
18669ef1f84bSDavid du Colombier 
18679ef1f84bSDavid du Colombier static int
seq_within(ulong x,ulong low,ulong high)18689ef1f84bSDavid du Colombier seq_within(ulong x, ulong low, ulong high)
18699ef1f84bSDavid du Colombier {
18709ef1f84bSDavid du Colombier 	if(low <= high){
18719ef1f84bSDavid du Colombier 		if(low <= x && x <= high)
18729ef1f84bSDavid du Colombier 			return 1;
18739ef1f84bSDavid du Colombier 	}
18749ef1f84bSDavid du Colombier 	else {
18759ef1f84bSDavid du Colombier 		if(x >= low || x <= high)
18769ef1f84bSDavid du Colombier 			return 1;
18779ef1f84bSDavid du Colombier 	}
18789ef1f84bSDavid du Colombier 	return 0;
18799ef1f84bSDavid du Colombier }
18809ef1f84bSDavid du Colombier 
18819ef1f84bSDavid du Colombier static int
seq_lt(ulong x,ulong y)18829ef1f84bSDavid du Colombier seq_lt(ulong x, ulong y)
18839ef1f84bSDavid du Colombier {
18849ef1f84bSDavid du Colombier 	return (int)(x-y) < 0;
18859ef1f84bSDavid du Colombier }
18869ef1f84bSDavid du Colombier 
18879ef1f84bSDavid du Colombier static int
seq_le(ulong x,ulong y)18889ef1f84bSDavid du Colombier seq_le(ulong x, ulong y)
18899ef1f84bSDavid du Colombier {
18909ef1f84bSDavid du Colombier 	return (int)(x-y) <= 0;
18919ef1f84bSDavid du Colombier }
18929ef1f84bSDavid du Colombier 
18939ef1f84bSDavid du Colombier static int
seq_gt(ulong x,ulong y)18949ef1f84bSDavid du Colombier seq_gt(ulong x, ulong y)
18959ef1f84bSDavid du Colombier {
18969ef1f84bSDavid du Colombier 	return (int)(x-y) > 0;
18979ef1f84bSDavid du Colombier }
18989ef1f84bSDavid du Colombier 
18999ef1f84bSDavid du Colombier static int
seq_ge(ulong x,ulong y)19009ef1f84bSDavid du Colombier seq_ge(ulong x, ulong y)
19019ef1f84bSDavid du Colombier {
19029ef1f84bSDavid du Colombier 	return (int)(x-y) >= 0;
19039ef1f84bSDavid du Colombier }
19049ef1f84bSDavid du Colombier 
19059ef1f84bSDavid du Colombier /*
19069ef1f84bSDavid du Colombier  *  use the time between the first SYN and it's ack as the
19079ef1f84bSDavid du Colombier  *  initial round trip time
19089ef1f84bSDavid du Colombier  */
19099ef1f84bSDavid du Colombier static void
tcpsynackrtt(Conv * s)19109ef1f84bSDavid du Colombier tcpsynackrtt(Conv *s)
19119ef1f84bSDavid du Colombier {
19129ef1f84bSDavid du Colombier 	Tcpctl *tcb;
19139ef1f84bSDavid du Colombier 	int delta;
19149ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
19159ef1f84bSDavid du Colombier 
19169ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
19179ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
19189ef1f84bSDavid du Colombier 
19199ef1f84bSDavid du Colombier 	delta = NOW - tcb->sndsyntime;
19209ef1f84bSDavid du Colombier 	tcb->srtt = delta<<LOGAGAIN;
19219ef1f84bSDavid du Colombier 	tcb->mdev = delta<<LOGDGAIN;
19229ef1f84bSDavid du Colombier 
19239ef1f84bSDavid du Colombier 	/* halt round trip timer */
19249ef1f84bSDavid du Colombier 	tcphalt(tpriv, &tcb->rtt_timer);
19259ef1f84bSDavid du Colombier }
19269ef1f84bSDavid du Colombier 
19279ef1f84bSDavid du Colombier static void
update(Conv * s,Tcp * seg)19289ef1f84bSDavid du Colombier update(Conv *s, Tcp *seg)
19299ef1f84bSDavid du Colombier {
19309ef1f84bSDavid du Colombier 	int rtt, delta;
19319ef1f84bSDavid du Colombier 	Tcpctl *tcb;
19329ef1f84bSDavid du Colombier 	ulong acked;
19339ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
19349ef1f84bSDavid du Colombier 
19359ef1f84bSDavid du Colombier 	if(seg->update)
19369ef1f84bSDavid du Colombier 		return;
19379ef1f84bSDavid du Colombier 	seg->update = 1;
19389ef1f84bSDavid du Colombier 
19399ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
19409ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
19419ef1f84bSDavid du Colombier 
19429ef1f84bSDavid du Colombier 	/* catch zero-window updates, update window & recover */
19439ef1f84bSDavid du Colombier 	if(tcb->snd.wnd == 0 && seg->wnd > 0 &&
19449ef1f84bSDavid du Colombier 	    seq_lt(seg->ack, tcb->snd.ptr)){
19459ef1f84bSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcp: zwu ack %lud una %lud ptr %lud win %lud\n",
19469ef1f84bSDavid du Colombier 			seg->ack,  tcb->snd.una, tcb->snd.ptr, seg->wnd);
19479ef1f84bSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19489ef1f84bSDavid du Colombier 		goto recovery;
19499ef1f84bSDavid du Colombier 	}
19509ef1f84bSDavid du Colombier 
19519ef1f84bSDavid du Colombier 	/* newreno fast retransmit */
19529ef1f84bSDavid du Colombier 	if(seg->ack == tcb->snd.una && tcb->snd.una != tcb->snd.nxt &&
19539ef1f84bSDavid du Colombier 	    ++tcb->snd.dupacks == 3){		/* was TCPREXMTTHRESH */
19549ef1f84bSDavid du Colombier recovery:
19559ef1f84bSDavid du Colombier 		if(tcb->snd.recovery){
19569ef1f84bSDavid du Colombier 			tpriv->stats[RecoveryCwind]++;
19579ef1f84bSDavid du Colombier 			tcb->cwind += tcb->mss;
19589ef1f84bSDavid du Colombier 		}else if(seq_le(tcb->snd.rxt, seg->ack)){
19599ef1f84bSDavid du Colombier 			tpriv->stats[Recovery]++;
19609ef1f84bSDavid du Colombier 			tcb->abcbytes = 0;
19619ef1f84bSDavid du Colombier 			tcb->snd.recovery = 1;
19629ef1f84bSDavid du Colombier 			tcb->snd.partialack = 0;
19639ef1f84bSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
19649ef1f84bSDavid du Colombier 			tcpcongestion(tcb);
19659ef1f84bSDavid du Colombier 			tcb->cwind = tcb->ssthresh + 3*tcb->mss;
19669ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery inflate %ld ss %ld @%lud\n",
19679ef1f84bSDavid du Colombier 				tcb->cwind, tcb->ssthresh, tcb->snd.rxt);
19689ef1f84bSDavid du Colombier 			tcprxmit(s);
19699ef1f84bSDavid du Colombier 		}else{
19709ef1f84bSDavid du Colombier 			tpriv->stats[RecoveryNoSeq]++;
19719ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "!recov %lud not ≤ %lud %ld\n",
19729ef1f84bSDavid du Colombier 				tcb->snd.rxt, seg->ack, tcb->snd.rxt - seg->ack);
19739ef1f84bSDavid du Colombier 			/* don't enter fast retransmit, don't change ssthresh */
19749ef1f84bSDavid du Colombier 		}
19759ef1f84bSDavid du Colombier 	}else if(tcb->snd.recovery){
19769ef1f84bSDavid du Colombier 		tpriv->stats[RecoveryCwind]++;
19779ef1f84bSDavid du Colombier 		tcb->cwind += tcb->mss;
19789ef1f84bSDavid du Colombier 	}
19799ef1f84bSDavid du Colombier 
19809ef1f84bSDavid du Colombier 	/*
19819ef1f84bSDavid du Colombier 	 *  update window
19829ef1f84bSDavid du Colombier 	 */
19839ef1f84bSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.wl2)
19849ef1f84bSDavid du Colombier 	|| (tcb->snd.wl2 == seg->ack && seg->wnd > tcb->snd.wnd)){
19859ef1f84bSDavid du Colombier 		/* clear dupack if we advance wl2 */
19869ef1f84bSDavid du Colombier 		if(tcb->snd.wl2 != seg->ack)
19879ef1f84bSDavid du Colombier 			tcb->snd.dupacks = 0;
19889ef1f84bSDavid du Colombier 		tcb->snd.wnd = seg->wnd;
19899ef1f84bSDavid du Colombier 		tcb->snd.wl2 = seg->ack;
19909ef1f84bSDavid du Colombier 	}
19919ef1f84bSDavid du Colombier 
19929ef1f84bSDavid du Colombier 	if(!seq_gt(seg->ack, tcb->snd.una)){
19939ef1f84bSDavid du Colombier 		/*
19949ef1f84bSDavid du Colombier 		 *  don't let us hangup if sending into a closed window and
19959ef1f84bSDavid du Colombier 		 *  we're still getting acks
19969ef1f84bSDavid du Colombier 		 */
19979ef1f84bSDavid du Colombier 		if((tcb->flags&RETRAN) && tcb->snd.wnd == 0)
19989ef1f84bSDavid du Colombier 			tcb->backedoff = MAXBACKMS/4;
19999ef1f84bSDavid du Colombier 		return;
20009ef1f84bSDavid du Colombier 	}
20019ef1f84bSDavid du Colombier 
20029ef1f84bSDavid du Colombier 	/* Compute the new send window size */
20039ef1f84bSDavid du Colombier 	acked = seg->ack - tcb->snd.una;
20049ef1f84bSDavid du Colombier 
20059ef1f84bSDavid du Colombier 	/* avoid slow start and timers for SYN acks */
20069ef1f84bSDavid du Colombier 	if((tcb->flags & SYNACK) == 0) {
20079ef1f84bSDavid du Colombier 		tcb->flags |= SYNACK;
20089ef1f84bSDavid du Colombier 		acked--;
20099ef1f84bSDavid du Colombier 		tcb->flgcnt--;
20109ef1f84bSDavid du Colombier 		goto done;
20119ef1f84bSDavid du Colombier 	}
20129ef1f84bSDavid du Colombier 
20139ef1f84bSDavid du Colombier 	/*
20149ef1f84bSDavid du Colombier 	 * congestion control
20159ef1f84bSDavid du Colombier 	 */
20169ef1f84bSDavid du Colombier 	if(tcb->snd.recovery){
20179ef1f84bSDavid du Colombier 		if(seq_ge(seg->ack, tcb->snd.rxt)){
20189ef1f84bSDavid du Colombier 			/* recovery finished; deflate window */
20199ef1f84bSDavid du Colombier 			tpriv->stats[RecoveryDone]++;
20209ef1f84bSDavid du Colombier 			tcb->snd.dupacks = 0;
20219ef1f84bSDavid du Colombier 			tcb->snd.recovery = 0;
20229ef1f84bSDavid du Colombier 			tcb->cwind = (tcb->snd.nxt - tcb->snd.una) + tcb->mss;
20239ef1f84bSDavid du Colombier 			if(tcb->ssthresh < tcb->cwind)
20249ef1f84bSDavid du Colombier 				tcb->cwind = tcb->ssthresh;
20259ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "recovery deflate %ld %ld\n",
20269ef1f84bSDavid du Colombier 				tcb->cwind, tcb->ssthresh);
20279ef1f84bSDavid du Colombier 		} else {
20289ef1f84bSDavid du Colombier 			/* partial ack; we lost more than one segment */
20299ef1f84bSDavid du Colombier 			tpriv->stats[RecoveryPA]++;
20309ef1f84bSDavid du Colombier 			if(tcb->cwind > acked)
20319ef1f84bSDavid du Colombier 				tcb->cwind -= acked;
20329ef1f84bSDavid du Colombier 			else{
20339ef1f84bSDavid du Colombier 				netlog(s->p->f, Logtcpwin, "partial ack neg\n");
20349ef1f84bSDavid du Colombier 				tcb->cwind = tcb->mss;
20359ef1f84bSDavid du Colombier 			}
20369ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcpwin, "partial ack %ld left %ld cwind %ld\n",
20379ef1f84bSDavid du Colombier 				acked, tcb->snd.rxt - seg->ack, tcb->cwind);
20389ef1f84bSDavid du Colombier 
20399ef1f84bSDavid du Colombier 			if(acked >= tcb->mss)
20409ef1f84bSDavid du Colombier 				tcb->cwind += tcb->mss;
20419ef1f84bSDavid du Colombier 			tcb->snd.partialack++;
20429ef1f84bSDavid du Colombier 		}
20439ef1f84bSDavid du Colombier 	} else
20449ef1f84bSDavid du Colombier 		tcpabcincr(tcb, acked);
20459ef1f84bSDavid du Colombier 
20469ef1f84bSDavid du Colombier 	/* Adjust the timers according to the round trip time */
20479ef1f84bSDavid du Colombier 	/* TODO: fix sloppy treatment of overflow cases here. */
20489ef1f84bSDavid du Colombier 	if(tcb->rtt_timer.state == TcptimerON && seq_ge(seg->ack, tcb->rttseq)) {
20499ef1f84bSDavid du Colombier 		tcphalt(tpriv, &tcb->rtt_timer);
20509ef1f84bSDavid du Colombier 		if((tcb->flags&RETRAN) == 0) {
20519ef1f84bSDavid du Colombier 			tcb->backoff = 0;
20529ef1f84bSDavid du Colombier 			tcb->backedoff = 0;
20539ef1f84bSDavid du Colombier 			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
20549ef1f84bSDavid du Colombier 			if(rtt == 0)
20559ef1f84bSDavid du Colombier 				rtt = 1; /* else all close sys's will rexmit in 0 time */
20569ef1f84bSDavid du Colombier 			rtt *= MSPTICK;
20579ef1f84bSDavid du Colombier 			if(tcb->srtt == 0) {
20589ef1f84bSDavid du Colombier 				tcb->srtt = rtt << LOGAGAIN;
20599ef1f84bSDavid du Colombier 				tcb->mdev = rtt << LOGDGAIN;
20609ef1f84bSDavid du Colombier 			} else {
20619ef1f84bSDavid du Colombier 				delta = rtt - (tcb->srtt>>LOGAGAIN);
20629ef1f84bSDavid du Colombier 				tcb->srtt += delta;
20639ef1f84bSDavid du Colombier 				if(tcb->srtt <= 0)
20649ef1f84bSDavid du Colombier 					tcb->srtt = 1;
20659ef1f84bSDavid du Colombier 
20669ef1f84bSDavid du Colombier 				delta = abs(delta) - (tcb->mdev>>LOGDGAIN);
20679ef1f84bSDavid du Colombier 				tcb->mdev += delta;
20689ef1f84bSDavid du Colombier 				if(tcb->mdev <= 0)
20699ef1f84bSDavid du Colombier 					tcb->mdev = 1;
20709ef1f84bSDavid du Colombier 			}
20719ef1f84bSDavid du Colombier 			tcpsettimer(tcb);
20729ef1f84bSDavid du Colombier 		}
20739ef1f84bSDavid du Colombier 	}
20749ef1f84bSDavid du Colombier 
20759ef1f84bSDavid du Colombier done:
20769ef1f84bSDavid du Colombier 	if(qdiscard(s->wq, acked) < acked)
20779ef1f84bSDavid du Colombier 		tcb->flgcnt--;
20789ef1f84bSDavid du Colombier 	tcb->snd.una = seg->ack;
20799ef1f84bSDavid du Colombier 
20809ef1f84bSDavid du Colombier 	/* newreno fast recovery */
20819ef1f84bSDavid du Colombier 	if(tcb->snd.recovery)
20829ef1f84bSDavid du Colombier 		tcprxmit(s);
20839ef1f84bSDavid du Colombier 
20849ef1f84bSDavid du Colombier 	if(seq_gt(seg->ack, tcb->snd.urg))
20859ef1f84bSDavid du Colombier 		tcb->snd.urg = seg->ack;
20869ef1f84bSDavid du Colombier 
20879ef1f84bSDavid du Colombier 	if(tcb->snd.una != tcb->snd.nxt){
20889ef1f84bSDavid du Colombier 		/* `impatient' variant */
20899ef1f84bSDavid du Colombier 		if(!tcb->snd.recovery || tcb->snd.partialack == 1){
20909ef1f84bSDavid du Colombier 			tcb->time = NOW;
20919ef1f84bSDavid du Colombier 			tcb->timeuna = tcb->snd.una;
20929ef1f84bSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
20939ef1f84bSDavid du Colombier 		}
20949ef1f84bSDavid du Colombier 	} else
20959ef1f84bSDavid du Colombier 		tcphalt(tpriv, &tcb->timer);
20969ef1f84bSDavid du Colombier 
20979ef1f84bSDavid du Colombier 	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
20989ef1f84bSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
20999ef1f84bSDavid du Colombier 
21009ef1f84bSDavid du Colombier 	if(!tcb->snd.recovery)
21019ef1f84bSDavid du Colombier 		tcb->flags &= ~RETRAN;
21029ef1f84bSDavid du Colombier 	tcb->backoff = 0;
21039ef1f84bSDavid du Colombier 	tcb->backedoff = 0;
21049ef1f84bSDavid du Colombier }
21059ef1f84bSDavid du Colombier 
21069ef1f84bSDavid du Colombier static void
tcpiput(Proto * tcp,Ipifc *,Block * bp)21079ef1f84bSDavid du Colombier tcpiput(Proto *tcp, Ipifc*, Block *bp)
21089ef1f84bSDavid du Colombier {
21099ef1f84bSDavid du Colombier 	Tcp seg;
21109ef1f84bSDavid du Colombier 	Tcp4hdr *h4;
21119ef1f84bSDavid du Colombier 	Tcp6hdr *h6;
21129ef1f84bSDavid du Colombier 	int hdrlen;
21139ef1f84bSDavid du Colombier 	Tcpctl *tcb;
21149ef1f84bSDavid du Colombier 	ushort length, csum;
21159ef1f84bSDavid du Colombier 	uchar source[IPaddrlen], dest[IPaddrlen];
21169ef1f84bSDavid du Colombier 	Conv *s;
21179ef1f84bSDavid du Colombier 	Fs *f;
21189ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
21199ef1f84bSDavid du Colombier 	uchar version;
21209ef1f84bSDavid du Colombier 
21219ef1f84bSDavid du Colombier 	f = tcp->f;
21229ef1f84bSDavid du Colombier 	tpriv = tcp->priv;
21239ef1f84bSDavid du Colombier 
21249ef1f84bSDavid du Colombier 	tpriv->stats[InSegs]++;
21259ef1f84bSDavid du Colombier 
21269ef1f84bSDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
21279ef1f84bSDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
21289ef1f84bSDavid du Colombier 
21299ef1f84bSDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
21309ef1f84bSDavid du Colombier 		version = V4;
21319ef1f84bSDavid du Colombier 		length = nhgets(h4->length);
21329ef1f84bSDavid du Colombier 		v4tov6(dest, h4->tcpdst);
21339ef1f84bSDavid du Colombier 		v4tov6(source, h4->tcpsrc);
21349ef1f84bSDavid du Colombier 
21359ef1f84bSDavid du Colombier 		h4->Unused = 0;
21369ef1f84bSDavid du Colombier 		hnputs(h4->tcplen, length-TCP4_PKT);
21379ef1f84bSDavid du Colombier 		if(!(bp->flag & Btcpck) && (h4->tcpcksum[0] || h4->tcpcksum[1]) &&
21389ef1f84bSDavid du Colombier 			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
21399ef1f84bSDavid du Colombier 			tpriv->stats[CsumErrs]++;
21409ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
21419ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "bad tcp proto cksum\n");
21429ef1f84bSDavid du Colombier 			freeblist(bp);
21439ef1f84bSDavid du Colombier 			return;
21449ef1f84bSDavid du Colombier 		}
21459ef1f84bSDavid du Colombier 
21469ef1f84bSDavid du Colombier 		hdrlen = ntohtcp4(&seg, &bp);
21479ef1f84bSDavid du Colombier 		if(hdrlen < 0){
21489ef1f84bSDavid du Colombier 			tpriv->stats[HlenErrs]++;
21499ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
21509ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "bad tcp hdr len\n");
21519ef1f84bSDavid du Colombier 			return;
21529ef1f84bSDavid du Colombier 		}
21539ef1f84bSDavid du Colombier 
21549ef1f84bSDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
21559ef1f84bSDavid du Colombier 		length -= hdrlen+TCP4_PKT;
21569ef1f84bSDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP4_PKT, length);
21579ef1f84bSDavid du Colombier 		if(bp == nil){
21589ef1f84bSDavid du Colombier 			tpriv->stats[LenErrs]++;
21599ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
21609ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "tcp len < 0 after trim\n");
21619ef1f84bSDavid du Colombier 			return;
21629ef1f84bSDavid du Colombier 		}
21639ef1f84bSDavid du Colombier 	}
21649ef1f84bSDavid du Colombier 	else {
21659ef1f84bSDavid du Colombier 		int ttl = h6->ttl;
21669ef1f84bSDavid du Colombier 		int proto = h6->proto;
21679ef1f84bSDavid du Colombier 
21689ef1f84bSDavid du Colombier 		version = V6;
21699ef1f84bSDavid du Colombier 		length = nhgets(h6->ploadlen);
21709ef1f84bSDavid du Colombier 		ipmove(dest, h6->tcpdst);
21719ef1f84bSDavid du Colombier 		ipmove(source, h6->tcpsrc);
21729ef1f84bSDavid du Colombier 
21739ef1f84bSDavid du Colombier 		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
21749ef1f84bSDavid du Colombier 		h6->ttl = proto;
21759ef1f84bSDavid du Colombier 		hnputl(h6->vcf, length);
21769ef1f84bSDavid du Colombier 		if((h6->tcpcksum[0] || h6->tcpcksum[1]) &&
21779ef1f84bSDavid du Colombier 		    (csum = ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) != 0) {
21789ef1f84bSDavid du Colombier 			tpriv->stats[CsumErrs]++;
21799ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
21809ef1f84bSDavid du Colombier 			netlog(f, Logtcp,
21819ef1f84bSDavid du Colombier 			    "bad tcpv6 proto cksum: got %#ux, computed %#ux\n",
21829ef1f84bSDavid du Colombier 				h6->tcpcksum[0]<<8 | h6->tcpcksum[1], csum);
21839ef1f84bSDavid du Colombier 			freeblist(bp);
21849ef1f84bSDavid du Colombier 			return;
21859ef1f84bSDavid du Colombier 		}
21869ef1f84bSDavid du Colombier 		h6->ttl = ttl;
21879ef1f84bSDavid du Colombier 		h6->proto = proto;
21889ef1f84bSDavid du Colombier 		hnputs(h6->ploadlen, length);
21899ef1f84bSDavid du Colombier 
21909ef1f84bSDavid du Colombier 		hdrlen = ntohtcp6(&seg, &bp);
21919ef1f84bSDavid du Colombier 		if(hdrlen < 0){
21929ef1f84bSDavid du Colombier 			tpriv->stats[HlenErrs]++;
21939ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
21949ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "bad tcpv6 hdr len\n");
21959ef1f84bSDavid du Colombier 			return;
21969ef1f84bSDavid du Colombier 		}
21979ef1f84bSDavid du Colombier 
21989ef1f84bSDavid du Colombier 		/* trim the packet to the size claimed by the datagram */
21999ef1f84bSDavid du Colombier 		length -= hdrlen;
22009ef1f84bSDavid du Colombier 		bp = trimblock(bp, hdrlen+TCP6_PKT, length);
22019ef1f84bSDavid du Colombier 		if(bp == nil){
22029ef1f84bSDavid du Colombier 			tpriv->stats[LenErrs]++;
22039ef1f84bSDavid du Colombier 			tpriv->stats[InErrs]++;
22049ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "tcpv6 len < 0 after trim\n");
22059ef1f84bSDavid du Colombier 			return;
22069ef1f84bSDavid du Colombier 		}
22079ef1f84bSDavid du Colombier 	}
22089ef1f84bSDavid du Colombier 
22099ef1f84bSDavid du Colombier 	/* lock protocol while searching for a conversation */
22109ef1f84bSDavid du Colombier 	qlock(tcp);
22119ef1f84bSDavid du Colombier 
22129ef1f84bSDavid du Colombier 	/* Look for a matching conversation */
22139ef1f84bSDavid du Colombier 	s = iphtlook(&tpriv->ht, source, seg.source, dest, seg.dest);
22149ef1f84bSDavid du Colombier 	if(s == nil){
22159ef1f84bSDavid du Colombier 		netlog(f, Logtcp, "iphtlook(src %I!%d, dst %I!%d) failed\n",
22169ef1f84bSDavid du Colombier 			source, seg.source, dest, seg.dest);
22179ef1f84bSDavid du Colombier reset:
22189ef1f84bSDavid du Colombier 		qunlock(tcp);
22199ef1f84bSDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "no conversation");
22209ef1f84bSDavid du Colombier 		freeblist(bp);
22219ef1f84bSDavid du Colombier 		return;
22229ef1f84bSDavid du Colombier 	}
22239ef1f84bSDavid du Colombier 
22249ef1f84bSDavid du Colombier 	/* if it's a listener, look for the right flags and get a new conv */
22259ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
22269ef1f84bSDavid du Colombier 	if(tcb->state == Listen){
22279ef1f84bSDavid du Colombier 		if(seg.flags & RST){
22289ef1f84bSDavid du Colombier 			limborst(s, &seg, source, dest, version);
22299ef1f84bSDavid du Colombier 			qunlock(tcp);
22309ef1f84bSDavid du Colombier 			freeblist(bp);
22319ef1f84bSDavid du Colombier 			return;
22329ef1f84bSDavid du Colombier 		}
22339ef1f84bSDavid du Colombier 
22349ef1f84bSDavid du Colombier 		/* if this is a new SYN, put the call into limbo */
22359ef1f84bSDavid du Colombier 		if((seg.flags & SYN) && (seg.flags & ACK) == 0){
22369ef1f84bSDavid du Colombier 			limbo(s, source, dest, &seg, version);
22379ef1f84bSDavid du Colombier 			qunlock(tcp);
22389ef1f84bSDavid du Colombier 			freeblist(bp);
22399ef1f84bSDavid du Colombier 			return;
22409ef1f84bSDavid du Colombier 		}
22419ef1f84bSDavid du Colombier 
22429ef1f84bSDavid du Colombier 		/*
22439ef1f84bSDavid du Colombier 		 *  if there's a matching call in limbo, tcpincoming will
22449ef1f84bSDavid du Colombier 		 *  return it in state Syn_received
22459ef1f84bSDavid du Colombier 		 */
22469ef1f84bSDavid du Colombier 		s = tcpincoming(s, &seg, source, dest, version);
22479ef1f84bSDavid du Colombier 		if(s == nil)
22489ef1f84bSDavid du Colombier 			goto reset;
22499ef1f84bSDavid du Colombier 	}
22509ef1f84bSDavid du Colombier 
22519ef1f84bSDavid du Colombier 	/* The rest of the input state machine is run with the control block
22529ef1f84bSDavid du Colombier 	 * locked and implements the state machine directly out of the RFC.
22539ef1f84bSDavid du Colombier 	 * Out-of-band data is ignored - it was always a bad idea.
22549ef1f84bSDavid du Colombier 	 */
22559ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
22569ef1f84bSDavid du Colombier 	if(waserror()){
22579ef1f84bSDavid du Colombier 		qunlock(s);
22589ef1f84bSDavid du Colombier 		nexterror();
22599ef1f84bSDavid du Colombier 	}
22609ef1f84bSDavid du Colombier 	qlock(s);
22619ef1f84bSDavid du Colombier 	qunlock(tcp);
22629ef1f84bSDavid du Colombier 
22639ef1f84bSDavid du Colombier 	/* fix up window */
22649ef1f84bSDavid du Colombier 	seg.wnd <<= tcb->rcv.scale;
22659ef1f84bSDavid du Colombier 
22669ef1f84bSDavid du Colombier 	/* every input packet in puts off the keep alive time out */
22679ef1f84bSDavid du Colombier 	tcpsetkacounter(tcb);
22689ef1f84bSDavid du Colombier 
22699ef1f84bSDavid du Colombier 	switch(tcb->state) {
22709ef1f84bSDavid du Colombier 	case Closed:
22719ef1f84bSDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
22729ef1f84bSDavid du Colombier 		goto raise;
22739ef1f84bSDavid du Colombier 	case Syn_sent:
22749ef1f84bSDavid du Colombier 		if(seg.flags & ACK) {
22759ef1f84bSDavid du Colombier 			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
22769ef1f84bSDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
22779ef1f84bSDavid du Colombier 					 "bad seq in Syn_sent");
22789ef1f84bSDavid du Colombier 				goto raise;
22799ef1f84bSDavid du Colombier 			}
22809ef1f84bSDavid du Colombier 		}
22819ef1f84bSDavid du Colombier 		if(seg.flags & RST) {
22829ef1f84bSDavid du Colombier 			if(seg.flags & ACK)
22839ef1f84bSDavid du Colombier 				localclose(s, Econrefused);
22849ef1f84bSDavid du Colombier 			goto raise;
22859ef1f84bSDavid du Colombier 		}
22869ef1f84bSDavid du Colombier 
22879ef1f84bSDavid du Colombier 		if(seg.flags & SYN) {
22889ef1f84bSDavid du Colombier 			procsyn(s, &seg);
22899ef1f84bSDavid du Colombier 			if(seg.flags & ACK){
22909ef1f84bSDavid du Colombier 				update(s, &seg);
22919ef1f84bSDavid du Colombier 				tcpsynackrtt(s);
22929ef1f84bSDavid du Colombier 				tcpsetstate(s, Established);
22939ef1f84bSDavid du Colombier 				tcpsetscale(s, tcb, seg.ws, tcb->scale);
22949ef1f84bSDavid du Colombier 			}
22959ef1f84bSDavid du Colombier 			else {
22969ef1f84bSDavid du Colombier 				tcb->time = NOW;
22979ef1f84bSDavid du Colombier 				tcpsetstate(s, Syn_received);	/* DLP - shouldn't this be a reset? */
22989ef1f84bSDavid du Colombier 			}
22999ef1f84bSDavid du Colombier 
23009ef1f84bSDavid du Colombier 			if(length != 0 || (seg.flags & FIN))
23019ef1f84bSDavid du Colombier 				break;
23029ef1f84bSDavid du Colombier 
23039ef1f84bSDavid du Colombier 			freeblist(bp);
23049ef1f84bSDavid du Colombier 			goto output;
23059ef1f84bSDavid du Colombier 		}
23069ef1f84bSDavid du Colombier 		else
23079ef1f84bSDavid du Colombier 			freeblist(bp);
23089ef1f84bSDavid du Colombier 
23099ef1f84bSDavid du Colombier 		qunlock(s);
23109ef1f84bSDavid du Colombier 		poperror();
23119ef1f84bSDavid du Colombier 		return;
23129ef1f84bSDavid du Colombier 	case Syn_received:
23139ef1f84bSDavid du Colombier 		/* doesn't matter if it's the correct ack, we're just trying to set timing */
23149ef1f84bSDavid du Colombier 		if(seg.flags & ACK)
23159ef1f84bSDavid du Colombier 			tcpsynackrtt(s);
23169ef1f84bSDavid du Colombier 		break;
23179ef1f84bSDavid du Colombier 	}
23189ef1f84bSDavid du Colombier 
23199ef1f84bSDavid du Colombier 	/*
23209ef1f84bSDavid du Colombier 	 *  One DOS attack is to open connections to us and then forget about them,
23219ef1f84bSDavid du Colombier 	 *  thereby tying up a conv at no long term cost to the attacker.
23229ef1f84bSDavid du Colombier 	 *  This is an attempt to defeat these stateless DOS attacks.  See
23239ef1f84bSDavid du Colombier 	 *  corresponding code in tcpsendka().
23249ef1f84bSDavid du Colombier 	 */
23259ef1f84bSDavid du Colombier 	if(tcb->state != Syn_received && (seg.flags & RST) == 0){
23269ef1f84bSDavid du Colombier 		if(tcpporthogdefense
23279ef1f84bSDavid du Colombier 		&& seq_within(seg.ack, tcb->snd.una-(1<<31), tcb->snd.una-(1<<29))){
23289ef1f84bSDavid du Colombier 			print("stateless hog %I.%d->%I.%d f %ux %lux - %lux - %lux\n",
23299ef1f84bSDavid du Colombier 				source, seg.source, dest, seg.dest, seg.flags,
23309ef1f84bSDavid du Colombier 				tcb->snd.una-(1<<31), seg.ack, tcb->snd.una-(1<<29));
23319ef1f84bSDavid du Colombier 			localclose(s, "stateless hog");
23329ef1f84bSDavid du Colombier 		}
23339ef1f84bSDavid du Colombier 	}
23349ef1f84bSDavid du Colombier 
23359ef1f84bSDavid du Colombier 	/* Cut the data to fit the receive window */
23369ef1f84bSDavid du Colombier 	tcprcvwin(s);
23379ef1f84bSDavid du Colombier 	if(tcptrim(tcb, &seg, &bp, &length) == -1) {
23389ef1f84bSDavid du Colombier 		if(seg.seq+1 != tcb->rcv.nxt || length != 1)
23399ef1f84bSDavid du Colombier 		netlog(f, Logtcp, "tcp: trim: !inwind: seq %lud-%lud win "
23409ef1f84bSDavid du Colombier 			"%lud-%lud l %d from %I\n", seg.seq,
23419ef1f84bSDavid du Colombier 			seg.seq + length - 1, tcb->rcv.nxt,
23429ef1f84bSDavid du Colombier 			tcb->rcv.nxt + tcb->rcv.wnd-1, length, s->raddr);
23439ef1f84bSDavid du Colombier 		update(s, &seg);
23449ef1f84bSDavid du Colombier 		if(qlen(s->wq)+tcb->flgcnt == 0 && tcb->state == Closing) {
23459ef1f84bSDavid du Colombier 			tcphalt(tpriv, &tcb->rtt_timer);
23469ef1f84bSDavid du Colombier 			tcphalt(tpriv, &tcb->acktimer);
23479ef1f84bSDavid du Colombier 			tcphalt(tpriv, &tcb->katimer);
23489ef1f84bSDavid du Colombier 			tcpsetstate(s, Time_wait);
23499ef1f84bSDavid du Colombier 			tcb->timer.start = MSL2*(1000 / MSPTICK);
23509ef1f84bSDavid du Colombier 			tcpgo(tpriv, &tcb->timer);
23519ef1f84bSDavid du Colombier 		}
23529ef1f84bSDavid du Colombier 		if(!(seg.flags & RST)) {
23539ef1f84bSDavid du Colombier 			tcb->flags |= FORCE;
23549ef1f84bSDavid du Colombier 			goto output;
23559ef1f84bSDavid du Colombier 		}
23569ef1f84bSDavid du Colombier 		qunlock(s);
23579ef1f84bSDavid du Colombier 		poperror();
23589ef1f84bSDavid du Colombier 		return;
23599ef1f84bSDavid du Colombier 	}
23609ef1f84bSDavid du Colombier 
23619ef1f84bSDavid du Colombier 	/* Cannot accept so answer with a rst */
23629ef1f84bSDavid du Colombier 	if(length && tcb->state == Closed) {
23639ef1f84bSDavid du Colombier 		sndrst(tcp, source, dest, length, &seg, version, "sending to Closed");
23649ef1f84bSDavid du Colombier 		goto raise;
23659ef1f84bSDavid du Colombier 	}
23669ef1f84bSDavid du Colombier 
23679ef1f84bSDavid du Colombier 	/* The segment is beyond the current receive pointer so
23689ef1f84bSDavid du Colombier 	 * queue the data in the resequence queue
23699ef1f84bSDavid du Colombier 	 */
23709ef1f84bSDavid du Colombier 	if(seg.seq != tcb->rcv.nxt)
23719ef1f84bSDavid du Colombier 	if(length != 0 || (seg.flags & (SYN|FIN))) {
23729ef1f84bSDavid du Colombier 		update(s, &seg);
23739ef1f84bSDavid du Colombier 		if(addreseq(f, tcb, tpriv, &seg, bp, length) < 0)
23749ef1f84bSDavid du Colombier 			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport,
23759ef1f84bSDavid du Colombier 				s->laddr, s->lport);
23769ef1f84bSDavid du Colombier 		tcb->flags |= FORCE;	/* force duplicate ack; RFC 5681 §3.2 */
23779ef1f84bSDavid du Colombier 		goto output;
23789ef1f84bSDavid du Colombier 	}
23799ef1f84bSDavid du Colombier 
23809ef1f84bSDavid du Colombier 	if(tcb->nreseq > 0)
23819ef1f84bSDavid du Colombier 		tcb->flags |= FORCE; /* filled hole in seq. space; RFC 5681 §3.2 */
23829ef1f84bSDavid du Colombier 
23839ef1f84bSDavid du Colombier 	/*
23849ef1f84bSDavid du Colombier 	 *  keep looping till we've processed this packet plus any
23859ef1f84bSDavid du Colombier 	 *  adjacent packets in the resequence queue
23869ef1f84bSDavid du Colombier 	 */
23879ef1f84bSDavid du Colombier 	for(;;) {
23889ef1f84bSDavid du Colombier 		if(seg.flags & RST) {
23899ef1f84bSDavid du Colombier 			if(tcb->state == Established) {
23909ef1f84bSDavid du Colombier 				tpriv->stats[EstabResets]++;
23919ef1f84bSDavid du Colombier 				if(tcb->rcv.nxt != seg.seq)
23929ef1f84bSDavid du Colombier 					print("out of order RST rcvd: %I.%d -> "
23939ef1f84bSDavid du Colombier 						"%I.%d, rcv.nxt %lux seq %lux\n",
23949ef1f84bSDavid du Colombier 						s->raddr, s->rport, s->laddr,
23959ef1f84bSDavid du Colombier 						s->lport, tcb->rcv.nxt, seg.seq);
23969ef1f84bSDavid du Colombier 			}
23979ef1f84bSDavid du Colombier 			localclose(s, Econrefused);
23989ef1f84bSDavid du Colombier 			goto raise;
23999ef1f84bSDavid du Colombier 		}
24009ef1f84bSDavid du Colombier 
24019ef1f84bSDavid du Colombier 		if((seg.flags&ACK) == 0)
24029ef1f84bSDavid du Colombier 			goto raise;
24039ef1f84bSDavid du Colombier 
24049ef1f84bSDavid du Colombier 		switch(tcb->state) {
24059ef1f84bSDavid du Colombier 		case Syn_received:
24069ef1f84bSDavid du Colombier 			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
24079ef1f84bSDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
24089ef1f84bSDavid du Colombier 					"bad seq in Syn_received");
24099ef1f84bSDavid du Colombier 				goto raise;
24109ef1f84bSDavid du Colombier 			}
24119ef1f84bSDavid du Colombier 			update(s, &seg);
24129ef1f84bSDavid du Colombier 			tcpsetstate(s, Established);
24139ef1f84bSDavid du Colombier 		case Established:
24149ef1f84bSDavid du Colombier 		case Close_wait:
24159ef1f84bSDavid du Colombier 			update(s, &seg);
24169ef1f84bSDavid du Colombier 			break;
24179ef1f84bSDavid du Colombier 		case Finwait1:
24189ef1f84bSDavid du Colombier 			update(s, &seg);
24199ef1f84bSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0){
24209ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
24219ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
24229ef1f84bSDavid du Colombier 				tcpsetkacounter(tcb);
24239ef1f84bSDavid du Colombier 				tcb->time = NOW;
24249ef1f84bSDavid du Colombier 				tcpsetstate(s, Finwait2);
24259ef1f84bSDavid du Colombier 				tcb->katimer.start = MSL2 * (1000 / MSPTICK);
24269ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->katimer);
24279ef1f84bSDavid du Colombier 			}
24289ef1f84bSDavid du Colombier 			break;
24299ef1f84bSDavid du Colombier 		case Finwait2:
24309ef1f84bSDavid du Colombier 			update(s, &seg);
24319ef1f84bSDavid du Colombier 			break;
24329ef1f84bSDavid du Colombier 		case Closing:
24339ef1f84bSDavid du Colombier 			update(s, &seg);
24349ef1f84bSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
24359ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
24369ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
24379ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
24389ef1f84bSDavid du Colombier 				tcpsetstate(s, Time_wait);
24399ef1f84bSDavid du Colombier 				tcb->timer.start = MSL2*(1000 / MSPTICK);
24409ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24419ef1f84bSDavid du Colombier 			}
24429ef1f84bSDavid du Colombier 			break;
24439ef1f84bSDavid du Colombier 		case Last_ack:
24449ef1f84bSDavid du Colombier 			update(s, &seg);
24459ef1f84bSDavid du Colombier 			if(qlen(s->wq)+tcb->flgcnt == 0) {
24469ef1f84bSDavid du Colombier 				localclose(s, nil);
24479ef1f84bSDavid du Colombier 				goto raise;
24489ef1f84bSDavid du Colombier 			}
24499ef1f84bSDavid du Colombier 		case Time_wait:
24509ef1f84bSDavid du Colombier 			tcb->flags |= FORCE;
24519ef1f84bSDavid du Colombier 			if(tcb->timer.state != TcptimerON)
24529ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
24539ef1f84bSDavid du Colombier 		}
24549ef1f84bSDavid du Colombier 
24559ef1f84bSDavid du Colombier 		if((seg.flags&URG) && seg.urg) {
24569ef1f84bSDavid du Colombier 			if(seq_gt(seg.urg + seg.seq, tcb->rcv.urg)) {
24579ef1f84bSDavid du Colombier 				tcb->rcv.urg = seg.urg + seg.seq;
24589ef1f84bSDavid du Colombier 				pullblock(&bp, seg.urg);
24599ef1f84bSDavid du Colombier 			}
24609ef1f84bSDavid du Colombier 		}
24619ef1f84bSDavid du Colombier 		else
24629ef1f84bSDavid du Colombier 		if(seq_gt(tcb->rcv.nxt, tcb->rcv.urg))
24639ef1f84bSDavid du Colombier 			tcb->rcv.urg = tcb->rcv.nxt;
24649ef1f84bSDavid du Colombier 
24659ef1f84bSDavid du Colombier 		if(length == 0) {
24669ef1f84bSDavid du Colombier 			if(bp != nil)
24679ef1f84bSDavid du Colombier 				freeblist(bp);
24689ef1f84bSDavid du Colombier 		}
24699ef1f84bSDavid du Colombier 		else {
24709ef1f84bSDavid du Colombier 			switch(tcb->state){
24719ef1f84bSDavid du Colombier 			default:
24729ef1f84bSDavid du Colombier 				/* Ignore segment text */
24739ef1f84bSDavid du Colombier 				if(bp != nil)
24749ef1f84bSDavid du Colombier 					freeblist(bp);
24759ef1f84bSDavid du Colombier 				break;
24769ef1f84bSDavid du Colombier 
24779ef1f84bSDavid du Colombier 			case Syn_received:
24789ef1f84bSDavid du Colombier 			case Established:
24799ef1f84bSDavid du Colombier 			case Finwait1:
24809ef1f84bSDavid du Colombier 				/* If we still have some data place on
24819ef1f84bSDavid du Colombier 				 * receive queue
24829ef1f84bSDavid du Colombier 				 */
24839ef1f84bSDavid du Colombier 				if(bp) {
24849ef1f84bSDavid du Colombier 					bp = packblock(bp);
24859ef1f84bSDavid du Colombier 					if(bp == nil)
24869ef1f84bSDavid du Colombier 						panic("tcp packblock");
24879ef1f84bSDavid du Colombier 					qpassnolim(s->rq, bp);
24889ef1f84bSDavid du Colombier 					bp = nil;
24899ef1f84bSDavid du Colombier 				}
24909ef1f84bSDavid du Colombier 				tcb->rcv.nxt += length;
24919ef1f84bSDavid du Colombier 
24929ef1f84bSDavid du Colombier 				/*
24939ef1f84bSDavid du Colombier 				 *  turn on the acktimer if there's something
24949ef1f84bSDavid du Colombier 				 *  to ack
24959ef1f84bSDavid du Colombier 				 */
24969ef1f84bSDavid du Colombier 				if(tcb->acktimer.state != TcptimerON)
24979ef1f84bSDavid du Colombier 					tcpgo(tpriv, &tcb->acktimer);
24989ef1f84bSDavid du Colombier 
24999ef1f84bSDavid du Colombier 				break;
25009ef1f84bSDavid du Colombier 			case Finwait2:
25019ef1f84bSDavid du Colombier 				/* no process to read the data, send a reset */
25029ef1f84bSDavid du Colombier 				if(bp != nil)
25039ef1f84bSDavid du Colombier 					freeblist(bp);
25049ef1f84bSDavid du Colombier 				sndrst(tcp, source, dest, length, &seg, version,
25059ef1f84bSDavid du Colombier 					"send to Finwait2");
25069ef1f84bSDavid du Colombier 				qunlock(s);
25079ef1f84bSDavid du Colombier 				poperror();
25089ef1f84bSDavid du Colombier 				return;
25099ef1f84bSDavid du Colombier 			}
25109ef1f84bSDavid du Colombier 		}
25119ef1f84bSDavid du Colombier 
25129ef1f84bSDavid du Colombier 		if(seg.flags & FIN) {
25139ef1f84bSDavid du Colombier 			tcb->flags |= FORCE;
25149ef1f84bSDavid du Colombier 
25159ef1f84bSDavid du Colombier 			switch(tcb->state) {
25169ef1f84bSDavid du Colombier 			case Syn_received:
25179ef1f84bSDavid du Colombier 			case Established:
25189ef1f84bSDavid du Colombier 				tcb->rcv.nxt++;
25199ef1f84bSDavid du Colombier 				tcpsetstate(s, Close_wait);
25209ef1f84bSDavid du Colombier 				break;
25219ef1f84bSDavid du Colombier 			case Finwait1:
25229ef1f84bSDavid du Colombier 				tcb->rcv.nxt++;
25239ef1f84bSDavid du Colombier 				if(qlen(s->wq)+tcb->flgcnt == 0) {
25249ef1f84bSDavid du Colombier 					tcphalt(tpriv, &tcb->rtt_timer);
25259ef1f84bSDavid du Colombier 					tcphalt(tpriv, &tcb->acktimer);
25269ef1f84bSDavid du Colombier 					tcphalt(tpriv, &tcb->katimer);
25279ef1f84bSDavid du Colombier 					tcpsetstate(s, Time_wait);
25289ef1f84bSDavid du Colombier 					tcb->timer.start = MSL2*(1000/MSPTICK);
25299ef1f84bSDavid du Colombier 					tcpgo(tpriv, &tcb->timer);
25309ef1f84bSDavid du Colombier 				}
25319ef1f84bSDavid du Colombier 				else
25329ef1f84bSDavid du Colombier 					tcpsetstate(s, Closing);
25339ef1f84bSDavid du Colombier 				break;
25349ef1f84bSDavid du Colombier 			case Finwait2:
25359ef1f84bSDavid du Colombier 				tcb->rcv.nxt++;
25369ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->rtt_timer);
25379ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->acktimer);
25389ef1f84bSDavid du Colombier 				tcphalt(tpriv, &tcb->katimer);
25399ef1f84bSDavid du Colombier 				tcpsetstate(s, Time_wait);
25409ef1f84bSDavid du Colombier 				tcb->timer.start = MSL2 * (1000/MSPTICK);
25419ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25429ef1f84bSDavid du Colombier 				break;
25439ef1f84bSDavid du Colombier 			case Close_wait:
25449ef1f84bSDavid du Colombier 			case Closing:
25459ef1f84bSDavid du Colombier 			case Last_ack:
25469ef1f84bSDavid du Colombier 				break;
25479ef1f84bSDavid du Colombier 			case Time_wait:
25489ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
25499ef1f84bSDavid du Colombier 				break;
25509ef1f84bSDavid du Colombier 			}
25519ef1f84bSDavid du Colombier 		}
25529ef1f84bSDavid du Colombier 
25539ef1f84bSDavid du Colombier 		/*
25549ef1f84bSDavid du Colombier 		 *  get next adjacent segment from the resequence queue.
25559ef1f84bSDavid du Colombier 		 *  dump/trim any overlapping segments
25569ef1f84bSDavid du Colombier 		 */
25579ef1f84bSDavid du Colombier 		for(;;) {
25589ef1f84bSDavid du Colombier 			if(tcb->reseq == nil)
25599ef1f84bSDavid du Colombier 				goto output;
25609ef1f84bSDavid du Colombier 
25619ef1f84bSDavid du Colombier 			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
25629ef1f84bSDavid du Colombier 				goto output;
25639ef1f84bSDavid du Colombier 
25649ef1f84bSDavid du Colombier 			getreseq(tcb, &seg, &bp, &length);
25659ef1f84bSDavid du Colombier 
25669ef1f84bSDavid du Colombier 			tcprcvwin(s);
25679ef1f84bSDavid du Colombier 			if(tcptrim(tcb, &seg, &bp, &length) == 0){
25689ef1f84bSDavid du Colombier 				tcb->flags |= FORCE;
25699ef1f84bSDavid du Colombier 				break;
25709ef1f84bSDavid du Colombier 			}
25719ef1f84bSDavid du Colombier 		}
25729ef1f84bSDavid du Colombier 	}
25739ef1f84bSDavid du Colombier output:
25749ef1f84bSDavid du Colombier 	tcpoutput(s);
25759ef1f84bSDavid du Colombier 	qunlock(s);
25769ef1f84bSDavid du Colombier 	poperror();
25779ef1f84bSDavid du Colombier 	return;
25789ef1f84bSDavid du Colombier raise:
25799ef1f84bSDavid du Colombier 	qunlock(s);
25809ef1f84bSDavid du Colombier 	poperror();
25819ef1f84bSDavid du Colombier 	freeblist(bp);
25829ef1f84bSDavid du Colombier 	tcpkick(s);
25839ef1f84bSDavid du Colombier }
25849ef1f84bSDavid du Colombier 
25859ef1f84bSDavid du Colombier /*
25869ef1f84bSDavid du Colombier  *  always enters and exits with the s locked.  We drop
25879ef1f84bSDavid du Colombier  *  the lock to ipoput the packet so some care has to be
25889ef1f84bSDavid du Colombier  *  taken by callers.
25899ef1f84bSDavid du Colombier  */
25909ef1f84bSDavid du Colombier static void
tcpoutput(Conv * s)25919ef1f84bSDavid du Colombier tcpoutput(Conv *s)
25929ef1f84bSDavid du Colombier {
25939ef1f84bSDavid du Colombier 	Tcp seg;
25949ef1f84bSDavid du Colombier 	uint msgs;
25959ef1f84bSDavid du Colombier 	Tcpctl *tcb;
25969ef1f84bSDavid du Colombier 	Block *hbp, *bp;
25979ef1f84bSDavid du Colombier 	int sndcnt;
25989ef1f84bSDavid du Colombier 	ulong ssize, dsize, sent;
25999ef1f84bSDavid du Colombier 	Fs *f;
26009ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
26019ef1f84bSDavid du Colombier 	uchar version;
26029ef1f84bSDavid du Colombier 
26039ef1f84bSDavid du Colombier 	f = s->p->f;
26049ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
26059ef1f84bSDavid du Colombier 	version = s->ipversion;
26069ef1f84bSDavid du Colombier 
26079ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
26089ef1f84bSDavid du Colombier 
26099ef1f84bSDavid du Colombier 	/* force ack every 2*mss */
26109ef1f84bSDavid du Colombier 	if((tcb->flags & FORCE) == 0 &&
26119ef1f84bSDavid du Colombier 	    tcb->rcv.nxt - tcb->rcv.ackptr >= 2*tcb->mss){
26129ef1f84bSDavid du Colombier 		tpriv->stats[Delayack]++;
26139ef1f84bSDavid du Colombier 		tcb->flags |= FORCE;
26149ef1f84bSDavid du Colombier 	}
26159ef1f84bSDavid du Colombier 
26169ef1f84bSDavid du Colombier 	/* force ack if window opening */
26179ef1f84bSDavid du Colombier 	if((tcb->flags & FORCE) == 0){
26189ef1f84bSDavid du Colombier 		tcprcvwin(s);
26199ef1f84bSDavid du Colombier 		if((int)(tcb->rcv.wptr - tcb->rcv.wsnt) >= 2*tcb->mss){
26209ef1f84bSDavid du Colombier 			tpriv->stats[Wopenack]++;
26219ef1f84bSDavid du Colombier 			tcb->flags |= FORCE;
26229ef1f84bSDavid du Colombier 		}
26239ef1f84bSDavid du Colombier 	}
26249ef1f84bSDavid du Colombier 
26259ef1f84bSDavid du Colombier 	for(msgs = 0; msgs < 100; msgs++) {
26269ef1f84bSDavid du Colombier 		switch(tcb->state) {
26279ef1f84bSDavid du Colombier 		case Listen:
26289ef1f84bSDavid du Colombier 		case Closed:
26299ef1f84bSDavid du Colombier 		case Finwait2:
26309ef1f84bSDavid du Colombier 			return;
26319ef1f84bSDavid du Colombier 		}
26329ef1f84bSDavid du Colombier 
26339ef1f84bSDavid du Colombier 		/* Don't send anything else until our SYN has been acked */
26349ef1f84bSDavid du Colombier 		if(tcb->snd.ptr != tcb->iss && (tcb->flags & SYNACK) == 0)
26359ef1f84bSDavid du Colombier 			break;
26369ef1f84bSDavid du Colombier 
26379ef1f84bSDavid du Colombier 		/* force an ack when a window has opened up */
26389ef1f84bSDavid du Colombier 		tcprcvwin(s);
26399ef1f84bSDavid du Colombier 		if(tcb->rcv.blocked && tcb->rcv.wnd > 0){
26409ef1f84bSDavid du Colombier 			tcb->rcv.blocked = 0;
26419ef1f84bSDavid du Colombier 			tcb->flags |= FORCE;
26429ef1f84bSDavid du Colombier 		}
26439ef1f84bSDavid du Colombier 
26449ef1f84bSDavid du Colombier 		sndcnt = qlen(s->wq)+tcb->flgcnt;
26459ef1f84bSDavid du Colombier 		sent = tcb->snd.ptr - tcb->snd.una;
26469ef1f84bSDavid du Colombier 		ssize = sndcnt;
26479ef1f84bSDavid du Colombier 		if(tcb->snd.wnd == 0){
26489ef1f84bSDavid du Colombier 			/* zero window probe */
26499ef1f84bSDavid du Colombier 			if(sent > 0 && !(tcb->flags & FORCE))
26509ef1f84bSDavid du Colombier 				break;	/* already probing, rto re-probes */
26519ef1f84bSDavid du Colombier 			if(ssize < sent)
26529ef1f84bSDavid du Colombier 				ssize = 0;
26539ef1f84bSDavid du Colombier 			else{
26549ef1f84bSDavid du Colombier 				ssize -= sent;
26559ef1f84bSDavid du Colombier 				if(ssize > 0)
26569ef1f84bSDavid du Colombier 					ssize = 1;
26579ef1f84bSDavid du Colombier 			}
26589ef1f84bSDavid du Colombier 		} else {
26599ef1f84bSDavid du Colombier 			/* calculate usable segment size */
26609ef1f84bSDavid du Colombier 			if(ssize > tcb->cwind)
26619ef1f84bSDavid du Colombier 				ssize = tcb->cwind;
26629ef1f84bSDavid du Colombier 			if(ssize > tcb->snd.wnd)
26639ef1f84bSDavid du Colombier 				ssize = tcb->snd.wnd;
26649ef1f84bSDavid du Colombier 
26659ef1f84bSDavid du Colombier 			if(ssize < sent)
26669ef1f84bSDavid du Colombier 				ssize = 0;
26679ef1f84bSDavid du Colombier 			else {
26689ef1f84bSDavid du Colombier 				ssize -= sent;
26699ef1f84bSDavid du Colombier 				if(ssize > tcb->mss)
26709ef1f84bSDavid du Colombier 					ssize = tcb->mss;
26719ef1f84bSDavid du Colombier 			}
26729ef1f84bSDavid du Colombier 		}
26739ef1f84bSDavid du Colombier 
26749ef1f84bSDavid du Colombier 		dsize = ssize;
26759ef1f84bSDavid du Colombier 		seg.urg = 0;
26769ef1f84bSDavid du Colombier 
26779ef1f84bSDavid du Colombier 		if(!(tcb->flags & FORCE))
26789ef1f84bSDavid du Colombier 			if(ssize == 0 ||
26799ef1f84bSDavid du Colombier 			    ssize < tcb->mss && tcb->snd.nxt == tcb->snd.ptr &&
26809ef1f84bSDavid du Colombier 			    sent > TCPREXMTTHRESH * tcb->mss)
26819ef1f84bSDavid du Colombier 				break;
26829ef1f84bSDavid du Colombier 
26839ef1f84bSDavid du Colombier 		tcb->flags &= ~FORCE;
26849ef1f84bSDavid du Colombier 
26859ef1f84bSDavid du Colombier 		/* By default we will generate an ack */
26869ef1f84bSDavid du Colombier 		tcphalt(tpriv, &tcb->acktimer);
26879ef1f84bSDavid du Colombier 		seg.source = s->lport;
26889ef1f84bSDavid du Colombier 		seg.dest = s->rport;
26899ef1f84bSDavid du Colombier 		seg.flags = ACK;
26909ef1f84bSDavid du Colombier 		seg.mss = 0;
26919ef1f84bSDavid du Colombier 		seg.ws = 0;
26929ef1f84bSDavid du Colombier 		seg.update = 0;
26939ef1f84bSDavid du Colombier 		switch(tcb->state){
26949ef1f84bSDavid du Colombier 		case Syn_sent:
26959ef1f84bSDavid du Colombier 			seg.flags = 0;
26969ef1f84bSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
26979ef1f84bSDavid du Colombier 				seg.flags |= SYN;
26989ef1f84bSDavid du Colombier 				dsize--;
26999ef1f84bSDavid du Colombier 				seg.mss = tcb->mss;
27009ef1f84bSDavid du Colombier 				seg.ws = tcb->scale;
27019ef1f84bSDavid du Colombier 			}
27029ef1f84bSDavid du Colombier 			break;
27039ef1f84bSDavid du Colombier 		case Syn_received:
27049ef1f84bSDavid du Colombier 			/*
27059ef1f84bSDavid du Colombier 			 *  don't send any data with a SYN/ACK packet
27069ef1f84bSDavid du Colombier 			 *  because Linux rejects the packet in its
27079ef1f84bSDavid du Colombier 			 *  attempt to solve the SYN attack problem
27089ef1f84bSDavid du Colombier 			 */
27099ef1f84bSDavid du Colombier 			if(tcb->snd.ptr == tcb->iss){
27109ef1f84bSDavid du Colombier 				seg.flags |= SYN;
27119ef1f84bSDavid du Colombier 				dsize = 0;
27129ef1f84bSDavid du Colombier 				ssize = 1;
27139ef1f84bSDavid du Colombier 				seg.mss = tcb->mss;
27149ef1f84bSDavid du Colombier 				seg.ws = tcb->scale;
27159ef1f84bSDavid du Colombier 			}
27169ef1f84bSDavid du Colombier 			break;
27179ef1f84bSDavid du Colombier 		}
27189ef1f84bSDavid du Colombier 		seg.seq = tcb->snd.ptr;
27199ef1f84bSDavid du Colombier 		seg.ack = tcb->rcv.nxt;
27209ef1f84bSDavid du Colombier 		seg.wnd = tcb->rcv.wnd;
27219ef1f84bSDavid du Colombier 
27229ef1f84bSDavid du Colombier 		/* Pull out data to send */
27239ef1f84bSDavid du Colombier 		bp = nil;
27249ef1f84bSDavid du Colombier 		if(dsize != 0) {
27259ef1f84bSDavid du Colombier 			bp = qcopy(s->wq, dsize, sent);
27269ef1f84bSDavid du Colombier 			if(BLEN(bp) != dsize) {
27279ef1f84bSDavid du Colombier 				seg.flags |= FIN;
27289ef1f84bSDavid du Colombier 				dsize--;
27299ef1f84bSDavid du Colombier 			}
27309ef1f84bSDavid du Colombier 		}
27319ef1f84bSDavid du Colombier 
27329ef1f84bSDavid du Colombier 		if(sent+dsize == sndcnt && dsize)
27339ef1f84bSDavid du Colombier 			seg.flags |= PSH;
27349ef1f84bSDavid du Colombier 
27359ef1f84bSDavid du Colombier 		tcb->snd.ptr += ssize;
27369ef1f84bSDavid du Colombier 
27379ef1f84bSDavid du Colombier 		/* Pull up the send pointer so we can accept acks
27389ef1f84bSDavid du Colombier 		 * for this window
27399ef1f84bSDavid du Colombier 		 */
27409ef1f84bSDavid du Colombier 		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
27419ef1f84bSDavid du Colombier 			tcb->snd.nxt = tcb->snd.ptr;
27429ef1f84bSDavid du Colombier 
27439ef1f84bSDavid du Colombier 		/* Build header, link data and compute cksum */
27449ef1f84bSDavid du Colombier 		switch(version){
27459ef1f84bSDavid du Colombier 		case V4:
27469ef1f84bSDavid du Colombier 			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
27479ef1f84bSDavid du Colombier 			hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
27489ef1f84bSDavid du Colombier 			if(hbp == nil) {
27499ef1f84bSDavid du Colombier 				freeblist(bp);
27509ef1f84bSDavid du Colombier 				return;
27519ef1f84bSDavid du Colombier 			}
27529ef1f84bSDavid du Colombier 			break;
27539ef1f84bSDavid du Colombier 		case V6:
27549ef1f84bSDavid du Colombier 			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
27559ef1f84bSDavid du Colombier 			hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
27569ef1f84bSDavid du Colombier 			if(hbp == nil) {
27579ef1f84bSDavid du Colombier 				freeblist(bp);
27589ef1f84bSDavid du Colombier 				return;
27599ef1f84bSDavid du Colombier 			}
27609ef1f84bSDavid du Colombier 			break;
27619ef1f84bSDavid du Colombier 		default:
27629ef1f84bSDavid du Colombier 			hbp = nil;	/* to suppress a warning */
27639ef1f84bSDavid du Colombier 			panic("tcpoutput: version %d", version);
27649ef1f84bSDavid du Colombier 		}
27659ef1f84bSDavid du Colombier 
27669ef1f84bSDavid du Colombier 		/* Start the transmission timers if there is new data and we
27679ef1f84bSDavid du Colombier 		 * expect acknowledges
27689ef1f84bSDavid du Colombier 		 */
27699ef1f84bSDavid du Colombier 		if(ssize != 0){
27709ef1f84bSDavid du Colombier 			if(tcb->timer.state != TcptimerON){
27719ef1f84bSDavid du Colombier 				tcb->time = NOW;
27729ef1f84bSDavid du Colombier 				tcb->timeuna = tcb->snd.una;
27739ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->timer);
27749ef1f84bSDavid du Colombier 			}
27759ef1f84bSDavid du Colombier 
27769ef1f84bSDavid du Colombier 			/*  If round trip timer isn't running, start it.
27779ef1f84bSDavid du Colombier 			 *  measure the longest packet only in case the
27789ef1f84bSDavid du Colombier 			 *  transmission time dominates RTT
27799ef1f84bSDavid du Colombier 			 */
27809ef1f84bSDavid du Colombier 			if(tcb->snd.retransmit == 0)
27819ef1f84bSDavid du Colombier 			if(tcb->rtt_timer.state != TcptimerON)
27829ef1f84bSDavid du Colombier 			if(ssize == tcb->mss) {
27839ef1f84bSDavid du Colombier 				tcpgo(tpriv, &tcb->rtt_timer);
27849ef1f84bSDavid du Colombier 				tcb->rttseq = tcb->snd.ptr;
27859ef1f84bSDavid du Colombier 			}
27869ef1f84bSDavid du Colombier 		}
27879ef1f84bSDavid du Colombier 
27889ef1f84bSDavid du Colombier 		tpriv->stats[OutSegs]++;
27899ef1f84bSDavid du Colombier 		if(tcb->snd.retransmit)
27909ef1f84bSDavid du Colombier 			tpriv->stats[RetransSegsSent]++;
27919ef1f84bSDavid du Colombier 		tcb->rcv.ackptr = seg.ack;
27929ef1f84bSDavid du Colombier 		tcb->rcv.wsnt = tcb->rcv.wptr;
27939ef1f84bSDavid du Colombier 
27949ef1f84bSDavid du Colombier 		/* put off the next keep alive */
27959ef1f84bSDavid du Colombier 		tcpgo(tpriv, &tcb->katimer);
27969ef1f84bSDavid du Colombier 
27979ef1f84bSDavid du Colombier 		switch(version){
27989ef1f84bSDavid du Colombier 		case V4:
27999ef1f84bSDavid du Colombier 			if(ipoput4(f, hbp, 0, s->ttl, s->tos, s) < 0){
28009ef1f84bSDavid du Colombier 				/* a negative return means no route */
28019ef1f84bSDavid du Colombier 				localclose(s, "no route");
28029ef1f84bSDavid du Colombier 			}
28039ef1f84bSDavid du Colombier 			break;
28049ef1f84bSDavid du Colombier 		case V6:
28059ef1f84bSDavid du Colombier 			if(ipoput6(f, hbp, 0, s->ttl, s->tos, s) < 0){
28069ef1f84bSDavid du Colombier 				/* a negative return means no route */
28079ef1f84bSDavid du Colombier 				localclose(s, "no route");
28089ef1f84bSDavid du Colombier 			}
28099ef1f84bSDavid du Colombier 			break;
28109ef1f84bSDavid du Colombier 		default:
28119ef1f84bSDavid du Colombier 			panic("tcpoutput2: version %d", version);
28129ef1f84bSDavid du Colombier 		}
28139ef1f84bSDavid du Colombier 		if((msgs%4) == 3){
28149ef1f84bSDavid du Colombier 			qunlock(s);
28159ef1f84bSDavid du Colombier 			qlock(s);
28169ef1f84bSDavid du Colombier 		}
28179ef1f84bSDavid du Colombier 	}
28189ef1f84bSDavid du Colombier }
28199ef1f84bSDavid du Colombier 
28209ef1f84bSDavid du Colombier /*
28219ef1f84bSDavid du Colombier  *  the BSD convention (hack?) for keep alives.  resend last uchar acked.
28229ef1f84bSDavid du Colombier  */
28239ef1f84bSDavid du Colombier static void
tcpsendka(Conv * s)28249ef1f84bSDavid du Colombier tcpsendka(Conv *s)
28259ef1f84bSDavid du Colombier {
28269ef1f84bSDavid du Colombier 	Tcp seg;
28279ef1f84bSDavid du Colombier 	Tcpctl *tcb;
28289ef1f84bSDavid du Colombier 	Block *hbp,*dbp;
28299ef1f84bSDavid du Colombier 
28309ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
28319ef1f84bSDavid du Colombier 
28329ef1f84bSDavid du Colombier 	dbp = nil;
28339ef1f84bSDavid du Colombier 	memset(&seg, 0, sizeof seg);
28349ef1f84bSDavid du Colombier 	seg.urg = 0;
28359ef1f84bSDavid du Colombier 	seg.source = s->lport;
28369ef1f84bSDavid du Colombier 	seg.dest = s->rport;
28379ef1f84bSDavid du Colombier 	seg.flags = ACK|PSH;
28389ef1f84bSDavid du Colombier 	seg.mss = 0;
28399ef1f84bSDavid du Colombier 	seg.ws = 0;
28409ef1f84bSDavid du Colombier 	if(tcpporthogdefense)
28419ef1f84bSDavid du Colombier 		seg.seq = tcb->snd.una-(1<<30)-nrand(1<<20);
28429ef1f84bSDavid du Colombier 	else
28439ef1f84bSDavid du Colombier 		seg.seq = tcb->snd.una-1;
28449ef1f84bSDavid du Colombier 	seg.ack = tcb->rcv.nxt;
28459ef1f84bSDavid du Colombier 	tcb->rcv.ackptr = seg.ack;
28469ef1f84bSDavid du Colombier 	tcprcvwin(s);
28479ef1f84bSDavid du Colombier 	seg.wnd = tcb->rcv.wnd;
28489ef1f84bSDavid du Colombier 	if(tcb->state == Finwait2){
28499ef1f84bSDavid du Colombier 		seg.flags |= FIN;
28509ef1f84bSDavid du Colombier 	} else {
28519ef1f84bSDavid du Colombier 		dbp = allocb(1);
28529ef1f84bSDavid du Colombier 		dbp->wp++;
28539ef1f84bSDavid du Colombier 	}
28549ef1f84bSDavid du Colombier 
28559ef1f84bSDavid du Colombier 	if(isv4(s->raddr)) {
28569ef1f84bSDavid du Colombier 		/* Build header, link data and compute cksum */
28579ef1f84bSDavid du Colombier 		tcb->protohdr.tcp4hdr.vihl = IP_VER4;
28589ef1f84bSDavid du Colombier 		hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
28599ef1f84bSDavid du Colombier 		if(hbp == nil) {
28609ef1f84bSDavid du Colombier 			freeblist(dbp);
28619ef1f84bSDavid du Colombier 			return;
28629ef1f84bSDavid du Colombier 		}
28639ef1f84bSDavid du Colombier 		ipoput4(s->p->f, hbp, 0, s->ttl, s->tos, s);
28649ef1f84bSDavid du Colombier 	}
28659ef1f84bSDavid du Colombier 	else {
28669ef1f84bSDavid du Colombier 		/* Build header, link data and compute cksum */
28679ef1f84bSDavid du Colombier 		tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
28689ef1f84bSDavid du Colombier 		hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
28699ef1f84bSDavid du Colombier 		if(hbp == nil) {
28709ef1f84bSDavid du Colombier 			freeblist(dbp);
28719ef1f84bSDavid du Colombier 			return;
28729ef1f84bSDavid du Colombier 		}
28739ef1f84bSDavid du Colombier 		ipoput6(s->p->f, hbp, 0, s->ttl, s->tos, s);
28749ef1f84bSDavid du Colombier 	}
28759ef1f84bSDavid du Colombier }
28769ef1f84bSDavid du Colombier 
28779ef1f84bSDavid du Colombier /*
28789ef1f84bSDavid du Colombier  *  set connection to time out after 12 minutes
28799ef1f84bSDavid du Colombier  */
28809ef1f84bSDavid du Colombier static void
tcpsetkacounter(Tcpctl * tcb)28819ef1f84bSDavid du Colombier tcpsetkacounter(Tcpctl *tcb)
28829ef1f84bSDavid du Colombier {
28839ef1f84bSDavid du Colombier 	tcb->kacounter = (12 * 60 * 1000) / (tcb->katimer.start*MSPTICK);
28849ef1f84bSDavid du Colombier 	if(tcb->kacounter < 3)
28859ef1f84bSDavid du Colombier 		tcb->kacounter = 3;
28869ef1f84bSDavid du Colombier }
28879ef1f84bSDavid du Colombier 
28889ef1f84bSDavid du Colombier /*
28899ef1f84bSDavid du Colombier  *  if we've timed out, close the connection
28909ef1f84bSDavid du Colombier  *  otherwise, send a keepalive and restart the timer
28919ef1f84bSDavid du Colombier  */
28929ef1f84bSDavid du Colombier static void
tcpkeepalive(void * v)28939ef1f84bSDavid du Colombier tcpkeepalive(void *v)
28949ef1f84bSDavid du Colombier {
28959ef1f84bSDavid du Colombier 	Tcpctl *tcb;
28969ef1f84bSDavid du Colombier 	Conv *s;
28979ef1f84bSDavid du Colombier 
28989ef1f84bSDavid du Colombier 	s = v;
28999ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29009ef1f84bSDavid du Colombier 	if(waserror()){
29019ef1f84bSDavid du Colombier 		qunlock(s);
29029ef1f84bSDavid du Colombier 		nexterror();
29039ef1f84bSDavid du Colombier 	}
29049ef1f84bSDavid du Colombier 	qlock(s);
29059ef1f84bSDavid du Colombier 	if(tcb->state != Closed){
29069ef1f84bSDavid du Colombier 		if(--(tcb->kacounter) <= 0) {
29079ef1f84bSDavid du Colombier 			localclose(s, Etimedout);
29089ef1f84bSDavid du Colombier 		} else {
29099ef1f84bSDavid du Colombier 			tcpsendka(s);
29109ef1f84bSDavid du Colombier 			tcpgo(s->p->priv, &tcb->katimer);
29119ef1f84bSDavid du Colombier 		}
29129ef1f84bSDavid du Colombier 	}
29139ef1f84bSDavid du Colombier 	qunlock(s);
29149ef1f84bSDavid du Colombier 	poperror();
29159ef1f84bSDavid du Colombier }
29169ef1f84bSDavid du Colombier 
29179ef1f84bSDavid du Colombier /*
29189ef1f84bSDavid du Colombier  *  start keepalive timer
29199ef1f84bSDavid du Colombier  */
29209ef1f84bSDavid du Colombier static char*
tcpstartka(Conv * s,char ** f,int n)29219ef1f84bSDavid du Colombier tcpstartka(Conv *s, char **f, int n)
29229ef1f84bSDavid du Colombier {
29239ef1f84bSDavid du Colombier 	Tcpctl *tcb;
29249ef1f84bSDavid du Colombier 	int x;
29259ef1f84bSDavid du Colombier 
29269ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29279ef1f84bSDavid du Colombier 	if(tcb->state != Established)
29289ef1f84bSDavid du Colombier 		return "connection must be in Establised state";
29299ef1f84bSDavid du Colombier 	if(n > 1){
29309ef1f84bSDavid du Colombier 		x = atoi(f[1]);
29319ef1f84bSDavid du Colombier 		if(x >= MSPTICK)
29329ef1f84bSDavid du Colombier 			tcb->katimer.start = x/MSPTICK;
29339ef1f84bSDavid du Colombier 	}
29349ef1f84bSDavid du Colombier 	tcpsetkacounter(tcb);
29359ef1f84bSDavid du Colombier 	tcpgo(s->p->priv, &tcb->katimer);
29369ef1f84bSDavid du Colombier 
29379ef1f84bSDavid du Colombier 	return nil;
29389ef1f84bSDavid du Colombier }
29399ef1f84bSDavid du Colombier 
29409ef1f84bSDavid du Colombier /*
29419ef1f84bSDavid du Colombier  *  turn checksums on/off
29429ef1f84bSDavid du Colombier  */
29439ef1f84bSDavid du Colombier static char*
tcpsetchecksum(Conv * s,char ** f,int)29449ef1f84bSDavid du Colombier tcpsetchecksum(Conv *s, char **f, int)
29459ef1f84bSDavid du Colombier {
29469ef1f84bSDavid du Colombier 	Tcpctl *tcb;
29479ef1f84bSDavid du Colombier 
29489ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29499ef1f84bSDavid du Colombier 	tcb->nochecksum = !atoi(f[1]);
29509ef1f84bSDavid du Colombier 
29519ef1f84bSDavid du Colombier 	return nil;
29529ef1f84bSDavid du Colombier }
29539ef1f84bSDavid du Colombier 
29549ef1f84bSDavid du Colombier /*
29559ef1f84bSDavid du Colombier  *  retransmit (at most) one segment at snd.una.
29569ef1f84bSDavid du Colombier  *  preserve cwind & snd.ptr
29579ef1f84bSDavid du Colombier  */
29589ef1f84bSDavid du Colombier static void
tcprxmit(Conv * s)29599ef1f84bSDavid du Colombier tcprxmit(Conv *s)
29609ef1f84bSDavid du Colombier {
29619ef1f84bSDavid du Colombier 	Tcpctl *tcb;
29629ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
29639ef1f84bSDavid du Colombier 	ulong tcwind, tptr;
29649ef1f84bSDavid du Colombier 
29659ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29669ef1f84bSDavid du Colombier 	tcb->flags |= RETRAN|FORCE;
29679ef1f84bSDavid du Colombier 
29689ef1f84bSDavid du Colombier 	tptr = tcb->snd.ptr;
29699ef1f84bSDavid du Colombier 	tcwind = tcb->cwind;
29709ef1f84bSDavid du Colombier 	tcb->snd.ptr = tcb->snd.una;
29719ef1f84bSDavid du Colombier 	tcb->cwind = tcb->mss;
29729ef1f84bSDavid du Colombier 	tcb->snd.retransmit = 1;
29739ef1f84bSDavid du Colombier 	tcpoutput(s);
29749ef1f84bSDavid du Colombier 	tcb->snd.retransmit = 0;
29759ef1f84bSDavid du Colombier 	tcb->cwind = tcwind;
29769ef1f84bSDavid du Colombier 	tcb->snd.ptr = tptr;
29779ef1f84bSDavid du Colombier 
29789ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
29799ef1f84bSDavid du Colombier 	tpriv->stats[RetransSegs]++;
29809ef1f84bSDavid du Colombier }
29819ef1f84bSDavid du Colombier 
29829ef1f84bSDavid du Colombier /*
29839ef1f84bSDavid du Colombier  *  TODO: RFC 4138 F-RTO
29849ef1f84bSDavid du Colombier  */
29859ef1f84bSDavid du Colombier static void
tcptimeout(void * arg)29869ef1f84bSDavid du Colombier tcptimeout(void *arg)
29879ef1f84bSDavid du Colombier {
29889ef1f84bSDavid du Colombier 	Conv *s;
29899ef1f84bSDavid du Colombier 	Tcpctl *tcb;
29909ef1f84bSDavid du Colombier 	int maxback;
29919ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
29929ef1f84bSDavid du Colombier 
29939ef1f84bSDavid du Colombier 	s = (Conv*)arg;
29949ef1f84bSDavid du Colombier 	tpriv = s->p->priv;
29959ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
29969ef1f84bSDavid du Colombier 
29979ef1f84bSDavid du Colombier 	if(waserror()){
29989ef1f84bSDavid du Colombier 		qunlock(s);
29999ef1f84bSDavid du Colombier 		nexterror();
30009ef1f84bSDavid du Colombier 	}
30019ef1f84bSDavid du Colombier 	qlock(s);
30029ef1f84bSDavid du Colombier 	switch(tcb->state){
30039ef1f84bSDavid du Colombier 	default:
30049ef1f84bSDavid du Colombier 		tcb->backoff++;
30059ef1f84bSDavid du Colombier 		if(tcb->state == Syn_sent)
30069ef1f84bSDavid du Colombier 			maxback = MAXBACKMS/2;
30079ef1f84bSDavid du Colombier 		else
30089ef1f84bSDavid du Colombier 			maxback = MAXBACKMS;
30099ef1f84bSDavid du Colombier 		tcb->backedoff += tcb->timer.start * MSPTICK;
30109ef1f84bSDavid du Colombier 		if(tcb->backedoff >= maxback) {
30119ef1f84bSDavid du Colombier 			localclose(s, Etimedout);
30129ef1f84bSDavid du Colombier 			break;
30139ef1f84bSDavid du Colombier 		}
30149ef1f84bSDavid du Colombier 		netlog(s->p->f, Logtcprxmt, "rxm %d/%d %ldms %lud rto %d %lud %s\n",
30159ef1f84bSDavid du Colombier 			tcb->srtt, tcb->mdev, NOW - tcb->time,
30169ef1f84bSDavid du Colombier 			tcb->snd.una - tcb->timeuna, tcb->snd.rto, tcb->snd.ptr,
30179ef1f84bSDavid du Colombier 			tcpstates[s->state]);
30189ef1f84bSDavid du Colombier 		tcpsettimer(tcb);
30199ef1f84bSDavid du Colombier 		if(tcb->snd.rto == 0)
30209ef1f84bSDavid du Colombier 			tcpcongestion(tcb);
30219ef1f84bSDavid du Colombier 		tcprxmit(s);
30229ef1f84bSDavid du Colombier 		tcb->snd.ptr = tcb->snd.una;
30239ef1f84bSDavid du Colombier 		tcb->cwind = tcb->mss;
30249ef1f84bSDavid du Colombier 		tcb->snd.rto = 1;
30259ef1f84bSDavid du Colombier 		tpriv->stats[RetransTimeouts]++;
30269ef1f84bSDavid du Colombier 
30279ef1f84bSDavid du Colombier 		if(tcb->snd.recovery){
30289ef1f84bSDavid du Colombier 			tcb->snd.dupacks = 0;		/* reno rto */
30299ef1f84bSDavid du Colombier 			tcb->snd.recovery = 0;
30309ef1f84bSDavid du Colombier 			tpriv->stats[RecoveryRTO]++;
30319ef1f84bSDavid du Colombier 			tcb->snd.rxt = tcb->snd.nxt;
30329ef1f84bSDavid du Colombier 			netlog(s->p->f, Logtcpwin,
30339ef1f84bSDavid du Colombier 				"rto recovery rxt @%lud\n", tcb->snd.nxt);
30349ef1f84bSDavid du Colombier 		}
30359ef1f84bSDavid du Colombier 
30369ef1f84bSDavid du Colombier 		tcb->abcbytes = 0;
30379ef1f84bSDavid du Colombier 		break;
30389ef1f84bSDavid du Colombier 	case Time_wait:
30399ef1f84bSDavid du Colombier 		localclose(s, nil);
30409ef1f84bSDavid du Colombier 		break;
30419ef1f84bSDavid du Colombier 	case Closed:
30429ef1f84bSDavid du Colombier 		break;
30439ef1f84bSDavid du Colombier 	}
30449ef1f84bSDavid du Colombier 	qunlock(s);
30459ef1f84bSDavid du Colombier 	poperror();
30469ef1f84bSDavid du Colombier }
30479ef1f84bSDavid du Colombier 
30489ef1f84bSDavid du Colombier static int
inwindow(Tcpctl * tcb,int seq)30499ef1f84bSDavid du Colombier inwindow(Tcpctl *tcb, int seq)
30509ef1f84bSDavid du Colombier {
30519ef1f84bSDavid du Colombier 	return seq_within(seq, tcb->rcv.nxt, tcb->rcv.nxt+tcb->rcv.wnd-1);
30529ef1f84bSDavid du Colombier }
30539ef1f84bSDavid du Colombier 
30549ef1f84bSDavid du Colombier /*
30559ef1f84bSDavid du Colombier  *  set up state for a received SYN (or SYN ACK) packet
30569ef1f84bSDavid du Colombier  */
30579ef1f84bSDavid du Colombier static void
procsyn(Conv * s,Tcp * seg)30589ef1f84bSDavid du Colombier procsyn(Conv *s, Tcp *seg)
30599ef1f84bSDavid du Colombier {
30609ef1f84bSDavid du Colombier 	Tcpctl *tcb;
30619ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
30629ef1f84bSDavid du Colombier 
30639ef1f84bSDavid du Colombier 	tcb = (Tcpctl*)s->ptcl;
30649ef1f84bSDavid du Colombier 	tcb->flags |= FORCE;
30659ef1f84bSDavid du Colombier 
30669ef1f84bSDavid du Colombier 	tcb->rcv.nxt = seg->seq + 1;
30679ef1f84bSDavid du Colombier 	tcb->rcv.wptr = tcb->rcv.nxt;
30689ef1f84bSDavid du Colombier 	tcb->rcv.wsnt = 0;
30699ef1f84bSDavid du Colombier 	tcb->rcv.urg = tcb->rcv.nxt;
30709ef1f84bSDavid du Colombier 	tcb->irs = seg->seq;
30719ef1f84bSDavid du Colombier 
30729ef1f84bSDavid du Colombier 	/* our sending max segment size cannot be bigger than what he asked for */
30739ef1f84bSDavid du Colombier 	if(seg->mss != 0 && seg->mss < tcb->mss) {
30749ef1f84bSDavid du Colombier 		tcb->mss = seg->mss;
30759ef1f84bSDavid du Colombier 		tpriv = s->p->priv;
30769ef1f84bSDavid du Colombier 		tpriv->stats[Mss] = tcb->mss;
30779ef1f84bSDavid du Colombier 	}
30789ef1f84bSDavid du Colombier 
30799ef1f84bSDavid du Colombier 	tcb->snd.wnd = seg->wnd;
30809ef1f84bSDavid du Colombier 	initialwindow(tcb);
30819ef1f84bSDavid du Colombier }
30829ef1f84bSDavid du Colombier 
30839ef1f84bSDavid du Colombier static int
dumpreseq(Tcpctl * tcb)30849ef1f84bSDavid du Colombier dumpreseq(Tcpctl *tcb)
30859ef1f84bSDavid du Colombier {
30869ef1f84bSDavid du Colombier 	Reseq *r, *next;
30879ef1f84bSDavid du Colombier 
30889ef1f84bSDavid du Colombier 	for(r = tcb->reseq; r != nil; r = next){
30899ef1f84bSDavid du Colombier 		next = r->next;
30909ef1f84bSDavid du Colombier 		freeblist(r->bp);
30919ef1f84bSDavid du Colombier 		free(r);
30929ef1f84bSDavid du Colombier 	}
30939ef1f84bSDavid du Colombier 	tcb->reseq = nil;
30949ef1f84bSDavid du Colombier 	tcb->nreseq = 0;
30959ef1f84bSDavid du Colombier 	tcb->reseqlen = 0;
30969ef1f84bSDavid du Colombier 	return -1;
30979ef1f84bSDavid du Colombier }
30989ef1f84bSDavid du Colombier 
30999ef1f84bSDavid du Colombier static void
logreseq(Fs * f,Reseq * r,ulong n)31009ef1f84bSDavid du Colombier logreseq(Fs *f, Reseq *r, ulong n)
31019ef1f84bSDavid du Colombier {
31029ef1f84bSDavid du Colombier 	char *s;
31039ef1f84bSDavid du Colombier 
31049ef1f84bSDavid du Colombier 	for(; r != nil; r = r->next){
31059ef1f84bSDavid du Colombier 		s = nil;
31069ef1f84bSDavid du Colombier 		if(r->next == nil && r->seg.seq != n)
31079ef1f84bSDavid du Colombier 			s = "hole/end";
31089ef1f84bSDavid du Colombier 		else if(r->next == nil)
31099ef1f84bSDavid du Colombier 			s = "end";
31109ef1f84bSDavid du Colombier 		else if(r->seg.seq != n)
31119ef1f84bSDavid du Colombier 			s = "hole";
31129ef1f84bSDavid du Colombier 		if(s != nil)
31139ef1f84bSDavid du Colombier 			netlog(f, Logtcp, "%s %lud-%lud (%ld) %#ux\n", s,
31149ef1f84bSDavid du Colombier 				n, r->seg.seq, r->seg.seq - n, r->seg.flags);
31159ef1f84bSDavid du Colombier 		n = r->seg.seq + r->seg.len;
31169ef1f84bSDavid du Colombier 	}
31179ef1f84bSDavid du Colombier }
31189ef1f84bSDavid du Colombier 
31199ef1f84bSDavid du Colombier static int
addreseq(Fs * f,Tcpctl * tcb,Tcppriv * tpriv,Tcp * seg,Block * bp,ushort length)31209ef1f84bSDavid du Colombier addreseq(Fs *f, Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
31219ef1f84bSDavid du Colombier {
31229ef1f84bSDavid du Colombier 	Reseq *rp, **rr;
31239ef1f84bSDavid du Colombier 	int qmax;
31249ef1f84bSDavid du Colombier 
31259ef1f84bSDavid du Colombier 	rp = malloc(sizeof *rp);
31269ef1f84bSDavid du Colombier 	if(rp == nil){
31279ef1f84bSDavid du Colombier 		freeblist(bp);		/* bp always consumed by addreseq */
31289ef1f84bSDavid du Colombier 		return 0;
31299ef1f84bSDavid du Colombier 	}
31309ef1f84bSDavid du Colombier 
31319ef1f84bSDavid du Colombier 	rp->seg = *seg;
31329ef1f84bSDavid du Colombier 	rp->bp = bp;
31339ef1f84bSDavid du Colombier 	rp->length = length;
31349ef1f84bSDavid du Colombier 
31359ef1f84bSDavid du Colombier 	tcb->reseqlen += length;
31369ef1f84bSDavid du Colombier 	tcb->nreseq++;
31379ef1f84bSDavid du Colombier 
31389ef1f84bSDavid du Colombier 	/* Place on reassembly list sorting by starting seq number */
31399ef1f84bSDavid du Colombier 	for(rr = &tcb->reseq; ; rr = &(*rr)->next)
31409ef1f84bSDavid du Colombier 		if(*rr == nil || seq_lt(seg->seq, (*rr)->seg.seq)){
31419ef1f84bSDavid du Colombier 			rp->next = *rr;
31429ef1f84bSDavid du Colombier 			*rr = rp;
31439ef1f84bSDavid du Colombier 			tpriv->stats[Resequenced]++;
31449ef1f84bSDavid du Colombier 			if(rp->next != nil)
31459ef1f84bSDavid du Colombier 				tpriv->stats[OutOfOrder]++;
31469ef1f84bSDavid du Colombier 			break;
31479ef1f84bSDavid du Colombier 		}
31489ef1f84bSDavid du Colombier 
31499ef1f84bSDavid du Colombier 	qmax = tcb->window;
31509ef1f84bSDavid du Colombier 	if(tcb->reseqlen > qmax){
31519ef1f84bSDavid du Colombier 		netlog(f, Logtcp, "tcp: reseq: queue > window: %d > %d; %d packets\n",
31529ef1f84bSDavid du Colombier 			tcb->reseqlen, qmax, tcb->nreseq);
31539ef1f84bSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31549ef1f84bSDavid du Colombier 		tpriv->stats[ReseqBytelim]++;
31559ef1f84bSDavid du Colombier 		return dumpreseq(tcb);
31569ef1f84bSDavid du Colombier 	}
31579ef1f84bSDavid du Colombier 	qmax = tcb->window / tcb->mss; /* ~190 for qscale=2, 390 for qscale=3 */
31589ef1f84bSDavid du Colombier 	if(tcb->nreseq > qmax){
31599ef1f84bSDavid du Colombier 		netlog(f, Logtcp, "resequence queue > packets: %d %d; %d bytes\n",
31609ef1f84bSDavid du Colombier 			tcb->nreseq, qmax, tcb->reseqlen);
31619ef1f84bSDavid du Colombier 		logreseq(f, tcb->reseq, tcb->rcv.nxt);
31629ef1f84bSDavid du Colombier 		tpriv->stats[ReseqPktlim]++;
31639ef1f84bSDavid du Colombier 		return dumpreseq(tcb);
31649ef1f84bSDavid du Colombier 	}
31659ef1f84bSDavid du Colombier 	return 0;
31669ef1f84bSDavid du Colombier }
31679ef1f84bSDavid du Colombier 
31689ef1f84bSDavid du Colombier static void
getreseq(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31699ef1f84bSDavid du Colombier getreseq(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31709ef1f84bSDavid du Colombier {
31719ef1f84bSDavid du Colombier 	Reseq *rp;
31729ef1f84bSDavid du Colombier 
31739ef1f84bSDavid du Colombier 	rp = tcb->reseq;
31749ef1f84bSDavid du Colombier 	if(rp == nil)
31759ef1f84bSDavid du Colombier 		return;
31769ef1f84bSDavid du Colombier 
31779ef1f84bSDavid du Colombier 	tcb->reseq = rp->next;
31789ef1f84bSDavid du Colombier 
31799ef1f84bSDavid du Colombier 	*seg = rp->seg;
31809ef1f84bSDavid du Colombier 	*bp = rp->bp;
31819ef1f84bSDavid du Colombier 	*length = rp->length;
31829ef1f84bSDavid du Colombier 
31839ef1f84bSDavid du Colombier 	tcb->nreseq--;
31849ef1f84bSDavid du Colombier 	tcb->reseqlen -= rp->length;
31859ef1f84bSDavid du Colombier 
31869ef1f84bSDavid du Colombier 	free(rp);
31879ef1f84bSDavid du Colombier }
31889ef1f84bSDavid du Colombier 
31899ef1f84bSDavid du Colombier static int
tcptrim(Tcpctl * tcb,Tcp * seg,Block ** bp,ushort * length)31909ef1f84bSDavid du Colombier tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
31919ef1f84bSDavid du Colombier {
31929ef1f84bSDavid du Colombier 	ushort len;
31939ef1f84bSDavid du Colombier 	uchar accept;
31949ef1f84bSDavid du Colombier 	int dupcnt, excess;
31959ef1f84bSDavid du Colombier 
31969ef1f84bSDavid du Colombier 	accept = 0;
31979ef1f84bSDavid du Colombier 	len = *length;
31989ef1f84bSDavid du Colombier 	if(seg->flags & SYN)
31999ef1f84bSDavid du Colombier 		len++;
32009ef1f84bSDavid du Colombier 	if(seg->flags & FIN)
32019ef1f84bSDavid du Colombier 		len++;
32029ef1f84bSDavid du Colombier 
32039ef1f84bSDavid du Colombier 	if(tcb->rcv.wnd == 0) {
32049ef1f84bSDavid du Colombier 		if(len == 0 && seg->seq == tcb->rcv.nxt)
32059ef1f84bSDavid du Colombier 			return 0;
32069ef1f84bSDavid du Colombier 	}
32079ef1f84bSDavid du Colombier 	else {
32089ef1f84bSDavid du Colombier 		/* Some part of the segment should be in the window */
32099ef1f84bSDavid du Colombier 		if(inwindow(tcb,seg->seq))
32109ef1f84bSDavid du Colombier 			accept++;
32119ef1f84bSDavid du Colombier 		else
32129ef1f84bSDavid du Colombier 		if(len != 0) {
32139ef1f84bSDavid du Colombier 			if(inwindow(tcb, seg->seq+len-1) ||
32149ef1f84bSDavid du Colombier 			seq_within(tcb->rcv.nxt, seg->seq,seg->seq+len-1))
32159ef1f84bSDavid du Colombier 				accept++;
32169ef1f84bSDavid du Colombier 		}
32179ef1f84bSDavid du Colombier 	}
32189ef1f84bSDavid du Colombier 	if(!accept) {
32199ef1f84bSDavid du Colombier 		freeblist(*bp);
32209ef1f84bSDavid du Colombier 		return -1;
32219ef1f84bSDavid du Colombier 	}
32229ef1f84bSDavid du Colombier 	dupcnt = tcb->rcv.nxt - seg->seq;
32239ef1f84bSDavid du Colombier 	if(dupcnt > 0){
32249ef1f84bSDavid du Colombier 		tcb->rerecv += dupcnt;
32259ef1f84bSDavid du Colombier 		if(seg->flags & SYN){
32269ef1f84bSDavid du Colombier 			seg->flags &= ~SYN;
32279ef1f84bSDavid du Colombier 			seg->seq++;
32289ef1f84bSDavid du Colombier 
32299ef1f84bSDavid du Colombier 			if(seg->urg > 1)
32309ef1f84bSDavid du Colombier 				seg->urg--;
32319ef1f84bSDavid du Colombier 			else
32329ef1f84bSDavid du Colombier 				seg->flags &= ~URG;
32339ef1f84bSDavid du Colombier 			dupcnt--;
32349ef1f84bSDavid du Colombier 		}
32359ef1f84bSDavid du Colombier 		if(dupcnt > 0){
32369ef1f84bSDavid du Colombier 			pullblock(bp, (ushort)dupcnt);
32379ef1f84bSDavid du Colombier 			seg->seq += dupcnt;
32389ef1f84bSDavid du Colombier 			*length -= dupcnt;
32399ef1f84bSDavid du Colombier 
32409ef1f84bSDavid du Colombier 			if(seg->urg > dupcnt)
32419ef1f84bSDavid du Colombier 				seg->urg -= dupcnt;
32429ef1f84bSDavid du Colombier 			else {
32439ef1f84bSDavid du Colombier 				seg->flags &= ~URG;
32449ef1f84bSDavid du Colombier 				seg->urg = 0;
32459ef1f84bSDavid du Colombier 			}
32469ef1f84bSDavid du Colombier 		}
32479ef1f84bSDavid du Colombier 	}
32489ef1f84bSDavid du Colombier 	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
32499ef1f84bSDavid du Colombier 	if(excess > 0) {
32509ef1f84bSDavid du Colombier 		tcb->rerecv += excess;
32519ef1f84bSDavid du Colombier 		*length -= excess;
32529ef1f84bSDavid du Colombier 		*bp = trimblock(*bp, 0, *length);
32539ef1f84bSDavid du Colombier 		if(*bp == nil)
32549ef1f84bSDavid du Colombier 			panic("presotto is a boofhead");
32559ef1f84bSDavid du Colombier 		seg->flags &= ~FIN;
32569ef1f84bSDavid du Colombier 	}
32579ef1f84bSDavid du Colombier 	return 0;
32589ef1f84bSDavid du Colombier }
32599ef1f84bSDavid du Colombier 
32609ef1f84bSDavid du Colombier static void
tcpadvise(Proto * tcp,Block * bp,char * msg)32619ef1f84bSDavid du Colombier tcpadvise(Proto *tcp, Block *bp, char *msg)
32629ef1f84bSDavid du Colombier {
32639ef1f84bSDavid du Colombier 	Tcp4hdr *h4;
32649ef1f84bSDavid du Colombier 	Tcp6hdr *h6;
32659ef1f84bSDavid du Colombier 	Tcpctl *tcb;
32669ef1f84bSDavid du Colombier 	uchar source[IPaddrlen];
32679ef1f84bSDavid du Colombier 	uchar dest[IPaddrlen];
32689ef1f84bSDavid du Colombier 	ushort psource, pdest;
32699ef1f84bSDavid du Colombier 	Conv *s, **p;
32709ef1f84bSDavid du Colombier 
32719ef1f84bSDavid du Colombier 	h4 = (Tcp4hdr*)(bp->rp);
32729ef1f84bSDavid du Colombier 	h6 = (Tcp6hdr*)(bp->rp);
32739ef1f84bSDavid du Colombier 
32749ef1f84bSDavid du Colombier 	if((h4->vihl&0xF0)==IP_VER4) {
32759ef1f84bSDavid du Colombier 		v4tov6(dest, h4->tcpdst);
32769ef1f84bSDavid du Colombier 		v4tov6(source, h4->tcpsrc);
32779ef1f84bSDavid du Colombier 		psource = nhgets(h4->tcpsport);
32789ef1f84bSDavid du Colombier 		pdest = nhgets(h4->tcpdport);
32799ef1f84bSDavid du Colombier 	}
32809ef1f84bSDavid du Colombier 	else {
32819ef1f84bSDavid du Colombier 		ipmove(dest, h6->tcpdst);
32829ef1f84bSDavid du Colombier 		ipmove(source, h6->tcpsrc);
32839ef1f84bSDavid du Colombier 		psource = nhgets(h6->tcpsport);
32849ef1f84bSDavid du Colombier 		pdest = nhgets(h6->tcpdport);
32859ef1f84bSDavid du Colombier 	}
32869ef1f84bSDavid du Colombier 
32879ef1f84bSDavid du Colombier 	/* Look for a connection */
32889ef1f84bSDavid du Colombier 	qlock(tcp);
32899ef1f84bSDavid du Colombier 	for(p = tcp->conv; *p; p++) {
32909ef1f84bSDavid du Colombier 		s = *p;
32919ef1f84bSDavid du Colombier 		tcb = (Tcpctl*)s->ptcl;
32929ef1f84bSDavid du Colombier 		if(s->rport == pdest)
32939ef1f84bSDavid du Colombier 		if(s->lport == psource)
32949ef1f84bSDavid du Colombier 		if(tcb->state != Closed)
32959ef1f84bSDavid du Colombier 		if(ipcmp(s->raddr, dest) == 0)
32969ef1f84bSDavid du Colombier 		if(ipcmp(s->laddr, source) == 0){
32979ef1f84bSDavid du Colombier 			qlock(s);
32989ef1f84bSDavid du Colombier 			qunlock(tcp);
32999ef1f84bSDavid du Colombier 			switch(tcb->state){
33009ef1f84bSDavid du Colombier 			case Syn_sent:
33019ef1f84bSDavid du Colombier 				localclose(s, msg);
33029ef1f84bSDavid du Colombier 				break;
33039ef1f84bSDavid du Colombier 			}
33049ef1f84bSDavid du Colombier 			qunlock(s);
33059ef1f84bSDavid du Colombier 			freeblist(bp);
33069ef1f84bSDavid du Colombier 			return;
33079ef1f84bSDavid du Colombier 		}
33089ef1f84bSDavid du Colombier 	}
33099ef1f84bSDavid du Colombier 	qunlock(tcp);
33109ef1f84bSDavid du Colombier 	freeblist(bp);
33119ef1f84bSDavid du Colombier }
33129ef1f84bSDavid du Colombier 
33139ef1f84bSDavid du Colombier static char*
tcpporthogdefensectl(char * val)33149ef1f84bSDavid du Colombier tcpporthogdefensectl(char *val)
33159ef1f84bSDavid du Colombier {
33169ef1f84bSDavid du Colombier 	if(strcmp(val, "on") == 0)
33179ef1f84bSDavid du Colombier 		tcpporthogdefense = 1;
33189ef1f84bSDavid du Colombier 	else if(strcmp(val, "off") == 0)
33199ef1f84bSDavid du Colombier 		tcpporthogdefense = 0;
33209ef1f84bSDavid du Colombier 	else
33219ef1f84bSDavid du Colombier 		return "unknown value for tcpporthogdefense";
33229ef1f84bSDavid du Colombier 	return nil;
33239ef1f84bSDavid du Colombier }
33249ef1f84bSDavid du Colombier 
33259ef1f84bSDavid du Colombier /* called with c qlocked */
33269ef1f84bSDavid du Colombier static char*
tcpctl(Conv * c,char ** f,int n)33279ef1f84bSDavid du Colombier tcpctl(Conv* c, char** f, int n)
33289ef1f84bSDavid du Colombier {
3329*41165bbaSDavid du Colombier 	if(n == 1 && strcmp(f[0], "close") == 0)
3330*41165bbaSDavid du Colombier 		return tcpclose2(c);
33319ef1f84bSDavid du Colombier 	if(n == 1 && strcmp(f[0], "hangup") == 0)
33329ef1f84bSDavid du Colombier 		return tcphangup(c);
333340398a43SDavid du Colombier 	if(n == 1 && strcmp(f[0], "hangupxmit") == 0)
333440398a43SDavid du Colombier 		return tcpxmitclose(c);
33359ef1f84bSDavid du Colombier 	if(n >= 1 && strcmp(f[0], "keepalive") == 0)
33369ef1f84bSDavid du Colombier 		return tcpstartka(c, f, n);
33379ef1f84bSDavid du Colombier 	if(n >= 1 && strcmp(f[0], "checksum") == 0)
33389ef1f84bSDavid du Colombier 		return tcpsetchecksum(c, f, n);
33399ef1f84bSDavid du Colombier 	if(n >= 1 && strcmp(f[0], "tcpporthogdefense") == 0)
33409ef1f84bSDavid du Colombier 		return tcpporthogdefensectl(f[1]);
33419ef1f84bSDavid du Colombier 	return "unknown control request";
33429ef1f84bSDavid du Colombier }
33439ef1f84bSDavid du Colombier 
33449ef1f84bSDavid du Colombier static int
tcpstats(Proto * tcp,char * buf,int len)33459ef1f84bSDavid du Colombier tcpstats(Proto *tcp, char *buf, int len)
33469ef1f84bSDavid du Colombier {
33479ef1f84bSDavid du Colombier 	Tcppriv *priv;
33489ef1f84bSDavid du Colombier 	char *p, *e;
33499ef1f84bSDavid du Colombier 	int i;
33509ef1f84bSDavid du Colombier 
33519ef1f84bSDavid du Colombier 	priv = tcp->priv;
33529ef1f84bSDavid du Colombier 	p = buf;
33539ef1f84bSDavid du Colombier 	e = p+len;
33549ef1f84bSDavid du Colombier 	for(i = 0; i < Nstats; i++)
33559ef1f84bSDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], priv->stats[i]);
33569ef1f84bSDavid du Colombier 	return p - buf;
33579ef1f84bSDavid du Colombier }
33589ef1f84bSDavid du Colombier 
33599ef1f84bSDavid du Colombier /*
33609ef1f84bSDavid du Colombier  *  garbage collect any stale conversations:
33619ef1f84bSDavid du Colombier  *	- SYN received but no SYN-ACK after 5 seconds (could be the SYN attack)
33629ef1f84bSDavid du Colombier  *	- Finwait2 after 5 minutes
33639ef1f84bSDavid du Colombier  *
33649ef1f84bSDavid du Colombier  *  this is called whenever we run out of channels.  Both checks are
33659ef1f84bSDavid du Colombier  *  of questionable validity so we try to use them only when we're
33669ef1f84bSDavid du Colombier  *  up against the wall.
33679ef1f84bSDavid du Colombier  */
33689ef1f84bSDavid du Colombier static int
tcpgc(Proto * tcp)33699ef1f84bSDavid du Colombier tcpgc(Proto *tcp)
33709ef1f84bSDavid du Colombier {
33719ef1f84bSDavid du Colombier 	Conv *c, **pp, **ep;
33729ef1f84bSDavid du Colombier 	int n;
33739ef1f84bSDavid du Colombier 	Tcpctl *tcb;
33749ef1f84bSDavid du Colombier 
33759ef1f84bSDavid du Colombier 
33769ef1f84bSDavid du Colombier 	n = 0;
33779ef1f84bSDavid du Colombier 	ep = &tcp->conv[tcp->nc];
33789ef1f84bSDavid du Colombier 	for(pp = tcp->conv; pp < ep; pp++) {
33799ef1f84bSDavid du Colombier 		c = *pp;
33809ef1f84bSDavid du Colombier 		if(c == nil)
33819ef1f84bSDavid du Colombier 			break;
33829ef1f84bSDavid du Colombier 		if(!canqlock(c))
33839ef1f84bSDavid du Colombier 			continue;
33849ef1f84bSDavid du Colombier 		tcb = (Tcpctl*)c->ptcl;
33859ef1f84bSDavid du Colombier 		switch(tcb->state){
33869ef1f84bSDavid du Colombier 		case Syn_received:
33879ef1f84bSDavid du Colombier 			if(NOW - tcb->time > 5000){
33889ef1f84bSDavid du Colombier 				localclose(c, Etimedout);
33899ef1f84bSDavid du Colombier 				n++;
33909ef1f84bSDavid du Colombier 			}
33919ef1f84bSDavid du Colombier 			break;
33929ef1f84bSDavid du Colombier 		case Finwait2:
33939ef1f84bSDavid du Colombier 			if(NOW - tcb->time > 5*60*1000){
33949ef1f84bSDavid du Colombier 				localclose(c, Etimedout);
33959ef1f84bSDavid du Colombier 				n++;
33969ef1f84bSDavid du Colombier 			}
33979ef1f84bSDavid du Colombier 			break;
33989ef1f84bSDavid du Colombier 		}
33999ef1f84bSDavid du Colombier 		qunlock(c);
34009ef1f84bSDavid du Colombier 	}
34019ef1f84bSDavid du Colombier 	return n;
34029ef1f84bSDavid du Colombier }
34039ef1f84bSDavid du Colombier 
34049ef1f84bSDavid du Colombier static void
tcpsettimer(Tcpctl * tcb)34059ef1f84bSDavid du Colombier tcpsettimer(Tcpctl *tcb)
34069ef1f84bSDavid du Colombier {
34079ef1f84bSDavid du Colombier 	int x;
34089ef1f84bSDavid du Colombier 
34099ef1f84bSDavid du Colombier 	/* round trip dependency */
34109ef1f84bSDavid du Colombier 	x = backoff(tcb->backoff) *
34119ef1f84bSDavid du Colombier 		(tcb->mdev + (tcb->srtt>>LOGAGAIN) + MSPTICK) / MSPTICK;
34129ef1f84bSDavid du Colombier 
34139ef1f84bSDavid du Colombier 	/* bounded twixt 0.3 and 64 seconds */
34149ef1f84bSDavid du Colombier 	if(x < 300/MSPTICK)
34159ef1f84bSDavid du Colombier 		x = 300/MSPTICK;
34169ef1f84bSDavid du Colombier 	else if(x > (64000/MSPTICK))
34179ef1f84bSDavid du Colombier 		x = 64000/MSPTICK;
34189ef1f84bSDavid du Colombier 	tcb->timer.start = x;
34199ef1f84bSDavid du Colombier }
34209ef1f84bSDavid du Colombier 
34219ef1f84bSDavid du Colombier void
tcpinit(Fs * fs)34229ef1f84bSDavid du Colombier tcpinit(Fs *fs)
34239ef1f84bSDavid du Colombier {
34249ef1f84bSDavid du Colombier 	Proto *tcp;
34259ef1f84bSDavid du Colombier 	Tcppriv *tpriv;
34269ef1f84bSDavid du Colombier 
34279ef1f84bSDavid du Colombier 	tcp = smalloc(sizeof(Proto));
34289ef1f84bSDavid du Colombier 	tpriv = tcp->priv = smalloc(sizeof(Tcppriv));
34299ef1f84bSDavid du Colombier 	tcp->name = "tcp";
34309ef1f84bSDavid du Colombier 	tcp->connect = tcpconnect;
34319ef1f84bSDavid du Colombier 	tcp->announce = tcpannounce;
34329ef1f84bSDavid du Colombier 	tcp->ctl = tcpctl;
34339ef1f84bSDavid du Colombier 	tcp->state = tcpstate;
34349ef1f84bSDavid du Colombier 	tcp->create = tcpcreate;
34359ef1f84bSDavid du Colombier 	tcp->close = tcpclose;
34369ef1f84bSDavid du Colombier 	tcp->rcv = tcpiput;
34379ef1f84bSDavid du Colombier 	tcp->advise = tcpadvise;
34389ef1f84bSDavid du Colombier 	tcp->stats = tcpstats;
34399ef1f84bSDavid du Colombier 	tcp->inuse = tcpinuse;
34409ef1f84bSDavid du Colombier 	tcp->gc = tcpgc;
34419ef1f84bSDavid du Colombier 	tcp->ipproto = IP_TCPPROTO;
34429ef1f84bSDavid du Colombier 	tcp->nc = scalednconv();
34439ef1f84bSDavid du Colombier 	tcp->ptclsize = sizeof(Tcpctl);
34449ef1f84bSDavid du Colombier 	tpriv->stats[MaxConn] = tcp->nc;
34459ef1f84bSDavid du Colombier 
34469ef1f84bSDavid du Colombier 	Fsproto(fs, tcp);
34479ef1f84bSDavid du Colombier }
34489ef1f84bSDavid du Colombier 
34499ef1f84bSDavid du Colombier static void
tcpsetscale(Conv * s,Tcpctl * tcb,ushort rcvscale,ushort sndscale)34509ef1f84bSDavid du Colombier tcpsetscale(Conv *s, Tcpctl *tcb, ushort rcvscale, ushort sndscale)
34519ef1f84bSDavid du Colombier {
34529ef1f84bSDavid du Colombier 	/*
34539ef1f84bSDavid du Colombier 	 * guess at reasonable queue sizes.  there's no current way
34549ef1f84bSDavid du Colombier 	 * to know how many nic receive buffers we can safely tie up in the
34559ef1f84bSDavid du Colombier 	 * tcp stack, and we don't adjust our queues to maximize throughput
34569ef1f84bSDavid du Colombier 	 * and minimize bufferbloat.  n.b. the offer (rcvscale) needs to be
34579ef1f84bSDavid du Colombier 	 * respected, but we still control our own buffer commitment by
34589ef1f84bSDavid du Colombier 	 * keeping a seperate qscale.
34599ef1f84bSDavid du Colombier 	 */
34609ef1f84bSDavid du Colombier 	tcb->rcv.scale = rcvscale & 0xff;
34619ef1f84bSDavid du Colombier 	tcb->snd.scale = sndscale & 0xff;
34629ef1f84bSDavid du Colombier 	tcb->qscale = rcvscale & 0xff;
34639ef1f84bSDavid du Colombier 	if(rcvscale > Maxqscale)
34649ef1f84bSDavid du Colombier 		tcb->qscale = Maxqscale;
34659ef1f84bSDavid du Colombier 
34669ef1f84bSDavid du Colombier 	if(rcvscale != tcb->rcv.scale)
34679ef1f84bSDavid du Colombier 		netlog(s->p->f, Logtcp, "tcpsetscale: window %lud "
34689ef1f84bSDavid du Colombier 			"qlen %d >> window %ud lport %d\n",
34699ef1f84bSDavid du Colombier 			tcb->window, qlen(s->rq), QMAX<<tcb->qscale, s->lport);
34709ef1f84bSDavid du Colombier 	tcb->window = QMAX << tcb->qscale;
34719ef1f84bSDavid du Colombier 	tcb->ssthresh = tcb->window;
34729ef1f84bSDavid du Colombier 
34739ef1f84bSDavid du Colombier 	/*
34749ef1f84bSDavid du Colombier 	 * it's important to set wq large enough to cover the full
34759ef1f84bSDavid du Colombier 	 * bandwidth-delay product.  it's possible to be in loss
34769ef1f84bSDavid du Colombier 	 * recovery with a big window, and we need to keep sending
34779ef1f84bSDavid du Colombier 	 * into the inflated window.  the difference can be huge
34789ef1f84bSDavid du Colombier 	 * for even modest (70ms) ping times.
34799ef1f84bSDavid du Colombier 	 */
34809ef1f84bSDavid du Colombier 	qsetlimit(s->rq, tcb->window);
34819ef1f84bSDavid du Colombier 	qsetlimit(s->wq, tcb->window);
34829ef1f84bSDavid du Colombier 	tcprcvwin(s);
34839ef1f84bSDavid du Colombier }
3484