/*  -*- Mode: C;  -*- */

/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe is free software; you can redistribute it and/or modify            *
*   it under the terms of the GNU General Public License as published by      *
*   the Free Software Foundation; either version 2 of the License, or         *
*   (at your option) any later version.                                       *
*                                                                             *
*   Nprobe is distributed in the hope that it will be useful,                 *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*   GNU General Public License for more details.                              *
*                                                                             *
*   You should have received a copy of the GNU General Public License         *
*   along with Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/


#ifndef _TCP_H_
#define _TCP_H_

#define SEQ_DEBUG

#define TCP_MSS_DEF 536

#ifndef SWIG


/*******************************************************************/

/* 
 * Stuff for service identification by port
 */

#define ISPORT(p) (dport == (p) || sport == (p))

/* Textbook code should set these up from /etc/services */

#define FTP_DATA_PORT 20
#define FTP_CNT_PORT 21

#define HIGHEST_WELLKNOWN_PORT 1024

/*******************************************************************/

/*
 * Tests on u32 sequence numbers allowing for wrap-round
 */

#define MAX_SEQ(x, y) (int)((x)-(y)) > 0 ? (x) : (y)
#define SEQ_GT(x, y)  ((int)((x)-(y)) > 0)
#define SEQ_GTE(x, y) ((int)((x)-(y)) >= 0)

/*******************************************************************/

/*
 * Some additional TCP option values 
 */


#ifndef TCPOPT_WSCALE
#define	TCPOPT_WSCALE		3	/* window scale factor (rfc1072) */
#endif
#ifndef TCPOPT_SACKOK
#define	TCPOPT_SACKOK		4	/* selective ack ok (rfc1072) */
#endif
#ifndef TCPOPT_SACK
#define	TCPOPT_SACK		5	/* selective ack (rfc1072) */
#endif
#ifndef TCPOPT_ECHO
#define	TCPOPT_ECHO		6	/* echo (rfc1072) */
#endif
#ifndef TCPOPT_ECHOREPLY
#define	TCPOPT_ECHOREPLY	7	/* echo (rfc1072) */
#endif
#ifndef TCPOPT_TSTAMP
#define	TCPOPT_TSTAMP	        8
#endif


/*******************************************************************/


struct tcp_closing 
{
  unsigned int closing_state;
  unsigned int valid_closing_states;
};
extern struct tcp_closing tcp_closing[2];
extern unsigned int tcp_closing_state, tcp_valid_closing_states;

/*******************************************************************/

typedef struct tcp_heldpkt 
{
  listhdr_t links;		/* must be first field in this struct */
  listhdr_t seq_timeo_q;	/* sequence gap timeout */
  unsigned int seq, ack;
  unsigned int len;
  unsigned char tcp_flags;
  unsigned char status;	
  prec_t *pp;
  struct tcp_simplex_flow *tsp;	/* back ptr. to simplex flow */
  struct tcp_conn *tconnp;		/* ditto tcp connection */
  us_clock_t arr_tm;
} tcp_heldpkt_t;

/* held pkt. status */
#define PKT_ON_HELD_LIST 0x1
#define PKT_ON_TIMEO_Q   0x2

#define MAX_STARTUP_NHELD 2
#define DROP_NO_SYN_PER TCP_FLOW_TIMEO_US

#define IS_ON_HELD_LIST(pp) ((pp)->status & PKT_ON_HELD_LIST)
#define IS_ON_TIMEO_Q(pp) ((pp)->status & PKT_ON_TIMEO_Q)
#define MAX_HELD_PER_FLOW 20

#define MIN_TEST_WDW_SIZE 8192	/* Min window size for pkt feasability test */
#define MWINDOW_FACT 2		/* Mult factor for window size in test */

/*
 * Reasons why tcp_catchup called - have to distinguish between packets delayed
 * because something dropped and re-transmitted (i.e also delayed from 
 * receiver perspective) and those delayed because we havn't seen something 
 * in the preceeding sequence space (ie we dropped a packet or routed round us 
 */
#define RETRANSMISSION 0x1
#define ACK_TIMEO 0x2
#define Q_TIMEO 0x4
#define TM_TIMEO 0x8
#define BUF_TIMEO 0x10
#define FINAL_DUMP 0x20
#define CONN_CLOSE 0x40
#define CONN_TIMEO 0x80 

#define CATCHUP_USE_CURRENT_TM (RETRANSMISSION |  CONN_CLOSE)
#define CATCHUP_USE_ARR_TM (ACK_TIMEO | Q_TIMEO | TM_TIMEO | BUF_TIMEO \
			    | FINAL_DUMP | CONN_TIMEO)

#endif /* ifndef SWIG */

/*******************************************************************/

/*
 * Record notifying TCP connection setup 
 */

struct tcp_open 
{
  unsigned int conn_id;
  struct flow_inner flow;
};

typedef struct tcp_open tcp_open_t;
  

/*******************************************************************/

/*
 * Contains interesting parts of TCP header to dump
 */

#define MAX_TCP_DUMPHDRS_HELD 32

#define MARK 1
#define DONT_MARK 0

struct tcp_dumphdr 
{ 
  int rtm;
#ifndef SWIG_ONLY
  unsigned int seq;
  unsigned int ack;
#else
  unsigned int seq_u32;
  unsigned int ack_u32;
#endif
  unsigned short window;
  unsigned short len;
  char flags;
  char way;
};
 
typedef struct tcp_dumphdr tcp_dumphdr_t;

struct tcp_hdrs 
{
  us_clock_t atm;		/* all hdr times are relative to this */
  int nheld;
  unsigned int conn_id;
#ifndef WREAD
  struct tcp_dumphdr hdrs[MAX_TCP_DUMPHDRS_HELD];
#else
  struct tcp_dumphdr *hdrs;
#endif
};

typedef struct tcp_hdrs tcp_hdrs_t; 

/*******************************************************************/

struct tcp_simplex_flow 
{
  unsigned int state;
  unsigned int firstack;
  unsigned int lastack;	        /* last ack sent in THIS direction */
  unsigned int firstseq;
  unsigned int solidseq;		/* hi-water for all received */
#ifdef PRINT_OUT
  unsigned int highseq;		/* highest seq seen so far */
#endif
  unsigned int mwindow;	 /* 32 bits to allow for WSCALE - MAX so far */
  unsigned int duplicate_bytes;
  unsigned int duplicate_pkts;
  unsigned int ooo_bytes;
  unsigned int ooo_pkts;
  unsigned int gap_bytes;
  unsigned int gap_pkts;
  unsigned int tot_bytes;		/* tcp payload */
  unsigned int tot_pkts;
  unsigned int tot_e_pkts;		/* pkts with no payload */
  unsigned int syn_us;		/* first arrival - SYN if state flag set */
  unsigned int acksyn_us;		/* time SYN acked */
  unsigned int fin_us;
  /* 
   * Last arrival time - will be RST time if state flag set
   * This is the time we actually use for results  - it reflects when 
   * packets actually become available
   * ie arrival time if sequence solid, or current pkt time if released by the
   * arrival of a later out of seq pkt.
   */
  unsigned int rst_us;
  unsigned int firstdata_us, lastdata_us;

#if !defined __ia64__
  listhdr_t held_list;
  struct tcp_simplex_flow *reverse;
#else
  int held_list[3];
#endif

  unsigned char nheld;

#ifdef __ia64__
  char padding;
#endif

  unsigned short mss;
  unsigned char wshift;

#if !defined SWIG_ONLY && defined __ia64__
  char padding2[3];
} __attribute__ ((packed));
#else
};
#endif

typedef struct tcp_simplex_flow tcp_simplex_flow_t;

/* 
 * tcp tcp_simplex_flow_t state values (32 bits)
 */

#define TSP_SERVER SERVER	/* 0x1 */
#define TSP_CLIENT CLIENT	/* 0x2 */


#define TSP_SYN 0x100
#define TSP_ACKSYN 0x200	/* this way's SYN is acked */
#define TSP_FIN 0x400
#define TSP_RST 0x800

#define TSP_SEQ_TIMEO        0x1000
#define TSP_SEQ_TIMEO_FORCED 0x2000
#define TSP_SEQ_TIMEO_QL     0x4000
#define TSP_SEQ_TIMEO_ACK    0x8000
#define TSP_SEQ_HELD	     0x10000	/* some packets have been delayed */


#define TSP_DUP_SYN       0x100000
#define TSP_SYN_TOO_LATE  0x200000
#define TSP_MAVERICK_SYN 0x400000

#define TSP_FORCED_OPEN   0x1000000
#define TSP_FORCED_CLOSE  0x2000000
#define TSP_FORCED_ALT   0x4000000
#define TSP_SEEN         0x8000000

#define TSP_ACKING 0x40000000	/* ack numbers now considered valid */
#define TSP_SEQUENCING 0x80000000 /* sequence no.s now regarded valid */

#define TSP_SYN_NONSENSE (TSP_DUP_SYN | TSP_SYN_TOO_LATE | TSP_MAVERICK_SYN)
#define TSP_CONN_FORCED (TSP_FORCED_OPEN | TSP_FORCED_CLOSE | TSP_FORCED_ALT)

/* all initialised to 0 */
struct tcp 
{
  tcp_simplex_flow_t client;
  tcp_simplex_flow_t server;
};
typedef struct tcp tcp_t;

struct tcp_service_data; /* forward */

/*
 * Service specific data - all initialised to 0 
 */

union service_data 
{
  struct http_conn http;
};

typedef union service_data service_data_t;


struct tcp_conn 
{
#ifndef WREAD		
  struct flow_common flow_common;
  /* other alternate connections may hang off this */
  struct tcp_conn *alt_prev, *alt_next;
  struct serv_control *serv_control;
  unsigned int service_tm;	/* effective time current packet seen */
  int payload_dump_fd[2];       /* per direction payload dump fd.s */
#else /* ifndef WREAD */
  flow_inner_t flow_inner;
  unsigned int indx;	/* used as identifier in Python class inst. */
#endif /* ifndef WREAD */
  struct tcp tcp;
  tcp_hdrs_t hdrs;
  union service_data su;
  unsigned char wshift_tmp;
 };

typedef struct tcp_conn tcp_conn_t;

#ifndef SWIG

#ifndef WREAD
#define flow_inner flow_common.inner
#endif /* ifndef WREAD */

#define TCP_STATE      tconnp->flow_inner.state
#define TCP_C_STATE    tconnp->tcp.client.state
#define TCP_S_STATE    tconnp->tcp.server.state
#define TCP_LAST_ARR_TM tconnp->flow_inner.last_arr_tm
#define TCP_FIRST_ARR_TM tconnp->flow_inner.first_arr_tm
#define TCP_C_FIRSTSEQ tconnp->tcp.client.firstseq
#define TCP_S_FIRSTSEQ tconnp->tcp.server.firstseq
#define TCP_C_SOLIDSEQ tconnp->tcp.client.solidseq
#define TCP_S_SOLIDSEQ tconnp->tcp.server.solidseq
#define TCP_C_LASTACK  tconnp->tcp.client.lastack
#define TCP_S_LASTACK  tconnp->tcp.server.lastack

#define TCP_CONN_ID    tconnp->hdrs.conn_id
#define TCP_HDRS       tconnp->hdrs.hdrs
#define TCP_HDRS_HELD  tconnp->hdrs.nheld
#define TCP_HDRS_ATM   tconnp->hdrs.atm

#define TCP_SERV_TYPE  tconnp->serv_control->rectypes.serv_type
#define TCP_SERV_OPEN_REC_TYPE  tconnp->serv_control->rectypes.open
#define TCP_SERV_HDRS_REC_TYPE  tconnp->serv_control->rectypes.hdrs
#define TCP_SERV_CLOSE_REC_TYPE  tconnp->serv_control->rectypes.close

#define TCP_CSYN_TM    tconnp->tcp.client.syn_us
#define TCP_SSYN_TM    tconnp->tcp.server.syn_us
#define TCP_CFIN_TM    tconnp->tcp.client.fin_us
#define TCP_SFIN_TM    tconnp->tcp.server.fin_us
#define TCP_CRST_TM    tconnp->tcp.client.rst_us
#define TCP_SRST_TM    tconnp->tcp.server.rst_us

#define TCP_CFIRSTDATA_TM tconnp->tcp.client.firstdata_us
#define TCP_SFIRSTDATA_TM tconnp->tcp.server.firstdata_us
#define TCP_CLASTDATA_TM tconnp->tcp.client.lastdata_us
#define TCP_SLASTDATA_TM tconnp->tcp.server.lastdata_us

#define TCP_CFIRST_TM TCP_CSYN_TM
#define TCP_SFIRST_TM TCP_SSYN_TM
#define TCP_CLAST_TM TCP_CRST_TM
#define TCP_SLAST_TM TCP_SRST_TM

#define TCP_SERV_TM tconnp->service_tm

#define TCP_CSOLID_TM  TCP_SERV_TM
#define TCP_SSOLID_TM  TCP_SERV_TM

#define TCP_OPEN_TM    TCP_FIRST_ARR_TM
#define TCP_CLOSE_TM    TCP_LAST_ARR_TM

#endif /* ifndef SWIG */

/*******************************************************************/


/* 
 * tcp flow_inner state values - NOT a tcp state machine (32 bits)
 */

#define TCP_SERVER_SEEN 0x1
#define TCP_CLIENT_SEEN 0x2

#define TCP_SERV_SYN 0x10
#define TCP_CLI_SYN 0x20
#define TCP_SERV_ACKSYN 0x40
#define TCP_CLI_ACKSYN 0x80

#define TCP_SERV_FIN 0x100
#define TCP_CLI_FIN 0x200
#define TCP_SERV_ACKFIN 0x400
#define TCP_CLI_ACKFIN 0x800

#define TCP_CLI_RST 0x1000
#define TCP_SERV_RST 0x2000
#define TCP_CLI_SERV_ERR 0x4000
#define TCP_SERV_SERV_ERR 0x8000

#define TCP_FULL_CLOSE 0x10000
#define TCP_EFFECTIVE_CLOSE   0x20000
#define TCP_TIMEO   0x40000
#define TCP_RUNEND     0x80000
#define TCP_FORCED_CLOSE 0x100000
#define TCP_FORCED_OPEN 0x200000
#define TCP_FORCED_ALT 0x400000
#define TCP_QUICK_CLOSE 0x800000

#define TCP_HDRS_DUMPED 0x10000000 /* a block already dumped */
#define DUMPTCP_HDRS 0x20000000

#define TCP_IS_ON_CONNLIST 0x40000000
#define TCP_CONN_ERROR 0x80000000


#define TCP_BOTH_SEEN ((TCP_STATE & (TCP_CLIENT_SEEN | TCP_SERVER_SEEN)) \
                        == (TCP_CLIENT_SEEN | TCP_SERVER_SEEN))


#define TCP_CLOSING_STATE (TCP_FULL_CLOSE | TCP_EFFECTIVE_CLOSE)

#define TCP_VALID_CLOSING_STATES (TCP_FULL_CLOSE | TCP_EFFECTIVE_CLOSE | TCP_TIMEO | TCP_RUNEND | TCP_FORCED_CLOSE)

#define TCP_CLOSING_STATE_QUICKCLOSE (TCP_FULL_CLOSE | TCP_EFFECTIVE_CLOSE | TCP_QUICK_CLOSE)

#define TCP_VALID_CLOSING_STATES_QUICKCLOSE (TCP_FULL_CLOSE | TCP_EFFECTIVE_CLOSE | TCP_QUICK_CLOSE | TCP_TIMEO | TCP_RUNEND | TCP_FORCED_CLOSE)


#define TCP_FORCED (TCP_FORCED_OPEN | TCP_FORCED_CLOSE | TCP_FORCED_ALT)

/*******************************************************************/

#ifndef SWIG

/*
 * tcp.c 
 */

int do_tcp(prec_t *pp, struct ip *ipp, us_clock_t us_time);
tcp_conn_t *get_tcp_conn(listhdr_t *tcp_conns, struct tcphdr *tcpp);
tcp_conn_t *new_tcp_conn(host_flow_t *hflow, int port_indx, 
			 struct tcphdr *tcpp, int way);
tcp_conn_t *force_alt_conn(tcp_conn_t *tconnp, tcp_simplex_flow_t **tsp, 
	       struct tcphdr *tcpp, int way, unsigned int len, us_clock_t us_time);
void free_tcp_conn(tcp_conn_t *tconnp);

void tcp_pkt(prec_t *pp, struct tcp_conn *tconnp, tcp_simplex_flow_t *tsp, 
	     int way, unsigned int seq, int len, unsigned int ack, unsigned char flags, 
	     unsigned int tm);
void dump_new_conn(struct tcp_conn *tconnp, us_clock_t us_time);
void dump_hdrs(struct tcp_conn *tconnp);
void rec_tcp_hdr(struct tcp_conn *tconnp, struct tcphdr *tcpp, unsigned int seq, 
		 unsigned int ack, unsigned short window, int way, 
	    us_clock_t tm, unsigned short len);
int do_tcp_options(struct tcp_conn *tconnp, tcp_simplex_flow_t *tsp, 
		   char *optp, int hlen, unsigned char flags);


/*
 * tcp_seq.c 
 */
void timeo_queues_init(void);
void tcp_hold(struct tcp_conn *tconnp, tcp_simplex_flow_t *tsp, 
	      prec_t *pp, unsigned int seq, unsigned int ack, int len, unsigned char flags);
int tcp_catch_up(tcp_simplex_flow_t *tsp, struct tcp_conn *tconnp, int way, 
		 int why);
void free_held_list(struct tcp_conn *tconnp);
void tcp_seq_timeout(tcp_heldpkt_t *hpp, int why); 
void check_seq_timeo(us_clock_t time_now);

/*
 * csum.c
 */
int tcp_csum(struct ip *ipp, struct tcphdr *tcpp, prec_t *pp);
inline unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);


/* http.c */

int do_http(prec_t *pp, tcp_conn_t *tconnp, int way);

#endif /* ifndef SWIG */



#endif /* _TCP_H_ */


/* 
 * end tcp.h 
 */
