/****************************************************************************
**
** 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 <pcap.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1