/*  -*- 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 <string.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <limits.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/param.h>


#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>

#include <net/route.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>


#include <netinet/tcp.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <signal.h>
#include <assert.h>

#include "list.h"
#include "pkt.h"
#include "seq.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 "tcpdump_patch.h"
#include "counters.h"
#include "writer.h"
#include "sundry_records.h"
#include "timeouts.h"
#include "if_stats.h"

static int procnetdev_vsn = 1;

static int get_dev_fields(char *bp, struct  net_device_stats *ife)
{ 
    switch (procnetdev_vsn) {
    case 3:
	sscanf(bp,
	"%llu %llu %u %u %u %u %u %u",
	       &ife->rx_bytes,
	       &ife->rx_packets,
	       &ife->rx_errors,
	       &ife->rx_dropped,
	       &ife->rx_fifo,
	       &ife->rx_frame,
	       &ife->rx_compressed,
	       &ife->rx_multicast);
	break;
    case 2:
	sscanf(bp, "%llu %llu %u %u %u %u",
	       &ife->rx_bytes,
	       &ife->rx_packets,
	       &ife->rx_errors,
	       &ife->rx_dropped,
	       &ife->rx_fifo,
	       &ife->rx_frame);
	ife->rx_multicast = 0;
	break;
    case 1:
	sscanf(bp, "%llu %u %u %u %u",
	       &ife->rx_packets,
	       &ife->rx_errors,
	       &ife->rx_dropped,
	       &ife->rx_fifo,
	       &ife->rx_frame);
	ife->rx_bytes = 0;
	ife->rx_multicast = 0;
	break;
    }
    return 0;
}


static int procnetdev_version(char *buf)
{
    if (strstr(buf, "compressed"))
	return 3;
    if (strstr(buf, "bytes"))
	return 2;
    return 1;
}


void ife_print_short(struct net_device_stats *ptr)
{
  
	printf("%8llu %6u %6u %6u",
	       ptr->rx_packets, ptr->rx_errors,
	       ptr->rx_dropped, ptr->rx_fifo);
    printf("\n");
}

static char *get_name(char *name, char *p)
{
    while (isspace(*p))
	p++;
    while (*p) {
	if (isspace(*p))
	    break;
	if (*p == ':') {	/* could be an alias */
	    char *dot = p, *dotname = name;
	    *name++ = *p++;
	    while (isdigit(*p))
		*name++ = *p++;
	    if (*p != ':') {	/* it wasn't, backup */
		p = dot;
		name = dotname;
	    }
	    if (*p == '\0')
		return NULL;
	    p++;
	    break;
	}
	*name++ = *p++;
    }
    *name++ = '\0';
    return p;
}



static void get_net_stats(char *target, struct net_device_stats *ife)
{
  int gotit;
  FILE *fh;
  char buf[512];
  
  fh = fopen(_PATH_PROCNET_DEV, "r");
  if (!fh) 
    {
      fprintf(stderr, ("Warning: cannot open %s (%s). Limited output.\n"),
	      _PATH_PROCNET_DEV, strerror(errno)); 
      exit (1);
    }
  
  fgets(buf, sizeof buf, fh);	/* eat line */
  fgets(buf, sizeof buf, fh);
  
  procnetdev_vsn = procnetdev_version(buf);
  
  while (fgets(buf, sizeof buf, fh)) 
    {
      char *s, name[IFNAMSIZ];
      s = get_name(name, buf);
      if (target && !strcmp(target,name))
	{
	  gotit = 1;
	  get_dev_fields(s, ife);
	  break;
	}
    }

  if (!gotit) 
    {
      fprintf(stderr, "get_net_stats(): can't find interface %s\n", target);
      exit (1); 
    }

  if (ferror(fh)) 
    {
      perror(_PATH_PROCNET_DEV);
      exit (1); 
    }
  
  fclose(fh);
  
}

static int
cmp_if_stats(struct net_device_stats *curr, 
	     struct net_device_stats *last, 
	     struct net_device_stats *diff)
{
  int fails = 0;
  
  if (
      ((diff->rx_errors = curr->rx_errors - last->rx_errors) > 0)
      || ((diff->rx_dropped = curr->rx_dropped - last->rx_dropped) > 0)
      || ((diff->rx_length = curr->rx_length - last->rx_length) > 0)
      || ((diff->rx_over = curr->rx_over - last->rx_over) > 0)
      || ((diff->rx_crc = curr->rx_crc - last->rx_crc) > 0)
      || ((diff->rx_frame = curr->rx_frame - last->rx_frame) > 0)
      || ((diff->rx_fifo = curr->rx_fifo - last->rx_fifo) > 0)
      || ((diff->rx_missed = curr->rx_missed - last->rx_missed) > 0)
      )
    {
      fails = diff->rx_errors + diff->rx_dropped 
	+ diff->rx_length + diff->rx_over 
	+ diff->rx_crc + diff->rx_frame 
	+ diff->rx_fifo + diff->rx_missed;
      return fails;
    }
  
  else
    {
      return 0;
    }
}

static void 
  record_if_fails(struct net_device_stats *ifs)
{
  rec_dump_start();
  DUMP_STRUCT(outp, ifs, struct net_device_stats);
  rec_dump_end(REC_NIC_FAIL);
  
  return;
}
    
 
    

int 
lookat_if(int chan, struct timeval *tvp)
{
  static int started = 0;
  static struct net_device_stats ifs[2];
  static struct net_device_stats *ifs_curr = &ifs[0], *ifs_last = &ifs[1];
  struct net_device_stats ifs_diff, *ifs_tmp;
  
  static char ifname[IFNAMSIZ];

  int fails;
  
  if (!started)
    {
      started = 1;
#ifdef ETHERNET
      sprintf(ifname, "eth%d", chan+1);
      sprintf(ifs_diff.if_name, "eth%d", chan+1);
      fprintf(stderr, "Ifstats for %s\n", ifname);
#else
      fprintf(stderr, "lookat_if(): don't know how to build interface name\n");
      exit(1);
#endif
      get_net_stats(ifname, ifs_curr);
      fails = 0;
    }
  else
    {
      get_net_stats(ifname, ifs_curr);
      if ((fails = cmp_if_stats(ifs_curr, ifs_last, &ifs_diff)))
	{
	  ifs_diff.ts = *tvp;   /* struct assignment */
	  record_if_fails(&ifs_diff);
	  BUMP_CTR_N(nic_fails, fails);
	}
      
    }

  ifs_tmp = ifs_curr;
  ifs_curr = ifs_last;
  ifs_last = ifs_tmp;
  
  return fails;
  
}
 
