/* * tcpdmerge - merge tcpdump files by timestamp */ /* * Copyright (C) 2001 KANAMARU Akira. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Version 1.0 (Alpha) */ /* if FreeBSD, define __STDC__ * if Solaris, undef __STDC__ * * for debug, define DEBUG */ #include #include #include #include #include #ifdef __STDC__ #include #else #include #endif #include #undef DEBUG char version[] = "1.0"; char *program_name; void error(const char *fmt, ...); void usage(void)__attribute__((volatile)); void merge_files(char file1_file_name[], char file2_file_name[], char write_file_name[]); void make_tmp_file( char filename[], pcap_t **pcap, struct pcap_pkthdr *hdr, const u_char **pkt ); void dump_remain_packet( pcap_dumper_t **dumper, pcap_t **file1_pcap, struct pcap_pkthdr *hdr, const u_char **pkt ); int compare_timestamp_less_than( struct timeval *t1, struct timeval *t2 ); extern char *optarg; extern int optind; int main(int argc, char **argv) { int op; register char *cp; char *write_file_name = "-"; /* default is stdout */ if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; while ((op = getopt(argc, argv, "w:")) != EOF) switch (op) { case 'w': write_file_name = optarg; break; default: usage(); /* NOTREACHED */ } if (! (optind + 2 == argc) ) error("two input file must be given"); #ifdef DEBUG printf("file1 file %s, last file %s\n", argv[optind], argv[optind+1]); #endif /* DEBUG */ if ( ! strcmp( write_file_name, "-" ) && isatty( fileno(stdout) ) ) error("stdout is a terminal; redirect or use -w"); /* start merge process */ merge_files(argv[optind], argv[optind+1], write_file_name); return 0; } void merge_files(char file1_file_name[], char file2_file_name[], char write_file_name[]) { struct pcap_pkthdr file1_hdr, file2_hdr, write_hdr; pcap_t *file1_pcap, *file2_pcap, *write_pcap; const u_char *file1_pkt, *file2_pkt, *write_pkt; pcap_dumper_t *dumper; char tmp_file_name[] = ".tcpdmerge.tmp"; char errbuf[PCAP_ERRBUF_SIZE]; /* open file1 dump file */ file1_pcap = pcap_open_offline(file1_file_name, errbuf); if (! file1_pcap) error( "bad tcpdump file %s: %s", file1_file_name, errbuf ); /* open file2 dump file */ file2_pcap = pcap_open_offline(file2_file_name, errbuf); if (! file2_pcap) error( "bad tcpdump file %s: %s", file2_file_name, errbuf ); /* make "temporary dump file" which is include only one packet. * * pcap_open_offline() require real dump file, * so make "temporary dump file" and re-open. */ file1_pkt = pcap_next( file1_pcap, &file1_hdr ); file2_pkt = pcap_next( file2_pcap, &file2_hdr ); if ( compare_timestamp_less_than( &file1_hdr.ts, &file2_hdr.ts ) ) { make_tmp_file( tmp_file_name, &file1_pcap, &file1_hdr, &file1_pkt ); file1_pkt = pcap_next( file1_pcap, &file1_hdr ); } else { make_tmp_file( tmp_file_name, &file2_pcap, &file2_hdr, &file2_pkt ); file2_pkt = pcap_next( file2_pcap, &file2_hdr ); } /* re-open "temporary write dump file" */ write_pcap = pcap_open_offline(tmp_file_name, errbuf); if (! write_pcap) error( "bad tcpdump file %s: %s", write_file_name, errbuf ); /* open dumper */ dumper = pcap_dump_open(write_pcap, write_file_name); if ( ! dumper ) error( "error creating output file %s: ", write_file_name, pcap_geterr( write_pcap ) ); /* dump packet which is included "temporary write dump file" */ write_pkt = pcap_next( write_pcap, &write_hdr ); pcap_dump((u_char *) dumper, &write_hdr, write_pkt); for( ; ; ) { #ifdef DEBUG printf("file1 %ld.%ld, file2 %ld.%ld\n", (file1_hdr.ts).tv_sec, (file1_hdr.ts).tv_usec, (file2_hdr.ts).tv_sec, (file2_hdr.ts).tv_usec); #endif /* DEBUG */ if ( file1_pkt == 0 ) { if ( file2_pkt != 0 ) { #ifdef DEBUG printf("write remain packets which is included file2_file\n"); #endif /* DEBUG */ dump_remain_packet( &dumper, &file2_pcap, &file2_hdr, &file2_pkt ); } break; } if ( file2_pkt == 0 ) { if ( file1_pkt != 0 ) { #ifdef DEBUG printf("write remain packets which is included file1_file\n"); #endif /* DEBUG */ dump_remain_packet( &dumper, &file1_pcap, &file1_hdr, &file1_pkt ); } break; } if ( compare_timestamp_less_than( &file1_hdr.ts, &file2_hdr.ts ) ) { #ifdef DEBUG printf("write packet which is included file1\n"); #endif /* DEBUG */ pcap_dump((u_char *) dumper, &file1_hdr, file1_pkt); file1_pkt = pcap_next( file1_pcap, &file1_hdr ); } else { #ifdef DEBUG printf("write packet which is included file2\n"); #endif /* DEBUG */ pcap_dump((u_char *) dumper, &file2_hdr, file2_pkt); file2_pkt = pcap_next( file2_pcap, &file2_hdr ); } } pcap_dump_close( dumper ); pcap_close( file1_pcap ); pcap_close( file2_pcap ); } /* Returns true if timestamp t1 is chronologically less than timestamp t2. */ int compare_timestamp_less_than( struct timeval *t1, struct timeval *t2 ) { return t1->tv_sec < t2->tv_sec || (t1->tv_sec == t2->tv_sec && t1->tv_usec < t2->tv_usec); } /* make temporary file which include only one packet */ void make_tmp_file( char filename[], pcap_t **pcap, struct pcap_pkthdr *hdr, const u_char **pkt ) { pcap_dumper_t *dumper; /* open dumper */ dumper = pcap_dump_open(*pcap, filename); if ( ! dumper ) error( "error creating output file %s: ", filename, pcap_geterr( *pcap ) ); pcap_dump((u_char *) dumper, hdr, *pkt); pcap_dump_close( dumper ); } /* dump remain packet */ void dump_remain_packet( pcap_dumper_t **dumper, pcap_t **pcap, struct pcap_pkthdr *hdr, const u_char **pkt ) { for( ; ; ) { if ( *pkt == 0 ) { #ifdef DEBUG printf("finish write remain packets\n"); #endif /* DEBUG */ break; } #ifdef DEBUG printf("file1 %ld.%ld\n", (hdr->ts).tv_sec, (hdr->ts).tv_usec); #endif /* DEBUG */ pcap_dump((u_char *) *dumper, hdr, *pkt); *pkt = pcap_next( *pcap, hdr ); } } /* print error messages */ void error(const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "tcpdmerge: "); #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } usage(); exit(1); /* NOTREACHED */ } /* print usages */ void usage(void) { extern char version[]; (void)fprintf(stderr, "Version %s\n", version); (void)fprintf(stderr, "Usage: tcpdmerge [-w file] file1 file2\n"); exit(-1); }