/******************************************************************************
*                                                                             *
*   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 <linux/limits.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.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 "if_nprobe.h"
#include "seq.h"
//#include "pool.h"
#include "output.h"
#include "arr_his.h"
//#include "probe_config.h"
#define _COUNTERS_C_
#include "timeouts.h"

#ifdef TCPDUMP_FED
extern char* o_infnm;
#endif

#include "probe_config.h"

/* print_util.c */
char *time_string(tmval *tvp);
char *ts_string(tmval *tvp);
char *get_hname(char *ap);

#ifndef MAX
#define MAX(x, y)  ((x) > (y) ? (x) : (y))
#endif

void
tvsub(tmval *tdiff, tmval *t1, tmval *t0)
{
    tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
    tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
    if (tdiff->tv_usec < 0)
	tdiff->tv_sec--, tdiff->tv_usec += 1000000;
}

#ifndef SWIG_ONLY
//long long
ll_t
utvsub(tmval *t1, tmval *t0)
{
  tmval tdiff;
  tdiff.tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff.tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff.tv_usec < 0)
    tdiff.tv_sec--, tdiff.tv_usec += 1000000;

  return ((long long)tdiff.tv_sec*1000000) + tdiff.tv_usec;
}
#endif

unsigned int 
mtvsub(tmval *t1, tmval *t0)
{
  tmval tdiff;
  tdiff.tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff.tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff.tv_usec < 0)
    tdiff.tv_sec--, tdiff.tv_usec += 1000000;

  return ((unsigned long)tdiff.tv_sec*1000) + tdiff.tv_usec/1000;
}

/*
 * Kludgy way of establishing #CPUs
 */

#define BUFLEN 2048
void 
ncpus_and_freq(counters_t *counters)
{
  int fd, cpus = 0;
  char *cp;
  char buf[BUFLEN];

  if ((fd = open("/proc/cpuinfo", O_RDONLY)) == -1)
    error("ncpus()", "open()");

  if (read(fd, buf, (BUFLEN -1)) <= 0)
    error("ncpus_and_freq()", "read()");

  cp = buf;
  while ((cp = strstr(cp, "processor")) != NULL)
    {
      cp += 1;
      cpus++;
    }

  counters->fh.ncpus = cpus;

  if ((counters->fh.hz = sysconf(_SC_CLK_TCK)) == -1)
    error("ncpus_and_freq()", "sysconf()");

  if ((counters->fh.pgsz = sysconf(_SC_PAGESIZE)) == -1)
    error("ncpus_and_freq()", "sysconf()");

  close(fd);
  
  return;
}


#if defined FINAL_REPORT || defined REPORT || defined WREAD

char 
*get_prefix(int i)
{
  char *prefix;

  if (i < 51)
    prefix = "client";
  else if (i < 101)
    prefix = "server";
  else if (i < 151)
    prefix = "";
  else if (i < 176)
    prefix = "client";
  else if (i < 201)
    prefix = "server";
  else prefix = "";

  return prefix;
}
  

void 
report_counters(counters_t *counters, FILE *file, char *leader)
{
  int i;
  double wire_pkts = counters->wire.pkts;
  double wire_octs = counters->wire.octs;
  double ip_pkts = counters->TRAFF_IP.pkts;
  double ip_octs = counters->TRAFF_IP.octs;
  double tcp_pkts = counters->TRAFF_TCP.pkts;
  double tcp_octs = counters->TRAFF_TCP.octs;
  double udp_pkts = counters->TRAFF_UDP.pkts;
  double udp_octs = counters->TRAFF_UDP.octs;

  int tot_http_errs = 0;
  
  double pkts, octs;

  struct timeval period_tv;
  unsigned int period = mtvsub(&counters->stop, &counters->start);

  double tot_drop_pkts = counters->MACHdr.pkts
    + counters->wrong_encaps.pkts
      + counters->wrong_llc_type.pkts
	+counters->notIP.pkts 
	  + counters->IPHdr.pkts
	    +counters->IPversion.pkts 
	      + counters->IPFrag.pkts
		+ counters->IPLen.pkts
		  + counters->IPHdr_opts.pkts
		    + counters->IPSum.pkts
		      +counters->UDPHdr.pkts
			+counters->UDPSum.pkts 
			  + counters->not_TCPUDP.pkts
			    + counters->TCPHdr.pkts
			      + counters->TCPSum.pkts
				+ counters->port_filtered_out.pkts
				  +counters->Nprobe_errpkts.pkts  
				    + counters->TCPHdr_opts.pkts
				      +counters->startup_nosyn.pkts
					+ counters->SYN_too_late.pkts 
					  + counters->serv_orig.pkts
					    +counters->ffilter.pkts;
  
  double tot_drop_octs = counters->MACHdr.octs
    + counters->wrong_encaps.octs
      + counters->wrong_llc_type.octs
	+counters->notIP.octs 
	  + counters->IPHdr.octs
	    +counters->IPversion.octs 
	      + counters->IPFrag.octs
		+ counters->IPLen.octs
		  + counters->IPHdr_opts.octs
		    + counters->IPHdr_opts.pkts
		      + counters->IPSum.octs
			+counters->UDPHdr.octs
			  +counters->UDPSum.octs 
			    + counters->not_TCPUDP.octs
			      + counters->TCPHdr.octs
				+ counters->TCPSum.octs
				  + counters->port_filtered_out.octs
				    +counters->Nprobe_errpkts.octs	      
				      + counters->TCPHdr_opts.octs
					+counters->startup_nosyn.octs 
					  + counters->SYN_too_late.octs 
					    + counters->serv_orig.octs
					      +counters->ffilter.octs;
  
  tvsub(&period_tv, &counters->stop, &counters->start);
  
  
  /* Header info */
  printf("Wan v %c.%u\n", counters->fh.vers_pre, counters->fh.vers_n);
  if (counters->fh.how_collected == TCPDUMP_COLL)
    REP_L(file, "Results from dump file %s:\n", counters->fh.data);
  else
    REP_L(file, "Results from online run:\n");

  if (counters->fh.quick_close)
    REP_L(file, "Using TCP quick close\n");
  if (counters->fh.accept_nosyn)
    REP_L(file, "Accepting non-SYNed TCP flows\n");

  REP_L(file, "%lu records\n", counters->nrecords);

  /* Time info */
  REP_L(file, "Start: %s ", time_string(&counters->start));
  REP_L(file, "End: %s\n", time_string(&counters->stop));
  REP_L(file, "Period %s (%lu ms)\n", ts_string(&period_tv), period);

  REP_L(file, "Timeout periods us: TCP %u  TCP seq %u  UDP %u\n", 
	counters->fh.tcp_timeout, counters->fh.tcp_seq_timeout, 
	counters->fh.udp_timeout);

  if (counters->buf_alloc_fails)
    REP_L(file, "XXX %u sk_buff alloc fails XXX\n", counters->buf_alloc_fails);
  else
    REP_L(file, "%u sk_buff alloc fails\n", counters->buf_alloc_fails);

  if (counters->nic_fails)
    REP_L(file, "XXX %u NIC fails XXX\n", counters->nic_fails);
  else
    REP_L(file, "%u NIC fails\n", counters->nic_fails);
  
  /* Traffic type info */
  REP_L(file, "Total wire: %.0f pkts %.0f octets\n", wire_pkts, wire_octs);
  SHOW_CTR(file, TCPDump_trunc, "truncated by tcpdump", wire, "wire");
  REP_L(file, "Total IP: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of wire\n", 
      ip_pkts, ip_octs, PCENT(ip_pkts, wire_pkts), PCENT(ip_octs, wire_octs));
  
  REP_L(file, "\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of IP\n", "TCP",
      tcp_pkts, tcp_octs, PCENT(tcp_pkts, ip_pkts), PCENT(tcp_octs, ip_octs));

  SHOW_TTYPE(file, TRAFF_TCP_HTTP, HTTP, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_FTP, FTP, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_FTP_DATA, FTP data, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_TELNET, TELNET, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_SMTP, SMTP, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_POP3, POP3, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_NNTP, NNTP, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_NETBIOS_SSN, NETBIOS_SSN, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_RTSP, RTSP, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_PNM, PNM, tcp_pkts, tcp_octs, TCP);
  SHOW_TTYPE(file, TRAFF_TCP_BGP, BGP, tcp_pkts, tcp_octs, TCP);

  if ((pkts = counters->startup_nosyn.pkts))
    {
      octs = counters->startup_nosyn.octs;
      REP_L(file, "\t\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of TCP\n", "Startup no syn",
	    pkts, octs, PCENT(pkts, tcp_pkts), PCENT(octs, tcp_octs));
    }
  
  SHOW_TTYPE(file, TRAFF_TCP_OTHER, Other, tcp_pkts, tcp_octs, TCP);

  REP_L(file, "\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of IP\n", "UDP",
      udp_pkts, udp_octs, PCENT(udp_pkts, ip_pkts), PCENT(udp_octs, ip_octs));
  pkts = counters->TRAFF_UDP_NFS.pkts;
  octs = counters->TRAFF_UDP_NFS.octs;
  REP_L(file, "\t\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of UDP\n", "NFS",
      pkts, octs, PCENT(pkts, udp_pkts), PCENT(octs, udp_octs));


  SHOW_TTYPE(file, TRAFF_UDP_DNS, DNS, udp_pkts, udp_octs, UDP);
  SHOW_TTYPE(file, TRAFF_UDP_ICQ, ICQ, udp_pkts, udp_octs, UDP);
  SHOW_TTYPE(file, TRAFF_UDP_OTHER, Other, udp_pkts, udp_octs, UDP);


  pkts = counters->TRAFF_ICMP.pkts;
  octs = counters->TRAFF_ICMP.octs;
  REP_L(file, "\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of IP\n", "ICMP",
      pkts, octs, PCENT(pkts, ip_pkts), PCENT(octs, ip_octs));


  pkts = counters->TRAFF_ICMP_UNREACH.pkts;
  octs = counters->TRAFF_ICMP_UNREACH.octs;
  REP_L(file, "\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of ICMP\n", "ICMP Unreachable",
      pkts, octs, PCENT(pkts, counters->TRAFF_ICMP.pkts), 
	PCENT(octs, counters->TRAFF_ICMP.octs));

  pkts = counters->TRAFF_IP_OTHER.pkts;
  octs = counters->TRAFF_IP_OTHER.octs;
  REP_L(file, "\t%-10s: %10.0f pkts %10.0f octets  %4.2f%%/%4.2f%% of IP\n", "Other IP",
      pkts, octs, PCENT(pkts, ip_pkts), PCENT(octs, ip_octs));

  REP_L(file, "\n");

  /* Analysis - connections, flows, etc. */
  REP_L(file, "\t%10llu host/host flows (max concurrent %u)\n", 
      counters->host_flows, counters->max_ctrs.hostconns.max);
  REP_L(file, "\t%10llu tcp connections (max concurrent %u) (one-way %llu)\n", 
      counters->tcpconns, counters->max_ctrs.tconns.max, 
	counters->tcp_one_way_only);
  REP_L(file, "\t%llu server only %llu client only)\n", 
      counters->tcp_serv_only, counters->tcp_cli_only);
  REP_L(file, "\t%10llu udp connections (max concurrent %u) (one-way %llu)\n", 
      counters->udpconns, counters->max_ctrs.uconns.max, 
	counters->udp_one_way_only);
  REP_L(file, "\t%10llu HTTP transactions (max concurrent %u)\n", 
      counters->http_trans, counters->max_ctrs.trans.max);
  if (counters->html_parsed.pkts)
    REP_L(file, "\t%lu HTML bytes parsed (%lu packets)\n", 
	  counters->html_parsed.octs, counters->html_parsed.pkts);
  REP_L(file, "\t%10llu buffers got %10llu buffers released (max concurrent %u)\n",
      counters->buffers_got, counters->buffers_released, counters->max_ctrs.buffers_held.max);
  REP_L(file, "\t\tmaximum pkt buffers held %u\n", counters->max_ctrs.heldpkts.max);
  REP_L(file, "\t\tmaximum saved header buffers held %u\n", counters->max_ctrs.saved_hdr_buffs.max);
  REP_L(file, "\t\tmaximum links buffers held %u\n", counters->max_ctrs.links_buffs.max);
  REP_L(file, "\t\tmaximum links chars held %u\n", counters->max_ctrs.links_chars.max);
  REP_L(file, "\t\tns records held %u\n", counters->max_ctrs.ns_recs.max);
  REP_L(file, "\t\tlarge tag buffers held %u\n", counters->max_ctrs.tag_bufs.max);
  REP_L(file, "\n");

  /* Ave. and max. bandwidths etc. */
  REP_L(file, "Ave. b/w wire %.2f Mbs HTTP %.2f Mb/s\n", 
      (((double)wire_octs*8)/period)/1000, (((double)counters->TRAFF_TCP_HTTP.octs*8)/period)/1000);
  REP_L(file, "\t(%.0f pkts/s %.0f pkts/s\n", 
      ((double)wire_pkts*1000)/period, 
      ((double)counters->TRAFF_TCP_HTTP.pkts*1000)/period);
  REP_L(file, "Max b/w wire %.2f Mbs HTTP %.2f Mbs (over 2s intervals)\n", 
      counters->max_ctrs.wire_bw, counters->max_ctrs.http_bw);
  REP_L(file, "Max inter get loop (sched) %u us\n", counters->max_ctrs.int_get);
  REP_L(file, "Max inter fetch loop (sched) %u us\n", counters->max_ctrs.int_fetch);
  REP_L(file, "\n");

  /* HTTP errors */
  for (i = 0; i < N_HTTP_ERRS; i++)
    tot_http_errs += counters->http_errs[i];
  REP_L(file, "HTTP errors: %d (%.2f%%)\n", 
	tot_http_errs, ((float)tot_http_errs*100)/counters->http_trans);
  for (i = 1; i < N_HTTP_ERRS; i++)
    if (counters->http_errs[i])
      REP_L(file, "\t%3d %52s %4d\n", -i, http_err_string(-i), counters->http_errs[i]);

  REP_L(file, "\n");

  /* HTTP disposal */
  REP_L(file, "Transaction completion:\n");
  REP_L(file, "\t%llu started %llu complete  %llu incomplete %llu finished  (%llu/2 resynched) %llu server only %llu client only %llu timing invalid\n", 
      counters->http_trans, counters->http_trans_comp, 
	counters->http_trans_incomp, counters->http_trans_fin, 
	counters->http_trans_resynch, counters->http_trans_serv_only, 
	counters->http_trans_cli_only, counters->http_trans_timeinvalid);
  REP_L(file, "\t%llu/2 dummy %llu/2 error %llu/2 lost synch\n\n", 
	counters->http_trans_dummy, counters->http_trans_err, 
	counters->http_trans_lost_synch);

  /* Fragmented HTTP headers etc*/
  if (counters->frag_hdr_trans)
    {
      SHOW_CTR(file, frag_hdr_pkts, "Fragmented HTTP hdrs", TRAFF_TCP_HTTP, "HTTP");
      REP_L(file, "\t(%u transactions - %u parsed ok)\n", counters->frag_hdr_trans,
	    counters->frag_hdr_trans_ok);
    }
  if (counters->http_trans_chunked)
    REP_L(file, "\t%llu chunked transfers\n", counters->http_trans_chunked);
  if (counters->http_trans_trailer_perm)
    REP_L(file, "\t%llu use trailer invitations\n", 
	  counters->http_trans_trailer_perm);
  if (counters->http_trans_trailer)
    REP_L(file, "\t%llu trailer fields used\n", 
	  counters->http_trans_trailer);
  if (counters->http_dump_objects_dropped)
    REP_L(file, "\t%llu objects not dumped\n", 
	  counters->http_dump_objects_dropped);

  REP_L(file, "\n");

  /* Analysis - dropped packets */
  REP_L(file, "Total drops:      %10.0f pkts %10.0f octets\n", 
      tot_drop_pkts, tot_drop_octs);

  SHOW_CTR(file, filter, "filtered out", wire, "wire");
  SHOW_CTR(file, notIP, "not IP", wire, "wire");
  SHOW_CTR(file, not_TCPUDP, "not TCP/UDP", TRAFF_IP, "IP");

  SHOW_CTR(file, MACHdr, "MAC header truncated", wire, "wire");
  SHOW_CTR(file, wrong_encaps, "unrecognised encapsulation", wire, "wire");
  SHOW_CTR(file, wrong_llc_type, "wrong llc type", wire, "wire");

  SHOW_CTR(file, IPHdr, "IP header truncated", TRAFF_IP, "IP");
  SHOW_CTR(file, IPversion, "IP version wrong", TRAFF_IP, "IP");
  SHOW_CTR(file, IPFrag, "IP fragmented", TRAFF_IP, "IP");
  SHOW_CTR(file, IPLen, "incorrect IP length", TRAFF_IP, "IP");
  SHOW_CTR(file, IPHdr_opts, "truncated IP header options", TRAFF_IP, "IP");
  SHOW_CTR(file, IPSum, "IP Hdr Checksum", TRAFF_IP, "IP");

  SHOW_CTR(file, ICMP_len, "ICMP message length", TRAFF_IP, "IP");

  SHOW_CTR(file, TCPHdr, "TCP header truncated", TRAFF_IP, "IP");
  SHOW_CTR(file, TCPSum, "TCP Checksum", TRAFF_IP, "IP");


  SHOW_CTR(file, UDPHdr, "UDP header truncated", TRAFF_IP, "IP");
  SHOW_CTR(file, UDPSum, "UDP Checksum", TRAFF_IP, "IP");

#ifdef FILTER_PORTS
  SHOW_CTR(file, port_filtered_out, "port filtered out", TRAFF_IP, "IP");
#endif
  SHOW_CTR(file, TCPHdr_opts, "truncated TCP header options", TRAFF_IP, "IP");
  if (!counters->fh.accept_nosyn)
    SHOW_CTR(file, startup_nosyn, "No syn seen during start up period", TRAFF_TCP, "TCP");
  SHOW_CTR(file, SYN_too_late, "SYN too late", TRAFF_TCP, "TCP");
  SHOW_CTR(file, serv_orig, "connection originated by server", TRAFF_IP, "IP");
  SHOW_CTR(file, Nprobe_errpkts, "Nprobe error packets", TRAFF_IP, "IP");
  SHOW_CTR(file, HTTP_port, "Pkts on HTTP port dumped", TRAFF_TCP, "TCP");
  SHOW_CTR(file, ffilter, "Rejected by flow filter", TRAFF_IP, "IP");
  REP_L(file, "<IP len pkt %llu pkts\n", counters->IP_ulen.pkts);
  

  REP_L(file, "\n");

  /* TCP timeouts, closes, mavericks, etc. */
  REP_L(file, "Info:\n");
  if (counters->tcp_maverick_SYN)
    REP_L(file, "\tMaverick SYN %llu\n", counters->tcp_maverick_SYN);
  if (counters->tcp_duplicate_SYN)
    REP_L(file, "\tDuplicate SYN %llu\n", counters->tcp_duplicate_SYN);
  REP_L(file, "\tTCP conn timeouts %llu\n", counters->tcp_timeo);
  REP_L(file, "\tTCP forced connections %llu\n", counters->tcp_forced_alt);
  REP_L(file, "\tTCP option confusion %llu\n", counters->tcp_opt_fail);
  REP_L(file, "\tTCP seq timeouts: to %llu tf %llu ta %llu tq %llu\n",
	counters->tcp_seq_timeo, counters->tcp_seq_timeo_forced,
	counters->tcp_seq_timeo_ack, counters->tcp_seq_timeo_ql);
  REP_L(file, "\tMinimum forced seq timeout %ums\n", counters->max_ctrs.min_tcp_seq_to);
  REP_L(file, "\tTCP close:\n");
  REP_L(file, "\t\t%-10s %10llu\n%s\t\t%-10s %10llu\n%s\t\t%-10s %10llu\n%s\t\t%-10s %10llu\n%s\t\t%-10s %10llu\n",
	"full", counters->tcp_full_close,
	leader, "effective", counters->tcp_eff_close,
	leader, "forced", counters->tcp_forced_close,
	leader, "quick", counters->tcp_quick_close,
	leader, "timed out", counters->tcp_timeo);
  if (counters->tcp_end_close)
    REP_L(file, "\t\t%-10s %10llu\n", "open", counters->tcp_end_close);

  if (counters->tcp_payload_dropped)
    REP_L(file, "\t%llu TCP payloads not dumped\n", 
	  counters->tcp_payload_dropped);

  REP_L(file, "\tTCP unidirectional %llu\n", counters->tcp_one_way_only);
  SHOW_CTR(file, IPLen_media_minpkt, "media min. segment", wire, "wire");
  SHOW_CTR(file, IP_pkt_too_long, "overlength packet", wire, "wire");
  SHOW_CTR(file, held_pkts, "held packets", TRAFF_IP, "IP");
  SHOW_CTR(file, held_epkts, "held epackets", TRAFF_IP, "IP");
  SHOW_CTR(file, http_unsynched, "unsynchronised HTTP", TRAFF_TCP_HTTP, "HTTP");


  
  REP_L(file, "\n");


  
  return;
}


void 
add_ctrs(counters_t *acc, counters_t *add)
{
  int i;

  acc->fh = add->fh;

  if (acc->fh.vers_pre != add->fh.vers_pre 
      || acc->fh.vers_n != add->fh.vers_n)
    wr_error("Attempting to accumulate counters from different versions");

  acc->fh.vers_pre = add->fh.vers_pre;
  acc->fh.vers_n = add->fh.vers_n;

  ADD_CTRS(wire);

  ADD_CTRS(TRAFF_IP);

  ADD_CTRS(TRAFF_TCP);
  ADD_CTRS(TRAFF_UDP);
  ADD_CTRS(TRAFF_ICMP);
  ADD_CTRS(TRAFF_ICMP_UNREACH);
  ADD_CTRS(TRAFF_IP_OTHER);

  ADD_CTRS(TRAFF_TCP_HTTP);
  ADD_CTRS(TRAFF_TCP_FTP);
  ADD_CTRS(TRAFF_TCP_FTP_DATA);
  ADD_CTRS(TRAFF_TCP_TELNET);
  ADD_CTRS(TRAFF_TCP_SMTP);
  ADD_CTRS(TRAFF_TCP_POP3);
  ADD_CTRS(TRAFF_TCP_NNTP);
  ADD_CTRS(TRAFF_TCP_NETBIOS_SSN);
  ADD_CTRS(TRAFF_TCP_RTSP);
  ADD_CTRS(TRAFF_TCP_PNM);
  ADD_CTRS(TRAFF_TCP_OTHER);
  ADD_CTRS(TRAFF_TCP_BGP);
  ADD_CTRS(TRAFF_TCP_TEST);

  ADD_CTRS(TRAFF_UDP_DNS);
  ADD_CTRS(TRAFF_UDP_NFS);
  ADD_CTRS(TRAFF_UDP_ICQ);
  ADD_CTRS(TRAFF_UDP_OTHER);

  ADD_CTRS(MACHdr);
  ADD_CTRS(wrong_encaps);
  ADD_CTRS(wrong_llc_type);
  ADD_CTRS(notIP);
  ADD_CTRS(IPHdr);
  ADD_CTRS(IPversion);
  ADD_CTRS(IPFrag);
  ADD_CTRS(IPLen);
  ADD_CTRS(IPHdr_opts);
  ADD_CTRS(IPSum);
  ADD_CTRS(UDPHdr);
  ADD_CTRS(UDPSum);
  ADD_CTRS(not_TCPUDP);
  ADD_CTRS(TCPHdr);
  ADD_CTRS(TCPSum);
  ADD_CTRS(port_filtered_out);
  ADD_CTRS(Nprobe_errpkts);
  ADD_CTRS(TCPHdr_opts);
  ADD_CTRS(startup_nosyn);
  ADD_CTRS(SYN_too_late);
  ADD_CTRS(serv_orig);
  ADD_CTRS(filter);
  ADD_CTRS(HTTP_port);
  ADD_CTRS(ICMP_len);
  ADD_CTRS(ffilter);
  ADD_CTRS(IP_ulen);

  ADD_CTRS(TCPDump_trunc);
  ADD_CTRS(IPLen_media_minpkt);
  ADD_CTRS(IP_pkt_too_long);
  ADD_CTRS(held_pkts);
  ADD_CTRS(held_epkts);
  ADD_CTRS(frag_hdr_pkts);
  ADD_CTRS(html_parsed);
  ADD_VAL(frag_hdr_trans);
  ADD_VAL(frag_hdr_trans_ok);
  ADD_VAL(dump_bytes_written);
  ADD_VAL(dump_blks_dumped);
  ADD_VAL(rep_bytes_written);
  ADD_VAL(rep_blks_dumped);

  ADD_VAL(host_flows);
  ADD_VAL(tcpconns);
  ADD_VAL(udpconns);
  ADD_VAL(buffers_got);
  ADD_VAL(buffers_released);

  ADD_VAL(tcp_timeo);
  ADD_VAL(tcp_seq_timeo);
  ADD_VAL(tcp_full_close);
  ADD_VAL(tcp_eff_close);
  ADD_VAL(tcp_quick_close);
  ADD_VAL(tcp_forced_close);
  ADD_VAL(tcp_end_close);
  ADD_VAL(tcp_one_way_only);
  ADD_VAL(tcp_seq_timeo_forced);
  ADD_VAL(tcp_seq_timeo_ack);
  ADD_VAL(tcp_seq_timeo_ql);
  ADD_VAL(tcp_maverick_SYN);
  ADD_VAL(tcp_duplicate_SYN);
  ADD_VAL(tcp_forced_alt);
  ADD_VAL(tcp_opt_fail);

  ADD_VAL(udp_one_way_only);
  ADD_VAL(udp_timeo);
  ADD_VAL(udp_open);

  ADD_VAL(buf_alloc_fails);
  ADD_VAL(nic_fails);

  ADD_VAL(tcp_cli_only);
  ADD_VAL(tcp_serv_only);

  if (acc->start.tv_sec == 0L && acc->start.tv_usec == 0L)
    acc->start = add->start;
  else
    acc->start = EARLIEST(acc->start, add->start);

  acc->stop = LATEST(acc->stop, add->stop);

  ADD_VAL(nrecords);

  for (i = 0; i < N_HTTP_ERRS; i++)
    acc->http_errs[i] += add->http_errs[i];

  ADD_VAL(http_trans);
  ADD_VAL(http_trans_comp);
  ADD_VAL(http_trans_incomp);
  ADD_VAL(http_trans_fin);
  ADD_VAL(http_trans_cli_only);
  ADD_VAL(http_trans_dummy);
  ADD_VAL(http_trans_err);
  ADD_VAL(http_trans_lost_synch);
  ADD_VAL(http_trans_resynch);
  ADD_VAL(http_trans_timeinvalid);
  ADD_VAL(http_trans_chunked);
  ADD_VAL(http_trans_trailer_perm);
  ADD_VAL(http_trans_trailer);
  ADD_VAL(http_trans_serv_only);

  ADD_CTRS(http_unsynched);

  ACCUM_MAX_CTR(hostconns);
  ACCUM_MAX_CTR(tconns);
  ACCUM_MAX_CTR(uconns);
  ACCUM_MAX_CTR(trans);
  ACCUM_MAX_CTR(heldpkts);
  ACCUM_MAX_CTR(buffers_held);
  ACCUM_MAX_CTR(saved_hdr_buffs);
  ACCUM_MAX_CTR(links_buffs);
  ACCUM_MAX_CTR(links_chars);
  ACCUM_MAX_CTR(ns_recs);
  ACCUM_MAX_CTR(tag_bufs);
  acc->max_ctrs.int_get = MAX(acc->max_ctrs.int_get, add->max_ctrs.int_get);
  acc->max_ctrs.int_fetch = MAX(acc->max_ctrs.int_fetch, add->max_ctrs.int_fetch);
  acc->max_ctrs.wire_bw = MAX(acc->max_ctrs.wire_bw, add->max_ctrs.wire_bw);
  acc->max_ctrs.http_bw = MAX(acc->max_ctrs.http_bw, add->max_ctrs.http_bw);
  acc->max_ctrs.min_tcp_seq_to = MIN(acc->max_ctrs.min_tcp_seq_to, add->max_ctrs.min_tcp_seq_to);

#if 0
  /* XXX TODO - this is wrong - these are cumulative anyway */
  ACCUM_RUN_CTR(wire_all);
  ACCUM_RUN_CTR(IP_all);
#endif

  return;
}
  

#endif /* if defined FINAL_REPORT || defined REPORT || defined WREAD */


void 
counters_init(counters_t *cp, tmval *start, unsigned int magic)
{

  memset(cp, 0, sizeof(counters_t));

  cp->fh.magic = WAN_MAGIC;
  cp->fh.vers_pre = VERS_PRE;
  cp->fh.vers_n = VERS_N;

  cp->start = *start;		/* struct assignment */
  ncpus_and_freq(cp);

#ifdef TCPDUMP_FED
  cp->fh.how_collected = TCPDUMP_COLL;
  strcpy(cp->fh.data, o_infnm);
#else
  cp->fh.how_collected = NPROBE_COLL;
#endif
  
  cp->fh.compressed = 0;

#ifndef WREAD
  cp->fh.quick_close = tcp_quickclose;
  cp->fh.accept_nosyn = tcp_accept_nosyn;

  cp->fh.rep_fsz = report_file_sz;
  cp->fh.dump_fsz = dump_file_sz;
#endif

  cp->fh.tcp_timeout = TCP_FLOW_TIMEO_US/1000;
  cp->fh.tcp_seq_timeout = TCP_SEQ_TIMEO_US/1000;
  cp->fh.udp_timeout =  UDP_FLOW_TIMEO_US/1000;

  cp->max_ctrs.int_get = 0U;
  cp->max_ctrs.int_fetch = 0U;
  cp->max_ctrs.wire_bw = 0.0;
  cp->max_ctrs.http_bw = 0.0;
  cp->max_ctrs.min_tcp_seq_to = SEQ_TIMEO_US;

  return;
}

/*
 * reset counters at file cycle - carry over as necessary 
 */

void 
reset_counters(counters_t *cp, tmval *cycle_time)
{
  
  max_ctrs_t max_tmp;
  run_ctrs_t run_tmp;

  max_tmp = cp->max_ctrs;	/* save max state */
  run_tmp = cp->run_ctrs;	/* and whole run counters */
  
  counters_init(cp, cycle_time, WAN_MAGIC);/* clear for next file cycle */

  cp->max_ctrs = max_tmp;	/* and restore */
  MAX_CTR_RESET(hostconns);
  MAX_CTR_RESET(tconns);
  MAX_CTR_RESET(trans);
  MAX_CTR_RESET(heldpkts);
  MAX_CTR_RESET(buffers_held);
  MAX_CTR_RESET(saved_hdr_buffs);
  MAX_CTR_RESET(links_buffs);
  MAX_CTR_RESET(links_chars);
  MAX_CTR_RESET(ns_recs);
  MAX_CTR_RESET(tag_bufs);
  cp->max_ctrs.int_get = 0U;
  cp->max_ctrs.int_fetch = 0U;
  cp->max_ctrs.wire_bw = 0.0;
  cp->max_ctrs.http_bw = 0.0;
  cp->max_ctrs.min_tcp_seq_to = SEQ_TIMEO_US;
  cp->run_ctrs = run_tmp;

  return;
}


void 
accumulate_run_counters(counters_t *cp)
{
  cp->run_ctrs.wire_all.pkts += cp->wire.pkts;
  cp->run_ctrs.wire_all.octs += cp->wire.octs;
  cp->run_ctrs.IP_all.pkts += cp->TRAFF_IP.pkts;
  cp->run_ctrs.IP_all.octs += cp->TRAFF_IP.octs;
  cp->run_ctrs.TCP_all.pkts += cp->TRAFF_TCP.pkts;
  cp->run_ctrs.TCP_all.octs += cp->TRAFF_TCP.octs;
  cp->run_ctrs.HTTP_all.pkts += cp->TRAFF_TCP_HTTP.pkts;
  cp->run_ctrs.HTTP_all.octs += cp->TRAFF_TCP_HTTP.octs;
  cp->run_ctrs.HTML_all.pkts += cp->html_parsed.pkts;
  cp->run_ctrs.HTML_all.octs += cp->html_parsed.octs;
  cp->run_ctrs.UDP_all.pkts += cp->TRAFF_UDP.pkts;
  cp->run_ctrs.UDP_all.octs += cp->TRAFF_UDP.octs;
  cp->run_ctrs.tot_records += cp->nrecords;
  cp->run_ctrs.rep_blks_all += cp->rep_blks_dumped;
  cp->run_ctrs.dump_blks_all += cp->dump_blks_dumped;
  cp->run_ctrs.rep_bytes_all += cp->rep_bytes_written;
  cp->run_ctrs.dump_bytes_all += cp->dump_bytes_written;
  cp->run_ctrs.IP_ulen_all.pkts += cp->IP_ulen.pkts;
  cp->run_ctrs.IP_ulen_all.octs += cp->IP_ulen.octs;

  return;
}

