/*  -*- 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 *
*                                                                             *
******************************************************************************/

#ifdef PROBE_FED

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <netinet/if_ether.h>

#include <linux/limits.h>

/* www analysis specific headers */
#include "basic_defs.h"
#include <netinet/ip.h>
#include "list.h"
#include "pkt.h"
#include "flows.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "output.h"
#include "report.h"
#include "pool.h"
#include "if_nprobe.h"
#include "writer.h"
#include "counters.h"
#include "timeouts.h"
#include "print_util.h"
#include "sundry_records.h"
#include "ace_clk.h"
#include "interface.h"

#ifdef USE_LZO_DUMP
#include "minilzo.h"
#endif

#include "sampleHistogram.h"
#include "sampleStatistic.h"

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

extern sampleHistogram *pkts_his;

/* External prototypes not inc. in hdrs above ***********************/
long long utvsub(struct timeval *t1, struct timeval *t0);


/* Configuration parameters ******************************************/

// all included from pkt.h

/* www analysis header ***********************************************/

#include "list.h"


/* Constants **********************************************************/


#define KERNEL_VA   (0xC0000000UL) 
#define MFN "/dev/mem"
#define PROC_FILE "/proc/nprobe"

struct timeval tsleep    = { 0, 1000 };       // 10ms
struct timeval timeout1s = { 1, 0 };           // 1s


/* Statics that are effectively constant after fork *******************/

unsigned int np_off = 0;
np_t * np            = NULL;
int chan             = -1;

/* Statics that are effectively worker thread specific ****************/

listhdr_t *host_hashtbl;
struct timeval start, end;
char * km = NULL;
char * km_np = NULL;

/* Statics that are effectively parent thread specific ****************/

int worker_pid [NUM_CPUS];

/* Local Prototypes ****************************************************/

void worker_thread( void );
void parent_exit( int );
void worker_exit( int );

/**********************************************************************/

/* store SIGSEGV/SIGABRT default action */
void (*sigsegv_def)(int signo);
void (*sigabrt_def)(int signo);

/************************************************************************/

inline void * kern2user( void * kern )
{
  return (void *) (km+( ((unsigned long)kern) & 0x3fffffff));
}

inline void * user2kern ( void * user )
{
  return (void *) ( ((unsigned long)((char*)user-km)) | 0xc0000000 );
}

/**********************************************************************/

#ifdef ATM

#include <atm.h>

int atm_open_vc( char *vc )
{
  struct sockaddr_atmpvc addr;
  struct atm_qos qos;
  int s;
  int on = 1;   /*  1 = non blocking  */

  if ((s = socket(PF_ATMPVC,SOCK_DGRAM,0)) < 0) {
    perror("socket");
    return -1;
  }

  memset(&addr,0,sizeof(addr));
  if (text2atm(vc,(struct sockaddr *) &addr,sizeof(addr),
	       T2A_PVC | T2A_UNSPEC | T2A_WILDCARD) < 0) 
    fprintf(stderr,"Incorrect use of open_vc: %s\n",vc);

  memset(&qos,0,sizeof(qos));
  qos.aal = ATM_AAL5;
  qos.rxtp.traffic_class = ATM_UBR;
  qos.rxtp.max_sdu = 1024;

  if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) {
    perror("setsockopt SO_ATMQOS");
    return -1;
  }

  if (bind(s,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
    perror("bind");
    return -1;
  }

  if (ioctl(s, FIONBIO, (char*)&on) < 0) {
    perror("FIONBIO");
    return -1;
  }

  return s;
}

#endif

typedef enum { off, on, poll } nmode_t;

int network( nmode_t mode, int num_ifaces )  
{
  
  if( mode == on )
    fprintf(stderr,"[%d] Network On - %d interfaces\n",chan, num_ifaces);

  else if( mode == off )
    fprintf(stderr,"[%d] Network Off\n",chan);


#ifdef ETHERNET
  {
    int rc,i;
    char buf[256];
    char eth[][6]={"eth1","eth2","eth3","eth4"};
    
    for(i=0;i<num_ifaces;i++)
      {
	switch( mode )
	  {
	  case off:
	    sprintf(buf,"/sbin/ifconfig %s down\n",eth[i]);
	    rc = system(buf);
	    break;
	    
	  case on:
	    sprintf(buf,"/sbin/ifconfig %s up promisc\n",eth[i]);
	    rc = system(buf);
	    break;	    
	    
	  case poll:
	    return 0;	    
	  }
	fprintf(stderr,"network %d: -%s- returned %d\n",on,buf,rc);
	
	//return 0;
      }
    return 0;
  }

#endif

#ifdef ATM
  {
    int rc,i;
    unsigned char buf[65535];
    char atm[16];
    static int fd[4];
    static unsigned int pks[4], bytes[4];

    for(i=0;i<num_ifaces;i++)
      {
	switch( mode )
	  {
	  case off:
	    if(fd[i]) close(fd[i]);
	    fd[i] = 0;
	    fprintf(stderr,"[%d] Network Down\n",chan);
	    break;

	  case on:
	    sprintf(atm,"%i.%i.%i", i, DEFAULT_VPI, DEFAULT_VCI);
	    fd[i] = atm_open_vc(atm);
	    fprintf(stderr,"open_vc %s: returned %d.\n",atm,fd[i]);
	    //fprintf(stderr,"[%d] Network Up\n",chan);
	    break;	    

	  case poll:
	    while( (rc = read( fd[i], buf, 65535 )) > 0 )
	      {
		pks[i]++;
		bytes[i] += rc;
		fprintf(stderr,
		"iface %d: got oversize pkt %d. %d.%d.%d.%d %d.%d.%d.%d (%ld pkts, %ld bytes)\n",
		i,rc, 
		buf[20],buf[21],buf[22],buf[23],
		buf[24],buf[25],buf[26],buf[27],
		pks[i],bytes[i]);
	      }
	    break;
	  }
      }

  return 0;
  }

#endif


}


void parent_sigcatch_int()
{
  fprintf(stderr, "[%d] Parent caught SIGINT.\n",chan); 

  parent_exit(0);  // should exit with 0
}

void worker_sigcatch_usr1()
{
  fprintf(stderr, "[%d] Child caught SIGUSR1.\n",chan); 

  worker_exit(0);  // should exit with 0
}

void worker_sigcatch_segv()
{
  fprintf(stderr, "[%d] Child caught SIGSEGV.\n",chan);
  output_finished++;		/* tell writer to quit */
  if (waitpid(writer_pid, NULL, 0) != writer_pid)
    error("worker_sigcatch_segv", "wait");
  /* restore */
  if (signal(SIGSEGV, sigsegv_def) == SIG_ERR)
    error(" worker_sigcatch_segv()", "signal");
  /* now do the right thing */
  if (kill(getpid(), SIGSEGV) != 0)
    error("worker_sigcatch_segv()", "kill()");
}

void worker_sigcatch_abrt()
{
  fprintf(stderr, "[%d] Child caught SIGABRT.\n",chan);
  output_finished++;		/* tell writer to quit */
  if (waitpid(writer_pid, NULL, 0) != writer_pid)
    error("worker_sigcatch_abrt", "wait");
  /* restore */
  if (signal(SIGABRT, sigabrt_def) == SIG_ERR)
    error(" worker_sigcatch_abrt()", "signal");
  /* now do the right thing */
  if (kill(getpid(), SIGABRT) != 0)
    error("worker_sigcatch_abrtx()", "kill()");		
}


int check_magic()
{
  int pfd2;

  printf("Magic1 read as %x %x\n", np->magic, np->end_magic );

  if( np->magic != NP_MAGIC ||  np->end_magic != NP_MAGIC ) 
    {
      fprintf(stderr,"MAGICFAIL %x %x\n",
	      np->magic, np->end_magic);        
    }


  // cause nprobe to write stage2 magic by frobbing /proc/nprobe

  np->magic = np->end_magic = NP_MAGIC2;

  printf("Magic2 read as %x %x\n", np->magic, np->end_magic );

  if( (pfd2 = open(PROC_FILE, O_RDONLY)) < 0) {
    perror("failed to open /proc/nprobe"); 
    exit(errno);
  } else fprintf(stderr, "Opened %s (fd = %d)\n", PROC_FILE, pfd2);

  close(pfd2);

  // stage 3 magic should now be present...

  printf("Magic3 read as %x %x\n", np->magic, np->end_magic );

  if( np->magic != NP_MAGIC3 ||  np->end_magic != NP_MAGIC3 )
    {
      printf("Magic3 FAIL!\n");
      return 0;
    }
 
  return 1;
}
  
inline struct sk_buff * get_skb( int chan )
{
  struct sk_buff *skb;
  
  if (np->x[chan].tous_in > np->x[chan].tous_out )
    {
      skb = np->tous_skb[chan][ np->x[chan].tous_out % FIFO_SIZE ];
      
#if 0
      np->tous_skb[chan][ np->x[chan].tous_out % FIFO_SIZE ] = 
	(struct sk_buff *) 0x05a5a5a5; // if paranoid
#endif	  

      np->x[chan].tous_out++;   // XXX should ensure ordering


#if 0
      if ( ((unsigned long)skb) & 0xf0000000 != 0xc0000000 )
	{
	  fprintf(stderr,"READ %p!!!!\n",skb); 
	}
#endif

      return kern2user( skb );
    }
  else
    return NULL;
}

// inline XXXX
void put_skb( int chan, struct sk_buff * skb )
{

#ifndef MAP_READONLY
  {
  // mark this buffer with a magic to indicate that it is returned 
  char * head = kern2user( skb->head );
  *((unsigned long*)(head+4)) = 0x55000001;
  }
#endif


  if( np->x[chan].frus_in - np->x[chan].frus_out < FIFO_SIZE )
    {
      np->frus_skb[chan][ np->x[chan].frus_in % FIFO_SIZE ] = 
	user2kern( skb );

      np->x[chan].frus_in++;   // XXX should really ensure ordering!!!!
      
    }
  else
    {
      /* this _really_ shouldn't happen. */  
      fprintf(stderr,
	      "return: FIFO full! - tous %d,%d : frus %d,%d \n",
	      np->x[chan].tous_in, np->x[chan].tous_out, 
	      np->x[chan].frus_in, np->x[chan].frus_out );      
    }
}

int probe_main( void )
{
    int pfd; 
    char buf[256], buf1[256];
    int maxdev, fifosize;
    int rc;


    network(off, NUM_IFACES); /* ensure network tap is down before 
				 messing with proc file */

    if( (pfd = open(PROC_FILE, O_RDONLY)) < 0) {
        perror("failed to open /proc/nprobe");
        exit(errno);
    } else fprintf(stderr, "Opened %s (fd = %d)\n", PROC_FILE, pfd); 

    if( (rc = read( pfd, buf, 255 )) < 0 ) {
	perror("read error on proc file");
	exit(errno);
    } 
    
    buf[(rc>255)?255:rc] = 0; // just in case...

    //    printf("Read %d bytes --- %s\n", rc, buf );

    buf1[0]=0;

    rc = sscanf(buf,"%s %p %d %d", buf1, &np_off, &maxdev, &fifosize);

    printf("rc = %d, %s ptr = %lx maxdev = %d fifosize = %d\n",
	   rc, buf1, np_off, maxdev, fifosize );
	

    /* final read to collect the EOF */

    if( (rc = read( pfd, buf, 255 )) < 0 ) {
	perror("read error on proc file");
	exit(errno);
    } 

    if (rc>0) 
	printf("Expected to read 0 bytes got %d --- %s\n", rc, buf );

    
    np_off &=0x3fffffff ;


#ifdef USE_ONE_THREAD_ONLY
    // only use a single thread for debugging purposes

    printf("XXXXX Using only a single thread for debug purposes!\n" );

    chan = 0;

    //network( on, NUM_IFACES );

    signal(SIGINT, worker_sigcatch_usr1);

    worker_thread();

    exit(-99);  /* can't get here */
#endif




    /* 
       this parent thread will become the status/control thread.
       we fork off NUM_CPUS threads to do the work.
       The parent thread is eventually identified as chan = -1 
       */
    

    while( ++chan < NUM_CPUS )
      {
	int pid;

	fprintf(stderr,"[%d] Ready to fork.\n",chan);

	worker_pid[chan] = pid = fork();

	if (pid == -1)
	  {
	    perror("unable to fork");
	    exit(-1);
	  }

	if (pid == 0)  
	  { // child process (chan = 0,1,2,3...)
	    worker_thread();
	    fprintf(stderr,"[%d] Shouldn't get here!!!!\n",chan);
	    exit(-1);
	  }
	
	/* else must be parent */
	

      }

    /* This is the parent thread */

    chan = -1;  /* set chan back to parent status */

    signal(SIGINT, parent_sigcatch_int);

    sleep(3);

    //    fprintf(stderr,"[%d] network On.\n",chan);
    network( on, NUM_IFACES );

    while(1)
      {
	struct timeval temp = timeout1s;
	int rc = waitpid(-1, NULL, WNOHANG);

	if( rc )
	  {	    
	    if( rc < 0 )
	      {
	      fprintf(stderr,"[%d] waitpid returned %d.\n",chan,rc);
	      perror("waitpid");
	      }
	    else
	      {
		fprintf(stderr,
			"[%d] Whoa! We've lost one! (%d). Bail.\n",chan,rc);
		parent_exit(-1);
	      }
	  }
       
	network( poll, NUM_IFACES );
	
	select(0, NULL, NULL, NULL, &temp);

      }
    /* on exit an oops is generated ;-( */

    return -1;
}


void worker_thread( void )
{
  
  int kfd;
  int linktype;
  struct timeval lastpkt, laststats, now ;
#ifdef NIC_STAMP
  unsigned long long lastw_stats;
#else
  struct timeval lastw_stats;
#endif
  
  fprintf(stderr,"[%d] Worker PID %d initialising...\n",chan, getpid());
  //sleep(5);

#ifdef NIC_STAMP
  acenic_start_clock(chan);
#endif  
 
  /* do some WWW analysis initialisation */
  
#if defined USE_LZO_DUMP
  linktype = DLT_NPROBE_LZO;
#elif defined ETHER_DUMP
  linktype =  LINK_ETH_10MB;
#else
  linktype = DLT_NPROBE;
#endif 
  
  host_hashtbl = host_hashtbl_initialise();
  pool_init();
  timeo_queues_init();

  output_init( 2000, linktype, chan, NULL); /*(snaplen, linktype) */
  /* XXX TODO XXX snaplen should be size of sk_buf data area */
  
#if defined PROBE_FED && defined ARR_HIST
  hist_init();
#endif
  
#ifdef USE_LZO_DUMP
  if (lzo_init() != LZO_E_OK)
    error("probe_main", "lzo_init() failed");
#endif
  
  fprintf(stderr,"[%d] Worker initialised\n",chan);

#ifdef PROBE_FED

#ifdef USE_ONE_THREAD_ONLY
  network( on, NUM_IFACES );
#endif

  /* jack up the priority of this process */

  if (setpriority(PRIO_PROCESS, 0, -5) != 0)
    error("worker_thread()", "setpriority()");

#endif /* ifdef PROBE_FED */

  fprintf(stderr,"[%d] Worker ready for action.\n",chan);
  
  if( (kfd = open(MFN, O_RDWR)) < 0) {
    perror("failed to open /dev/[k]mem");
    exit(errno);
  } else fprintf(stderr, "[%d] Opened /dev/[k]mem (fd = %d)\n",
		 chan, kfd); 
  
  /* must MAP_SHARED otherwise copy_on_write happens!!! */
  
#ifdef MAP_READONLY  
  km_np = mmap(NULL, sizeof(np_t), 
	       PROT_READ | PROT_WRITE, MAP_SHARED, kfd, np_off );
  
//#ifdef MAP_READONLY
  km = mmap(NULL, MEM_SIZE, PROT_READ, MAP_SHARED, kfd, 0 );

#else
  km = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, kfd, 0 );

  km_np = (km + np_off);

#endif
  
  close(kfd);
  
  if(km == MAP_FAILED ) {
    perror("failed to map /dev/mem (km)"); 
    exit(errno); 
  }
  
#ifdef MAP_READONLY
  if( km_np == MAP_FAILED ) {
    perror("failed to map /dev/mem (km_np)"); 
    exit(errno); 
  }
#endif

fprintf(stderr, "MEM_SIZE = %d\n", MEM_SIZE);
  
  *km_np = *km_np;
  
  
  np = (np_t*) (km_np);
  


#if 0
  if ( !check_magic() )  /* just for the parnoid */
    {
      fprintf(stderr,"[%d] Magic failed\n",chan);
      exit(-1);
    }
#endif
  
  /* ignore SIGINT, wait to recv a USR1 from parent */
  
  signal(SIGUSR1, worker_sigcatch_usr1);
#ifdef USE_ONE_THREAD_ONLY
  signal(SIGINT, worker_sigcatch_usr1);
#else
   signal(SIGINT, SIG_IGN );
#endif
  
  
  /* intercept SIGSEGV to allow writer process to be terminated */
  sigsegv_def = signal(SIGSEGV, worker_sigcatch_segv);
  if (sigsegv_def == SIG_ERR)
    error("worker_thread", "signal sigsegv");
  
  
  /* intercept SIGABRT to allow writer process to be terminated */
  sigabrt_def = signal(SIGABRT, worker_sigcatch_abrt);
  if (sigabrt_def == SIG_ERR)
    error("worker_thread", "signal sigabrt");
  
  
  /*
   * get ready to enter while loop
   * - Use the 3seconds the parent gives us to recycle stale packets
   */
  
  {
    struct sk_buff *skb;
    while( (skb = get_skb(chan)) )
      put_skb(chan, skb);
#ifdef NIC_STAMP
    /* make sure acenic clock has good first calibration time */
    sleep(3);
#endif
  }
  
  
  
  /* initialise */
  GETTIMEOFDAY("Worker_thread init", &start, (struct timezone *)0);
  lastpkt = start;		/* struct */
  laststats = start;		/* struct */
#ifdef NIC_STAMP
  lastw_stats = ((unsigned int long)start.tv_sec*1000000) + start.tv_usec;
  fprintf(stderr, "start=%s\n", time_string(&start));
  fprintf(stderr, "lastw start= %llu\n", lastw_stats);
  
#else
  lastw_stats = start;		/* struct */
#endif
  
  period_report(np, &start, 0ULL, 0U, &start, DO_RUSAGE, DO_PROCSTATS);
  
  
  while(1)
    {
      int i,iplen,pkts=0;
      struct sk_buff *skb;
      unsigned char  *ipdata;
      struct timeval temp;
      long long udiff;
      unsigned int fetch_per = 0U, max_fetch_per = 0U, max_wfetch_per = 0U;


#if defined PROBE_FED && defined ARR_HIST
      arr_hist();
#endif
      
#if 0
      if ( np->x[chan].tous_in % 64 == 0 )
	for(i=0;i<FIFO_SIZE;i++)
	  {
	    printf("[%d] %d = %08x %08x\n",
		   chan,i,np->x[chan].tous_skb[i],np->x[chan].frus_skb[i] );
	  }  
#endif
      
      
      while( (skb = get_skb(chan)) )
	{
	  prec_t *precp;

	  fetch_per = fetch_hist();
	  max_fetch_per = MAX(fetch_per, max_fetch_per);
	  max_wfetch_per = MAX(fetch_per, max_wfetch_per);
	  
	  /* got packet */
#if defined ATM_LANE
	  ipdata = kern2user( skb->data )+8; /* XXXX */
	  iplen  = skb->len - 8 ; /* XXXXX */

#elif defined VLAN_HEADER
	  ipdata = kern2user( skb->data )+4; /* //XXXX */
	  iplen  = skb->len-4; /*XXXXX */
#else
	  ipdata = kern2user( skb->data ); /* //XXXX */
	  iplen  = skb->len; /*XXXXX */
	  
#endif  
	  pkts++; /* stats */
	  
	  if( iplen <= 0 )
	    {
	      /* probably just not IP... */
	      if( iplen < 0 )
		fprintf(stderr,
			"[%d] recvd %d len pkt. s=%p h=%p d=+%d\n",
			chan, iplen, skb, kern2user( skb->head ), 
			ipdata - ((unsigned char*) kern2user( skb->head ))
			);
	      
	      put_skb(chan, skb);  /* remember to do this !!!! */
	      counters.IP_ulen.pkts++;
	      fprintf(stderr, "XXXXXXXXXXXXXXX\nXXXXXXXXX\nXXXXXXXXXX\nXXXXXXXXX\n");
	      
	      continue;
	    }

#define FILTER_DIVIDE 0   // set to 0 to turn off - n to turn on

#if FILTER_DIVIDE
	  {
	    unsigned int ports = ((unsigned int*)ipdata)[5];
	    unsigned int hash  = (ports>>8) ^ (ports>>24); // little endian

	    if( (hash % FILTER_DIVIDE) != 0 )
	      {
		counters.filter.pkts++;
		counters.filter.octs += iplen;
		// recycle this guy immediaitely
		put_skb(chan, skb);  
		continue;
	      }
	  }
#endif	
	  
	  counters.wire.pkts++;
	  counters.wire.octs += iplen;  

//#define IP_SANITY_CHECK
#ifdef IP_SANITY_CHECK

	  if( (ipdata[0] & 0xf) != 4 )
	    {
	      fprintf(stderr,"IP version = %d, len = %d\n",
		      ipdata[0] & 0xf, iplen );   
	    }

	  if( (ipdata[0] >> 4) != 5 )
	    {
	      fprintf(stderr,"IP header len = %d, len = %d\n",
		      ipdata[0] >> 4 , iplen );   
	    }

#endif
	  /* dump every few packets */
	  if ( np->x[chan].tous_out % (1024*128) == 0 )
	    {
	
#if 0      
#ifndef MAP_READONLY
	      /* dump all packets we're currently holding */
	      {
		int i;
		
		fprintf(stderr,"[X] Start buffer audit\n"); 
		for(i=0;i<FIFO_SIZE;i++)
		  {
		    struct sk_buff *skb = kern2user( np->bufs[i] );
		    
		    char * ipdata = kern2user( skb->data );
		    
		    char * head   = kern2user( skb->head );
		    
		    unsigned int foo = *((unsigned long*)(head+4));
		    
		    if ( foo == 0xAA000002 )
		      {
			fprintf(stderr,"[X] Buffer audit: found RX magic in buf %d (%p, len %d)\n",
				i, ipdata, skb->len );
			dump_pkt( ipdata, skb->len, &skb->stamp );
		      }
		    
		  }
	      }
	      
#endif
#endif

	      fprintf(stderr,"[%d] s=%p h=%p d=+%d %d - tous %d,%d : frus %d,%d : (%d %d)\n",
		      chan, skb, kern2user( skb->head ), 
		      ipdata - ((unsigned char*) kern2user( skb->head )), iplen, 
		      np->x[chan].tous_in, np->x[chan].tous_out, 
		      np->x[chan].frus_in, np->x[chan].frus_out,
		      np->x[chan].tous_in - np->x[chan].tous_out, 
		      np->x[chan].frus_in - np->x[chan].frus_out
		      );
	      
	      fprintf(stderr,"[%d] ",chan);
	      
	      fprintf(stderr,"% 4d %02x%02x%02x%02x#", iplen,
		      ipdata[-4],ipdata[-3],ipdata[-2],ipdata[-1]);
	      
	      for(i=0;i<32;i+=4)
		fprintf(stderr,"%02x%02x%02x%02x:",
			ipdata[i],ipdata[i+1],ipdata[i+2],ipdata[i+3]);
	      fprintf(stderr,"\n");
	      
	    }
	

 
	  /* call into the WWW analysis code */
  
          precp = get_prec_t();
	  
	  precp->brecp = skb;
	  precp->buf = ipdata;
	  precp->len = iplen;
	  precp->trunc = 0;

#ifdef NIC_STAMP
	  precp->arr_tm = acenic_maptotimeofday((ipdata[-14]<<24) | (ipdata[-13]<<16) | (ipdata[-12]<<8) | ipdata[-11]);
	  if ((udiff = (long long)(precp->arr_tm - lastw_stats)) > 2000000LL)
	    {
	      fprintf(stderr, "udiff=%lld lastw=%llu\n", udiff, lastw_stats);
	      lastw_stats += 2000000;
#else
	  if ( (udiff = utvsub(&precp->tstamp, &lastw_stats)) > 2000000LL )
	    {
	      lastw_stats.tv_sec += 2;
#endif
	      /* make 2 second progress report */
	      period_report(np, &now, udiff, max_wfetch_per, NULL, 
			    NO_RUSAGE, NO_PROCSTATS);
	      max_wfetch_per = 0;
	    }
	      
	  TIME_NP_CALL("Worker main-loop", &call_times.ip, 
	         do_ip(precp, host_hashtbl));
#if 0
	  {
	    int burn_cycles;
	    for (burn_cycles = 0; burn_cycles < 1000; burn_cycles++)
	      ; /* do nothing */
	    put_skb(chan, skb);
	    FREE_PREC(precp);
	  }
#endif /* 0 */	  
	  
	} /* end got packets */
      
      GETTIMEOFDAY("Worker thread - end get loop", &now, (struct timezone *)0);

#if defined PROBE_FED && defined ARR_HIST
      sampleHistogram_add(pkts_his, (double)pkts);
#endif
            
      if( pkts )
	{
	  lastpkt = now;
	  pkts = 0;
	}
      else
	{ 
	  /* we didn't get any packets this time round */
	  GETTIMEOFDAY("Worker thread - no pkts", &now, (struct timezone *)0);
	  udiff = utvsub(&now, &lastpkt);
	  
	  if ( udiff > 4000000LL )
	    {
	      fprintf(stderr,
		      "[%d] No packets seen! tous %d,%d : frus %d,%d : (%d %d)\n",
		      chan,
		      np->x[chan].tous_in, np->x[chan].tous_out, 
		      np->x[chan].frus_in, np->x[chan].frus_out,
		      np->x[chan].tous_in - np->x[chan].tous_out, 
		      np->x[chan].frus_in - np->x[chan].frus_out
		      );
	      /* XXX TMP provoke a dump - see what's going on */
	      //kill(getpid(), SIGSEGV);
	      /* XXX TMP provoke a dump - see what's going on */
	      kill(getpid(), SIGUSR1);
	      /* pretend we've sen a packet to stop message repeating */
	      lastpkt = now;
	    }

	}
      
      
      if ( (udiff = utvsub(&now, &laststats)) > 2000000LL )
	{
#ifdef NIC_STAMP
	  struct timeval lastw_stats_tv, *tvp = &lastw_stats_tv;
	  lastw_stats_tv.tv_sec = (long)lastw_stats/1000000;
	  lastw_stats_tv.tv_usec = (long)lastw_stats%1000000;
#else
	  struct timeval *tvp = &lastw_stats;
#endif
	  /* make 2 second progress report */
	  fprintf(stderr, "PR2 %lld\n", udiff);
	  period_report(np, &now, udiff, max_fetch_per, tvp, 
			NO_RUSAGE, NO_PROCSTATS);

	  //laststats = now;	/* this will drift */
	  laststats.tv_sec += 2;
	  max_fetch_per = 0U;

	  
	  
#if 1 
	  if(chan == 1) 
	    { 
	      /* gross hack for debugging */
	      int tous0 = np->x[0].tous_in - np->x[0].tous_out;
	      int frus0 = np->x[0].frus_in - np->x[0].frus_out;
	      int tous1 = np->x[1].tous_in - np->x[1].tous_out;
	      int frus1 = np->x[1].frus_in - np->x[1].frus_out;
	      
	      fprintf(stderr,
		      "[X] delta tous[ %d %d ], delta frus [ %d %d ], tot = %d\n",
		      tous0, tous1, frus0, frus1,
		      tous0 + tous1 + frus0 + frus1
		      );
	    }
#endif
	  
	  
	} /* end of print stats if stmt */
      
#ifdef USE_ONE_THREAD_ONLY          
//XXX
      network( poll, NUM_IFACES );  /* gross debugging hack */
#endif      
      
      temp = tsleep;
      select(0, NULL, NULL, NULL, &temp);
      
      
    }  /* end of main while 1 */
  
  
}

void parent_exit( int code )
{
  int i;

fprintf(stderr, "[%d] Parent exit called\n",chan);

  // shut the network down
  network(off, NUM_IFACES);

  // send works a SIGUSR1
  for(i=0;i<NUM_CPUS;i++)
    {
      int rc;
      rc = kill( worker_pid[i], SIGUSR1 );
      fprintf(stderr, "[%d] Parent sent pid %d a SIGUSR1, got rc %d.\n",
	      chan, worker_pid[i], rc); 

    }

#if 1
  // wait for child exit
  for(i=0;i<NUM_CPUS;i++)
    {
      int rc;

      rc = waitpid( worker_pid[i], NULL, 0 );

      fprintf(stderr, "[%d] Parent waitpid %d returned rc %d.\n",
	      chan, worker_pid[i], rc); 
      if(rc<1)
	perror("waitpid : ");

    }

#else
  sleep(5);
#endif
 
  fprintf(stderr, "[%d] Parent exiting with code %d.\n",
	      chan, code); 

  exit(code);
}


void worker_exit( int code )
{
  double period;
  int tot_bytes;


  fprintf(stderr, "[%d] worker_exit called.\n",chan); 

  get_end(&end);


#ifdef USE_ONE_THREAD_ONLY          
  // shut the network down
  network(off, NUM_IFACES);
#endif 
 



  // calculate and print a few stats

  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 defined PROBE_FED && defined ARR_HIST
hist_end();
#endif


  if (period > 0)
    {
      printf("[%d] Rusage %f ", chan, period);
      tot_bytes = counters.run_ctrs.IP_all.octs;
      printf("[%d] Tot. Pkts. %.0f Bytes %d Capacity %.2fMbs (IP)\n", chan,
	      counters.run_ctrs.IP_all.pkts, tot_bytes, 
	     (((double)tot_bytes)*8)/(period*1000000));
    }
  else
    {
      printf("Tot. Pkts. %.0f Bytes %d\n",  counters.run_ctrs.IP_all.pkts, tot_bytes);
    }
fprintf(stderr, "5NUM BUFS = %d\n",np->num_bufs);


//while(1){sleep(1000);}

  exit(code);
}


#endif /* ifdef PROBE_FED */
