/* ipaudit.c * * ipaudit - network traffic data gathering * By Jon Rifkin * 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 #include #include #include #include #include #include #include #include #include #include #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 /* 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); \ } #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; 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; U_CHAR ip_m[4] = ""; /* ------------------------------------------------------------------------ Local Function Prototypes ------------------------------------------------------------------------ */ void ihandler (int); void dum2 (pkt_struct_t *); void dum1 (struct pcap_pkthdr *); void dum3 (hlist_t **); int storepkt (struct pcap_pkthdr *, eth_struct_t *, ip_struct_t *, hlist_t **, int); void writepkttxt (hlist_t **, char *); void writepktbin (hlist_t **, char *); 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 */ hlist_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; /* 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 */ strncpy (config_name, getenv("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; } } /* 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 */ } /* Initialize hash table */ hconn = init_htable(N_HASH_SLOTS); /* 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); /* Initialize info for select() */ if (!readfile_m) { WRITEMSG FD_ZERO (&rdfs_init); WRITEMSG max_fd = 0; WRITEMSG for (i=0;imax_fd) max_fd=fd; WRITEMSG } 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 */ while (isig_m==0) { /* 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) 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;ibytesrcpt, 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) pcap_dump ((U_CHAR *) df, &pkthdr, raw_pkt_save); } no_dump: /* Select next file in list for checking */ if (!readfile_m) next_intf++; } /* Read tcpdump data */ /* Close files */ for (i=0;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); fprintf (stderr, "number of connections <%d>\n", getcount()); } #endif } /* 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, hlist_t **ha, 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; /* 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 */ if (! finddata(ha,(U_CHAR *)&key, keysize, (U_CHAR **)&data,&ndata) ) { datasize = sizeof(idata); addnode (ha, (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 */ void writepktbin (hlist_t **ha, char *outname) { hlist_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) ) outfile_m = fopen (outname, "wb"); if (NULL==outfile_m) { fprintf (stderr, "ERROR: Cannot open output file <%s>\n", outname); exit(1); } /* Walk list */ getfirst (ha); while ((t=getnext(ha))) { /* 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); } /* Retrieve and print packets from hash table in text format .. sort by time if writing it */ void writepkttxt (hlist_t **ha, char *outname) { hlist_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; hlist_t **conn = NULL; char eth1str[13], eth2str[13]; if (outname && strcmp("-",outname)) outfile_m = fopen (outname, "wt"); if (NULL==outfile_m) { fprintf (stderr, "ERROR: Cannot open output file <%s>\n", outname); exit(1); } /* Get number of connections */ nconn = getcount(); conn = (hlist_t **) calloc (nconn, sizeof(hlist_t *)); if (NULL==conn) { fprintf (stderr, "ERROR: Cannot allocate memory for connection index\n"); exit(1); } /* Make pointer list */ getfirst (ha); for (iconn=0;iconnkey[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 */ fprintf (outfile_m, "%s %s %u %u %u", ip2, ip1, prot, pt2, pt1); /* Data */ data = (data_t *) t->data; fprintf (outfile_m, " %d %d %d %d", data->nbyte2, data->nbyte1, data->npkt2, data->npkt1); } else { /* Print key info */ fprintf (outfile_m, "%s %s %u %u %u", ip1, ip2, prot, pt1, pt2); /* Data */ data = (data_t *) t->data; 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) fprintf (outfile_m, " %02d:%02d:%02d.%04d", hour,min,sec,msec); msec = data->time.last_time; HMS(hour,min,sec,msec) fprintf (outfile_m, " %02d:%02d:%02d.%04d", hour,min,sec,msec); /* 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) fprintf (outfile_m, " %s %s", eth2str, eth1str); else fprintf (outfile_m, " %s %s", eth1str, eth2str); } fprintf (outfile_m, "\n"); } /* Close file */ if (outname) fclose(outfile_m); /* reclaim storage */ if (NULL!=conn) free (conn); } 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 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 (" -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 ("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) { hlist_t *ah = *(hlist_t **) ai; hlist_t *bh = *(hlist_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) { hlist_t *ah = *(hlist_t **) ai; hlist_t *bh = *(hlist_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; *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> (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,"CGHI:L:MN:Sabc:dei:f:g:l:mo:p:r:s:tx:w:v"))) { 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 's': nlen_m = atoi(optarg); if (nlen_m, 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