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

#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 <assert.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"
#include "output.h"
#include "probe_config.h"

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

listhdr_t seq_timeo_q;
listhdr_t tcp_conn_timeo_q;
listhdr_t udp_conn_timeo_q;

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

/* timeouts this reporting interval */
unsigned int tcp_flo_timeo = 0U;
unsigned int udp_flo_timeo = 0U;

/* TCP sequence gap timeouts */
unsigned int sto = 0U;
unsigned int sto_f = 0U;
unsigned int sto_q = 0U;
unsigned int sto_a = 0U;

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

/*
 * Initialisation 
 */

void 
timeo_queues_init(void)
{
  L_INIT(&tcp_conn_timeo_q);
  L_INIT(&udp_conn_timeo_q);
  L_INIT(&seq_timeo_q);

  return;
}
void 
seq_timeo_q_init(void)
{
  L_INIT(&seq_timeo_q);
  return;
}

/***************************************************************************/
  
/*
 * Get held TCP packets on and off seq timeout queue 
 */

void 
seq_timeo_q_insert(tcp_heldpkt_t *hpp)
{
  assert(IS_ON_HELD_LIST(hpp));

  L_INS_TAIL(&seq_timeo_q, hpp, seq_timeo_q, tcp_heldpkt_t);
  hpp->status |= PKT_ON_TIMEO_Q;

  assert(seq_timeo_q.lprev == (list_t *)hpp 
	 && hpp->seq_timeo_q.lnext == &seq_timeo_q);
  return;
}

void 
seq_timeo_q_rem(tcp_heldpkt_t *hpp)
{
  assert(IS_ON_TIMEO_Q(hpp));
  assert(IS_ON_HELD_LIST(hpp));

  L_REM(&seq_timeo_q, hpp, seq_timeo_q, tcp_heldpkt_t);
  hpp->status &= ~PKT_ON_TIMEO_Q;
  /* XXX TMP */
  hpp->seq_timeo_q.lprev = hpp->seq_timeo_q.lnext = NULL;
  return;
}

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

/* 
 * Find any timed-out UDP connections 
 */

void 
check_udp_timeo(us_clock_t time_now)
{
  udp_conn_t *uconnp = (udp_conn_t *)udp_conn_timeo_q.lnext;

  while (uconnp != (udp_conn_t *)&udp_conn_timeo_q)
    {
      if (time_now - uconnp->flow_inner.last_arr_tm > UDP_FLOW_TIMEO_US)
	{
	  udp_conn_t *uconnp_tmp = (udp_conn_t *)uconnp->flow_common.conn_timeo_q.lnext;
	  UDP_STATE |= UDP_TIMEO;
#if defined REPORT
	  if (report)
	    report_udp_close(uconnp);
#endif
	  BUMP_CTR(udp_timeo);
	  udp_flo_timeo++;
	  free_udp_conn(uconnp);
	  uconnp = uconnp_tmp;
	}
      else
	{
	  break;
	}
    }

return;
}

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

/* 
 * Find any timed-out TCP connections 
 */

void 
check_tcp_timeo(us_clock_t time_now)
{
  tcp_conn_t *tconnp = (tcp_conn_t *)tcp_conn_timeo_q.lnext;

  while (tconnp != (tcp_conn_t *)&tcp_conn_timeo_q)
    {
      if (time_now - tconnp->flow_inner.last_arr_tm > TCP_FLOW_TIMEO_US)
	{
	  tcp_conn_t *tconnp_tmp = (tcp_conn_t *)tconnp->flow_common.conn_timeo_q.lnext;
	  TCP_STATE |= TCP_TIMEO;
	  tconn_cleanup(tconnp, CONN_TIMEO);
#if defined REPORT
	  if (report)
	    report_tcp_close(tconnp);
#endif
	  if (!(tconnp->flow_common.inner.state & tcp_closing_state))
	    BUMP_CTR(tcp_timeo);
	  tcp_flo_timeo++;
	  free_tcp_conn(tconnp);
	  tconnp = tconnp_tmp;
	}
      else
	{
	  break;
	}
    }

return;
}

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


/* 
 * find any timed out tcp sequence gaps 
 */

void 
check_seq_timeo(us_clock_t time_now)
{
  tcp_heldpkt_t *hpp; // = (tcp_heldpkt_t *)seq_timeo_q.lnext;  
  while ((hpp = (tcp_heldpkt_t *)seq_timeo_q.lnext) != (tcp_heldpkt_t *)&seq_timeo_q)
    {
	unsigned int timeo_us =  SEQ_TIMEO_US;

	assert(IS_ON_TIMEO_Q(hpp));
	assert(IS_ON_HELD_LIST(hpp));
	assert (hpp->seq_timeo_q.lprev == &seq_timeo_q);

	if (time_now - hpp->arr_tm > timeo_us)
	    {
	      int done = 0;	/* XXX TMP */
	      tcp_conn_t *tconnp = hpp->tconnp;
	      flow_inner_t *fi = &tconnp->flow_common.inner;

	      BUMP_CTR(tcp_seq_timeo);
	      sto++;
	      hpp->tsp->state |= TSP_SEQ_TIMEO;
	      //fprintf(stderr, "STO %lums (%u) ",  
		      //timeo_us/1000, counters.max_ctrs.buffers_held.current);
	      //print_flow(stderr, fi, way);
	      tcp_seq_timeout(hpp, TM_TIMEO);

	      /* check close */
	      if (fi->state & tcp_closing_state)
		{
		  tconn_cleanup(tconnp, TM_TIMEO); /* ? */
		  free_tcp_conn(tconnp);
		  done++;	/* XXX TMP */
		}
	      //hpp = hpp_tmp;
	      //fprintf(stderr, "cleared %u\n", 
	      //buffs_held - counters.max_ctrs.buffers_held.current);
#if 0
	      assert(IS_ON_TIMEO_Q(hpp));
	      assert(IS_ON_HELD_LIST(hpp));
	      assert (hpp->seq_timeo_q.lprev == &seq_timeo_q);
#endif
	    }
	else
	  {
	    break;
	  }
      }
  
  return;
}

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

/* 
 * Check input buffers held - release to maintain free pool if necessary 
 */

void 
check_inbufs_held(us_clock_t time_now)
{
  unsigned int buffs_held = counters.max_ctrs.buffers_held.current;

  /* ensure at least one buffer available for next held packet */
  while (buffs_held >= BUFFS_HOLDABLE)
    {
      tcp_heldpkt_t *hpp = (tcp_heldpkt_t *)seq_timeo_q.lnext;
      tcp_conn_t *tconnp = hpp->tconnp;
      flow_inner_t *fi;
      int way;

      fi = &tconnp->flow_common.inner;
      way = hpp->tsp->state & (TSP_CLIENT | TSP_SERVER);
#if 0
      fprintf(stderr, "STO FORCED %llums (%u) ",  
	      (time_now - hpp->arr_tm)/1000, buffs_held);
      print_flow(stderr, fi, way);
#endif    
      BUMP_CTR(tcp_seq_timeo_forced);
      counters.max_ctrs.min_tcp_seq_to = 
	MIN(counters.max_ctrs.min_tcp_seq_to, (time_now - hpp->arr_tm)/1000);
      sto_f++;
      hpp->tsp->state |= TSP_SEQ_TIMEO_FORCED;
      tcp_seq_timeout(hpp, BUF_TIMEO);

      /* check close */
      if (fi->state & tcp_closing_state)
	{
	  tconn_cleanup(tconnp, BUF_TIMEO); /* ? */
	  free_tcp_conn(tconnp);
	}

      //fprintf(stderr, "cleared %u\n", 
	      //buffs_held - counters.max_ctrs.buffers_held.current);
      buffs_held = counters.max_ctrs.buffers_held.current;
    }

  return;
}

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



void 
check_timeouts(us_clock_t time_now)
{

  /* Find any timed out tcp connections */
  check_tcp_timeo(time_now);

  /* Find any timed out udp connections */
  check_udp_timeo(time_now);

  /* find any timed out tcp sequence gaps */
  check_seq_timeo(time_now);

  /* maintain free input buffer pool */
  check_inbufs_held(time_now);
    
  return;
}
#endif /* ifndef FINAL_REPORT */


/*
 * end timeouts.c
 */
