/*  -*- 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 *
*                                                                             *
******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/param.h>
#undef __STDC__
#include <netinet/ip.h>

#ifdef __alpha
#include <netinet/ip_var.h>
#endif
#ifdef __linux__
#include <netinet/in.h>
#define __FAVOR_BSD
#endif
#include <netinet/tcp.h>
#include <sys/param.h>

#include "list.h"
#include "pkt.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "pool.h"
#include "probe_config.h"

#include "writer.h"

#include "arr_his.h"

extern sampleHistogram *hw_his, *pw_his;

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

/*
 * Initialise host/host flow hash table 
 */

listhdr_t *
host_hashtbl_initialise()
{
  int i;
  listhdr_t *hfp;

  if ((hfp = (listhdr_t *)malloc(N_HOST_BUCKETS * sizeof(listhdr_t)))
      == NULL)
    error("host_hashtbl_initialise", "malloc");

  for (i = 0; i < N_HOST_BUCKETS; i++)
      L_INIT(&hfp[i]);

  fprintf(stderr, "\tFlow hash table initialised %d slots\n", N_HOST_BUCKETS);

  return hfp;
}

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

/*
 * Return host/host flow hash table index for ip header 
 */

int 
hash_flow(struct ip *ipp)
{
  unsigned int src = ntohl(*((unsigned int *)&ipp->ip_src));
  unsigned int dst = ntohl(*((unsigned int *)&ipp->ip_dst));

  return ((src & 0xffff) + (src >> 16) + (dst & 0xffff) + (dst >> 16)) 
    % N_HOST_BUCKETS; 
}

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


/*
 * Dump data for live connections at termination 
 */
/* TMP */
extern int held, held_empty;

void 
flow_final_dump(listhdr_t *htbl)
{
  int i, j;
  for (i = 0; i < N_HOST_BUCKETS; i++)
    {
      host_flow_t *hostlistp, *hlp_tmp;
      listhdr_t *hlp = &htbl[i];
      for (hostlistp = (host_flow_t *)hlp->lnext;
	   hostlistp != (host_flow_t *)hlp;
	   hostlistp = hlp_tmp)
	{
	  hlp_tmp = (host_flow_t *)hostlistp->hlist.lnext;
	  for (j = 0; j < N_CONN_BUCKETS; j++)
	    {
	      flow_common_t *connlistp, *tmpp;
	      listhdr_t *lp = &hostlistp->flow_conns[j];
	      for (connlistp = (flow_common_t *)lp->lnext; 
		   connlistp != (flow_common_t *)lp;
		   connlistp = tmpp)
		{
		  tmpp = (flow_common_t *)connlistp->hlist.lnext;

		  if (connlistp->type == FLOW_TCP)
		    {
		      tcp_conn_t *tconn = (tcp_conn_t *)connlistp;
		      tconn->flow_common.inner.state |= TCP_RUNEND;
		      tconn_cleanup(tconn, FINAL_DUMP);
		      if (!(tconn->flow_common.inner.state & tcp_closing_state))
			BUMP_CTR(tcp_end_close);
		      free_tcp_conn(tconn);
		    }
		  else if (connlistp->type == FLOW_UDP)
		    {
		      udp_conn_t *uconn = (udp_conn_t *)connlistp;
		      uconn->flow_common.inner.state |= UDP_RUNEND;
		      BUMP_CTR(udp_open);
		      free_udp_conn(uconn);
		    }
		  else 
		    {
		      char errmsg[100];
		      sprintf(errmsg, "unknown flow type %hu", 
			      connlistp->type & 0xff);
		      error("flow_final_dump()", errmsg);
		    }
		}
	    }
	}
      /* 
       * We're potentially dumping a vast amount of data here which may
       * over run the ouput buffers - check and if in danger pause to give 
       * the writer a chance
       */
      if (repblk_used - repblk_written > (N_REPBLKS -  N_REP_ORUNBLKS)/2)
	{
	  struct timeval timeo;

	  timeo.tv_sec = 1;
	  timeo.tv_usec = 0;
	  if (select(0, NULL, NULL, NULL, &timeo) < 0)
	    writer_error("flow_final_dump", "select");
	}
    }

/* TMP */
printf("HELD %d HELD_EMPTY %d\n", held, held_empty);  
  return;
}
/*****************************************************************************/

/*
 * Return pointer to host/host flow record if exists, otherwise NULL
 */

host_flow_t *
get_host_flow(listhdr_t *hflow_list, struct ip *ipp)
{
  host_flow_t *hostflowp;

  if ((histflags & HIS_HOSTWALK))
    {
      int steps = 0;
      
      L_WALK(hostflowp, hflow_list, hlist, host_flow_t)
	{
	  steps++;
	  if (IS_SAME_HOST_FLOW(ipp, hostflowp))
	    {
	      sampleHistogram_add(hw_his, (double)steps);
	      return hostflowp;
	    }
	}
      sampleHistogram_add(hw_his, (double)steps);
    }
  else
    {
      L_WALK(hostflowp, hflow_list, hlist, host_flow_t)
	if (IS_SAME_HOST_FLOW(ipp, hostflowp))
	  return hostflowp;
    }

  return NULL;
}

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

/*
 * Get and return pointer to new host/host flow record, link into hash list
 * - client side is always recorded as src address
 */

host_flow_t *
new_hostflow(listhdr_t *hflow_list, struct ip *ipp, int way)
{
  host_flow_t *hostflowp;

  hostflowp = get_host_flow_t();

  if (way == CLIENT)
    {
      hostflowp->srcaddr = *((unsigned int *)&ipp->ip_src);
      hostflowp->dstaddr = *((unsigned int *)&ipp->ip_dst);
    }
  else
    {
      hostflowp->srcaddr = *((unsigned int *)&ipp->ip_dst);
      hostflowp->dstaddr = *((unsigned int *)&ipp->ip_src);
    }

  hostflowp->hflow_list = hflow_list;

  L_INS_HEAD(hflow_list, hostflowp, hlist, host_flow_t);

  return hostflowp;
}

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

void 
free_host_flow(host_flow_t *hfp)
{
  L_REM(hfp->hflow_list, hfp, hlist, host_flow_t);
 
  recycle_host_flow_t(hfp);

  return;
}

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

/*
 * Return pointer to port/port flow record if exists, otherwise NULL
 */

flow_common_t *
get_flow_conn(listhdr_t *conns, unsigned short sport, unsigned short dport, unsigned char type)
{
  flow_common_t *connp;

  if ((histflags & HIS_PORTWALK))
    {
      int steps = 0;
      
      L_WALK(connp, conns, hlist, flow_common_t)
	{
	  steps++;
	  if (IS_SAME_TCP_PORT_FLOW(sport, dport, connp, type))
	    {
	      sampleHistogram_add(pw_his, (double)steps);
	      return connp;
	    }
	}
      
      sampleHistogram_add(pw_his, (double)steps);
    }
  else
    {
      L_WALK(connp, conns, hlist, flow_common_t)
	if (IS_SAME_TCP_PORT_FLOW(sport, dport, connp, type))
	  return connp;
    }
  

  return NULL;
}

/*
 * end flows.c 
 */  
  

