/*
 *	This file contains definitions and prototypes for the structures
 *	and functions used throughout TCP.
 *
 *	04/17/94, Kay Roemer.
 */

#ifndef _TCP_H
#define _TCP_H

#include "inet.h"
#include "timer.h"
#include "iov.h"
#include "buf.h"

/*
 * Default and max. maximum segment size.
 */
#define TCP_MSS		536
#define TCP_MAXMSS	2000
#define TCP_MINMSS	100

/*
 * Maximum segment lifetime. We chose to use 30 seconds, because it turns
 * out to be very unpleasant to wait 2 minutes (2*MSL when using 60 sec
 * as MSL) before the timewaiting socket goes away when you want to bind
 * a new socket to the same address.
 */
#define TCP_MSL		(30000/EVTGRAN)

/*
 * Minimum timeout between (and before) retransmissions. This has to be
 * greater than or equal to 200ms because of BSD's delayed acks.
 */
#define TCP_MINRETRANS	(200/EVTGRAN)

/*
 * Maximum retransmission timeout, should be greater than or equal to MSL.
 */
#define TCP_MAXRETRANS	(30000/EVTGRAN)

/*
 * Maximum timeout between window probes.
 */
#define TCP_MAXPROBE	(30000/EVTGRAN)

/*
 * Maximum number of retransmissions before giving up with ETIMEDOUT.
 */
#define TCP_MAXRETRY	15

/*
 * Time to delay acks. This is currently 15ms. Everything greater than
 * 20ms leads to *immense* performance losses over lo0 due to the way
 * MintNet handles input packets.
 */
#define TCP_ACKDELAY	(15/EVTGRAN)

/*
 * When no response from peer (when he is to respond) for this time
 * then close connection.
 */
#define TCP_CONN_TMOUT	(10L*60L*1000L/EVTGRAN)

/*
 * Dupack threshhold. When the TCP receives this many completely duplicate
 * acks it starts to resend some segments to keep the congestion window open.
 */
#define TCP_DUPTHRESH	3

/*
 * Dropped segments threshhold. When the TCP encounters this many dropped
 * retransmitted segments it resets the backoff. (because a line error
 * caused the packet to time out, not a too small rtt).
 */
#define TCP_DROPTHRESH	1

#define TCP_RESERVE	140
#define TCP_MINLEN	(sizeof (struct tcp_dgram))

#define TCP_DATA(th)	((char *)(th) + (th)->hdrlen * 4)
#define TCP_HDRLEN(th)	((th)->hdrlen * 4)

/* TCP datagram header */
struct tcp_dgram {
	unsigned short	srcport;	/* source port */
	unsigned short	dstport;	/* destination port */
	long		seq;		/* sequence number */
	long		ack;		/* acknowledge number */
	unsigned short	hdrlen:4;	/* TCP header len in longs */
	unsigned short	flags:12;	/* segment flags */
#define TCPF_URG	0x020		/* segment contains urgent data */
#define TCPF_ACK	0x010		/* segment contains acknowledgement */
#define TCPF_PSH	0x008		/* push function */
#define TCPF_RST	0x004		/* reset connection */
#define TCPF_SYN	0x002		/* syncronice sequence numbers */
#define TCPF_FIN	0x001		/* finish connection */
#define TCPF_FREEME	0x800
	unsigned short	window;		/* window size */
	short		chksum;		/* checksum */
	unsigned short	urgptr;		/* urgent data offset rel to seq */
	char		data[0];	/* options/user data */
};

/* TCP options */
#define TCPOPT_EOL	0	/* end of option list */
#define TCPOPT_NOP	1	/* no operation */
#define TCPOPT_MSS	2	/* maximum segment size */

/* TCP setsockopt options */
#define TCP_NODELAY	1	/* disable Nagle algorithm */

/* Sequence space comparators */
#define SEQEQ(x, y)	((long)(x) == (long)(y))	/* (x == y) mod 2^32 */
#define SEQNE(x, y)	((long)(x) != (long)(y))	/* (x != y) mod 2^32 */
#define SEQLT(x, y)	((long)(x) - (long)(y) <  0)	/* (x <  y) mod 2^32 */
#define SEQLE(x, y)	((long)(x) - (long)(y) <= 0)	/* (x <= y) mod 2^32 */
#define SEQGT(x, y)	((long)(x) - (long)(y) >  0)	/* (x >  y) mod 2^32 */
#define SEQGE(x, y)	((long)(x) - (long)(y) >= 0)	/* (x >= y) mod 2^32 */

/* TCB states */
#define TCBS_CLOSED		0
#define TCBS_LISTEN		1
#define TCBS_SYNSENT		2
#define TCBS_SYNRCVD		3
#define TCBS_ESTABLISHED	4
#define TCBS_FINWAIT1		5
#define TCBS_FINWAIT2		6
#define TCBS_CLOSEWAIT		7
#define TCBS_LASTACK		8
#define TCBS_CLOSING		9
#define TCBS_TIMEWAIT		10

/* TCB output states */
#define TCBOS_IDLE		0
#define TCBOS_RETRANS		1
#define TCBOS_PERSIST		2
#define TCBOS_XMIT		3

/* TCB output events */
#define TCBOE_SEND		0
#define TCBOE_ACKRCVD		1
#define TCBOE_WNDOPEN		2
#define TCBOE_WNDCLOSE		3
#define TCBOE_TIMEOUT		4
#define TCBOE_DUPACK		5

#define TCB_OSTATE(t, e)	((*tcb_ostate[(t)->ostate]) (t, e))

/* TCP control block */
struct tcb {
	struct in_data	*data;		/* backlink to inet socket */
	short		state;		/* TCB state, TCBS_* */
	short		ostate;		/* TCB output state, TCBOS_* */
	short		flags;		/* misc flags */
#define TCBF_PSH	0x01		/* PUSH seen */
#define TCBF_FIN	0x02		/* FIN seen */
#define TCBF_PASSIVE	0x04		/* tcb is forked off a server */
#define TCBF_DORTT	0x08		/* measuring RTT */
#define TCBF_NDELAY	0x10		/* disable nagle algorithm */
#define TCBF_DELACK	0x20		/* need delayed ack */

	long		snd_isn;	/* initial send sequence number */
	long		snd_una;	/* oldest unacknowledged seq number */
	long		snd_nxt;	/* next seq number to send */
	long		snd_max;	/* max sent seq number */
	long		snd_wnd;	/* send window size */
	long		snd_cwnd;	/* congestion window size */
	long		snd_thresh;	/* cong. window threshold */
	long		snd_wndseq;	/* seq number of last window update */
	long		snd_wndack;	/* ack number of last window update */
	long		snd_wndmax;	/* max. send window seen on conn. */
	short		snd_ppw;	/* # of mss sized pkts that fit into
					   snd_wndmax */
	long		snd_mss;	/* send max segment size */
	long		snd_urg;	/* send urgent pointer */

	long		rcv_isn;	/* initial recv sequence number */
	long		rcv_nxt;	/* next seq number to recv */
	long		rcv_wnd;	/* receive window size */
	long		rcv_mss;	/* recv max segment size */
	long		rcv_urg;	/* receive urgent pointer */

	long		seq_psh;	/* sequence number of PUSH */
	long		seq_fin;	/* sequence number of FIN */
	long		seq_read;	/* sequence of next byte to read() */
	long		seq_uread;	/* sequence of next urg byte to read */
	long		seq_write;	/* sequence of next byte to write */

	long		rttseq;		/* calc RTT when this gets acked */
	long		rtt;		/* estimated round trip time */
	long		rttdev;		/* deviation in round trip time */
	short		backoff;	/* timer backoff */

	short		dupacks;	/* # of duplicate acks */

	short		nretrans;	/* number of retransmits */

	long		retrans_tmo;	/* retransmit timeout value */
	long		persist_tmo;	/* persist timeout value */
	long		keep_tmo;	/* keepalive timeout value */

	struct event	timer_evt;	/* persist/retransmit timer event */
	struct event	delete_evt;	/* tcb deletion timer event */
	struct event	ack_evt;	/* delayed ack event */

	long		last_recv;	/* last time we received something */
};

#ifndef NOEXTERNS
extern void		(*tcb_state[])	(struct tcb *, BUF *);
extern void		(*tcb_ostate[])	(struct tcb *, short);

extern struct tcb	*tcb_alloc	(void);
extern void		tcb_free	(struct tcb *);
extern void		tcb_reset	(struct tcb *, long);
extern void		tcb_error	(struct tcb *, long);
extern void		tcb_delete	(struct tcb *);
extern void		tcb_wait	(struct tcb *);

extern long		tcp_isn		(void);
static short		tcp_finished	(struct tcb *);
extern long		tcp_sndrst	(BUF *);
extern long		tcp_sndack	(struct tcb *, BUF *);
extern short		tcp_valid	(struct tcb *, BUF *);
static long		tcp_seglen	(BUF *, struct tcp_dgram *);
static long		tcp_datalen	(BUF *, struct tcp_dgram *);
extern long		tcp_options	(struct tcb *, struct tcp_dgram *);
extern long		tcp_mss		(struct tcb *, unsigned long, long);
extern unsigned short	tcp_checksum	(struct tcp_dgram *, unsigned short,
					unsigned long, unsigned long);

extern long		tcp_rcvurg	(struct tcb *, BUF *);

extern long		tcp_timeout	(struct tcb *);
extern long		tcp_rcvwnd	(struct tcb *, short);
extern long		tcp_output	(struct tcb *, struct iovec *, short,
					long, long, short);

extern void		tcp_init	(void);
extern void		tcp_dump	(BUF *);

extern void		tcp_siginit	(void);
extern void		tcp_sendsig	(struct tcb *, short);
#endif

/*
 * Some heavily used one-liners as inlines.
 */

static inline short
tcp_finished (tcb)
	struct tcb *tcb;
{
	return (tcb->flags & TCBF_FIN && SEQLT (tcb->seq_fin, tcb->rcv_nxt));
}

/* Return the length of the TCP segment in `buf'. */
static inline long
tcp_seglen (buf, tcph)
	BUF *buf;
	struct tcp_dgram *tcph;
{
	long len;

	len = (long)buf->dend - (long)tcph - tcph->hdrlen*4;
	if (tcph->flags & TCPF_SYN) ++len;
	if (tcph->flags & TCPF_FIN) ++len;
	return len;
}

static inline long
tcp_datalen (buf, tcph)
	BUF *buf;
	struct tcp_dgram *tcph;
{
	return ((long)buf->dend - (long)tcph - tcph->hdrlen*4);
}
#endif /* _TCP_H */
