/**************************************************************************** ** ** File: file.c ** ** Author: Mike Borella ** ** Comments: Support for various trace file formats. Currently ** we support: ** - Libpcap ** - Sun Snoop ** ** $Id: file.c,v 1.4 2002/01/03 00:04:01 mborella Exp $ ** ** 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 Library 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. ** *****************************************************************************/ #include #include "file.h" #include "error.h" #include "parse_cl.h" #include "ipgrab.h" #include "datalink.h" #include "utilities.h" #define FILE_PCAP_SIG 0xa1b2c3d4 #define FILE_PCAP_SIG2 0xa1b2cd34 #define FILE_SNOOP_SIG1 0x736e #define FILE_SNOOP_SIG2 0x6f6f #define FILE_SNOOP_SIG3 0x7000 #define FILE_SNOOP_SIG4 0x0000 /* * Trace file formats */ typedef enum file_ { FILE_TYPE_NULL = 0, /* No file type yet assigned */ FILE_TYPE_PCAP, /* Libpcap */ FILE_TYPE_SNOOP, /* Sun Snoop */ FILE_TYPE_UNKNOWN /* File type unknown */ } file_t; /* * Snoop packet header format * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Original Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Included Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Packet Record Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Cumulative Drops | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Timestamp Seconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Timestamp Microseconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ typedef struct snoop_packet_header { u_int32_t orig_len; u_int32_t inc_len; u_int32_t rec_len; u_int32_t drops; u_int32_t ts_sec; u_int32_t ts_usec; } snoop_packet_header_t; static file_t file_type = FILE_TYPE_NULL; static char * pcap_cmd; static pcap_t * pd; static struct bpf_program fcode; static char errorbuf[PCAP_ERRBUF_SIZE]; static int mylink; static pcap_dumper_t * p; static FILE * fp; extern struct arg_t * my_args; /*---------------------------------------------------------------------------- ** ** file_id() ** ** Attempt to identify a file format by looking at the first few bytes. ** Most trace file formats begin with a distinctive signature. ** Libpcap: a1b3 c3d4 (1 32 bit little endian word) ** Sun Snoop: 736e 6f6f 7000 0000 0000 (5 16 bit big endian words) ** ** This function returns 1 on successful identification and 0 otherwise. ** **---------------------------------------------------------------------------- */ int file_id(char *s) { FILE * fp; u_int32_t word32; u_int16_t word16[4]; int ret; /* Open the file */ fp = fopen(s, "r"); if (fp == NULL) error_system("can't open %s", s); /* Check against libpcap signature */ ret = fread((void *) &word32, 4, 1, fp); if (ret < 1) error_fatal("can't read file format signature from %s", s); #ifdef WORDS_BIGENDIAN reverse_byte_order((u_int8_t *) &word32, 4); #endif if (word32 == FILE_PCAP_SIG || word32 == FILE_PCAP_SIG2) { file_type = FILE_TYPE_PCAP; fclose (fp); return 1; } /* Check against Sun Snoop signature (first 3 bytes should be enough) */ rewind(fp); /* Go to beginning of the file */ ret = fread((void *) &word16, 2, 4, fp); if (ret < 4) error_fatal("can't read file format signature from %s", s); if (ntohs(word16[0]) == FILE_SNOOP_SIG1 && ntohs(word16[1]) == FILE_SNOOP_SIG2 && ntohs(word16[2]) == FILE_SNOOP_SIG3 && ntohs(word16[3]) == FILE_SNOOP_SIG4) { file_type = FILE_TYPE_SNOOP; fclose (fp); return 1; } /* Can't ID the file, return an error */ file_type = FILE_TYPE_UNKNOWN; fclose(fp); return 0; } /*---------------------------------------------------------------------------- ** ** file_open() ** ** Open an ID'd file ** **---------------------------------------------------------------------------- */ int file_open(char *s) { fprintf(stderr, "Reading from file %s", s); switch (file_type) { case FILE_TYPE_PCAP: { fprintf(stderr, " (libpcap)\n"); pd = pcap_open_offline(s, errorbuf); if (pd == NULL) error_fatal("%s", errorbuf); /* Compile command line filter spec info fcode FSM */ if (pcap_compile(pd, &fcode, pcap_cmd, 0, 0) < 0) error_fatal("pcap_compile: %s", pcap_geterr(pd)); /* Set the pcap filter with our fcode FSM. That should do it... */ if (pcap_setfilter(pd, &fcode) < 0) error_fatal("pcap_setfilter: %s", pcap_geterr(pd)); /* Get the data link type */ mylink = pcap_datalink(pd); if (mylink < 0) error_fatal("pcap_datalink: %s", pcap_geterr(pd)); switch(mylink) { case DLT_NULL: fprintf(stderr,"(loopback)\n"); break; case DLT_EN10MB: fprintf(stderr, "(ethernet)\n"); break; case DLT_SLIP: fprintf(stderr, "(slip)\n"); break; #ifdef DLT_RAW /* Not supported in some arch or older pcap versions */ case DLT_RAW: fprintf(stderr, "(raw)\n"); break; #endif case DLT_PPP: fprintf(stderr, "(ppp)\n"); break; default: error_fatal("\n cannot handle data link type %d", mylink); } /* Open the file for writing if -w is used */ if (my_args->w) { p = pcap_dump_open(pd, my_args->w); if (p == NULL) error_system("pcap_dump_open: %s", pcap_geterr(pd)); } /* Return to the caller w/o reading a packet */ return 1; } break; case FILE_TYPE_SNOOP: { u_int32_t version; int ret; /* Open the file */ fp = fopen(s, "r"); if (fp == NULL) error_system("can't open %s", s); /* Get the first four bytes which are the signature, throw them out */ fseek(fp, 8, SEEK_SET); /* Get the version number */ ret = fread((void *) &version, 4, 1, fp); if (ret < 1) error_fatal("can't read version number from %s", s); version = ntohl(version); fprintf(stderr, " (snoop v%d)\n", version); return 1; } break; default: error_fatal("trying to open invalid file %s", my_args->r); } return 0; } /*---------------------------------------------------------------------------- ** ** file_read() ** ** Read packets from a file. The cnt parameter is the number of packets to ** read. ** **---------------------------------------------------------------------------- */ int file_read(int cnt) { switch (file_type) { case FILE_TYPE_PCAP: { /* Make sure that the file is open */ if (pd == NULL) error_fatal("trying to read from unopened file %s", my_args->r); if (my_args->w) { /* Read the specified number of packets */ if (pcap_loop(pd, cnt, pcap_dump, (u_char *) p) < 0) error_fatal("pcap_loop: %s", pcap_geterr(pd)); } else { /* Read until cnt packets read */ if (pcap_loop(pd, cnt, (pcap_func_t) datalink_pcap, (u_char *) &mylink) < 0) error_fatal("pcap_loop: %s", pcap_geterr(pd)); } } break; case FILE_TYPE_SNOOP: { u_int32_t linktype; int ret; snoop_packet_header_t header; u_int8_t * packet; int pad_len; struct timeval tv; /* Make sure that the file is open */ if (fp == NULL) error_fatal("trying to read from unopened file %s", my_args->r); /* Read the datalink type */ ret = fread((void *) &linktype, 4, 1, fp); if (ret < 1) error_fatal("can't read link type from %s", my_args->r); linktype = ntohl(linktype); /* Translate the link type to local definitions */ switch(linktype) { case 4: linktype = DATALINK_TYPE_ETHERNET; break; default: /* We really can handle linktypes better but I'm lazy */ error_fatal("unsupported snoop link type %d", linktype); } /* Loop to read individual packets */ while(1) { /* Read the header */ ret = fread((void *) &header, sizeof(snoop_packet_header_t), 1, fp); if (ret < 1) { if (feof(fp)) break; else error_fatal("can't read packet header from %s", my_args->r); } /* Do conversions */ header.orig_len = ntohl(header.orig_len); header.inc_len = ntohl(header.inc_len); header.rec_len = ntohl(header.rec_len); header.drops = ntohl(header.drops); header.ts_sec = ntohl(header.ts_sec); header.ts_usec = ntohl(header.ts_usec); /* Get the actual packet */ packet = my_malloc(header.inc_len+1); ret = fread((void *) packet, header.inc_len, 1, fp); if (ret < 1) { if (feof(fp)) break; else error_fatal("can't read packet from %s", my_args->r); } /* Calculate the pad length, then move the pointer */ pad_len = header.rec_len - header.inc_len - 24; fseek(fp, pad_len, SEEK_CUR); /* Set up the timeval */ tv.tv_sec = header.ts_sec; tv.tv_usec = header.ts_usec; /* Call the datalink() function */ datalink(linktype, tv, header.inc_len, header.inc_len, packet); /* Deallocate memory */ my_free(packet); } return 1; } break; default: error_fatal("trying to read invalid file %s", my_args->r); } return 0; }