/******************************************************************************
*                                                                             *
*   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 <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#include <machine/endian.h>
#endif

#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>
#include <netinet/udp.h>
#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#ifdef __linux__
#define __FAVOR_BSD
#endif
#include <netinet/tcp.h>
#ifdef __alpha__
#include <net/if_llc.h>
#include <netinet/if_ether.h>
#endif
#include <netinet/if_fddi.h>

#ifdef __linux__
/*#include <linux/if_ether.h>*/
#include <net/ethernet.h>
#include "linux_tweaks.h"
#endif

#include "basic_defs.h"
#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "flows.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "if_nprobe.h"
#include "np_file.h"
#include "sundry_records.h"
#include "wread_util.h"

#include "servers.h"

#ifdef PRINT_OUT
#include "print_util.h"
#include "cprintf.h"
#endif

#ifdef SWIG
#include "except.h"
#endif

listhdr_t *htbl;
unsigned int nservers = 0;


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

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

  if ((hfp = (listhdr_t *)malloc(N_SERVER_BUCKETS * sizeof(listhdr_t)))
      == NULL)
    ERROR("server_hashtbl_initialise: malloc");

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

  htbl = hfp;

  return 0;
}

/*
 * Return server hash table index for server IP addr 
 */

int 
hash_server(unsigned int addr)
{
  //unsigned int dst = ntohl(*((unsigned int *)&ipp->ip_dst));
  unsigned int hashaddr = ntohl(addr);

  return ((hashaddr & 0xffff) + (hashaddr >> 16)) % N_SERVER_BUCKETS;
}


server_t *
get_server_rec(unsigned int addr)
{
  server_t *serverp;
  int hash = hash_server(addr);


  L_WALK(serverp, &htbl[hash], hlist, server_t)
    if (SAME_SERVER(serverp, addr))
      return serverp;

  if ((serverp = (server_t *)malloc(sizeof(server_t))) == NULL)
    ERROR("get_server_rec: malloc");

  memset(serverp, '\0', sizeof(server_t));
  nservers++;
  serverp->addr = addr;
  L_INS_HEAD(&htbl[hash], serverp, hlist, server_t);
  return serverp;
}

void 
do_server(tcp_conn_t *tconnp)
{

  int i;
  unsigned int addr;
  http_trans_t *tp = tconnp->su.http.trans;
  tcp_simplex_flow_t *server = &tconnp->tcp.server;
  int server_seen = tconnp->flow_inner.state & TCP_SERVER_SEEN;
  short dstport = ntohs(tconnp->flow_inner.dstport);
  server_t *serverp;

  if (!server_seen)
    return;

  if(dstport == 80 || dstport == 8008 || dstport == 8080)
    addr = tconnp->flow_inner.dstaddr;
  else
    addr = tconnp->flow_inner.srcaddr;
  
  serverp = get_server_rec(addr);

  serverp->tcp_conns++;
  serverp->bytes_tx += server->tot_bytes;
  serverp->pkts_tx += server->tot_pkts;
  serverp->bytes_retrans += server->duplicate_bytes + server->ooo_bytes;
  serverp->pkts_retrans += server->duplicate_pkts + server->ooo_pkts;
  
  for (i = tconnp->su.http.meta.ntrans, tp = tconnp->su.http.trans; 
       i > 0; 
       i--, tp = tp->next)
    {
      serverp->http_reqs++; 
      serverp->bytes_served += tp->inner.hserver.recd_len;
    }



  return;
}

int 
cmp_req(const void *first, const void *second)
{
  server_t *s1 =  (server_t *)first;
  server_t *s2 =  (server_t *)second;

  if (s1->http_reqs < s2->http_reqs)
    return 1;
  else if (s1->http_reqs > s2->http_reqs)
    return -1;
  else 
    return 0;
}

int 
cmp_tcp_conns(const void *first, const void *second)
{
  server_t *s1 =  (server_t *)first;
  server_t *s2 =  (server_t *)second;

  if (s1->tcp_conns < s2->tcp_conns)
    return 1;
  else if (s1->tcp_conns > s2->tcp_conns)
    return -1;
  else 
    return 0;
}

int 
cmp_bytes_served(const void *first, const void *second)
{
  server_t *s1 =  (server_t *)first;
  server_t *s2 =  (server_t *)second;

  if (s1->bytes_served < s2->bytes_served)
    return 1;
  else if (s1->bytes_served > s2->bytes_served)
    return -1;
  else 
    return 0;
}


void 
print_server_preamble(void)
{
  printf("\t%-30s %8s %8s %12s\n", "Server", "conns", "reqs", "bytes");

  return;
}


void 
print_server(server_t *serverp)
{

  printf("\t%-30s %8u %8u %12llu\n",
	 get_hname(&serverp->addr),
	 serverp->tcp_conns,
	 serverp->http_reqs,
	 serverp->bytes_served);


  return;
}
	 


int 
servers_finish(int howmany)
{
  int i;
  int j = 0;
  server_t *servers;
  server_t *serverp;

  int nranked;

  if ((servers = (server_t *)malloc(nservers*sizeof(server_t))) ==NULL)
    ERROR("servers_finish: malloc");

  for (i = 0; i < N_SERVER_BUCKETS; i++)
    L_WALK(serverp, &htbl[i], hlist, server_t)
      {
	servers[j++] = *serverp;
      }

  if (howmany < 1)
    nranked = MIN(nservers, N_SERVERS_REPORT);
  else 
    nranked = MIN(nservers, howmany);

  printf("Servers ordered by # bytes served\n\n");
  qsort(servers, nservers, sizeof(server_t), cmp_bytes_served);
  print_server_preamble();
  for (i = 0; i <  nranked; i++)
    print_server(&servers[i]);

  printf("\n");

  printf("Servers ordered by # requests\n\n");
  qsort(servers, nservers, sizeof(server_t), cmp_req);
  print_server_preamble();
  for (i = 0; i <  nranked; i++)
    print_server(&servers[i]);

  printf("\n");

  printf("Servers ordered by # TCP connections\n\n");
  qsort(servers, nservers, sizeof(server_t), cmp_tcp_conns);
  print_server_preamble();
  for (i = 0; i <  nranked; i++)
    print_server(&servers[i]);

  printf("\n");

  return 0;
}

	
  


  
