/* ipaudit.c
 *
 * ipaudit - network traffic data gathering
 * By Jon Rifkin <jon.rifkin@uconn.edu>
 * Copyright 1999-2001 Jonathan Rifkin
 *
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


/*
------------------------------------------------------------------------
Compile Switches
------------------------------------------------------------------------
*/
#define DEBUG

#define DUMP
#undef  DUMP


/*
------------------------------------------------------------------------
Include Files
------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <pcap.h>
#include <signal.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include "hash.h"



/*
------------------------------------------------------------------------
Defines
------------------------------------------------------------------------
*/
#define VERSION_STR "ipaudit 0.95"

#define TRUE 1
#define FALSE 0

#define U_CHAR unsigned char


/*  Protocols with port info  */
#define PROT_TCP        6
#define PROT_UDP       17

/* Data damping period in standalone mode, seconds */
#define DUMP_PERIOD	1800

/*  Flags for udp/tcp accepting all/some ports  */
#define PROT_ACC_ALL    1
#define PROT_ACC_SOME   2

/*  Length of saved packets  */
#define PLEN_DEF 96  /* default  */
#define PLEN_MIN 68  /* min allowed  */

/*  
Length of packet headers  
(culled this info from tcpdump source code)
*/
#define POFF_ETH  14
#define POFF_NULL  4   /* Used by loopback ?  */
#define POFF_PPP   4
#define POFF_RAW   0

/*  
Number of hash slots
NOT number of packets, they're unlimited (except for memory)
*/
#define N_HASH_SLOTS 1000000

/*  Number of 1/10,000 of second in second  */
#define M0SEC 10000

#define NO_FILE_WAITING -1

/*
------------------------------------------------------------------------
DEBUGGING MACROS
------------------------------------------------------------------------
*/
#ifdef DEBUG
#define WRITEMSG \
	if (debug_m) { \
	printf ("File %s line %d: ", __FILE__, __LINE__); \
	printf ("errmsg <%s>\n", strerror(errno)); fflush(stdout); \
	}
#define WRITETXT(txt) \
	if (debug_m) { \
	printf ("File %s line %d: ** %s **\n", __FILE__, __LINE__, (txt)); \
	}
#define WRITEVAR(VAL,FMT) \
	if (debug_m) { \
	printf ("File %s line %d: ", __FILE__, __LINE__); \
	printf ("%s=",#VAL); printf (#FMT, VAL); printf ("\n"); \
	fflush(stdout); \
	}
#define WRITEHEX(VAL,N) \
	if (debug_m) { \
	int i; \
	printf ("File %s line %d: ", __FILE__, __LINE__); \
	printf ("%s :", #VAL); \
	for (i=0;i<N;i++) { printf (" %02x", VAL[i]); } \
	printf ("\n"); \
	fflush(stdout); \
	}
#else
#define WRITEMSG
#define WRITEVAR(VAL,FMT)
#endif



/*
------------------------------------------------------------------------
MACROS
------------------------------------------------------------------------
*/
/*  Convert time in 1/M0SEC to hours, min, seconds, 1/M0SEC  */
#define HMS(hour,min,sec,sec4) \
	sec   = sec4/M0SEC; \
	sec4 -= M0SEC*sec; \
	min   = sec/60; \
	sec  -= 60*min; \
	hour  = min/60; \
	min  -= 60*hour;


/*
------------------------------------------------------------------------
Type Definitions
------------------------------------------------------------------------
*/

/*  Packet structure used by pcap library  */
typedef struct {
	U_CHAR src[6];
	U_CHAR dst[6];
	U_CHAR ptype[2];     /*  ==0x800 if ip  */
	U_CHAR version[1];
	U_CHAR service[1];
	U_CHAR length[2];
	U_CHAR id[2];
	U_CHAR flag[2];
	U_CHAR ttl[1];
	U_CHAR prot[1];
	U_CHAR chksum[2];
	U_CHAR srcip[4];
	U_CHAR dstip[4];
	U_CHAR srcpt[2];
	U_CHAR dstpt[2];
	} pkt_struct_t;

typedef struct {
	U_CHAR src[6];
	U_CHAR dst[6];
	U_CHAR ptype[2];     /*  ==0x800 if ip  */
	} eth_struct_t;

typedef struct {
	U_CHAR version[1];
	U_CHAR service[1];
	U_CHAR length[2];
	U_CHAR id[2];
	U_CHAR flag[2];
	U_CHAR ttl[1];
	U_CHAR prot[1];
	U_CHAR chksum[2];
	U_CHAR srcip[4];
	U_CHAR dstip[4];
	U_CHAR srcpt[2];
	U_CHAR dstpt[2];
	} ip_struct_t;



/*  Start and stop time of each connection  */
typedef struct {
	/*  Time (in sec/10,000) of first and last packet  */
	int first_time, last_time;
	/*  Indentity of machine source for first, last packet  */
	unsigned char first_mach, last_mach;
} datatime_t;


/*  All data for connection  */
typedef struct {
	long       nbyte1, nbyte2;
	int        npkt1, npkt2;
	U_CHAR     intf;
	datatime_t time;
} data_t;







/*
------------------------------------------------------------------------
Global variables
------------------------------------------------------------------------
*/
extern int errno;
extern char pcap_version[];



/*
------------------------------------------------------------------------
Module variables
------------------------------------------------------------------------
*/
int isig_m=0;

/*  Program options  */
U_CHAR *prots_m = NULL;
U_CHAR *tcp_ports_m = NULL;
U_CHAR *udp_ports_m = NULL;


/*  Flag for writing connection time in output  */
int write_time_m = FALSE;
/*  Flag for printing ethernet addresses  */
int printeth_m   = FALSE;
/*  Flag for printing IP addresses in short format  */
int printshort_m = FALSE;
/*  Flag for SQL output */
int dbf_output = FALSE;
/*  Flag for display of source host info */
int probename = FALSE;

int  debug_m     = FALSE;

/*  Pcap input file  */
pcap_t **pcapfile_m = NULL;
char   **pcapfilename_m = NULL;
int    *pcapfiletype_m = NULL;
int    *pcapoffset_m = NULL;
int    npcapfile_m  = 0;

int npkt_m = 0;      /*  Number of    packets  */
int nippkt_m  = 0;   /*  Number of ip packets  */
int nconn_m   = 0;   /*  Number of connections */

/*  IP address range for sorting  */
int *iplist_m = NULL;
int niplist_m = 0;

/*  Variables for input options  */
int  outputbin_m     = FALSE;
int  promisc_m       = 1;          /*  Default, set promisc_muius mode */
FILE *pidfile_m      = NULL;
char *progfile_m     = NULL;
char *writefile_m    = NULL;
char *readfile_m     = NULL;
char *outfile_m      = NULL;
int  maxpkt_m        = 0;
int  hostonly_m      = FALSE;
int  uselimit_m      = FALSE;
int  useicmptype_m   = FALSE;
int  hostportlimit_m = 0;
int  hostlimit_m     = 0;
int  nlen_m          = PLEN_DEF;   /*  Packet length to dump  */
char *filtercmd_m    = "";
int  nhashslots_m    = N_HASH_SLOTS;
int  allow_duplicate_m = 0;
int  ndump_limit_m     = 0;
int standalone_m	= FALSE;
int fork_m			= FALSE;
int dump_period_m		= DUMP_PERIOD;	/* Period of data dumping, seconds */

U_CHAR ip_m[4]       = "";

/*
------------------------------------------------------------------------
Local Function Prototypes
------------------------------------------------------------------------
*/
void ihandler (int);
void parent_ihandler (int);
int  storepkt 
   (struct pcap_pkthdr *, eth_struct_t *, ip_struct_t *, htable_t *, int);
int writepkttxt (htable_t *, char *, time_t);
int writepktbin (htable_t *, char *, time_t);
void PrintUsage();

void parse_portstr(char *str);
void add_protocol(int val);
void add_port (int prot, int port);

void *alloc_err (int, int);

int  get_pkttime (struct pcap_pkthdr *);
int  get_packetoffset (int);


int cmptime (const void *, const void *);
int cmpip   (const void *, const void *);

void  str2ip (char *, int *, int *);
char *ip2str (int);
void  parse_ip_range (char *, int **, int *);
int   in_iprange (int ip, int *iplist, int niplist);
void split (char *instr, char ***list, int *nlist);

void read_options   (int argc, char *argv[]);
int  read_config    (char *);
void read_interface_str (char *);
void alloc_interface (void);
void open_interface (void);
void set_defaults   (void);


/*
------------------------------------------------------------------------
Main Function
------------------------------------------------------------------------
*/
int main (int argc, char *argv[]) {
	struct pcap_pkthdr pkthdr;
	U_CHAR       *raw_pkt = NULL;
	U_CHAR       *raw_pkt_save = NULL;
	eth_struct_t *eth_pkt = NULL;
	ip_struct_t  *ip_pkt  = NULL;
	pcap_dumper_t *df = NULL;
	int dump_this = FALSE;
	int ival;
	int length;
	int i;
	U_CHAR *dumptable = NULL;
	int  optchar;
	char *progarg   = NULL;
	char config_name[512];
	char *config_name_base = "ipaudit.conf";

	/*  Hash table for ip connections  */
	htable_t *hconn = NULL;
	U_CHAR nullip[4] = {0,0,0,0};
	int  DataLinkType;
	int  PacketOffset;
	int  fd, max_fd;
	int  next_intf;
	int  retval;
	char ebuf[PCAP_ERRBUF_SIZE];
	fd_set rdfs, rdfs_init;
	int  is_not_duplicate;
	int  ndump = 0;
	int  status;
	pid_t oldpid, newpid;

	/* Time variables */
	time_t start_t, cur_t;


	/*  Set default values for options  */
	set_defaults();

	/*  Read default config file from current directory */
	if (read_config(config_name_base)) {
		/*  Read default config from home directory  */
		char *home;
		
		home=getenv("HOME");
		if (home == NULL) home="";
		strncpy (config_name, home, 512-strlen(config_name_base)-2);
		strcat  (config_name, "/");
		strcat  (config_name, config_name_base);
		read_config(config_name);
	}


	/*  Read command line options (override's config file) and interfaces */
	read_options (argc, argv);


	/*  If not reading pcap file need the interface name  */
	/*  Check for interfaces  */
	if (!readfile_m) {
		/*  No interfaces from config file, check command line  */
		if (npcapfile_m==0 && argc-optind>0) read_interface_str(argv[optind]);
		/*  Still no interfaces - print usage and quit  */
		if (npcapfile_m==0) {
			PrintUsage();
			return 0;
		}
	}
	
	/*  Fork into parent control and child monitoring processes  */
	oldpid = 0;
	while (fork_m) {

		/*  Set previous signal value to 0  */
		isig_m = 0;
		
		/*  Start new child monitoring process  */
		newpid = fork();

		/*  If parent control (stop and start) child monitoring processes  */
		if (newpid) {

			/*  Kill old process  */
			start_t = time(NULL);
			if (oldpid) kill (oldpid, SIGTERM);

			/*  Set signal handling for parent  */
			signal (SIGALRM, parent_ihandler);  /*  intercepts ALARM        */
			signal (SIGINT,  parent_ihandler);  /*  intercepts ^C           */
			signal (SIGTERM, parent_ihandler);  /*  intercepts ^kill <PID>  */

			/*  Set alarm for end of new child  */
			alarm (dump_period_m - time(NULL) + start_t);

			/*  Wait on old child to complete */
			/*  NOTE:  Instead might want to use wait() below  */
			if (oldpid) waitpid (oldpid, &status, 0);

			/*  Wait for alrm signal to stop new child  */
			pause ();

			/*  Send SIGTERM to child so it will clean up  */
			kill (newpid, SIGTERM);

			oldpid = newpid;

			/*  End parent if received SIGTERM OR SIGINT  */
			if (isig_m==SIGTERM || isig_m==SIGINT) {
				if (oldpid) waitpid (oldpid, &status, 0);
				return (0);
			}


		/*  Child process breaks loop and continues below  */
		} else {
			break;
		}
	}
	/*  End of parent control loop  */

	
	/*  Open pcap file  */
	if (readfile_m) {
		npcapfile_m = 1;
		pcapfile_m = malloc (sizeof(pcap_t *));
		pcapfilename_m = (char **) malloc (sizeof(pcapfilename_m[0]));
		pcapfilename_m[0] = readfile_m;
		pcapfile_m[0]     = pcap_open_offline(readfile_m, ebuf);
		pcapoffset_m      = (int *) malloc (sizeof(int));
		pcapoffset_m[0]   = get_packetoffset(pcap_datalink(pcapfile_m[0]));
		if (NULL==pcapfile_m[0]) {
			fprintf (stderr, "ERROR:  Cannot open read file %s.\n", readfile_m);
			exit(1);
		}

	/*  Read live interface(s)  */
	} else if (npcapfile_m) {
		open_interface ();
	}

	/*  Allocate room for saved raw packet  */
	raw_pkt_save = (U_CHAR *) malloc (nlen_m);
	

if (debug_m) {
	for (i=0;i<npcapfile_m;i++) {
		printf ("Interface (%s) ", pcapfilename_m[i]);
		switch (pcap_datalink(pcapfile_m[i])) {
			case DLT_EN10MB: 
				printf ("DataLinkType = %s\n", "DLT_EN10MB"); break;
			case DLT_IEEE802: 
				printf ("DataLinkType = %s\n", "DLT_IEEE802"); break;
			case DLT_SLIP: 
				printf ("DataLinkType = %s\n", "DLT_SLIP"); break;
			case DLT_SLIP_BSDOS: 
				printf ("DataLinkType = %s\n", "DLT_SLIP_BSDOS"); break;
			case DLT_PPP: 
				printf ("DataLinkType = %s\n", "DLT_PPP"); break;
			case DLT_PPP_BSDOS: 
				printf ("DataLinkType = %s\n", "DLT_PPP_BSDOS"); break;
			case DLT_FDDI: 
				printf ("DataLinkType = %s\n", "DLT_FDDI"); break;
			case DLT_NULL: 
				printf ("DataLinkType = %s\n", "DLT_NULL"); break;
			case DLT_RAW: 
				printf ("DataLinkType = %s\n", "DLT_RAW"); break;
			case DLT_ATM_RFC1483: 
				printf ("DataLinkType = %s\n", "DLT_ATM_RFC1483"); break;
			default:
				printf ("DataLinkType = %d\n", DataLinkType);
		}
	}
}

#if 0
   if (debug_m) 
	   printf ("PacketOffset = %d\n", PacketOffset);

	/*  
	Insure if ethernet addresses requested that
	captured packets contain them
	*/
	if (printeth_m && PacketOffset<0) {
		fprintf (stderr, 
			"ERROR:  Cannot print ethernet addresses as requested.\n");
		fprintf (stderr, "Current network interface (%s) ", argv[optind]);
		fprintf (stderr, "is not an ethernet interface.\n");
		exit(2);
	}
#endif



	/*  Install interupt handler if reading live */
	if (!readfile_m) {
		signal (SIGINT,    ihandler);   /*  intercepts  ^C           */
		signal (SIGTERM,   ihandler);   /*  intercepts  ^kill <PID>  */
	}

	/*  Initialize hash table  */
	hconn = ht_init(N_HASH_SLOTS,0);


	/*  Counters  */
	npkt_m    = 0;
	nippkt_m  = 0;

	/*  Open dump file for first interface (will use for all interface
	 *  data?)  */
	if (writefile_m)  {
		df     = pcap_dump_open(pcapfile_m[0], writefile_m);
		if (NULL==df) {
			fprintf (stderr, "ERROR:  ");
			fprintf (stderr, "Cannot open raw packet file <%s>\n", writefile_m);
			exit(1);
		}
	}



	/*  Initialize info for select()  */
	if (!readfile_m) {
		FD_ZERO (&rdfs_init);
		max_fd = 0;
		for (i=0;i<npcapfile_m;i++) {
			fd = pcap_fileno(pcapfile_m[i]);
			FD_SET (fd, &rdfs_init);
			if (fd>max_fd) max_fd=fd;
		}
		max_fd++;
	}

	/*  If reading live set intf number to cycle throught list, 
	 *  if reading file just set interface number to first interface
	 *  */
	if (readfile_m) next_intf = 0;
	else            next_intf = npcapfile_m;

	/*  Read packets until interupt signal  */
	start_t=time(NULL);
	while (isig_m==0) {
		/*  Print data for this increment  */
		if(standalone_m) 
		{
			cur_t=time(NULL);
			if(cur_t - start_t >= dump_period_m) 
			{
				int result;
				if (outputbin_m) 
				{
					result = writepktbin(hconn, outfile_m, cur_t);
				} 
				else 
				{
					result = writepkttxt(hconn, outfile_m, cur_t);
				}
				/* Free hash table */
                        	if ( result == 0 )
                        	{
                                	ht_free(hconn);
                                	hconn = ht_init(N_HASH_SLOTS,0);
                                	start_t=time(NULL);
                        	}
			}
		}

		/*  No pending file, run select again  */
		if (!readfile_m) {

			if (next_intf==npcapfile_m) {
				/*  Wait for packet on one of the interfaces  */
				memcpy (&rdfs, &rdfs_init, sizeof(rdfs));
				retval = select (max_fd, &rdfs, NULL, NULL, NULL); 
				next_intf =  0;
				/*  If user interupt caught during select() call, retval will
				 *  be <0.  By continuing we re-test isig_m which should now
				 *  be set by the interupt handler */
				if (retval<0) continue;
			}

			/*   Search list to find waiting file  */
			while (next_intf<npcapfile_m) {
				if (FD_ISSET(pcap_fileno(pcapfile_m[next_intf]), &rdfs)) break;
				next_intf++;
			}

			/*  No pending files left, jump to top of loop and run select again  */
			if (next_intf>=npcapfile_m) continue;
		}

		/*  Read packet from next available file  */
		raw_pkt = (U_CHAR *) pcap_next (pcapfile_m[next_intf], &pkthdr);

		/*  If pkt is null and we're reading file then we're done  */
		/*  Otherwise if reading live try again                    */
		if (raw_pkt==NULL) {
			if (readfile_m) break;
			else          continue;
		}

		/*  Number of packets read (ip or not)  */
		npkt_m++;

		/*  Skip this packet if ethernet and not ip  */
		if (pcapoffset_m[next_intf]==POFF_ETH) {
			eth_pkt = (eth_struct_t *) raw_pkt;
			if (! (eth_pkt->ptype[0]==8 && eth_pkt->ptype[1]==0) ) continue;
		}

		/*  Find pointer to ip packet  */
		ip_pkt = (ip_struct_t *) (raw_pkt + pcapoffset_m[next_intf]);

		/*  Don't exceed limit of ip packets  */
		nippkt_m++;
		if (maxpkt_m && nippkt_m>maxpkt_m)
			break;


		/*  Dump packet contents  */
		if (debug_m) {
			int ibyte, iwidth;
			printf ("IP Packet Count   %d\n", nippkt_m);
			printf ("Raw Packet Length %d\n", pkthdr.len);
			printf ("Captured   Length %d\n", pkthdr.caplen);
			printf ("Captured bytes ...\n");
			iwidth=0;
			for (ibyte=0;ibyte<pkthdr.caplen;ibyte++) {
				printf (" %03d", raw_pkt[ibyte]);
				if (++iwidth==16) {
					printf ("\n");
					iwidth=0;
				}
			}
			printf ("\n");
		}

		/*  Increase packet size if captured size greater than allocated size
		 *  this can only happen when reading packets from a dump file
		 */
		if (pkthdr.caplen>nlen_m) {
			if (raw_pkt_save) free (raw_pkt_save);
			nlen_m = pkthdr.caplen;
			raw_pkt_save = (U_CHAR *) malloc (nlen_m);
		}
		

		/*  Save raw packet so can write original packet later to capture
		 *  file  */
		memcpy (raw_pkt_save, raw_pkt, pkthdr.caplen);

		/*  Host only storage, set prot, port to zero  */
		if (hostonly_m) {
			memset (ip_pkt->srcpt, 0, 2);
			memset (ip_pkt->dstpt, 0, 2);
			memset (ip_pkt->prot,  0, 1);
		}

		/*  If number of stored packets with full host,port info exceeded
		then just store host info.  If number of packets with host info
		exceeded, then just increment byte and packet info with a dummy
		host pair 0.0.0.0 0.0.0.0   */


		/*  Exceeded host/port limit  */
		if (uselimit_m && nconn_m >= hostportlimit_m) {

			/*  Set both host ports to 0  */
			memset (ip_pkt->srcpt, 0, 2);
			memset (ip_pkt->dstpt, 0, 2);

			/*  Exceeed host-only limit also  */
			if (nconn_m >= hostportlimit_m + hostlimit_m) {

				/*  Set both host IPs to 0.0.0.0  */
				memset (ip_pkt->srcip, 0, 4);
				memset (ip_pkt->dstip, 0, 4);
				if (eth_pkt) {
					memset (eth_pkt->src, 0, 6);
					memset (eth_pkt->dst, 0, 6);
				}
			}
		}
				
		/*  Set ports to 0 if not UDP or TCP  */
		if ( ip_pkt->prot[0]!=0x11 && ip_pkt->prot[0]!=0x06 ) {
			if (ip_pkt->prot[0]==1 && useicmptype_m) {
				memset (ip_pkt->dstpt, 0, 2);
			} else {
				memset (ip_pkt->srcpt, 0, 2);
				memset (ip_pkt->dstpt, 0, 2);
			}
		}


		if (debug_m) {
		printf ("%03d.%03d.%03d.%03d %03d.%03d.%03d.%03d  %3d %5d %5d\n", 
			ip_pkt->srcip[0],ip_pkt->srcip[1],ip_pkt->srcip[2],ip_pkt->srcip[3],
			ip_pkt->dstip[0],ip_pkt->dstip[1],ip_pkt->dstip[2],ip_pkt->dstip[3],
			ip_pkt->prot[0],
			ip_pkt->srcpt[0]*256+ip_pkt->srcpt[1],
			ip_pkt->dstpt[0]*256+ip_pkt->dstpt[1]);
		}

		/*  Store packets  */
		is_not_duplicate = storepkt (&pkthdr, eth_pkt, ip_pkt, hconn, next_intf);

		/*  Dump raw packets  */
		if (is_not_duplicate && writefile_m) {

			/*  Check to see if current file has same offset as dump file */
			if (pcapoffset_m[0]!=pcapoffset_m[next_intf]) goto no_dump;

			/*  No protocols/ports specified, so dump all */
			dump_this = FALSE;
			if (NULL==prots_m && 0==ip_m[0]) 
				dump_this = TRUE;

			/*  Find pointer to ip packet  */
			ip_pkt = (ip_struct_t *) (raw_pkt_save + pcapoffset_m[next_intf]);

			/*  Is this packet correct protocol/port  */
			if (prots_m) {
				dump_this = prots_m[ip_pkt->prot[0]];

				/*  If udp or tcp, are ports specified ?  */
				if (PROT_ACC_SOME==dump_this) {
					dumptable = (PROT_TCP==ip_pkt->prot[0]) ? tcp_ports_m : udp_ports_m;
					dump_this = 
						dumptable[(ip_pkt->srcpt[0]<<8)+ip_pkt->srcpt[1]] ||
						dumptable[(ip_pkt->dstpt[0]<<8)+ip_pkt->dstpt[1]];
				}
			} 

			if (!dump_this && ip_m) {
				dump_this = (!memcmp(ip_m,ip_pkt->srcip,4) || !memcmp(ip_m,ip_pkt->dstip,4));
			}

			/*  Dump packet  */
			if (dump_this && (ndump_limit_m==0 || ndump<ndump_limit_m)) {
				pcap_dump ((U_CHAR *) df, &pkthdr, raw_pkt_save);
				ndump++;
			}
		}

		no_dump:


		/*  Select next file in list for checking  */
		if (!readfile_m) next_intf++;

	}  /*  Read tcpdump data  */

	/*  Close files  */
	for (i=0;i<npcapfile_m;i++) 
		if (NULL!=pcapfile_m[i])  pcap_close(pcapfile_m[i]);

	/*  Clear error if breaking during pcap call  */
	errno = 0;


	/*  Close dump file  */
	if (writefile_m  ) pcap_dump_close (df);

	/*  Write packet info  */
	cur_t=time(NULL);
	if (outputbin_m) {
		writepktbin(hconn, outfile_m, cur_t);
	} else {
		writepkttxt(hconn, outfile_m, cur_t);
	}

	/*  Free hash table  */
	ht_free(hconn);

	/*  Call next program  */
	if (progfile_m) {
		progarg = strchr (progfile_m, ' ');
		if (progarg) {
		*progarg++ = '\0';
		}
		execl (progfile_m, progfile_m, progarg, NULL);
	}
}



/*
Interupt handler (called when program recieves SIGTERM, SIGINT)
*/
void ihandler (int cursig) {
	int i;

	/*  Set flag to terminate main() polling loop 
	 *  when excution reaches bottom  */
	isig_m = 1;

	/*  Change interface read() to non-blocking so that program does not
	 *  have to wait for read() to encounter network data before function
	 *  call returns
	 */
#if 0
	i = pcap_fileno(pcapfile_m);
	fcntl (i, F_SETFL, fcntl(i,F_GETFL) | O_NONBLOCK);
#endif

	/*  FLUSH BUFFERS  */
	fflush (stdout);

	/*  RE-INSTALL SIGNAL HANDLER  */
	signal (cursig, SIG_DFL);
#ifdef DEBUG
	if (debug_m) {
		struct tm *tm;
		time_t    seconds;
		fprintf (stderr, "ipaudit received signal number <%i>\n", cursig);
		time (&seconds);
		tm = localtime (&seconds);
		fprintf (stderr, "date is <%04d-%02d-%02d-%02d:%02d:%02d>\n", 
		  tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, 
		  tm->tm_hour, tm->tm_min, tm->tm_sec);
		fprintf (stderr, "number of     packets read <%d>\n", npkt_m);
		fprintf (stderr, "number of ip  packets read <%d>\n", nippkt_m);
		}
#endif
	}


/*
Interupt handler (called when parent receives ALRM or SIGINT, SIGTERM)
*/
void parent_ihandler (int cursig) {
	int i;

	/*  Set flag to terminate main() polling loop 
	 *  when excution reaches bottom  */
	isig_m = cursig;


	/*  RE-INSTALL SIGNAL HANDLER  */
	signal (cursig, SIG_DFL);

	}


/*
Store packet info in hash table, 
keyed by ip1,ip2,port1,por2,protocol
data  is number of incoming/outgoing bytes, packets
*/
int storepkt (
struct pcap_pkthdr *pkthdr, 
eth_struct_t *ep, 
ip_struct_t *ip, 
htable_t *ht,
int intf
) {
	U_CHAR     key[25];  /*  space for ip1,ip2,prot,prt1,prt2,eth1,eth2  */
	data_t     *data;
	int        ndata;
	int        length;
	data_t     idata;
	datatime_t idatatime;
	int        datasize;
	int        keysize;
	int        is_unique_packet;
   int        is_found;

	/*  Calculate data packet length  */
	length = ip->length[1] + 256*(int) ip->length[0] + 14;

	/*  
	Make key - order so smallest ip first 
	store data
	*/
	if (memcmp(ip->srcip, ip->dstip, 4) < 0) {
		memcpy (key+ 0, ip->srcip, 4);
		memcpy (key+ 4, ip->dstip, 4);
		memcpy (key+ 8, ip->srcpt, 2);
		memcpy (key+10, ip->dstpt, 2);
		if (ep) {
			memcpy (key+13, ep->src,   6);
			memcpy (key+19, ep->dst,   6);
		}
		idata.nbyte1 = 0;
		idata.nbyte2 = length;
		idata.npkt1  = 0;
		idata.npkt2  = 1;
		idata.intf   = intf;
	} else {
		memcpy (key+ 0, ip->dstip, 4);
		memcpy (key+ 4, ip->srcip, 4);
		memcpy (key+ 8, ip->dstpt, 2);
		memcpy (key+10, ip->srcpt, 2);
		if (ep) {
			memcpy (key+13, ep->dst,   6);
			memcpy (key+19, ep->src,   6);
		}
		idata.nbyte1 = length;
		idata.nbyte2 = 0;
		idata.npkt1  = 1;
		idata.npkt2  = 0;
		idata.intf   = intf;
	}
	memcpy (key+12, ip->prot,  1);


	/*  Store time if requested  */
	if (write_time_m) {
		idata.time.first_time = get_pkttime (pkthdr);
		/*  If machine 1 received packet, then source was 2, else 1  */
		idata.time.first_mach = (idata.npkt1==1) ? 2 : 1;
		/*  Set first and last machine info the same  */
		idata.time.last_time  = idata.time.first_time;
		idata.time.last_mach  = idata.time.first_mach;
		/*  Set size of full data structure  */
		datasize = sizeof(data_t);

	/*  Set datasize to not store time (saves memory space)  */
	} else {
		datasize = sizeof(data_t) - sizeof(datatime_t);
	}

#ifdef DUMP
printf ("%03u.%03u.%03u.%03u ", key[0], key[1], key[2], key[3]);
printf ("%03u.%03u.%03u.%03u ", key[4], key[5], key[6], key[7]);
printf ("%u %u %u %d %d\n", key[12], (int )key[8]*256+key[9], (int )key[10]*256+key[11], length, 1);
#endif

	/*  Set keysize according to whether we are storing eth packets  */
	if (printeth_m) {
		keysize = sizeof(key);
	} else {
		keysize = sizeof(key)-12;
	}

	/*  Assume for now that this packet is unique  */
	is_unique_packet = 1;

	/*   Add first instance of this key to table  */
	is_found = ht_findkey(ht,(U_CHAR *)&key, keysize, (U_CHAR **)&data,&ndata);
	if ( ! is_found ) {
		datasize = sizeof(idata);
		ht_storekey (ht, (U_CHAR *) &key, keysize, (U_CHAR *) &idata, datasize);
		nconn_m++;  /*  Increment number of connections  */

	/*  Key already present, update info  
	 *  If this key (ip address/protocol/port) already seen on a
	 *  different interface, we ignore this instance since it
	 *  must(?) be duplicate information  */
	} else if (allow_duplicate_m || data->intf==intf) {
		data->nbyte1 += idata.nbyte1;
		data->nbyte2 += idata.nbyte2;
		data->npkt1  += idata.npkt1;
		data->npkt2  += idata.npkt2;
		/*  Update last packet time  */
		if (write_time_m) {
			data->time.last_time  = idata.time.last_time  ;
			data->time.last_mach  = idata.time.last_mach;
		}
#ifdef DUMP
printf ("data idata  <%u %u %u %u>a  <%u %u %u %u>\n", 
data[0], data[1], data[2], data[3],
idata[0], idata[1], idata[2], idata[3]);
#endif

	/*  This packet must also be present on another interface  */
	} else {
		is_unique_packet = 0;
	}
	return is_unique_packet;
}


/*
Retrieve and print packets from hash table in bin format
*/
int writepktbin (htable_t *ht, char *outname, time_t cur_t) {
	helem_t *t;
	data_t  *data;
	FILE    *outfile_m = stdout;
	int     switch_mach;
	int     first_mach, last_mach;

	/*  Open file if outname is not -  */
	if ( NULL!=outname && strcmp("-",outname) ) {
		if(!standalone_m)
		outfile_m = fopen (outname, "wb");
		else
		outfile_m = fopen (outname, "ab");
		}
	if (NULL==outfile_m) {
		fprintf (stderr, "ERROR:  Cannot open output file <%s>\n", outname);
		return 1;
		/* exit(1); */
	}

	/*  Walk list  */
	ht_initwalk (ht);
	while ((t=ht_getnext(ht))) {

		/*  Get ip addresses and ports  */
		data = (data_t *) t->data;

		/*  Re-order ip addresses if 2nd is local and first is not  */
		switch_mach = 
			!in_iprange (*(int*)(t->key),   iplist_m, niplist_m) &&
			 in_iprange (*(int*)(t->key+4), iplist_m, niplist_m);

		if (switch_mach) {
		
			fwrite (t->key+4, 1, 4, outfile_m);   /* 2nd ip  */
			fwrite (t->key  , 1, 4, outfile_m);   /* 1st ip  */
			fwrite (t->key+12,1, 1, outfile_m);   /* protocol */
			fwrite (t->key+10,1, 2, outfile_m);   /* 2nd port  */
			fwrite (t->key+8, 1, 2, outfile_m);   /* 1st port  */
			fwrite (&data->nbyte2, 8, 1, outfile_m);  /* 2nd ip, bytes received  */ 
			fwrite (&data->nbyte1, 8, 1, outfile_m);  /* 1st ip, bytes received  */
			fwrite (&data->npkt2,  4, 1, outfile_m);  /* 2nd ip, packets recevied  */
			fwrite (&data->npkt1,  4, 1, outfile_m);  /* 1st ip, packets received  */

		} else {
		
			fwrite (t->key  , 1, 4, outfile_m);   /* 1st ip  */
			fwrite (t->key+4, 1, 4, outfile_m);   /* 2nd ip  */
			fwrite (t->key+12,1, 1, outfile_m);   /* protocol */
			fwrite (t->key+8, 1, 2, outfile_m);   /* 1st port  */
			fwrite (t->key+10,1, 2, outfile_m);   /* 2nd port  */
			fwrite (&data->nbyte1, 8, 1, outfile_m); /* 1st ip, bytes received  */
			fwrite (&data->nbyte2, 8, 1, outfile_m); /* 2nd ip, bytes received  */
			fwrite (&data->npkt1,  4, 1, outfile_m); /* 1st ip, packets received  */
			fwrite (&data->npkt2,  4, 1, outfile_m); /* 2nd ip, packets recevied  */
		}

		/*  If switching machine order, correct first/last machine id  */
		if (write_time_m) {
			if (switch_mach) {
				first_mach  = 3 - data->time.first_mach;
				last_mach   = 3 - data->time.last_mach;
			} else { 
				first_mach  = data->time.first_mach;
				last_mach   = data->time.last_mach;
			}
			fwrite (&first_mach, sizeof(first_mach), 1, outfile_m);
			fwrite (&last_mach, sizeof(last_mach), 1, outfile_m);
		}
	}

	/*  Close file  */
	if (outname)
		fclose(outfile_m);

	return 0;
}


/*
Retrieve and print packets from hash table in text format
.. sort by time if writing it
*/
int writepkttxt (htable_t *ht, char *outname, time_t cur_t) {
	helem_t *t;
	data_t  *data;
	FILE    *outfile_m = stdout;
	char    ip1[16], ip2[16];
	int     pt1, pt2, prot;
	int     hour,min,sec,msec;
	int     switch_mach;
	int     first_mach, last_mach;
	int     iconn, nconn;
	helem_t **conn = NULL;
	char    eth1str[13], eth2str[13];
	int	sys_info_err;
	char    hostname[81];

	if (outname && strcmp("-",outname)) { 
		if(!standalone_m)
		outfile_m = fopen (outname, "wt");
		else 
		outfile_m = fopen (outname, "at");
		}
	if (NULL==outfile_m) {
		fprintf (stderr, "ERROR:  Cannot open output file <%s>\n", outname);
		return 1;
		/* exit(1); */
	}

	/*  Get number of connections  */
	nconn = ht_getcount(ht);
	conn = (helem_t **) calloc (nconn, sizeof(helem_t *));
	if (NULL==conn) {
		fprintf (stderr, 
			"ERROR:  Cannot allocate memory for connection index\n");
		exit(1);
	}

	/*  Make pointer list  */
	ht_initwalk (ht);
	for (iconn=0;iconn<nconn;iconn++) {
		conn[iconn] = ht_getnext(ht);
	}

	/*  Sort pointer list  */
	if (write_time_m) {
		qsort ( (void *) conn, nconn, sizeof(conn[0]), cmptime);
	} else {
		qsort ( (void *) conn, nconn, sizeof(conn[0]), cmpip  );
	}

	
	/* Get system info if required */
	if (probename) {
		sys_info_err = gethostname(hostname, 80);
	}

	/* Write timestamp */
	if (standalone_m) {
		fprintf (outfile_m, "#Time: %d\n",cur_t);
	}


	/*  Walk list  */
	for (iconn=0;iconn<nconn;iconn++) {
		t = conn[iconn];

		if (dbf_output)
			fprintf (outfile_m, "INSERT INTO ipaudit SET ");

		/* Display probe info if requested */
		if (probename) {
			if (dbf_output)
				fprintf (outfile_m, "probename='%s',", 
						sys_info_err != 0?"unknown":hostname);
			else 
				fprintf (outfile_m, "%s ", 
						sys_info_err != 0?"unknown":hostname);
		}

		/*  Get ip addresses and ports  */
		if (printshort_m) {
			sprintf (ip1, "%u.%u.%u.%u", 
				t->key[0], t->key[1], t->key[2], t->key[3]);
			sprintf (ip2, "%u.%u.%u.%u", 
				t->key[4], t->key[5], t->key[6], t->key[7]);
		} else {
			sprintf (ip1, "%03u.%03u.%03u.%03u", 
				t->key[0], t->key[1], t->key[2], t->key[3]);
			sprintf (ip2, "%03u.%03u.%03u.%03u", 
				t->key[4], t->key[5], t->key[6], t->key[7]);
		}
		pt1  = (int) t->key[ 8]*256 + t->key[ 9];
		pt2  = (int) t->key[10]*256 + t->key[11];
		prot = t->key[12];

		/*  Re-order ip addresses if 2nd is local and first is not  */
		switch_mach = 
			!in_iprange (ntohl(*(int*)(t->key)),   iplist_m, niplist_m) &&
			 in_iprange (ntohl(*(int*)(t->key+4)), iplist_m, niplist_m);

		if (switch_mach) {
		
			/*  Print key info  */
			if (dbf_output) {
				fprintf (outfile_m, "ip2='%s',ip1='%s',protocol=%u,ipport2=%u,ipport1=%u", 
						ip2, ip1, prot, pt2, pt1);
			} else {
				fprintf (outfile_m, "%s %s %u %u %u", ip2, ip1, prot, pt2, pt1);
			}

			/*  Data  */
			data = (data_t *) t->data;
			if (dbf_output) {
				fprintf (outfile_m, ",ip2bytes=%d,ip1bytes=%d,ip2pkts=%d,ip1pkts=%d", 
					data->nbyte2, data->nbyte1, data->npkt2, data->npkt1);
			} else {
				fprintf (outfile_m, " %d %d %d %d", 
					data->nbyte2, data->nbyte1, data->npkt2, data->npkt1);
			}

		} else {
		
			/*  Print key info  */
			if (dbf_output) {
				fprintf (outfile_m, "ip1='%s',ip2='%s',protocol=%u,ip1port=%u,ip2port=%u", 
						ip1, ip2, prot, pt1, pt2);
			} else {
				fprintf (outfile_m, "%s %s %u %u %u", ip1, ip2, prot, pt1, pt2);
			}

			/*  Data  */
			data = (data_t *) t->data;
			if (dbf_output) {
				fprintf (outfile_m, ",ip1bytes=%d,ip2bytes=%d,ip1pkts=%d,ip2pkts=%d", 
					data->nbyte1, data->nbyte2, data->npkt1, data->npkt2);
			} else {
				fprintf (outfile_m, " %d %d %d %d", 
					data->nbyte1, data->nbyte2, data->npkt1, data->npkt2);
			}
		}

		if (write_time_m) {
			/*  Convert seconds from midnight to 24 hour time  */
			msec  = data->time.first_time;
			HMS(hour,min,sec,msec)

			if (dbf_output) {
				fprintf (outfile_m, ",constart='%02d:%02d:%02d',constartmsec=%04d", hour,min,sec,msec);
			} else {
				fprintf (outfile_m, " %02d:%02d:%02d.%04d", hour,min,sec,msec);
			}

			msec  = data->time.last_time;
			HMS(hour,min,sec,msec)

			if (dbf_output) {
				fprintf (outfile_m, ",constop='%02d:%02d:%02d',constopmsec=%04d", hour,min,sec,msec);
			} else {
				fprintf (outfile_m, " %02d:%02d:%02d.%04d", hour,min,sec,msec);
			}

			/* Don't display machine order if dbf output */
			if (!dbf_output) {
			/*  If switching machine order, correct first/last machine id  */
				if (switch_mach) {
					first_mach  = 3 - data->time.first_mach;
					last_mach   = 3 - data->time.last_mach;
				} else { 
					first_mach  = data->time.first_mach;
					last_mach   = data->time.last_mach;
				}

				fprintf (outfile_m, " %1d %1d", first_mach, last_mach);
			}
		} 

		/*  Print optional ethernet addresses  */
		if (printeth_m) {
			sprintf (eth1str, "%02x%02x%02x%02x%02x%02x", 
				t->key[13], t->key[14], t->key[15], 
				t->key[16], t->key[17], t->key[18]);
			sprintf (eth2str, "%02x%02x%02x%02x%02x%02x", 
				t->key[19], t->key[20], t->key[21], 
				t->key[22], t->key[23], t->key[24]);

			if (switch_mach) {
				if (dbf_output)
					fprintf (outfile_m, ",eth2='%s',eth1='%s'", eth2str, eth1str);
				else
					fprintf (outfile_m, " %s %s", eth2str, eth1str);
			} else {
				if (dbf_output)
					fprintf (outfile_m, ",eth1='%s',eth2='%s'", eth1str, eth2str);
				else
					fprintf (outfile_m, " %s %s", eth1str, eth2str);
			}
		}


		if (dbf_output)
			/* ; is line terminator for SQL */
			fprintf (outfile_m, ";\n");
		else
			fprintf (outfile_m, "\n");

	}

	/*  Close file  */
	fflush(outfile_m);
	if (outname)
		fclose(outfile_m);
	/*  reclaim storage  */
	if (NULL!=conn) free (conn);
	return 0;
	
}

void PrintUsage(void) {
	printf ("\nUsage: ipaudit [OPTIONS] [interface[:interface[:interface..]]]\n");
	printf ("  Read and record info on ip connections and optionally\n");
	printf ("  dump packets to file\n");
	printf ("\n");
	printf ("  -r readfile   -  Read packets from pcap format file\n");
	printf ("                   Don't need interface with this option\n");
	printf ("  -w writefile  -  Dump selected packets to pcap format file\n");
	printf ("  -i pidfile    -  Write process id to file\n");
	printf ("  -l ip-range   -  Order output ip address pairs by ip range\n");
	printf ("  -s nlen       -  Dump first <nlen> bytes of each packet (default %d, min %d)\n",
		PLEN_DEF, PLEN_MIN);
	printf ("  -x program    -  Run program when done\n");
	printf ("  -c npacket    -  Only read in specific number of ip packets\n");
	printf ("  -e            -  Write out ethernet addresses\n");
	printf ("  -t             - Write out connection start and stop times\n");
	printf ("  -o outfile    -  Place output in 'outfile'. Default is stdout\n");
	printf ("  -p string     -  Dump only selected ip protocols and ports\n");
	printf ("     string format -p n:n:n,p,p:n where n is protocol number\n");
	printf ("     and p is port number (only for protocols 6 (tcp) and 17 (udp)\n");
	printf ("  -v            -  Print version and exit.\n");
	printf ("  -g config     -  Read config file (instead of default\n");
	printf ("  -G            -  Do not read config file\n");
	printf ("  -b            -  Write output in binary format (experimental)\n");
	printf ("  -f filterstr  -  Use pcap filters (see tcpdump)\n");
	printf ("  -C            -  Preserve ICMP type/code in source port field\n");
	printf ("  -F period     -  Forking mode.\n");
	printf ("  -H            -  Store hosts only (protocol, ports set to zero)\n");
	printf ("  -I ipaddr     -  Dump all packets with 'ipaddr'\n");
	printf ("  -L hostportlimit,hostlimit\n");
	printf ("                -  Max number of hostport,host packets recorded\n");   
	printf ("  -N nhashslot  -  Number of hash slots\n");
	printf ("  -S            -  Print ip addresses in short format (no leading 0s)\n");
	printf ("  -M            -  Allow double logging of packets which pass between\n");
  	printf ("                   multiple interfaces\n");
	printf ("  -m		 -  Don't  put  the  interface  into  promiscuous mode.\n");
	printf ("  -q            -  Output in SQL format for direct database input\n");
	printf ("  -P            -  Display hostname as part of output\n");
	printf ("  -W dumplimit  -  Limit to number of packets written with -w command\n");
	printf ("  -D period     -  Standalone mode, append results to output stream\n");
	printf ("                   dumps data approx. each period seconds\n");
	printf ("Example:\n");
	printf ("  ipaudit -w dump.fil -p1:2:6,21,23  eth0\n");
	printf ("Write only packets with protocols 1 (icmp), 2 (?), and 6 (tcp)\n");
	printf ("and tcp ports 21 and 23 (ftp,telnet)\n\n");
}



/*
Determine protocols and ports to accept from string of format
	"1:6,21,23,6667:17" means protocols 1 (icmp), 6 (tcp) and 17 (udp)
	For tcp only ports 21,23,6667.
	For udp all ports (default)
*/
void parse_portstr(char *str) {
	char *p;
	char delim;
	int  lastprot = -1;
	int  is_prot;
	int  val;

	is_prot = TRUE;
	delim=' ';
	while (delim) {
		/*  Find : or '\0'  */
		p=str;
		/*  Find next : , '\0'  */
		while (*p!=':' && *p!=',' && *p!='\0') 
		p++;
		delim = *p;
		*p = '\0';
		val = atoi(str);
		str = p+1;

		if (is_prot) {
		lastprot = val;
		add_protocol(val);
		} else {
		add_port (lastprot,val);
		}
		is_prot = (delim==':');
	}
}


void add_protocol(int val) {
	/*  Check for valid protocol  */
	if (val<0 || val>255)
		return;
	/*  ALlocate if not done already  */
	if (NULL==prots_m)
		prots_m = (U_CHAR *) alloc_err(256, sizeof(U_CHAR));
	if (NULL==prots_m) {
		fprintf (stderr, "ERROR: Out of memory\n");
		exit (1);
	}
	/* Accept all ports  */
	prots_m[val] = PROT_ACC_ALL;
	}


void add_port (int prot, int port) {
	/*  Check for valid port  */
	if (prot<0 || prot>0x0ffff)
		return;
	/*  Check for valid protocol  */
	if (prot==PROT_TCP) {
		prots_m[prot] = PROT_ACC_SOME;
		if (NULL==tcp_ports_m) 
		tcp_ports_m = (U_CHAR *) alloc_err(0x010000, sizeof(U_CHAR));
		tcp_ports_m[port] = 1;
	} else if (prot==PROT_UDP) {
		prots_m[prot] = PROT_ACC_SOME;
		if (NULL==udp_ports_m) 
		udp_ports_m = (U_CHAR *) alloc_err(0x010000, sizeof(U_CHAR));
		tcp_ports_m[port] = 1;
	}
}



void *alloc_err (int n, int size) {
	void *p;
	p = calloc (n, size);
	if (NULL==p) {
		fprintf (stderr, "ERROR: Out of memory.\n");
		exit(1);
	}
	return p;
}




/*
Convert time from pcap pkthdr format (double longs) to 
1/10,000 seconds since midnight
*/
int get_pkttime (struct pcap_pkthdr *pkthdr) {
	struct tm *time;
	time = localtime( (time_t *) &pkthdr->ts.tv_sec);
	return  
		(int)
		pkthdr->ts.tv_usec/100 +  /*  Convert microseconds */
		M0SEC * (time->tm_sec + 60*(time->tm_min + 60*time->tm_hour));
}



/*  Comparison function for sorting by time  */
int cmptime (const void *ai, const void *bi) {
	helem_t *ah = *(helem_t **) ai;
	helem_t *bh = *(helem_t **) bi;
	data_t  *ad = (data_t *) (ah->data);
	data_t  *bd = (data_t *) (bh->data);
	if (ad->time.first_time < bd->time.first_time) return -1;
	if (ad->time.first_time > bd->time.first_time) return  1;
	if (ad->time.last_time  < bd->time.last_time ) return -1;
	if (ad->time.last_time  > bd->time.last_time ) return  1;
	return 0;
}


/*  Comparison function for sorting by ip packet keys  */
int cmpip  (const void *ai, const void *bi) {

	helem_t *ah = *(helem_t **) ai;
	helem_t *bh = *(helem_t **) bi;
	return memcmp (ah->key, bh->key, 13);
}


int get_packetoffset (int DataLinkType) {
	int PacketOffset;
	switch (DataLinkType) {
		case DLT_EN10MB:
		case DLT_IEEE802:
			PacketOffset = POFF_ETH;
			break;
		case DLT_PPP:
			PacketOffset = POFF_PPP;
			break;
		case DLT_RAW:
			PacketOffset = POFF_RAW;
			break;
		case DLT_NULL:
			PacketOffset = POFF_NULL;
			break;
		/*  Currently only know ethernet, ppp, for others we guess  */
		default:
			PacketOffset = 0;
	}
	return PacketOffset;
}



void parse_ip_range (char *arg_in, int **iplist, int *niplist) {
	char *arg_cpy = (char *) malloc (strlen(arg_in)+1);
	char *ipstr   = (char *) malloc (strlen(arg_in)+1);
	char *netstr  = (char *) malloc (strlen(arg_in)+1);
	char *range1  = NULL;
	char *range2  = NULL;
	int  nrange;
	int  mask;
	int  net;
	int  ip1, ip2;
	int  n;
	char *p;

	/*  Free iplist if previously allocated 
	 *  (should be initialized to NULL at program load)
	 */
	if (*iplist) free(*iplist);
	*iplist = NULL;

	/*  Count number of ranges (equals number of : + 1 )  */
	p = arg_in;
	n = 1;
	while (*p++) {
		if (*p==':') n++;
	}

	/*  allocate storage  */
	*iplist = (int *) malloc (2 * n * sizeof(int));
	if (*iplist==NULL) {
		*niplist = 0;
		return;
	}

	strcpy  (arg_cpy, arg_in);
	range2 = arg_cpy;

	/*  break string into separate ranges  */
	*niplist = 0;
	while (NULL!=range2) {

		/*  Break arg into (1st range):(remaining ranges)  */
		range1 = range2;
		range2 = strchr(range1, ':');
		if (NULL!=range2) *range2++ = '\0';


		/*  Look for range expressed as (lo ip)-(hi ip)  */
	 	if (2==sscanf (range1, "%[0-9.]-%[0-9.]", ipstr, netstr)) {
			str2ip(ipstr,  &ip1, &mask);
			str2ip(netstr, &ip2, &mask);

		/*  break range into (ip)/(net)  */
		} else if (2==sscanf (range1, "%[0-9.]/%[0-9]", ipstr, netstr)) {

			/*  read ip address  */
			str2ip (ipstr, &ip1, &mask);

			net = atoi(netstr);
			if (net<0) net=0;
			else if (net>32) net=32;
			mask = 0xffffffff >> net;
			if (mask==-1) mask = 0;
			ip2 = ip1 | mask;

		/*  Look for single ip address  */
		} else if (sscanf (range1, "%[0-9.].%[0-9].", ipstr, netstr)) {
			str2ip (ipstr, &ip1, &mask);
			ip2 = ip1 | mask;

		/*  Bad input format  */
		} else {
			fprintf (stderr, "ERROR:  Cannot read network range argument (-l option).\n");
			fprintf (stderr, "  Program continues with using default network range.\n");
			*niplist = 0;
			if (NULL!=*iplist) free (*iplist);
			return;
		}

		/* Store results  */
		(*iplist)[(*niplist)++] = ip1;
		(*iplist)[(*niplist)++] = ip2;

	} 


	free (netstr);
	free (ipstr);
	free (arg_cpy);


	/*  Correct double counting of niplist  */
	*niplist /= 2;


	/*  Print ip range  */
	if (debug_m) {
		/*  Print network ranges  */
		int i;
		printf ("\nIP Range\n");
		for (i=0;i<*niplist;i++) {
			printf ("%15s ",  ip2str((*iplist)[2*i  ]));
			printf ("%15s\n", ip2str((*iplist)[2*i+1]));
		}
		printf ("\n");
	}

}


/*  Determine if ipaddresses is within one of the ranges in iplist  */
int in_iprange (int ip, int *iplist, int niplist) {
	int i;
	for (i=0;i<2*niplist;i+=2) 
		if (ip>=iplist[i] && ip<=iplist[i+1])   return 1;
	return 0;
}


/*  Convert strings like "138.99.201.5" or "137.99.26" to int ipaddress  */
void str2ip (char *ipstr, int *ipout, int *mask) {
	int ip[4];
	int n = sscanf (ipstr, "%d.%d.%d.%d", ip, ip+1, ip+2, ip+3);
	int i;
	*ipout = 0;
	for (i=0;i<4;i++) {
		*ipout = *ipout<<8;
		if (i<n) *ipout |= (ip[i] & 0xff);
	}
	*mask = 0xffffffff >> (8*n);

	/* for reasons unknown 0xffffffff >> 32 -> -1, so set to 0  */
	if (*mask==-1)  *mask=0;
}


char *ip2str (int ip) {
	static char buffer[255];
	int p[4];
	int i;
	for (i=0;i<4;i++) {
		p[i] = ip & 0xff;
		ip >>= 8;
	}
	sprintf (buffer, "%d.%d.%d.%d", p[3], p[2], p[1], p[0]);
	return buffer;
}


/*  Split string of names separated by SPLIT_CHAR  */
#define SPLIT_CHAR " :"
void split (char *instr, char ***list, int *nlist) {
	char *s1, *s2;
	int n = 0;
	char *str = strdup(instr);
	/*  No input string  */
	if (*str=='\0') {
		*list = NULL;
		*nlist = 0;
		return;
	}
	/*  Count number of SPLIT_CHAR in string  */
	s1 = str;
	n=1;
	while (s2=strpbrk(s1,SPLIT_CHAR)) {
		n++;
		s1 = s2+1;
	}
	/*  Break string into substrings and store beginning  */
	*list = malloc (n*sizeof(char *));
	s1 = str;
	(*list)[0] = s1;
	n=1;
	while (s2=strpbrk(s1,SPLIT_CHAR)) {
		*(s2++) = '\0';
		(*list)[n++] = s2;
		s1  = s2+1;
	}
	*nlist = n;		
}

/*  Read options from command line  */
void read_options (int argc, char *argv[]) {
	char optchar;
	while (-1 != (optchar=getopt(argc,argv,"CGF:HI:L:MN:SW:abc:dei:f:g:l:mo:p:Pqr:s:tx:w:v:D:"))) {
		switch (optchar) {
		case '?':
			exit(1);
		case 'v':
			printf ("%s (compiled %s)\n", VERSION_STR, __DATE__);
			printf ("libpcap version %s\n", pcap_version);
			printf ("Default number of hash slots = %d\n", N_HASH_SLOTS);
			exit(0);
		/*  Output packet statistics in binary file format  */
		case 'b':
			outputbin_m = TRUE;
			break;
		/*  Debugging option  */
		case 'd':
			printf ("%s (compiled %s)\n", VERSION_STR, __DATE__);
			printf ("libpcap version %s\n", pcap_version);
			printf ("Default number of hash slots = %d\n", N_HASH_SLOTS);
			debug_m = TRUE;
			break;
		/*  Print ethernet addresses if present  */
		case 'e':
			printeth_m = TRUE;
			break;
		/*  Get pcap filter string  */
		case 'f':
			filtercmd_m = strdup (optarg);
			break;
		/*  Read config file  */
		case 'g':
			set_defaults();
			read_config(optarg);
			break;
		/*  Ignore config file  */
		case 'G':
			set_defaults();
			break;
		/*  Write pid file  */
		case 'i':
			pidfile_m = fopen(optarg,"wt");
			if (NULL==pidfile_m) {
				fprintf (stderr, "ERROR:  Cannot open pidfile_m '%s'\n", optarg);
				exit(1);
			}
			fprintf (pidfile_m, "%d\n", getpid());
			fclose (pidfile_m);
			break;
		case 'x':
			progfile_m = strdup(optarg);
			break;
		case 'r':
			readfile_m = strdup(optarg);
			break;
		case 'w':
			writefile_m = strdup(optarg);
			break;
		case 'c':
			maxpkt_m    = atoi(optarg);
			break;
		case 'o':
			outfile_m   = strdup(optarg);
			break;
		case 'm':
			promisc_m   = 0;
			break;
		case 'p':
			parse_portstr(strdup(optarg));
			break;
		case 'P':
			probename   = TRUE;
			break;
		case 'q':
			dbf_output   = TRUE;
			break;
		case 's':
			nlen_m = atoi(optarg);
			if (nlen_m<PLEN_MIN)  
				nlen_m = PLEN_MIN;
			break;
		case 'l':
			parse_ip_range (optarg, &iplist_m, &niplist_m);
			break;
		case 't':
			write_time_m = TRUE;
			break;
		case 'C':
			useicmptype_m = TRUE;
			break;
		case 'D':
			if (atoi(optarg) == 0) {
				standalone_m = FALSE;
			} else {
				standalone_m = TRUE;
				dump_period_m = atoi(optarg);
			}
			break;
		case 'F':
			if (atoi(optarg) == 0) {
				fork_m = FALSE;
			} else {
				fork_m = TRUE;
				dump_period_m = atoi(optarg);
			}
			break;
		case 'H':
			hostonly_m = TRUE;
			break;
		case 'I':
			sscanf (optarg, "%u.%u.%u.%u", &ip_m[0],&ip_m[1],&ip_m[2],&ip_m[3]);
			break;
		case 'L':
			sscanf (optarg, "%d,%d", &hostportlimit_m, &hostlimit_m);
			uselimit_m = TRUE;
			break;
		case 'N':
			nhashslots_m = atoi(optarg);
			if (nhashslots_m<1) {
				printf (
				"ERROR: Number of hash slots (-N%d) must be one or greater\n", 
				nhashslots_m);
				exit(1);
			}
			break;
		case 'M':
			allow_duplicate_m = 1;
			break;
		case 'S':
			printshort_m = TRUE;
			break;
		case 'W':
			ndump_limit_m = atoi(optarg);
			break;
		default:
			exit(1);
		}
	}
}


/*  Test for space *OR* equals sign 
 *  (to allow shell scripts lines like TERM=vt1000 to be used as config
 *  files
 *  */
int is_space(char c) {
	return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='=';
}

char *find_nonspace (char *str) {
	while (*str && is_space(*str)) str++;
	return str;
}

char *find_space (char *str) {
	while (*str && !is_space(*str)) str++;
	return str;
}


/*  Return pointers to first two space delimited tokens, 
 *  null terminating first token if necessary.
 *  If no first,second token then pointers point to '\0'
 *  */
void get_two_tok(char *str, char **tok1, char **tok2) {

	/*  Find start of first token  */
	str = find_nonspace(str);
	*tok1 = *tok2 = str;
	if (*str=='\0') return;

	/*  Find end of first token  */
	*tok2 = str = find_space (str);
	if (*str=='\0') return;

	/*  terminate first token  */
	*(str++) = '\0';

	/*  find second token   */
	*tok2 = find_nonspace(str);

	/*  Remove trailing space  */
	str = str + strlen(str) - 1;
	while (is_space(*str)) {
		str--;
	}
	*(++str) = '\0';
}
	

/*  Compare two strings ignoring case  */
int strcmpi (char *a, char *b) {
	int equal = 1;
	char c,d;
	while (equal && *a) {
		c = *a++;
		d = *b++;
		if ('a'<=c && c<='z') c += 'A' - 'a';
		if ('a'<=d && d<='z') d += 'A' - 'a';
		equal = (c==d);
	}
	if (equal) return 0;
	if (c<d)   return -1;
	return 1;
}

/*  Return true of string is yes, on, ok ignoring case  */
int is_true_str (char *str) {
	return 
		(! strcmpi("yes",str)) || 
		(! strcmpi("true",str)) ||
		(! strcmpi("on",str)) ||
		(! strcmpi("ok",str));
}



/*  Read options from config file  */
int read_config (char *filename) {
	FILE *fin = NULL;
	char buffer[512];
	char *str;
	char *key, *val;
	int  use_strict=0; /*  Turn config warnings on/off  */


	if (!strcmp("-",filename)) {
		fin = stdin;
	} else {
		fin = fopen (filename, "rt");
		if (NULL==fin)  return errno;
	}

	/*  Read lines from input file  */
	while (str=fgets(buffer, 512, fin)) {

		get_two_tok(str, &key, &val);

		/*  Test for comment  */
		if (*key=='#') continue;

		if (!strcmpi("debug",key)) {
			debug_m = is_true_str (val);
			if (debug_m) {
				printf ("%s (compiled %s)\n", VERSION_STR, __DATE__);
				printf ("libpcap version %s\n", pcap_version);
				printf ("Default number of hash slots = %d\n", N_HASH_SLOTS);
			}
		} else if (!strcmpi("strict",key)) {
			use_strict =  is_true_str(val);
		} else if (!strcmpi("ethernet",key)) {
			printeth_m = is_true_str(val);
		} else if (!strcmpi("filter",key)) {
			filtercmd_m = strdup(val);
		} else if (!strcmpi("pidfile",key)) {
			pidfile_m = fopen(val,"wt");
			if (NULL==pidfile_m) {
				fprintf (stderr, "ERROR:  Cannot open pidfile_m '%s'\n", val);
				exit(1);
			}
			fprintf (pidfile_m, "%d\n", getpid());
			fclose (pidfile_m);
		} else if (!strcmpi("progfile",key)) {
			progfile_m = strdup(val);
		} else if (!strcmpi("readfile",key)) {
			readfile_m = strdup(val);
		} else if (!strcmpi("writefile",key)) {
			writefile_m = strdup(val);
		} else if (!strcmpi("savefile",key)) {
			writefile_m = strdup(val);
		} else if (!strcmpi("count",key)) {
			maxpkt_m = atoi(val);
		} else if (!strcmpi("outfile",key)) {
			outfile_m = strdup(val);
		} else if (!strcmpi("promisc",key)) {
			promisc_m = is_true_str(val);
		} else if (!strcmpi("sql",key)) {
			dbf_output = is_true_str(val);
		} else if (!strcmpi("probename",key)) {
			probename = is_true_str(val);
		} else if (!strcmpi("saveport",key)) {
			parse_portstr(val);
		} else if (!strcmpi("packetlen",key)) {
			nlen_m = atoi(val);
			if (nlen_m<PLEN_MIN)  nlen_m = PLEN_MIN;
		} else if (!strcmpi("localrange",key)) {
			parse_ip_range (val, &iplist_m, &niplist_m);
		} else if (!strcmpi("writetime",key)) {
			write_time_m = is_true_str(val);
		} else if (!strcmpi("icmptype",key)) {
			useicmptype_m = is_true_str(val);
		} else if (!strcmpi("hostonly",key)) {
			hostonly_m = is_true_str(val);
		} else if (!strcmpi("hostip",key)) {
			sscanf (val, "%u.%u.%u.%u", &ip_m[0],&ip_m[1],&ip_m[2],&ip_m[3]);
		} else if (!strcmpi("hostportlimit",key)) {
			sscanf (val, "%d,%d", &hostportlimit_m, &hostlimit_m);
			uselimit_m = TRUE;
		} else if (!strcmpi("allowduplicate",key)) {
			allow_duplicate_m = TRUE;
		} else if (!strcmpi("hashslots",key)) {
			nhashslots_m = atoi(val);
			if (nhashslots_m<1) {
				printf (
				"ERROR: Number of hash slots (-N%d) must be one or greater\n", 
				nhashslots_m);
				exit(1);
			}
		} else if (!strcmpi("shortip",key)) {
			printshort_m = is_true_str(val);
		} else if (!strcmpi("interface",key)) {
			read_interface_str(val);
		} else if (!strcmpi("writepacketlimit",key)) {
			ndump_limit_m = atoi(val);
		} else if (!strcmpi("dumpperiod",key)) {
			if (atoi(val)>=0) {
				standalone_m = TRUE;
				dump_period_m = atoi(val);
			} else {
				standalone_m = FALSE;
			}
		} else if (!strcmpi("fork",key)) {
			if (atoi(val)>=0) {
				fork_m = TRUE;
				dump_period_m = atoi(val);
			} else {
				fork_m = FALSE;
			}
		} else if (use_strict) {
			fprintf (stderr, "ipaudit: Error reading ipaudit config file. ");
			fprintf (stderr, "   Unrecognized option: \"%s\"", key);
		}
	}

	if (fin!=stdin)  fclose(fin);
}


/*  read interface string  */
void read_interface_str (char *str) {
	/*  Split argument into interface names on ':' or ' '  */
	split (str, &pcapfilename_m, &npcapfile_m);
	/*  Allocate space for pcap file storage  */
	pcapfile_m   = malloc (npcapfile_m * sizeof(pcap_t *));
	/*  Allocate space for pcap file storage  */
	pcapoffset_m = malloc (npcapfile_m * sizeof(int     ));
}

void open_interface (void) {
	int i;
	struct bpf_program  fcode;
	char   ebuf[PCAP_ERRBUF_SIZE];

	/*   Open all files  */
	for (i=0;i<npcapfile_m;i++) {
		pcapfile_m[i] = 
			pcap_open_live(pcapfilename_m[i], nlen_m, promisc_m, 1000, ebuf);
		if (pcapfile_m[i]==NULL) {
			printf("ipaudit: Trouble opening <%s>, msg=\"%s\"  (%s)\n", 
					pcapfilename_m[i], ebuf, "Do you need root?");
			exit(1);
		}

		/*  Find packet offset  */
		pcapoffset_m[i] = get_packetoffset(pcap_datalink(pcapfile_m[i]));

		/*  Apply user requested packet filter code */
		if (pcap_compile(pcapfile_m[i], &fcode, filtercmd_m, 0, 0) < 0)
			printf("compile: %s", pcap_geterr(pcapfile_m[i]));
		if (pcap_setfilter(pcapfile_m[i], &fcode) < 0)
			printf("setfilter:  %s", pcap_geterr(pcapfile_m[i]));

		/*  Problem with pcap_setfilter?  Sets error, unset here  */
		errno = 0;
	}
}


/*  Set all options to default values (not implemented yet)  */
#define FREE(P) if ((P)!=NULL) { free(P); (P)=NULL; }
void set_defaults(void) {
	int i;

	FREE(prots_m)
	FREE(tcp_ports_m)
	FREE(udp_ports_m)
   
   
	/*  Flag for writing connection time in output  */
	write_time_m = FALSE;
	/*  Flag for printing ethernet addresses  */
	printeth_m	= FALSE;
	/*  Flag for printing IP addresses in short format  */
	printshort_m = FALSE;
   
	debug_m     = FALSE;

   
	/*  Pcap input file  */
	if (pcapfilename_m)
	for (i=0;i<npcapfile_m;i++) {
		FREE(pcapfilename_m[i])
		}
	FREE (pcapfilename_m)
	FREE (pcapfile_m)
	FREE (pcapfiletype_m)
	FREE (pcapoffset_m)
   npcapfile_m  = 0;
   
   npkt_m = 0;      /*  Number of    packets  */
   nippkt_m  = 0;   /*  Number of ip packets  */
   nconn_m   = 0;   /*  Number of connections */
   
   /*  IP address range for sorting  */
	FREE(iplist_m)
   niplist_m = 0;
   
   /*  Variables for input options  */
   outputbin_m		= FALSE;
   standalone_m		= FALSE;
   promisc_m       = 1;          /*  Default, set promiscuius mode */
	FREE(pidfile_m)
   FREE(progfile_m)
   FREE(writefile_m)
   FREE(readfile_m)
   FREE(outfile_m)
   maxpkt_m        = 0;
   hostonly_m      = FALSE;
   uselimit_m      = FALSE;
   useicmptype_m   = FALSE;
   hostportlimit_m = 0;
   hostlimit_m     = 0;
   nlen_m          = PLEN_DEF;   /*  Packet length to dump  */
	filtercmd_m     = "";
   filtercmd_m    = "";
   nhashslots_m    = N_HASH_SLOTS;
	allow_duplicate_m = 0;
   
   ip_m[0]       = 0;
}

