/*  -*- 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 <ctype.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>

#ifdef __alpha__
#include <netinet/ip_var.h>
#endif

#include <netinet/tcp.h>
#include <netinet/if_ether.h>

#include "config.h"
#include "basic_defs.h"
#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "timeouts.h"
#include "seq.h"
#include "pool.h"

#define MTRACE(x, field) \
MACRO_BEGIN   \
  printf(#x " MALLOC %d bytes - now holding %u\n", sizeof(x), counters.max_ctrs.field.current);\
MACRO_END

#define MTRACE2(x, n, field) \
MACRO_BEGIN   \
  printf(#x " MALLOC %d = %d bytes - now holding %u\n", (n), (n)*sizeof(x), counters.max_ctrs.field.current);\
MACRO_END

#if 0
#define malloc(a) { \
printf("MALLOC of %d bytes at " __FILE__ ":" __LINE__ "\n", \
	(a) ); malloc(a); }
#endif

/* 
 * Pool list heads 
 */

static listhdr_t hostflow_pool, tconn_pool, uconn_pool;
static http_trans_t *trans_pool;
static tcp_heldpkt_t *heldpkt_pool;
static prec_t *prec_pool;
static hdr_buf_t *hdr_buf_pool;
static links_buf_t *links_buf_pool;

static ns_q_t *ns_rec_pool;
static tag_buf_t *tag_buf_pool;

#ifdef TCPDUMP_FED
static bufrec_t *bufrec_pool;
#endif /* TCPDUMP_FED */

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

void 
hostflow_pool_init(int n, int init)
{
  int i;
  host_flow_t *hfp;

  if (init)
    fprintf(stderr, "\t\t%d hostflows ", n);

  if ((hfp = (host_flow_t *)malloc(n * sizeof(host_flow_t))) 
      == NULL)
    error("hostflow_pool_init", "malloc");

  L_INIT(&hostflow_pool);

  for (i = 0; i < n; i++)
    {
      L_INS_TAIL(&hostflow_pool, &hfp[i], hlist, host_flow_t);
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  return;
}

void 
hostflow_t_init(host_flow_t *hfp)
{
  int i;

  hfp->n_conns = 0;
  for (i = 0; i < N_CONN_BUCKETS; i++)
    L_INIT(&hfp->flow_conns[i]);

  return;
}

host_flow_t 
*get_host_flow_t(void)
{
  host_flow_t *hfp;

  BUMP_CTR(host_flows);
  MAX_CTR(hostconns);

  if (L_EMPTY(&hostflow_pool))
    {
      MTRACE2(host_flow_t, HOSTFLOW_POOL_INC, hostconns);
      hostflow_pool_init(HOSTFLOW_POOL_INC, POOL_ADDITION);
    }

  hfp = (host_flow_t *)hostflow_pool.lnext;
  L_REM(&hostflow_pool, hfp, hlist, host_flow_t);

  hostflow_t_init(hfp);

  return hfp;
}

void 
recycle_host_flow_t(host_flow_t *hfp)
{
  DROP_MAX_CTR(hostconns);
  L_INS_HEAD(&hostflow_pool, hfp, hlist, host_flow_t);

  return;
}


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

void 
tconn_pool_init(int n, int init)
{
  int i;
  tcp_conn_t *tcp;

  if (init)
    fprintf(stderr, "\t\t%d TCP connections ", n);

  if ((tcp = (tcp_conn_t *)malloc(n * sizeof(tcp_conn_t))) 
      == NULL)
    error("tconn_pool_init", "malloc");

  L_INIT(&tconn_pool);

  for (i = 0; i < n; i++)
    {
      L_INS_TAIL(&tconn_pool, &tcp[i], flow_common.hlist, tcp_conn_t);
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  return;
}

void 
tcp_conn_t_init(tcp_conn_t *tcp)
{

  tcp->flow_common.type = FLOW_TCP;
  tcp->flow_common.inner.state = 0UL;

  tcp->alt_prev = tcp->alt_next = tcp;

  memset(&tcp->tcp, '\0', sizeof(tcp_t));
  memset(&tcp->su, '\0', sizeof(service_data_t));

  L_INIT(&tcp->tcp.client.held_list);
  L_INIT(&tcp->tcp.server.held_list);

  tcp->tcp.client.state = TSP_CLIENT;
  tcp->tcp.server.state = TSP_SERVER;

  tcp->tcp.client.mss = TCP_MSS_DEF;
  tcp->tcp.server.mss = TCP_MSS_DEF;

  tcp->tcp.client.reverse = &tcp->tcp.server;
  tcp->tcp.server.reverse = &tcp->tcp.client;

  tcp->hdrs.nheld = 0;
  tcp->wshift_tmp = '\0';

  tcp->payload_dump_fd[0] = 0;
  tcp->payload_dump_fd[1] = 0;
  

  return;
}

tcp_conn_t 
*get_tcp_conn_t(void)
{
  tcp_conn_t *tcp;

  BUMP_CTR(tcpconns);
  MAX_CTR(tconns);

  if (L_EMPTY(&tconn_pool))
    {
      MTRACE2(tcp_conn_t, TCONN_POOL_INC, tconns);
      tconn_pool_init(TCONN_POOL_INC, POOL_ADDITION);
    }

  tcp = (tcp_conn_t *)tconn_pool.lnext;
  L_REM(&tconn_pool, tcp, flow_common.hlist, tcp_conn_t);

  tcp_conn_t_init(tcp);

  return tcp;
}

void 
recycle_tcp_conn_t(tcp_conn_t *tcp)
{

  DROP_MAX_CTR(tconns);
  L_INS_HEAD(&tconn_pool, tcp, flow_common.hlist, tcp_conn_t);

  return;
}


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

void 
uconn_pool_init(int n, int init)
{
  int i;
  udp_conn_t *ucp;

  if (init)
    fprintf(stderr, "\t\t%d UDP connections ", n);

  if ((ucp = (udp_conn_t *)malloc(n * sizeof(udp_conn_t))) 
      == NULL)
    error("uconn_pool_init", "malloc");

  L_INIT(&uconn_pool);

  for (i = 0; i < n; i++)
    {
      L_INS_TAIL(&uconn_pool, &ucp[i], flow_common.hlist, udp_conn_t);
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  return;
}

void 
udp_conn_t_init(udp_conn_t *ucp)
{

  ucp->flow_common.type = FLOW_UDP;
  ucp->flow_common.inner.state = 0UL;

  memset(&ucp->udp, '\0', sizeof(udp_t));
  ucp->service_data = NULL;

  ucp->udp.client.state = UDP_CLIENT;
  ucp->udp.server.state = UDP_SERVER;

  ucp->udp.client.reverse = &ucp->udp.server;
  ucp->udp.server.reverse = &ucp->udp.client;

  return;
}

udp_conn_t 
*get_udp_conn_t(void)
{
  udp_conn_t *ucp;

  BUMP_CTR(udpconns);
  MAX_CTR(uconns);

  if (L_EMPTY(&uconn_pool))
    {
      MTRACE2(udp_conn_t, UCONN_POOL_INC, uconns);
      uconn_pool_init(UCONN_POOL_INC, POOL_ADDITION);
    }

  ucp = (udp_conn_t *)uconn_pool.lnext;
  L_REM(&uconn_pool, ucp, flow_common.hlist, udp_conn_t);

  udp_conn_t_init(ucp);

  return ucp;
}

void 
recycle_udp_conn_t(udp_conn_t *ucp)
{

  DROP_MAX_CTR(uconns);
  L_INS_HEAD(&uconn_pool, ucp, flow_common.hlist, udp_conn_t);

  return;
}


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

void 
trans_pool_init(int n, int init)
{
  int i;
  http_trans_t *tp;

  if (init)
    fprintf(stderr, "\t\t%d transactions ", n);

  if ((tp = (http_trans_t *)malloc(n * sizeof(http_trans_t))) 
      == NULL)
    error("trans_pool_init", "malloc");

  trans_pool = tp;

  for (i = 0; i < n - 1; i++)
    {
      tp[i].next = &tp[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  tp[n-1].next = NULL;

  return;
}

void 
http_trans_t_init(http_trans_t *tp)
{
  //unsigned short *field = &tp->inner.s.ti.marzipan;
  memset(&tp->inner, '\0', sizeof(struct http_trans_inner));
  //tp->inner.c.ti.body_len = HTTP_BODY_LEN_UNKNOWN;
  tp->inner.s.ti.body_len = HTTP_BODY_LEN_UNKNOWN;
  tp->inner.c.ti.status = TRANS_INCOMPLETE;
  tp->inner.s.ti.status = TRANS_INCOMPLETE;

  tp->inner.cinf.reqlen = 0;
  tp->inner.cinf.reflen = 0;

  //tp->inner.sinf.html_baselen = 0;
 
  tp->links.buf = NULL; 
  tp->links.chain = NULL; 
  tp->links.nbufs = 0U; 
  tp->links.nchars = 0U; 
  tp->links.totchars = 0UL;

  tp->inner.ob_p_state.tagbuf = tp->inner.ob_p_state.tagbuf_buf;
  tp->inner.ob_p_state.tagbuf_sz = TAGBUF_SZ;
  tp->next = NULL;

  return;
}

  

http_trans_t 
*get_http_trans_t(void)
{
  http_trans_t *tp;

  MAX_CTR(trans);

  if (trans_pool == NULL)
    {
      MTRACE2(http_trans_t, TRANS_POOL_INC, trans);
      trans_pool_init(TRANS_POOL_INC, POOL_ADDITION);
    }

  tp = trans_pool;
  trans_pool = tp->next;

  http_trans_t_init(tp);

  return tp;
}

void 
recycle_http_trans_t(http_trans_t *tp)
{
  DROP_MAX_CTR(trans);

  if (tp->inner.c.http_p_state.buffer != NULL)
    recycle_saved_hdr_buffer(tp->inner.c.http_p_state.buffer);
  if (tp->inner.s.http_p_state.buffer != NULL)
    recycle_saved_hdr_buffer(tp->inner.s.http_p_state.buffer);

  tp->next = trans_pool;
  trans_pool = tp;

  return;
}


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


void 
heldpkt_pool_init(int n, int init)
{
  int i;
  tcp_heldpkt_t *hpp;

  if (init)
    fprintf(stderr, "\t\t%d held packet records ", n);

  if ((hpp = (tcp_heldpkt_t *)malloc(n * sizeof(tcp_heldpkt_t))) 
      == NULL)
    error("heldpkt_pool_init", "malloc");

  heldpkt_pool = hpp;

  for (i = 0; i < n - 1; i++)
    {
      hpp[i].links.lnext = (list_t *)&hpp[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  hpp[n-1].links.lnext = NULL;

  return;
} 

  

tcp_heldpkt_t *
get_tcp_heldpkt_t(void)
{
  tcp_heldpkt_t *hpp;

  MAX_CTR(heldpkts);

  if (heldpkt_pool == NULL)
    {
      MTRACE2(tcp_heldpkt_t, HELDPKT_POOL_INC, heldpkts);
      heldpkt_pool_init(HELDPKT_POOL_INC, POOL_ADDITION);
    }

  hpp = heldpkt_pool;
  heldpkt_pool = (tcp_heldpkt_t *)hpp->links.lnext;


  return hpp;
}

void 
recycle_tcp_heldpkt_t(tcp_heldpkt_t *hpp)
{
  DROP_MAX_CTR(heldpkts);
  hpp->links.lnext = (list_t *)heldpkt_pool;
  heldpkt_pool = hpp;

  return;
}


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


void 
prec_pool_init()
{
  int i;
  prec_t *pp;

  fprintf(stderr, "\t\t%d IO buffer carriers ", PREC_POOL_SZ);

  if ((pp = (prec_t *)malloc(PREC_POOL_SZ * sizeof(prec_t))) 
      == NULL)
    error("prec_pool_init", "malloc");

  prec_pool = pp;

  for (i = 0; i < PREC_POOL_SZ - 1; i++)
    {
      pp[i].next = &pp[i+1];
      if (!(i%(PREC_POOL_SZ/20)))
	fprintf(stderr, ".");
    }

  fprintf(stderr, "\n");

  pp[PREC_POOL_SZ-1].next = NULL;

  return;
}

  
prec_t 
*get_prec_t(void)
{
  prec_t *pp;

  BUMP_CTR(buffers_got);
  MAX_CTR(buffers_held);

  if (prec_pool == NULL)
    {
      /* SHOULD NEVER HAPPEN */
      MTRACE(prec_t, buffers_held);
      if ((pp = 
	   (prec_t *)malloc(sizeof(prec_t))) 
	       == NULL)
	error("prec_pool_init", "malloc");
    }
  else
    {
      pp = prec_pool;
      prec_pool = pp->next;
    }

  return pp;
}

void 
recycle_prec_t(prec_t *pp)
{
  BUMP_CTR(buffers_released);
  DROP_MAX_CTR(buffers_held);
  pp->next = prec_pool;
  prec_pool = pp;

  return;
}


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

void 
saved_hdr_buffer_pool_init(int n, int init)
{
  int i;
  hdr_buf_t *bp;

  if (init)
    fprintf(stderr, "\t\t%d fragmented header buffers ", n);

    if ((bp = (hdr_buf_t *)malloc(n * sizeof(hdr_buf_t))) 
      == NULL)
    error("saved_hdr_buffer_pool_init", "malloc");

  hdr_buf_pool = bp;

  for (i = 0; i < n - 1; i++)
    {
      bp[i].next = &bp[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  bp[n-1].next = NULL;

  return;
}
  

hdr_buf_t 
*get_saved_hdr_buffer(void)
{
  hdr_buf_t *bp;

  MAX_CTR(saved_hdr_buffs);

  if (hdr_buf_pool == NULL)
    {
      MTRACE2(hdr_buf_t, HDR_BUF_POOL_INC, saved_hdr_buffs);
      saved_hdr_buffer_pool_init(HDR_BUF_POOL_INC, POOL_ADDITION);
    }

  bp = hdr_buf_pool;
  hdr_buf_pool = bp->next;

  return bp;
}

void 
recycle_saved_hdr_buffer(hdr_buf_t *bp)
{
  DROP_MAX_CTR(saved_hdr_buffs);
  bp->next = hdr_buf_pool;
  hdr_buf_pool = bp;

  return;
}


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



void 
links_pool_init(int n, int init)
{
  int i;
  links_buf_t *bp;

  if (init)
    fprintf(stderr, "\t\t%d link strings buffers ", n);

  if ((bp = 
       (links_buf_t *)malloc(n*sizeof(links_buf_t))) 
      == NULL)
    error("links_pool_init", "malloc");
  

  for (i = 0; i < n - 1; i++)
    {
      bp[i].next = &bp[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  bp[n-1].next = NULL;

  links_buf_pool = bp;

  return;
}


links_buf_t 
*get_links_buffer(void)
{
  links_buf_t *bp;

  MAX_CTR(links_buffs);

  if (links_buf_pool == NULL)
    {
      MTRACE2(links_buf_t, LINKS_POOL_INC, links_buffs);
      links_pool_init(LINKS_POOL_INC, POOL_ADDITION);
    }


  bp = links_buf_pool;
  links_buf_pool = bp->next;

  /* initialisation */
  bp->next = NULL;
  bp->nchars = 0U;

  

  return bp;
}

void 
recycle_links_buffer(links_buf_t *bp)
{
  
  DROP_MAX_CTR(links_buffs);
  bp->next = links_buf_pool;
  links_buf_pool = bp;

  return;
}


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

void 
ns_rec_pool_init(int n, int init)
{
  int i;
  ns_q_t *np;

  if (init)
    fprintf(stderr, "\t\t%d ns lookup structures ", n);

    if ((np = (ns_q_t *)malloc(n * sizeof(ns_q_t))) 
      == NULL)
    error("ns_pool_init", "malloc");

  ns_rec_pool = np;

  for (i = 0; i < n - 1; i++)
    {
      np[i].q = &np[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  np[n-1].q = NULL;

  return;
}
  

ns_rec_t 
*get_ns_rec(void)
{
  ns_q_t *np;

  MAX_CTR(ns_recs);

  if (ns_rec_pool == NULL)
    {
      MTRACE2(ns_q_t, NS_REC_POOL_INC, ns_recs);
      ns_rec_pool_init(NS_REC_POOL_INC, POOL_ADDITION);
    }

  np = ns_rec_pool;
  ns_rec_pool = np->q;

  /* initialisation */
  memset(&np->rec, '\0', sizeof(ns_rec_t));

  return &np->rec;
}

void 
recycle_ns_rec(ns_rec_t *np)
{
  DROP_MAX_CTR(ns_recs);
  ((ns_q_t *)np)->q = ns_rec_pool;
  ns_rec_pool = (ns_q_t *)np;

  return;
}


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

void 
tag_buf_pool_init(int n, int init)
{
  int i;
  tag_buf_t *tp;

  if (init)
    fprintf(stderr, "\t\t%d large tag buffs ", n);

    if ((tp = (tag_buf_t *)malloc(n * sizeof(tag_buf_t))) 
      == NULL)
    error("tag_buf_pool_init", "malloc");

  tag_buf_pool = tp;

  for (i = 0; i < n - 1; i++)
    {
      tp[i].q = &tp[i+1];
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  tp[n-1].q = NULL;

  return;
}
  

char 
*get_tag_buf(void)
{
  tag_buf_t *tp;

  MAX_CTR(tag_bufs);

  if (tag_buf_pool == NULL)
    {
      MTRACE2(tag_buf_t, TAG_BUF_POOL_INC, tag_bufs);
      tag_buf_pool_init(TAG_BUF_POOL_INC, POOL_ADDITION);
    }

  tp = tag_buf_pool;
  tag_buf_pool = tp->q;

  return (char *)tp;
}

void 
recycle_tag_buf(char *tp)
{
  DROP_MAX_CTR(tag_bufs);
  ((tag_buf_t *)tp)->q = tag_buf_pool;
  tag_buf_pool = (tag_buf_t *)tp;

  return;
}


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

#ifdef TCPDUMP_FED

void 
bufrec_pool_init()
{
  int i;
  bufrec_t *bp;

  fprintf(stderr, "\t\t%d IO buffers ", BUFREC_POOL_SZ);

  if ((bp = (prec_t *)malloc(BUFREC_POOL_SZ * sizeof(bufrec_t))) 
      == NULL)
    error("bufrec_pool_init", "malloc");

  bufrec_pool = bp;

  for (i = 0; i < BUFREC_POOL_SZ - 1; i++)
    {
      bp[i].base = (unsigned char *)&bp[i+1];
      if (!(i%(BUFREC_POOL_SZ/20)))
	fprintf(stderr, ".");
    }

  fprintf(stderr, "\n");

  bp[PREC_POOL_SZ-1].base = NULL;

  return;
}

  
bufrec_t 
*get_bufrec_t(void)
{
  bufrec_t *bp;

  if (bufrec_pool == NULL)
    {
      /* SHOULD NEVER HAPPEN */
      if ((bp = 
	   (bufrec_t *)malloc(sizeof(bufrec_t))) 
	       == NULL)
	error("get_bufrec_t()", "malloc");
    }
  else
    {
      bp = bufrec_pool;
      bufrec_pool = (bufrec_t *)bp->base;
    }
  

  return bp;
}

void 
recycle_bufrec_t(bufrec_t *bp)
{
  bp->base = (unsigned char *)bufrec_pool;
  bufrec_pool = bp;

  return;
}

#endif /* #ifdef TCPDUMP_FED */


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


void 
pool_init(void)
{
  fprintf(stderr, "\tInitialising data pools\n");
  hostflow_pool_init(HOSTFLOW_POOL_SZ, POOL_INITIALISATION);
  tconn_pool_init(TCONN_POOL_SZ, POOL_INITIALISATION);
  uconn_pool_init(UCONN_POOL_SZ, POOL_INITIALISATION);
  trans_pool_init(TRANS_POOL_SZ, POOL_INITIALISATION);
  heldpkt_pool_init(HELDPKT_POOL_SZ, POOL_INITIALISATION);
  prec_pool_init();
  saved_hdr_buffer_pool_init(HDR_BUF_POOL_SZ, POOL_INITIALISATION);
  links_pool_init(LINKS_POOL_SZ, POOL_INITIALISATION);
  ns_rec_pool_init(NS_REC_POOL_SZ, POOL_INITIALISATION);
  tag_buf_pool_init(TAG_BUF_POOL_SZ,  POOL_INITIALISATION);

#ifdef TCPDUMP_FED
  bufrec_pool_init();
#endif

  fprintf(stderr, "\tData pools initialised\n\n");

  return;
}
