/*

Copyright (C) 2003, 2004 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifndef __libnd_conntrack_h
#define __libnd_conntrack_h

#include <libnd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* There are a few globals, for timeouts and time-related values.
 * You can tweak them to see how it affects state-keeping, should
 * this be necessary.
 */

/* TCP MSL, 60s */
extern int libnd_conntrack_tcp_msl;

/* TCP Timeout (5 minutes) */
extern int libnd_conntrack_tcp_timeout;

/* TCP Handshake Timeout (75s) */
extern int libnd_conntrack_tcp_setup_timeout;

/* Catch-all TCP timeout, for all states (bit over 2h) */
extern int libnd_conntrack_tcp_total_timeout;

/* Timeout for everything else, including UDP (60s) */
extern int libnd_conntrack_generic_timeout;

typedef enum {
  LND_TCP_ERROR            =  0,
  LND_TCP_LISTEN           =  1,
  LND_TCP_CLOSED_NORMAL    =  2,
  LND_TCP_RST_WAIT         =  3,
  LND_TCP_CLOSED_RST       =  4,
  LND_TCP_SYN_SENT         =  5,
  LND_TCP_SYN_ACK_SENT     =  6,
  LND_TCP_ESTABLISHED      =  7,
  LND_TCP_SHUTDOWN         =  8,
  LND_TCP_TIME_WAIT        =  9,
  LND_TCP_CLOSED_TIMEOUT   = 10,
} LND_TCPState;

/* The policy of a connection table regarding
 * dead connections -- they can be ignored from
 * lookups, removals etc or included. The default
 * policy is specified when the table is created.
 * If your code doesn't take care of removing connections
 * from the table once they're dead, you'll likely want
 * LND_CONN_TABLE_IGNORE_DEAD to avoid successful lookups
 * for older connections.
 */
typedef enum {
  LND_CONN_TABLE_IGNORE_DEAD,
  LND_CONN_TABLE_INCLUDE_DEAD,
  LND_CONN_TABLE_NA, /* for errors */
} LND_ConnTablePolicy;

/* Common handle for all connection types */
typedef struct lnd_conn_id
{
  guchar              proto;

  struct bpf_timeval  start_ts;
  struct bpf_timeval  latest_ts;

  struct in_addr      ip_src;
  struct in_addr      ip_dst;
  guint16             sport;
  guint16             dport;

  guint               content_tx;
  guint               content_rx;

  /* Arbitrary data storage hashtable. */
  GHashTable         *data;

} LND_ConnID;

/* Opaque structure for generic connection state tracking */
typedef struct lnd_ip_conn LND_IPConn;

/* Opaque structure for TCP connection state tracking */
typedef struct lnd_tcp_conn LND_TCPConn;

/* Opaque structure for UDP connection state tracking */
typedef struct lnd_udp_conn LND_UDPConn;

/* Opaque structure for connection tables */
typedef struct lnd_conn_table LND_ConnTable;

typedef gboolean (*LND_ConnFunc)(LND_ConnID *conn, void *user_data);

/* IP (non TCP/UDP) connections ------------------------------------ */

LND_IPConn     *libnd_ipconn_new(const LND_Packet *packet);
void            libnd_ipconn_free(LND_IPConn *udpc);

/* UDP Connections ------------------------------------------------- */

LND_UDPConn    *libnd_udpconn_new(const LND_Packet *packet);
void            libnd_udpconn_free(LND_UDPConn *udpc);

/* TCP Connections ------------------------------------------------- */

LND_TCPConn    *libnd_tcpconn_new(const LND_Packet *packet);
void            libnd_tcpconn_free(LND_TCPConn *tcpc);
void            libnd_tcpconn_update_time(LND_TCPConn *tcpc,
					  const struct bpf_timeval *tv);
void            libnd_tcpconn_update(LND_TCPConn *tcpc,
				     const LND_Packet *packet);

gboolean        libnd_tcpconn_is_timeout(LND_TCPConn *tcpc,
					 const LND_Packet *packet);

LND_TCPState    libnd_tcpconn_state_get(const LND_TCPConn *tcpc);
const char     *libnd_tcpconn_state_get_string(LND_TCPState state);

gboolean        libnd_tcpconn_handshake_seen(const LND_TCPConn *tcpc);
gboolean        libnd_tcpconn_teardown_seen(const LND_TCPConn *tcpc);
gboolean        libnd_tcpconn_timeout_pending(const LND_TCPConn *tcpc);
gboolean        libnd_tcpconn_is_dead(LND_TCPConn *tcpc,
				      const LND_Packet *packet);


/* Connections in general ------------------------------------------ */

LND_ConnID     *libnd_conn_new(const LND_Packet *packet);
void            libnd_conn_free(LND_ConnID *conn);

void            libnd_conn_update(LND_ConnID *conn,
				  const LND_Packet *packet);

/**
 * libnd_conn_get_packet_dir - direction of a packet relative to a connection.
 * @conn: connection to use.
 * @packet: packet to compare.
 *
 * The function compares the direction indicated by the IP header
 * of @packet to that of the connection, by looking at the IP addresses.
 *
 * Returns: 1 if the source IPs match and the destination IPs match,
 * -1 when they are reversed, and 0 when the address pairs differ.
 */
gboolean        libnd_conn_get_packet_dir(const LND_ConnID *conn,
					  const LND_Packet *packet);

/**
 * libnd_conn_is_dead - reports whether a connection is dead.
 * @conn: connection to test.
 * @packet: most recent packet.
 *
 * The function reports whether @conn is dead. What exactly defines
 * a dead connection depends on the protocol. @packet is used as a
 * reference time point, so that connections that have been silent
 * for too long can be recognized as dead. @packet does not have
 * to be part of @conn.
 *
 * FIXME: @packet should probably rather be a timestamp than a
 * packet, but it appears convenient to use this way.
 *
 * Returns: %TRUE if connection is dead, %FALSE if it isn't.
 */
gboolean        libnd_conn_is_dead(LND_ConnID *conn,
				  const LND_Packet *packet);
void            libnd_conn_get_src(const LND_ConnID *conn,
				   struct in_addr *ip_src,
				   guint16 *sport);
void            libnd_conn_get_dst(const LND_ConnID *conn,
				   struct in_addr *ip_dst,
				   guint16 *dport);
void            libnd_conn_to_string(const LND_ConnID *conn,
				     char *str, int strlen);
void            libnd_conn_data_set(LND_ConnID *conn,
				    const char *key,
				    void *data);
void           *libnd_conn_data_get(const LND_ConnID *conn,
				    const char *key);
void           *libnd_conn_data_remove(LND_ConnID *conn,
				       const char *key);

/* Connection hashtables ------------------------------------------- */

LND_ConnTable  *libnd_conn_table_new(LND_ConnTablePolicy policy);
void            libnd_conn_table_free(LND_ConnTable *ct);

LND_ConnTablePolicy libnd_conn_table_get_policy(const LND_ConnTable *ct);
void            libnd_conn_table_set_policy(LND_ConnTable *ct, LND_ConnTablePolicy policy);

void            libnd_conn_table_add(LND_ConnTable *ct, LND_ConnID *conn);
  
LND_ConnID     *libnd_conn_table_lookup(LND_ConnTable *ct,
					const LND_Packet *packet);

LND_ConnID     *libnd_conn_table_remove(LND_ConnTable *ct, LND_ConnID *conn);

LND_ConnID     *libnd_conn_table_get_oldest(LND_ConnTable *ct);
LND_ConnID     *libnd_conn_table_get_youngest(LND_ConnTable *ct);
  
guint           libnd_conn_table_size(LND_ConnTable *ct);

/* libnd_conn_table_foreach - iterates over all connections in a table.
 * @ct: table handle.
 * @func: callback function.
 * @user_data: arbitrary user data passed through to @func.
 *
 * The function calls @func for every connection currently stored
 * in the table referenced by @ct. Iteration is stopped once @func
 * returns %FALSE.
 */
void            libnd_conn_table_foreach(LND_ConnTable *ct,
					 LND_ConnFunc func,
					 void *user_data);

void            libnd_conn_table_foreach_oldest(LND_ConnTable *ct,
						LND_ConnFunc func,
						void *user_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
