#include "tcpflow.h"

extern stats_t stats;
extern int thr_seconds;
extern int thr_packets;
extern int thr_bytes;
extern char daemond;

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


/* This is called when we receive an IP datagram.  We make sure that
 * it's valid and contains a TCP segment; if so, we pass it to
 * process_tcp() for further processing.
 *
 * Note: we currently don't know how to handle IP fragments. */
void process_ip(const u_char *data, u_int32_t caplen)
{
  const struct ip *ip_header = (struct ip *) data;
  u_int ip_header_len;
  u_int ip_total_len;

  /* make sure that the packet is at least as long as the min IP header */
  if (caplen < sizeof(struct ip)) {
    DEBUG(6) ("received truncated IP datagram!");
    return;
  }

  /* for now we're only looking for TCP; throw away everything else */
  if (ip_header->ip_p != IPPROTO_TCP) {
    DEBUG(50) ("got non-TCP frame -- IP proto %d", ip_header->ip_p);
    return;
  }

  /* check and see if we got everything.  NOTE: we must use
   * ip_total_len after this, because we may have captured bytes
   * beyond the end of the packet (e.g. ethernet padding). */
  ip_total_len = ntohs(ip_header->ip_len);
  if (caplen < ip_total_len) {
    DEBUG(6) ("warning: captured only %ld bytes of %ld-byte IP datagram",
	 (long) caplen, (long) ip_total_len);
  }

  /* XXX - throw away everything but fragment 0; this version doesn't
   * know how to do fragment reassembly. */
  if (ntohs(ip_header->ip_off) & 0x1fff) {
    DEBUG(2) ("warning: throwing away IP fragment from X to X");
    return;
  }

  /* figure out where the IP header ends */
  ip_header_len = ip_header->ip_hl * 4;

  /* make sure there's some data */
  if (ip_header_len > ip_total_len) {
    DEBUG(6) ("received truncated IP datagram!");
    return;
  }

  /* do TCP processing */
  process_tcp(data + ip_header_len, ip_total_len - ip_header_len,
	      ntohl(ip_header->ip_src.s_addr),
	      ntohl(ip_header->ip_dst.s_addr));
}


void process_tcp(const u_char *data, u_int32_t length, u_int32_t src,
		 u_int32_t dst)
{
struct tcphdr *tcp_header = (struct tcphdr *) data;
flow_t flow_rx, flow_tx;
flow_state_t *state;
int flow_direction;
time_t current_time;
int flow_type;
int bytes_tx, bytes_rx;

  if (length < sizeof(struct tcphdr)) {
    DEBUG(6) ("received truncated TCP segment!");
    return;
  }

  time (&current_time);  

  /* fill in the flow_t structure with info that identifies this flow (tx and rx) */
  flow_tx.src = src;
  flow_tx.dst = dst;
  flow_tx.sport = ntohs(tcp_header->source);
  flow_tx.dport = ntohs(tcp_header->dest);

  flow_rx.dst = src;
  flow_rx.src = dst;
  flow_rx.dport = ntohs(tcp_header->source);
  flow_rx.sport = ntohs(tcp_header->dest);

  /* Statistics */
  stats.packets++;

  /* Find if the flow exists... */
  state = find_flow_state(flow_tx, flow_rx, &flow_type);

  /* It's a new flow? */
  if( tcp_header->syn && tcp_header->ack ) {

     DEBUG(5) ("Detecting a new flow: %s", flow_name(flow_rx) );

     if( !state ) 
        state = create_flow_state(flow_rx);

     state->flow = flow_rx;
     state->isn_tx = ntohl(tcp_header->ack_seq);
     state->isn_rx = ntohl(tcp_header->seq);
     state->packet_rx = 0;
     state->packet_tx = 0;
     state->time_last_access = current_time;
     state->time_creation = current_time;
     return;
  }

  /* Drops */
  if( !state ) {

     /* Statistics */
     stats.drops++;

     return;
  }

  if( tcp_header->rst || tcp_header->fin ) {
     distroy_flow_state( state );
     return;
  }

  state->time_last_access = current_time;

  /* detect tx flow */
  if( flow_type == FLOW_TX ) {

        state->packet_tx++;
        bytes_tx = ntohl(tcp_header->seq) - state->isn_tx;
        bytes_rx = ntohl(tcp_header->ack_seq) - state->isn_rx;

   /* detect rx flow */
   } else {

        state->packet_rx++;
        bytes_tx = ntohl(tcp_header->ack_seq) - state->isn_tx;
        bytes_rx = ntohl(tcp_header->seq) - state->isn_rx;

   }

    /* It's a potencial tunnel? */
    if( ( state->packet_tx > thr_packets && state->packet_rx > thr_packets ) ||
        ( bytes_tx > thr_bytes && bytes_rx > thr_bytes ) ||
        ( current_time - state->time_creation > thr_seconds ) ) {

             if( daemond )
                syslog( LOG_INFO, "Potencial tunnel = %s\n", flow_state_name(state, bytes_tx, bytes_rx) );
             else
                printf( "Potencial tunnel = %s\n", flow_state_name(state, bytes_tx, bytes_rx) );

             distroy_flow_state( state );
             return;

    }
}
