/*
 * Copyright (c) 2007 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 * DESCRIPTION:
 *             Uses libpcap to access network packets through a network interface.
 *             The network interface can either be a NIC or a DAG card. This
 *             applications allows the user to measure the amount of data
 *             captured by each device at varying traffic rates. Assumes that
 *             libpcap is already installed. This code is based on the
 *             example application sniffex.c (available from various sources,
 *             see for example, www.tcpdump.org).
 * $Id: dag_nic.c 10347 2008-11-25 04:46:58Z dlim $
 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
#elif defined(_WIN32)
#include <wintypedefs.h>
#endif
#include <pcap.h>
#include "xpack.h"

unsigned int sat[6]= {0,0,0,0,0,0};
//sat[0] -- total number of packets received
//sat[1] -- IP
//sat[2] -- TCP
//sat[3] -- UDP
//sat[4] -- ICMP
//sat[5] -- other


//calculates how many packets per second
static unsigned int CCC=0;
static unsigned int AAA=0;
static unsigned int BBB=0;


void show_sta(void)
{
  double f;

LLL:
  f = sat[0];
  if (f==0) f=1.0;
  f = f/100;
  printf("total:%08x, IP:%05.2f%%, TCP:%05.2f%%, UDP:%05.2f%%, ICMP:%05.2f%%, other:%05.2f%%\n", 
    sat[0], sat[1]/f, sat[2]/f, sat[3]/f, sat[4]/f, sat[5]/f);
  sleep(1);
  CCC++;
  if ((CCC%10)==0) {
    AAA = BBB;
    BBB = sat[0];
    printf("pps: %d\n", (BBB-AAA)/10);
  }
  goto LLL;
}

void get_pack(char *agv, struct pcap_pkthdr *header, char *p)
{
  unsigned int *up;
  struct pack_ipnet *ip;

  up = (unsigned int *)agv;
  ip = (struct pack_ipnet *)(p + ETH_PACK_SZ);
  up[0] += 1;
  if (ip->ip_p == IPPROTO_IP) up[1] +=1;
  else if (ip->ip_p == IPPROTO_TCP) up[2] +=1;
  else if (ip->ip_p == IPPROTO_UDP) up[3] +=1;
  else if (ip->ip_p == IPPROTO_ICMP) up[4] +=1;
  else up[5] +=1;
}

int main(int agc, char **agv)
{
  char *ifdev = NULL;
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_t *handle;

  char filter_exp[] = "ip";
  struct bpf_program   fp;
  bpf_u_int32  mask;
  bpf_u_int32  net;

#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
  pthread_t thd1;
  int       ret1;
#elif defined(_WIN32)
  DWORD thd1;
  HANDLE hThread;
#endif



//1. get ifdev
  if (agc==1) {
    ifdev = pcap_lookupdev(errbuf);
    if (ifdev==NULL) {
      printf("no device found: %s\n", errbuf);
      exit(0);
    }
  }
  else if (agc==2 && agv[1][0]!='-') ifdev =agv[1];
  else {
    printf("usage: %s  [eth? | dag?]\n", agv[0]);
    printf("usage: %s  \n", agv[0]);
    exit(0);
  }


//2. get network
  if (pcap_lookupnet(ifdev, &net, &mask, errbuf)==-1) {
    printf("no net/mask got: %s\n", errbuf);
    exit(0);
  }
  printf("ifdev: %s\n", ifdev);
  printf("net:   %08x\n", ntohl(net));
  printf("mask:  %08x\n", ntohl(mask));

//3. open capture device
  handle = pcap_open_live(ifdev, SNAP_LEN, 1, 1000, errbuf);
  if (handle==NULL) {
    printf("no handle created: %s\n", errbuf);
    exit(0);
  }

//4. verify a ethernet device
//  if (pcap_datalink(handle)!=DLT_EN10MB) {
//    printf("no ethernet dev: %s\n", ifdev);
//    exit(0);
//  }

//5. complie filter 
  if (pcap_compile(handle, &fp, filter_exp, 0, net)==-1) {
    printf("no filter compiled: %s\n", filter_exp);
    exit(0);
  }

//6. install filter
  if (pcap_setfilter(handle, &fp)==-1) {
    printf("no filter installed.\n");
    exit(0);
  }


//7. set callback function and run
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
  ret1 = pthread_create( &thd1, NULL, show_sta, (void*)sat);
#elif defined(_WIN32)
  if ( CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)show_sta, (LPVOID)sat, 0, &thd1) == NULL )
    return EINVAL;
#endif

  pcap_loop(handle, 0, get_pack, sat);

//8. cleanup
#if defined(__FreeBSD__) || defined(__linux__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
  pthread_cancel(thd1);
#elif defined(_WIN32)
  hThread = OpenThread (THREAD_TERMINATE, FALSE, thd1);
  if ( hThread == NULL )
    return EINVAL;
  TerminateThread (hThread, 0);
#endif

  pcap_freecode(&fp);
  pcap_close(handle);

  return 0;
}
