Update from Scott Renfro to allow mergecap to merge multiple capture

files into one capture file.

svn path=/trunk/; revision=3714
This commit is contained in:
Guy Harris 2001-07-13 08:16:16 +00:00
parent e2a238b16b
commit 997940bcfe
2 changed files with 298 additions and 169 deletions

View File

@ -6,28 +6,28 @@ mergecap - Merges two capture files into one
=head1 SYNOPSYS =head1 SYNOPSYS
B<mergecap> B<mergecap>
S<[ B<-F> file format ]> S<[ B<-F> I<file format> ]>
S<[ B<-T> encapsulation type ]> S<[ B<-T> I<encapsulation type> ]>
S<[ B<-a> ]> S<[ B<-a> ]>
S<[ B<-v> ]> S<[ B<-v> ]>
S<[ B<-s> snaplen ]> S<[ B<-s> I<snaplen> ]>
S<[ B<-h> ]> S<[ B<-h> ]>
I<infile1> S<B<-w> I<outfile>>
I<infile2> I<infile>
I<outfile> I<...>
=head1 DESCRIPTION =head1 DESCRIPTION
B<Mergecap> is a program that reads two saved capture files and merges B<Mergecap> is a program that combines multiple saved capture files into
all of the packets in those capture files into a third capture a single output file specified by the B<-w> argument. B<Mergecap> knows
file. B<Mergecap> knows how to read B<libpcap> capture files, including how to read B<libpcap> capture files, including those of B<tcpdump>. In
those of B<tcpdump>. In addition, B<Mergecap> can read capture files addition, B<Mergecap> can read capture files from B<snoop> (including
from B<snoop> (including B<Shomiti>) and B<atmsnoop>, B<LanAlyzer>, B<Shomiti>) and B<atmsnoop>, B<LanAlyzer>, B<Sniffer> (compressed or
B<Sniffer> (compressed or uncompressed), Microsoft B<Network Monitor>, uncompressed), Microsoft B<Network Monitor>, AIX's B<iptrace>,
AIX's B<iptrace>, B<NetXray>, B<Sniffer Pro>, B<RADCOM>'s WAN/LAN B<NetXray>, B<Sniffer Pro>, B<RADCOM>'s WAN/LAN analyzer,
analyzer, B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, and B<Lucent/Ascend> router debug output, HP-UX's B<nettl>, and the dump
the dump output from B<Toshiba's> ISDN routers. There is no need to output from B<Toshiba's> ISDN routers. There is no need to tell
tell B<Mergecap> what type of file you are reading; it will determine the B<Mergecap> what type of file you are reading; it will determine the
file type by itself. B<Mergecap> is also capable of reading any of file type by itself. B<Mergecap> is also capable of reading any of
these file formats if they are compressed using gzip. B<Mergecap> these file formats if they are compressed using gzip. B<Mergecap>
recognizes this directly from the file; the '.gz' extension is not recognizes this directly from the file; the '.gz' extension is not
@ -43,12 +43,12 @@ SuSE Linux 6.3), B<snoop> format, uncompressed B<Sniffer> format,
Microsoft B<Network Monitor> 1.x format, and the format used by Microsoft B<Network Monitor> 1.x format, and the format used by
Windows-based versions of the B<Sniffer> software. Windows-based versions of the B<Sniffer> software.
By default, the packets in the input files are merged in chronological Packets from the input files are merged in chronological order based on
order based on each frame's timestamp, unless the B<-a> flag is each frame's timestamp, unless the B<-a> flag is specified. B<Mergecap>
specified. B<Mergecap> assumes that frames within a single capture file assumes that frames within a single capture file are already stored in
are already stored in chronological order. When the B<-a> flag is chronological order. When the B<-a> flag is specified, packets are
specified, all the packets from the first input capture file are output, copied directly from each input file to the output file, independent of
followed by all of the packets from the second input capture file. each frame's timestamp.
If the B<-s> flag is used to specify a snapshot length, frames in the If the B<-s> flag is used to specify a snapshot length, frames in the
input file with more captured data than the specified snapshot length input file with more captured data than the specified snapshot length
@ -75,6 +75,10 @@ fddi>' is specified).
=over 4 =over 4
=item -w
Sets the output filename.
=item -F =item -F
Sets the file format of the output capture file. Sets the file format of the output capture file.

View File

@ -1,6 +1,6 @@
/* Combine two dump files, either by appending or by merging by timestamp /* Combine two dump files, either by appending or by merging by timestamp
* *
* $Id: mergecap.c,v 1.1 2001/07/12 19:59:39 guy Exp $ * $Id: mergecap.c,v 1.2 2001/07/13 08:16:15 guy Exp $
* *
* Written by Scott Renfro <scott@renfro.org> based on * Written by Scott Renfro <scott@renfro.org> based on
* editcap by Richard Sharpe and Guy Harris * editcap by Richard Sharpe and Guy Harris
@ -34,20 +34,32 @@
#include "getopt.h" #include "getopt.h"
#endif #endif
#ifndef MAX
# define MAX(a,b) ((a)>(b))?(a):(b)
#endif
/* /*
* Some globals so we can pass things to various routines * Global variables
*/ */
static int count = 1;
static int out_file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
static int out_frame_type = -2; /* Leave frame type alone */
static int verbose = 0; /* Not so verbose */ static int verbose = 0; /* Not so verbose */
static int do_append = 0; /* append, don't merge */
static unsigned int snaplen = 0; /* No limit */
/*
* Structures to manage our files
*/
typedef struct in_file_t {
const char *filename;
wtap *wth;
int err;
int data_offset;
gboolean ok;
} in_file_t;
typedef struct out_file_t {
const char *filename;
wtap_dumper *pdh;
int file_type;
int frame_type;
unsigned int snaplen;
int count;
} out_file_t;
static out_file_t out_file;
/* /*
* Routine to write frame to output file * Routine to write frame to output file
@ -59,55 +71,55 @@ write_frame(u_char *user, const struct wtap_pkthdr *phdr, int offset,
wtap_dumper *pdh = (wtap_dumper*)user; wtap_dumper *pdh = (wtap_dumper*)user;
int err; int err;
struct wtap_pkthdr snap_phdr; struct wtap_pkthdr snap_phdr;
if (verbose) if (verbose)
printf("Record: %u\n", count++); printf("Record: %u\n", out_file.count++);
/* We simply write it, perhaps after truncating it; we could do other /* We simply write it, perhaps after truncating it; we could do other
* things, like modify it. */ * things, like modify it. */
if (snaplen != 0 && phdr->caplen > snaplen) { if (out_file.snaplen != 0 && phdr->caplen > out_file.snaplen) {
snap_phdr = *phdr; snap_phdr = *phdr;
snap_phdr.caplen = snaplen; snap_phdr.caplen = out_file.snaplen;
phdr = &snap_phdr; phdr = &snap_phdr;
} }
if (!wtap_dump(pdh, phdr, pseudo_header, buf, &err)) { if (!wtap_dump(pdh, phdr, pseudo_header, buf, &err)) {
fprintf(stderr, "editcap: Error writing to output: %s\n", fprintf(stderr, "mergecap: Error writing to %s: %s\n",
wtap_strerror(err)); out_file.filename, wtap_strerror(err));
exit(1); exit(1);
} }
} }
/* /*
* routine to append file2 to file1 * routine to concatenate files
*/ */
static void static void
append(wtap *wth_1, wtap *wth_2, wtap_dumper *pdh) append(int count, in_file_t in_files[], out_file_t *out_file)
{ {
int i;
int err; int err;
if (!wtap_loop(wth_1, 0, write_frame, (u_char*)pdh, &err)) { for (i = 0; i < count; i++) {
fprintf(stderr, "mergecap: Error appending: %s\n", if (!wtap_loop(in_files[i].wth, 0, write_frame,
wtap_strerror(err)); (u_char*)out_file->pdh, &err)) {
} fprintf(stderr, "mergecap: Error appending from %s to %s: %s\n",
if (!wtap_loop(wth_2, 0, write_frame, (u_char*)pdh, &err)) { in_files[i].filename, out_file->filename, wtap_strerror(err));
fprintf(stderr, "mergecap: Error appending: %s\n", }
wtap_strerror(err));
} }
} }
/* /*
* judges whether the first argument has an earlier * returns TRUE if first argument is earlier than second
* timestamp than the second
*/ */
static gboolean static gboolean
is_earlier(struct wtap_pkthdr *one, struct wtap_pkthdr *two) is_earlier(struct timeval *l, struct timeval *r) {
{ if (l->tv_sec > r->tv_sec) { /* left is later */
if (one->ts.tv_sec > two->ts.tv_sec) {
return FALSE; return FALSE;
} else if (one->ts.tv_sec < two->ts.tv_sec) { } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
return TRUE; return TRUE;
} else if (one->ts.tv_usec > two->ts.tv_usec) { } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
return FALSE; return FALSE;
} }
/* either one < two or one == two /* either one < two or one == two
@ -116,160 +128,248 @@ is_earlier(struct wtap_pkthdr *one, struct wtap_pkthdr *two)
return TRUE; return TRUE;
} }
/*
* returns index of earliest timestamp in set of input files
* or -1 if no valid files remain
*/
static int
earliest(int count, in_file_t in_files[]) {
int i;
int ei = -1;
struct timeval tv = {LONG_MAX, LONG_MAX};
for (i = 0; i < count; i++) {
struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
tv = phdr->ts;
ei = i;
}
}
return ei;
}
/* /*
* actually merge the files * actually merge the files
*/ */
static void static void
merge(wtap *wth_1, wtap *wth_2, wtap_dumper *pdh) merge(int count, in_file_t in_files[], out_file_t *out_file)
{ {
int err; int i;
int data_offset_1, data_offset_2, loop = 0; int data_offset_1, data_offset_2, loop = 0;
gboolean ok_1, ok_2; gboolean ok_1, ok_2;
ok_1 = wtap_read(wth_1, &err, &data_offset_1); /* prime the pump (read in first frame from each file) */
ok_2 = wtap_read(wth_2, &err, &data_offset_2); for (i = 0; i < count; i++) {
in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
&(in_files[i].data_offset));
}
while (ok_1 && ok_2) { /* now keep writing the earliest frame until we're out of frames */
while ( -1 != (i = earliest(count, in_files))) {
/* if wth_1 is earlier, then write it and fetch another
* otherwise, write wth_2 and increment it /* write out earliest frame, and fetch another from its
* input file
*/ */
if (is_earlier(wtap_phdr(wth_1), wtap_phdr(wth_2))) { write_frame((u_char*)out_file->pdh,
write_frame((u_char*)pdh, wtap_phdr(wth_1), data_offset_1, wtap_phdr(in_files[i].wth),
wtap_pseudoheader(wth_1), wtap_buf_ptr(wth_1)); in_files[i].data_offset,
ok_1 = wtap_read(wth_1, &err, &data_offset_1); wtap_pseudoheader(in_files[i].wth),
} else{ wtap_buf_ptr(in_files[i].wth));
write_frame((u_char*)pdh, wtap_phdr(wth_2), data_offset_2, in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
wtap_pseudoheader(wth_2), wtap_buf_ptr(wth_2)); &(in_files[i].data_offset));
ok_2 = wtap_read(wth_2, &err, &data_offset_2);
}
}
while (ok_1) {
write_frame((u_char*)pdh, wtap_phdr(wth_1), data_offset_1,
wtap_pseudoheader(wth_1), wtap_buf_ptr(wth_1));
ok_1 = wtap_read(wth_1, &err, &data_offset_1);
}
while (ok_2) {
write_frame((u_char*)pdh, wtap_phdr(wth_2), data_offset_2,
wtap_pseudoheader(wth_2), wtap_buf_ptr(wth_2));
ok_2 = wtap_read(wth_2, &err, &data_offset_2);
} }
} }
/* /*
* routine to open the input and output files, then call the merge * Close the output file
*/ */
static void static void
merge_files(const char *from1, const char *from2, const char *into) close_outfile(out_file_t *out_file)
{ {
int err; int err;
wtap *wth_1, *wth_2; if (!wtap_dump_close(out_file->pdh, &err)) {
wtap_dumper *pdh; fprintf(stderr, "mergecap: Error closing file %s: %s\n",
out_file->filename, wtap_strerror(err));
/* open the input files */
wth_1 = wtap_open_offline(from1, &err, FALSE);
if (!wth_1) {
fprintf(stderr, "mergecap: Can't open %s: %s\n", from1,
wtap_strerror(err));
exit(1);
}
wth_2 = wtap_open_offline(from2, &err, FALSE);
if (!wth_2) {
fprintf(stderr, "mergecap: Can't open %s: %s\n", from2,
wtap_strerror(err));
wtap_close(wth_1);
exit(1);
}
if (verbose) {
fprintf(stderr, "File %s is a %s capture file.\n", from1,
wtap_file_type_string(wtap_file_type(wth_1)));
fprintf(stderr, "File %s is a %s capture file.\n", from2,
wtap_file_type_string(wtap_file_type(wth_2)));
}
/* open the output file */
if (out_frame_type == -2)
out_frame_type = wtap_file_encap(wth_1);
pdh = wtap_dump_open(into, out_file_type, out_frame_type,
MAX(wtap_snapshot_length(wth_1),
wtap_snapshot_length(wth_2)), &err);
if (!pdh) {
fprintf(stderr, "mergecap: Can't open/create %s: %s\n", into,
wtap_strerror(err));
wtap_close(wth_1);
wtap_close(wth_2);
exit(1);
}
if (do_append)
append(wth_1, wth_2, pdh);
else
merge(wth_1, wth_2, pdh);
wtap_close(wth_1);
wtap_close(wth_2);
if (!wtap_dump_close(pdh, &err)) {
fprintf(stderr, "mergecap: Error writing to %s: %s\n", into,
wtap_strerror(err));
exit(1); exit(1);
} }
} }
void usage()
/*
* Open the output file
*
* Return FALSE if file cannot be opened (so caller can clean up)
*/
static gboolean
open_outfile(out_file_t *out_file, int snapshot_len)
{
int err;
if (!out_file) {
fprintf(stderr, "mergecap: internal error (null out_file)\n");
exit(1);
}
out_file->pdh = wtap_dump_open(out_file->filename, out_file->file_type,
out_file->frame_type, snapshot_len, &err);
if (!out_file->pdh) {
fprintf(stderr, "mergecap: Can't open/create %s: %s\n",
out_file->filename, wtap_strerror(err));
return FALSE;
}
return TRUE;
}
/*
* Scan through input files and find maximum snapshot length
*/
static int
max_snapshot_length(int count, in_file_t in_files[])
{
int i;
int max_snapshot = 0;
for (i = 0; i < count; i++) {
if (wtap_snapshot_length(in_files[i].wth) > max_snapshot)
max_snapshot = wtap_snapshot_length(in_files[i].wth);
}
return max_snapshot;
}
/*
* Scan through and close each input file
*/
static void
close_in_files(int count, in_file_t in_files[])
{
int i;
for (i = 0; i < count; i++) {
wtap_close(in_files[i].wth);
}
}
/*
* Scan through the arguments and open the input files
*/
static int
open_in_files(int argc, char *argv[], in_file_t *in_files[])
{
int i;
int count = 0;
int err;
in_file_t *files;
int files_size = argc * sizeof(in_file_t);
files = malloc(files_size);
if (!files) {
fprintf(stderr, "mergecap: error allocating %d bytes of memory\n",
files_size);
exit(1);
}
*in_files = files;
for (i = 0; i < argc; i++) {
files[count].filename = argv[i];
files[count].wth = wtap_open_offline(argv[i], &err, FALSE);
files[count].err = 0;
files[count].data_offset = 0;
files[count].ok = TRUE;
if (!files[count].wth) {
fprintf(stderr, "mergecap: skipping %s: %s\n", argv[i],
wtap_strerror(err));
} else {
if (verbose) {
fprintf(stderr, "File %s is a %s capture file.\n", argv[i],
wtap_file_type_string(wtap_file_type(files[count].wth)));
}
count++;
}
}
if (verbose)
fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
argc);
return count;
}
/*
* Show the usage
*/
static void
usage()
{ {
int i; int i;
const char *string; const char *string;
fprintf(stderr, "Usage: mergecap [-v] [-a] [-s <snaplen>]\n"); fprintf(stderr, "Usage: mergecap [-h] [-v] [-a] [-s <snaplen>] [-T <encap type>]\n");
fprintf(stderr, " [-T <encap type>] [-F <capture type>]\n"); fprintf(stderr, " [-F <capture type>] -w <outfile> <infile> [...]\n\n");
fprintf(stderr, " <infile1> <infile2> <outfile>\n\n"); fprintf(stderr, " where\t-h produces this help listing.\n");
fprintf(stderr, " where\t-a infile2 should be appended, not merged.\n");
fprintf(stderr, " \t Default merges based on frame timestamps.\n");
fprintf(stderr, " \t-s <snaplen> truncate packets to <snaplen>\n");
fprintf(stderr, " \t bytes of data\n");
fprintf(stderr, " \t-v verbose operation, default is silent\n"); fprintf(stderr, " \t-v verbose operation, default is silent\n");
fprintf(stderr, " \t-h produces this help listing.\n"); fprintf(stderr, " \t-a files should be concatenated, not merged\n");
fprintf(stderr, " \t Default merges based on frame timestamps\n");
fprintf(stderr, " \t-s <snaplen>: truncate packets to <snaplen> bytes of data\n");
fprintf(stderr, " \t-w <outfile>: sets output filename to <outfile>\n");
fprintf(stderr, " \t-T <encap type> encapsulation type to use:\n"); fprintf(stderr, " \t-T <encap type> encapsulation type to use:\n");
for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) { for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
string = wtap_encap_short_string(i); string = wtap_encap_short_string(i);
if (string != NULL) if (string != NULL)
fprintf(stderr, " \t %s - %s\n", fprintf(stderr, " \t %s - %s\n",
string, wtap_encap_string(i)); string, wtap_encap_string(i));
} }
fprintf(stderr, " \t default is the same as the first input file\n"); fprintf(stderr, " \t default is the same as the first input file\n");
fprintf(stderr, " \t-F <capture type> capture file type to write:\n"); fprintf(stderr, " \t-F <capture type> capture file type to write:\n");
for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) { for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
if (wtap_dump_can_open(i)) if (wtap_dump_can_open(i))
fprintf(stderr, " \t %s - %s\n", fprintf(stderr, " \t %s - %s\n",
wtap_file_type_short_string(i), wtap_file_type_string(i)); wtap_file_type_short_string(i), wtap_file_type_string(i));
} }
fprintf(stderr, " \t default is libpcap\n"); fprintf(stderr, " \t default is libpcap\n");
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
wtap *wth; int i;
int i, err;
extern char *optarg; extern char *optarg;
extern int optind; extern int optind;
char opt; int opt;
char *p; char *p;
gboolean do_append = FALSE;
int in_file_count = 0;
in_file_t *in_files = NULL;
/* initialize out_file */
out_file.filename = NULL;
out_file.pdh = NULL; /* wiretap dumpfile */
out_file.file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
out_file.frame_type = -2; /* leave type alone */
out_file.snaplen = 0; /* no limit */
out_file.count = 1; /* frames output */
/* Process the options first */ /* Process the options first */
while ((opt = getopt(argc, argv, "aT:F:vs:h")) != EOF) { while ((opt = getopt(argc, argv, "w:aT:F:vs:h")) != EOF) {
switch (opt) { switch (opt) {
case 'w':
out_file.filename = optarg;
break;
case 'a': case 'a':
do_append = !do_append; do_append = !do_append;
break; break;
case 'T': case 'T':
out_frame_type = wtap_short_string_to_encap(optarg); out_file.frame_type = wtap_short_string_to_encap(optarg);
if (out_frame_type < 0) { if (out_file.frame_type < 0) {
fprintf(stderr, "mergecap: \"%s\" is not a valid encapsulation type\n", fprintf(stderr, "mergecap: \"%s\" is not a valid encapsulation type\n",
optarg); optarg);
exit(1); exit(1);
@ -277,9 +377,9 @@ main(int argc, char *argv[])
break; break;
case 'F': case 'F':
out_file_type = wtap_short_string_to_file_type(optarg); out_file.file_type = wtap_short_string_to_file_type(optarg);
if (out_file_type < 0) { if (out_file.file_type < 0) {
fprintf(stderr, "editcap: \"%s\" is not a valid capture file type\n", fprintf(stderr, "mergecap: \"%s\" is not a valid capture file type\n",
optarg); optarg);
exit(1); exit(1);
} }
@ -290,9 +390,9 @@ main(int argc, char *argv[])
break; break;
case 's': case 's':
snaplen = strtol(optarg, &p, 10); out_file.snaplen = strtol(optarg, &p, 10);
if (p == optarg || *p != '\0') { if (p == optarg || *p != '\0') {
fprintf(stderr, "editcap: \"%s\" is not a valid snapshot length\n", fprintf(stderr, "mergecap: \"%s\" is not a valid snapshot length\n",
optarg); optarg);
exit(1); exit(1);
} }
@ -313,18 +413,43 @@ main(int argc, char *argv[])
} }
/* should now have three arguments left: infile1, infile2, outfile */ /* check for proper args; at a minimum, must have an output
#ifdef DEBUG * filename and one input file
printf("Optind = %i, argc = %i\n", optind, argc); */
#endif in_file_count = argc - optind;
if (!out_file.filename) {
if ((argc - optind) < 3) { fprintf(stderr, "mergecap: an output filename must be set with -w\n");
usage(); usage();
exit(1); exit(1);
} }
merge_files(argv[optind], argv[optind+1], argv[optind+2]); /* open the input files */
in_file_count = open_in_files(in_file_count, &argv[optind], &in_files);
if (in_file_count < 1) {
fprintf(stderr, "mergecap: No valid input files\n");
exit(1);
}
/* set the outfile frame type */
if (out_file.frame_type == -2)
out_file.frame_type = wtap_file_encap(in_files[0].wth);
/* open the outfile */
if (!open_outfile(&out_file, max_snapshot_length(in_file_count, in_files))) {
close_in_files(in_file_count, in_files);
exit(1);
}
/* do the merge (or append) */
if (do_append)
append(in_file_count, in_files, &out_file);
else
merge(in_file_count, in_files, &out_file);
close_in_files(in_file_count, in_files);
close_outfile(&out_file);
free(in_files);
return 0; return 0;
} }