/*  -*- 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 <getopt.h>
#include <sys/types.h>
#include <sys/time.h>

#include <unistd.h>
#include <signal.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>

#ifdef __linux__
#endif
#include <netinet/ip.h>


#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef __alpha__
#include <net/if_llc.h>
#endif
#include <netinet/if_fddi.h>
#include <netinet/if_ether.h>

#ifdef __linux__
#include "if_llc.h"
#endif

#include <linux/limits.h>
#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "tcpdump_patch.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "print_util.h"
#include "report.h"
#include "output.h"
#include "pool.h"
#include "if_nprobe.h"
#include "writer.h"
#include "timeouts.h"
#include "probe_config.h"

#ifdef TCPDUMP_FED
int tcpdump_worker_thread( char * fnm );
#endif /* ifdef TCPDUMP_FED */

#ifdef PROBE_FED 
int  probe_main( void );
#endif /* ifdef PROBE_FED */

void 
usage(void)
{
  fprintf(stderr, "%s: usage %s tcpdumpfile\n", prog, prog);
  exit (1);
}

int 
main(int argc, char **argv)
{
  char c;
  int rc;
  char *fnm;
  int ip_offset = 999999;

  prog = argv[0];
  opterr = 0;

#ifdef PROBE_FED
  if (getuid() != 0)
    {
      fprintf(stderr, "Wan - sorry - root required to execute - Bye!\n");
      exit(1);
    }
#endif

  while ((c = getopt(argc, argv, ":rpP:qsd:I:o:")) != EOF)
    switch (c) 
      {
#if defined FINAL_REPORT || defined REPORT
      case 'r': report++;break;
#endif
#ifdef PRINT_OUT
      case 'p': print_packets++;break;
#endif
      case 'P': fport = atoi(optarg); break;

      case 'q':
	/* Toggle compiled default flag value */ 
	tcp_quickclose = !tcp_quickclose; 
	tcp_closing_state = tcp_closing[tcp_quickclose].closing_state;
	tcp_valid_closing_states = tcp_closing[tcp_quickclose].valid_closing_states;
	break;

      case 'o': ip_offset = atoi(optarg); break;

      case 's': 
	/* Toggle compiled default flag value */
	tcp_accept_nosyn = !tcp_accept_nosyn; break;

      case 'd':
	/* Override compiled default path/owan tcpdump dir */
	strncpy(log_dir, optarg, MAX_LOGDIR_LEN); log_dir_specified++; break;

      case 'I': 
	/* Override compiled default dump file type*/
	printf("%s\n", optarg);
	if (!strcmp(optarg, "ether"))
	  {
	    dump_linktype = LINK_ETH_10MB;
	    pkt_dump = dump_ethpkt;
	  }
	else if (!strcmp(optarg, "nprobe"))
	{
	  dump_linktype = LINK_NPROBE;
	  pkt_dump = dump_pkt;
	}
	else
	  {
	    fprintf(stderr, "Unrecognised dump file format \'%s\' (\'ether\' or \'nprobe\' required)\n", optarg);
	    usage();
	    /* NOTREACHED */
	  }
	break;

      case '?': fprintf(stderr, "Don\'t understand option \'%c\'\n", optopt);
	usage();
	/* NOTREACHED */

      case ':': fprintf(stderr, "Option \'%c\' requires an argument\n", optopt);
	usage();
	/* NOTREACHED */
	

      default:
	fprintf(stderr, "%c\n", c);
	usage();
	/* NOTREACHED */
      }

#ifdef TCPDUMP_FED
  if (optind == argc)
    {
      fprintf(stderr, "%s: no tcpdump output file specified\n", prog);
      usage();
      return 1;
    }
  else
    {
      fnm = argv[optind];
    }
#endif /* ifdef TCPDUMP_FED */

  probe_config();

  if (ip_offset != 999999)
    {
      ip_hdr_offset = ip_offset;
    }
  

#ifdef TCPDUMP_FED
  rc = tcpdump_worker_thread( fnm );
#endif /* ifdef TCPDUMP_FED */

#ifdef PROBE_FED 
  rc = probe_main();
#endif /* ifdef PROBE_FED */

  return rc;
}


#ifdef TCPDUMP_FED

int
tcpdump_worker_thread( char *fnm )
{
  link_data_t link_data;
  bufrec_t *buffer;
  
  listhdr_t *host_hashtbl;
  struct timeval start, end;
  double period;
  double tot_bytes;

  ulonglong pkts_last = 0UL;
  ulonglong octs_last = 0UL;
  ulonglong http_pkts_last = 0UL;
  ulonglong http_octs_last = 0UL;

  link_data.linux_format = link_data.sf_swapped = link_data.swap_len = link_data.stdin = 0;

  host_hashtbl = host_hashtbl_initialise();

  link_data.fnm = fnm;
  open_tcpdumpfile(&link_data);
  

  output_init(link_data.snaplen, -1, fnm);
  pool_init();
  timeo_queues_init();
  if (histflags)
    hist_init();

#if 0
  if (linktype == LINK_NPROBE_LZO)
    if (lzo_init() != LZO_E_OK)
      error("probe_main", "lzo_init() failed");
#endif
  /* find link specific encapsulation stripper */
  get_linkstripper(&link_data);
  get_start(&start);
  

  for (;;)
    {
      buffer = get_bufrec_t();
      buffer->link_data = &link_data;
      
      pno += 1;
      
      if (next_pkt(buffer) != 0)
		break;

      do_pkt(buffer, host_hashtbl);

      if (!(counters.wire.pkts % 10000))
	{
	  fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
	  fprintf(stderr, "TOTAL %llu packets %llu octets\n",
		  counters.wire.pkts, 
		  counters.wire.octs);
	  fprintf(stderr, "WIRE %llu packets %llu octets\n",
		  counters.wire.pkts - pkts_last, 
		  counters.wire.octs - octs_last);
	  fprintf(stderr, "HTTP %llu packets %llu octets (holding %u buffers - timeo = %ums)\n",
		  counters.TRAFF_TCP_HTTP.pkts - http_pkts_last, 
		  counters.TRAFF_TCP_HTTP.octs - http_octs_last, 
		  counters.max_ctrs.buffers_held.current,
		  SEQ_TIMEO_US/1000);

	  fprintf(stderr, "report - %3.0f%% of cycle (%u records %u total)\n", 
		  (float)(counters.rep_bytes_written*100)/report_file_sz, 
		  counters.nrecords, 
		  counters.run_ctrs.tot_records + counters.nrecords); 
    
          fprintf(stderr, "dump - %3.0f%% of cycle\n", 
		  (float)(counters.dump_bytes_written*100)/dump_file_sz);
	  fprintf(stderr, "Timeouts: tcp flow: %u  seq: to %u tf %u tq %u ta %u udp flow: %u\n",
		  tcp_flo_timeo, sto, sto_f, sto_q, sto_a, udp_flo_timeo);
	  pkts_last = counters.wire.pkts;
	  octs_last = counters.wire.octs;
	  http_pkts_last = counters.TRAFF_TCP_HTTP.pkts;
	  http_octs_last = counters.TRAFF_TCP_HTTP.octs;
	  tcp_flo_timeo = 0U;
	  udp_flo_timeo = 0U;
	  sto = 0U;
	  sto_f = 0U;
	  sto_q = 0U;
	  sto_a = 0U;
	}
	  
    }

  get_end(&end);
  period = calc_period(&start, &end);

#if defined FINAL_REPORT || defined REPORT
  if (report)
    {
      final_report(host_hashtbl);
    }
#endif

#ifndef PRINT_OUT 
  output_end(host_hashtbl);
#endif

  if (histflags)
    hist_end();

  tot_bytes = (double)counters.run_ctrs.wire_all.octs;

  if (period > 0)
    {
      printf("Rusage %f ", period);
      printf("Tot. Pkts. %llu Bytes %.0f Capacity %.2fMbs (wire)\n", 
	      counters.run_ctrs.wire_all.pkts, tot_bytes, 
	     (tot_bytes*8)/(double)(period*1000000));
    }
  else
    {
      printf("Tot. Pkts. %llu Bytes %.0f\n", counters.run_ctrs.wire_all.pkts, tot_bytes);
    }

  return 0;
}

#endif /* ifdef TCPDUMP_FED */

/*
 * end main.c 
 */
