diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:49:13 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:49:13 +0000 |
commit | f078b88a160c467761b3f3641f05dfd0aa3f7753 (patch) | |
tree | 1f058a53dd274694716ec00c288d27223606ee45 /usr.sbin/tcpdump | |
download | FreeBSD-src-f078b88a160c467761b3f3641f05dfd0aa3f7753.zip FreeBSD-src-f078b88a160c467761b3f3641f05dfd0aa3f7753.tar.gz |
Initial import, 0.1 + pk 0.2.4-B1
Diffstat (limited to 'usr.sbin/tcpdump')
-rw-r--r-- | usr.sbin/tcpdump/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/tcpdump/Makefile.inc | 3 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpslice/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpslice/gwtm2secs.c | 75 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpslice/search.c | 563 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpslice/tcpslice.1 | 260 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpslice/tcpslice.c | 645 |
7 files changed, 1569 insertions, 0 deletions
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile new file mode 100644 index 0000000..0a1f253 --- /dev/null +++ b/usr.sbin/tcpdump/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 0.1 (RGrimes) 4/4/93 + +SUBDIR= tcpdump tcpslice + +.include <bsd.subdir.mk> diff --git a/usr.sbin/tcpdump/Makefile.inc b/usr.sbin/tcpdump/Makefile.inc new file mode 100644 index 0000000..94899950 --- /dev/null +++ b/usr.sbin/tcpdump/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 + +BINDIR?= /usr/local/bin diff --git a/usr.sbin/tcpdump/tcpslice/Makefile b/usr.sbin/tcpdump/tcpslice/Makefile new file mode 100644 index 0000000..d1cd63b --- /dev/null +++ b/usr.sbin/tcpdump/tcpslice/Makefile @@ -0,0 +1,18 @@ +# @(#)Makefile 0.1 (RWGrimes) 3/24/93 + +PROG= tcpslice +CFLAGS+=-DCSLIP -I. -I$(.CURDIR)/../tcpdump +MAN1= tcpslice.0 +SRCS= version.c tcpslice.c gwtm2secs.c search.c \ + savefile.c bpf_filter.c md.c util.c +.PATH: ${.CURDIR}/../tcpdump /sys/net +CLEANFILES+= version.c version.h + +version.c version.h: $(.CURDIR)/../tcpdump/VERSION + rm -f version.c ; \ + sed 's/.*/char version[] = "&";/' $(.CURDIR)/../tcpdump/VERSION > version.c + set `sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \2/' $(.CURDIR)/../tcpdump/VERSION` ; \ + { echo '#define VERSION_MAJOR' $$1 ; \ + echo '#define VERSION_MINOR' $$2 ; } > version.h + +.include <bsd.prog.mk> diff --git a/usr.sbin/tcpdump/tcpslice/gwtm2secs.c b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c new file mode 100644 index 0000000..aeb377f --- /dev/null +++ b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#)$Header: gwtm2secs.c,v 1.1 92/06/02 11:35:19 mccanne Exp $ (LBL)"; +#endif + +/* + * gwtm2secs.c - convert "tm" structs for Greenwich time to Unix timestamp + */ + +#include <stdio.h> +#include <sys/types.h> +#include <time.h> + +static int days_in_month[] = + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +#define IS_LEAP_YEAR(year) \ + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + +time_t gwtm2secs( tm ) +struct tm *tm; + { + int i, days, year; + + year = tm->tm_year; + + /* Allow for year being specified with either 2 digits or 4 digits. + * 2-digit years are either 19xx or 20xx - a simple heuristic + * distinguishes them, since we can't represent any time < 1970. + */ + if ( year < 100 ) + if ( year >= 70 ) + year += 1900; + else + year += 2000; + + days = 0; + for ( i = 1970; i < year; ++i ) + { + days += 365; + if ( IS_LEAP_YEAR(i) ) + ++days; + } + + for ( i = 0; i < tm->tm_mon; ++i ) + days += days_in_month[i]; + + if ( IS_LEAP_YEAR(year) && tm->tm_mon > 1 ) /* 1 is February */ + ++days; + + days += tm->tm_mday - 1; /* -1 since days are numbered starting at 1 */ + + return days * 86400 + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; + } diff --git a/usr.sbin/tcpdump/tcpslice/search.c b/usr.sbin/tcpdump/tcpslice/search.c new file mode 100644 index 0000000..8bea0d2 --- /dev/null +++ b/usr.sbin/tcpdump/tcpslice/search.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#)$Header: search.c,v 1.3 92/05/01 15:14:45 vern Exp $ (LBL)"; +#endif + +/* + * search.c - supports fast searching through tcpdump files for timestamps + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> + +#include "interface.h" +#include "savefile.h" + + +/* Maximum number of seconds that we can conceive of a dump file spanning. */ +#define MAX_REASONABLE_FILE_SPAN (3600*24*366) /* one year */ + +/* Maximum packet length we ever expect to see. */ +#define MAX_REASONABLE_PACKET_LENGTH 65535 + +/* Size of a packet header in bytes; easier than typing the sizeof() all + * the time ... + */ +#define PACKET_HDR_LEN (sizeof( struct packet_header )) + +/* The maximum size of a packet, including its header. */ +#define MAX_PACKET_SIZE (PACKET_HDR_LEN + snaplen) + +/* Number of contiguous bytes from a dumpfile in which there's guaranteed + * to be enough information to find a "definite" header if one exists + * therein. This takes 3 full packets - the first to be just misaligned + * (one byte short of a full packet), missing its timestamp; the second + * to have the legitimate timestamp; and the third to provide confirmation + * that the second is legit, making it a "definite" header. We could + * scrimp a bit here since not the entire third packet is required, but + * it doesn't seem worth it + */ +#define MAX_BYTES_FOR_DEFINITE_HEADER (3 * MAX_PACKET_SIZE) + +/* Maximum number of seconds that might reasonably separate two headers. */ +#define MAX_REASONABLE_HDR_SEPARATION (3600 * 24 * 7) /* one week */ + +/* When searching a file for a packet, if we think we're within this many + * bytes of the packet we just search linearly. Since linear searches are + * probably much faster than random ones (random ones require searching for + * the beginning of the packet, which may be unaligned in memory), we make + * this value pretty hefty. + */ +#define STRAIGHT_SCAN_THRESHOLD (100 * MAX_PACKET_SIZE) + +/* Extracts a long integer from a possibly unaligned buffer containing + * unsigned characters. + */ +#define EXTRACT_LONG(buf) (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) + + +/* Given a header and an acceptable first and last time stamp, returns non-zero + * if the header looks reasonable and zero otherwise. + */ +static int reasonable_header( hdr, first_time, last_time ) +struct packet_header *hdr; +long first_time, last_time; + { + if ( last_time == 0 ) + last_time = first_time + MAX_REASONABLE_FILE_SPAN; + + return hdr->ts.tv_sec >= first_time && + hdr->ts.tv_sec <= last_time && + hdr->len > 0 && + hdr->len <= MAX_REASONABLE_PACKET_LENGTH && + hdr->caplen > 0 && + hdr->caplen <= MAX_REASONABLE_PACKET_LENGTH; + } + + +/* Given a buffer, extracts a (properly aligned) packet header from it. */ + +static void extract_header( buf, hdr ) +u_char *buf; +struct packet_header *hdr; + { + hdr->ts.tv_sec = EXTRACT_LONG(buf); + buf += sizeof( long ); + hdr->ts.tv_usec = EXTRACT_LONG(buf); + buf += sizeof( long ); + hdr->len = EXTRACT_LONG(buf); + buf += sizeof( long ); + hdr->caplen = EXTRACT_LONG(buf); + + if ( sf_swapped ) + { + hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec); + hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec); + hdr->len = SWAPLONG(hdr->len); + hdr->caplen = SWAPLONG(hdr->caplen); + } + } + + +/* Search a buffer to locate the first header within it. Return values + * are HEADER_NONE, HEADER_CLASH, HEADER_PERHAPS, and HEADER_DEFINITELY. + * The first indicates that no evidence of a header was found; the second + * that two or more possible headers were found, neither more convincing + * than the other(s); the third that exactly one "possible" header was + * found; and the fourth that exactly one "definite" header was found. + * + * Headers are detected by looking for positions in the buffer which have + * reasonable timestamps and lengths. If there is enough room in the buffer + * for another header to follow a candidate header, a check is made for + * that following header. If it is present then the header is *definite* + * (unless another "perhaps" or "definite" header is found); if not, then + * the header is discarded. If there is not enough room in the buffer for + * another header then the candidate is *perhaps* (unless another header + * is subsequently found). A "tie" between a "definite" header and a + * "perhaps" header is resolved in favor of the definite header. Any + * other tie leads to HEADER_CLASH. + * + * The buffer position of the header is returned in hdrpos_addr and + * for convenience the corresponding header in return_hdr. + * + * first_time is the earliest possible acceptable timestamp in the + * header. last_time, if non-zero, is the last such timestamp. If + * zero, then up to MAX_REASONABLE_FILE_SPAN seconds after first_time + * is acceptable. + */ + +#define HEADER_NONE 0 +#define HEADER_CLASH 1 +#define HEADER_PERHAPS 2 +#define HEADER_DEFINITELY 3 + +static int find_header( buf, buf_len, first_time, last_time, + hdrpos_addr, return_hdr ) +u_char *buf; +unsigned buf_len; +long first_time, last_time; +u_char **hdrpos_addr; +struct packet_header *return_hdr; + { + u_char *bufptr, *bufend, *last_pos_to_try; + struct packet_header hdr, hdr2; + int status = HEADER_NONE; + int saw_PERHAPS_clash = 0; + + /* Initially, try each buffer position to see whether it looks like + * a valid packet header. We may later restrict the positions we look + * at to avoid seeing a sequence of legitimate headers as conflicting + * with one another. + */ + bufend = buf + buf_len; + last_pos_to_try = bufend - PACKET_HDR_LEN; + + for ( bufptr = buf; bufptr < last_pos_to_try; ++bufptr ) + { + extract_header( bufptr, &hdr ); + + if ( reasonable_header( &hdr, first_time, last_time ) ) + { + u_char *next_header = bufptr + PACKET_HDR_LEN + hdr.caplen; + + if ( next_header + PACKET_HDR_LEN < bufend ) + { /* check for another good header */ + extract_header( next_header, &hdr2 ); + + if ( reasonable_header( &hdr2, hdr.ts.tv_sec, + hdr.ts.tv_sec + MAX_REASONABLE_HDR_SEPARATION ) ) + { /* a confirmed header */ + switch ( status ) + { + case HEADER_NONE: + case HEADER_PERHAPS: + status = HEADER_DEFINITELY; + *hdrpos_addr = bufptr; + *return_hdr = hdr; + + /* Make sure we don't demote this "definite" + * to a "clash" if we stumble across its + * successor. + */ + last_pos_to_try = next_header - PACKET_HDR_LEN; + break; + + case HEADER_DEFINITELY: + return HEADER_CLASH; + + default: + error( "bad status in find_header()" ); + } + } + + /* ... else the header is bogus - we've verified that it's + * not followed by a reasonable header. + */ + } + + else + { /* can't check for another good header */ + switch ( status ) + { + case HEADER_NONE: + status = HEADER_PERHAPS; + *hdrpos_addr = bufptr; + *return_hdr = hdr; + break; + + case HEADER_PERHAPS: + /* We don't immediately turn this into a + * clash because perhaps we'll later see a + * "definite" which will save us ... + */ + saw_PERHAPS_clash = 1; + break; + + case HEADER_DEFINITELY: + /* Keep the definite in preference to this one. */ + break; + + default: + error( "bad status in find_header()" ); + } + } + } + } + + if ( status == HEADER_PERHAPS && saw_PERHAPS_clash ) + status = HEADER_CLASH; + + return status; + } + + +/* Positions the sf_readfile stream such that the next sf_read() will + * read the final full packet in the file. Returns non-zero if + * successful, zero if unsuccessful. If successful, returns the + * timestamp of the last packet in last_timestamp. + * + * Note that this routine is a special case of sf_find_packet(). In + * order to use sf_find_packet(), one first must use this routine in + * order to give sf_find_packet() an upper bound on the timestamps + * present in the dump file. + */ +int sf_find_end( first_timestamp, last_timestamp ) +struct timeval *first_timestamp; +struct timeval *last_timestamp; + { + long first_time = first_timestamp->tv_sec; + unsigned num_bytes; + u_char *buf, *bufpos, *bufend; + u_char *hdrpos; + struct packet_header hdr, successor_hdr; + int status; + + /* Allow enough room for at least two full (untruncated) packets, + * perhaps followed by a truncated packet, so we have a shot at + * finding a "definite" header and following its chain to the + * end of the file. + */ + num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER; + if ( fseek( sf_readfile, (long) -num_bytes, 2 ) < 0 ) + return 0; + + buf = (u_char *)malloc((unsigned) num_bytes); + if ( ! buf ) + return 0; + + status = 0; + bufpos = buf; + bufend = buf + num_bytes; + + if ( fread( (char *) bufpos, num_bytes, 1, sf_readfile ) != 1 ) + goto done; + + if ( find_header( bufpos, num_bytes, first_time, 0L, &hdrpos, &hdr ) != + HEADER_DEFINITELY ) + goto done; + + /* Okay, we have a definite header in our hands. Follow its + * chain till we find the last valid packet in the file ... + */ + for ( ; ; ) + { + /* move to the next header position */ + bufpos = hdrpos + PACKET_HDR_LEN + hdr.caplen; + + /* bufpos now points to a candidate packet, which if valid + * should replace the current packet pointed to by hdrpos as + * the last valid packet ... + */ + if ( bufpos >= bufend - PACKET_HDR_LEN ) + /* not enough room for another header */ + break; + + extract_header( bufpos, &successor_hdr ); + + first_time = hdr.ts.tv_sec; + if ( ! reasonable_header( &successor_hdr, first_time, 0L ) ) + /* this bodes ill - it means bufpos is perhaps a + * bogus packet header after all ... + */ + break; + + /* Note that the following test is for whether the next + * packet starts at a position > bufend, *not* for a + * position >= bufend. If this is the last packet in the + * file and there isn't a subsequent partial packet, then + * we expect the first buffer position beyond this packet + * to be just beyond the end of the buffer, i.e., at bufend + * itself. + */ + if ( bufpos + PACKET_HDR_LEN + successor_hdr.caplen > bufend ) + /* the packet is truncated */ + break; + + /* Accept this packet as fully legit. */ + hdrpos = bufpos; + hdr = successor_hdr; + } + + /* Success! Last valid packet is at hdrpos. */ + *last_timestamp = hdr.ts; + status = 1; + + /* Seek so that the next read will start at last valid packet. */ + if ( fseek( sf_readfile, (long) -(bufend - hdrpos), 2 ) < 0 ) + error( "final fseek() failed in sf_find_end()" ); + + done: + free( (char *) buf ); + + return status; + } + + +/* Takes two timeval's and returns the difference, tv2 - tv1, as a double. */ + +static double timeval_diff( tv1, tv2 ) +struct timeval *tv1, *tv2; + { + double result = (tv2->tv_sec - tv1->tv_sec); + result += (tv2->tv_usec - tv1->tv_usec) / 1000000.0; + + return result; + } + + +/* Returns true if timestamp t1 is chronologically less than timestamp t2. */ + +int sf_timestamp_less_than( t1, t2 ) +struct timeval *t1, *t2; + { + return t1->tv_sec < t2->tv_sec || + (t1->tv_sec == t2->tv_sec && + t1->tv_usec < t2->tv_usec); + } + + +/* Given two timestamps on either side of desired_time and their positions, + * returns the interpolated position of the desired_time packet. Returns a + * negative value if the desired_time is outside the given range. + */ + +static +long interpolated_position( min_time, min_pos, max_time, max_pos, desired_time ) +struct timeval *min_time, *max_time, *desired_time; +long min_pos, max_pos; + { + double full_span = timeval_diff( max_time, min_time ); + double desired_span = timeval_diff( desired_time, min_time ); + long full_span_pos = max_pos - min_pos; + double fractional_offset = desired_span / full_span; + + if ( fractional_offset < 0.0 || fractional_offset > 1.0 ) + return -1; + + return min_pos + (long) (fractional_offset * (double) full_span_pos); + } + + +/* Reads packets linearly until one with a time >= the given desired time + * is found; positions the dump file so that the next read will start + * at the given packet. Returns non-zero on success, 0 if an EOF was + * first encountered. + */ + +static int read_up_to( desired_time ) +struct timeval *desired_time; + { + int status = 1; + struct packet_header hdr; + u_char *buf; + long pos; + + buf = (u_char *) malloc( (unsigned) snaplen ); + + for ( ; ; ) + { + struct timeval *timestamp; + + pos = ftell( sf_readfile ); + status = sf_next_packet( &hdr, buf, snaplen ); + + if ( status ) + { + if ( status == SFERR_EOF ) + { + status = 0; + break; + } + + error( "bad status %d in read_up_to()", status ); + } + + timestamp = &hdr.ts; + + if ( ! sf_timestamp_less_than( timestamp, desired_time ) ) + break; + } + + if ( fseek( sf_readfile, pos, 0 ) < 0 ) + error( "fseek() failed in read_up_to()" ); + + free( (char *) buf ); + + return status; + } + + +/* Positions the sf_readfile stream so that the next sf_read() will + * return the first packet with a time greater than or equal to + * desired_time. desired_time must be greater than min_time and less + * than max_time, which should correspond to actual packets in the + * file. min_pos is the file position (byte offset) corresponding to + * the min_time packet and max_pos is the same for the max_time packet. + * + * Returns non-zero on success, 0 if the given position is beyond max_pos. + * + * NOTE: when calling this routine, the sf_readfile stream *must* be + * already aligned so that the next call to sf_next_packet() will yield + * a valid packet. + */ + +int sf_find_packet( min_time, min_pos, max_time, max_pos, desired_time ) +struct timeval *min_time, *max_time; +long min_pos, max_pos; +struct timeval *desired_time; + { + int status = 1; + struct timeval min_time_copy, max_time_copy; + unsigned num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER; + int num_bytes_read; + long desired_pos, present_pos; + u_char *buf, *hdrpos; + struct packet_header hdr; + + buf = (u_char *) malloc( num_bytes ); + if ( ! buf ) + error( "malloc() failured in sf_find_packet()" ); + + min_time_copy = *min_time; + min_time = &min_time_copy; + + max_time_copy = *max_time; + max_time = &max_time_copy; + + for ( ; ; ) /* loop until positioned correctly */ + { + desired_pos = + interpolated_position( min_time, min_pos, + max_time, max_pos, + desired_time ); + + if ( desired_pos < 0 ) + { + status = 0; + break; + } + + present_pos = ftell( sf_readfile ); + + if ( present_pos <= desired_pos && + desired_pos - present_pos < STRAIGHT_SCAN_THRESHOLD ) + { /* we're close enough to just blindly read ahead */ + status = read_up_to( desired_time ); + break; + } + + /* Undershoot the target a little bit - it's much easier to + * then scan straight forward than to try to read backwards ... + */ + desired_pos -= STRAIGHT_SCAN_THRESHOLD / 2; + if ( desired_pos < min_pos ) + desired_pos = min_pos; + + if ( fseek( sf_readfile, desired_pos, 0 ) < 0 ) + error( "fseek() failed in sf_find_packet()" ); + + num_bytes_read = + fread( (char *) buf, 1, num_bytes, sf_readfile ); + + if ( num_bytes_read == 0 ) + /* This shouldn't ever happen because we try to + * undershoot, unless the dump file has only a + * couple packets in it ... + */ + error( "fread() failed in sf_find_packet()" ); + + if ( find_header( buf, num_bytes, min_time->tv_sec, + max_time->tv_sec, &hdrpos, &hdr ) != + HEADER_DEFINITELY ) + error( "can't find header at position %ld in dump file", + desired_pos ); + + /* Correct desired_pos to reflect beginning of packet. */ + desired_pos += (hdrpos - buf); + + /* Seek to the beginning of the header. */ + if ( fseek( sf_readfile, desired_pos, 0 ) < 0 ) + error( "fseek() failed in sf_find_packet()" ); + + if ( sf_timestamp_less_than( &hdr.ts, desired_time ) ) + { /* too early in the file */ + *min_time = hdr.ts; + min_pos = desired_pos; + } + + else if ( sf_timestamp_less_than( desired_time, &hdr.ts ) ) + { /* too late in the file */ + *max_time = hdr.ts; + max_pos = desired_pos; + } + + else + /* got it! */ + break; + } + + free( (char *) buf ); + + return status; + } diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.1 b/usr.sbin/tcpdump/tcpslice/tcpslice.1 new file mode 100644 index 0000000..b130f2c --- /dev/null +++ b/usr.sbin/tcpdump/tcpslice/tcpslice.1 @@ -0,0 +1,260 @@ +.\" @(#) $Header: tcpslice.1,v 1.2 91/10/16 11:42:46 vern Exp $ (LBL) +.\" +.\" Copyright (c) 1988-1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.TH TCPSLICE 1 "14 Oct 1991" +.SH NAME +tcpslice \- extract pieces of and/or glue together tcpdump files +.SH SYNOPSIS +.na +.B tcpslice +[ +.B \-dRrt +] [ +.B \-w +.I file +] +.br +.ti +9 +[ +.I start-time +[ +.I end-time +] ] +.I file ... +.br +.ad +.SH DESCRIPTION +.LP +.I Tcpslice +is a program for extracting portions of packet-trace files generated using +\fItcpdump(l)\fP's +.B \-w +flag. +It can also be used to glue together several such files, as discussed +below. +.LP +The basic operation of +.I tcpslice +is to copy to +.I stdout +all packets from its input file(s) whose timestamps fall +within a given range. The starting and ending times of the range +may be specified on the command line. All ranges are inclusive. +The starting time defaults +to the time of the first packet in the first input file; we call +this the +.I first time. +The ending time defaults to ten years after the starting time. +Thus, the command +.I tcpslice trace-file +simply copies +.I trace-file +to \fIstdout\fP (assuming the file does not include more than +ten years' worth of data). +.LP +There are a number of ways to specify times. The first is using +Unix timestamps of the form +.I sssssssss.uuuuuu +(this is the format specified by \fItcpdump\fP's +.B \-tt +flag). +For example, +.B 654321098.7654 +specifies 38 seconds and 765,400 microseconds +after 8:51PM PDT, Sept. 25, 1990. +.LP +All examples in this manual are given +for PDT times, but when displaying times and interpreting times symbolically +as discussed below, +.I tcpslice +uses the local timezone, regardless of the timezone in which the \fItcpdump\fP +file was generated. The daylight-savings setting used is that which is +appropriate for the local timezone at the date in question. For example, +times associated with summer months will usually include daylight-savings +effects, and those with winter months will not. +.LP +Times may also be specified relative +to either the +.I first time +(when specifying a starting time) +or the starting time (when specifying an ending time) +by preceding a numeric value in seconds with a `+'. +For example, a starting time of +.B +200 +indicates 200 seconds after the +.I first time, +and the two arguments +.B +200 +300 +indicate from 200 seconds after the +.I first time +through 500 seconds after the +.I first time. +.LP +Times may also be specified in terms of years (y), months (m), days (d), +hours (h), minutes (m), seconds (s), and microseconds(u). For example, +the Unix timestamp 654321098.7654 discussed above could also be expressed +as +.B 90y9m25d20h51m38s765400u. +.LP +When specifying times using this style, fields that are omitted default +as follows. If the omitted field is a unit +.I greater +than that of the first specified field, then its value defaults to +the corresponding value taken from either +.I first time +(if the starting time is being specified) or the starting time +(if the ending time is being specified). +If the omitted field is a unit +.I less +than that of the first specified field, then it defaults to zero. +For example, suppose that the input file has a +.I first time +of the Unix timestamp mentioned above, i.e., 38 seconds and 765,400 microseconds +after 8:51PM PDT, Sept. 25, 1990. To specify 9:36PM PDT (exactly) on the +same date we could use +.B 21h36m. +To specify a range from 9:36PM PDT through 1:54AM PDT the next day we +could use +.B 21h36m 26d1h54m. +.LP +Relative times can also be specified when using the +.I ymdhmsu +format. Omitted fields then default to 0 if the unit of the field is +.I greater +than that of the first specified field, and to the corresponding value +taken from either the +.I first time +or the starting time if the omitted field's unit is +.I less +than that of the first specified field. Given a +.I first time +of the Unix timestamp mentioned above, +.B 22h +1h10m +specifies a range from 10:00PM PDT on that date through 11:10PM PDT, and +.B +1h +1h10m +specifies a range from 38.7654 seconds after 9:51PM PDT through 38.7654 +seconds after 11:01PM PDT. The first hour of the file could be extracted +using +.B +0 +1h. +.LP +Note that with the +.I ymdhmsu +format there is an ambiguity between using +.I m +for `month' or for `minute'. The ambiguity is resolved as follows: if an +.I m +field is followed by a +.I d +field then it is interpreted as specifying months; otherwise it +specifies minutes. +.LP +If more than one input file is specified then +.I tcpslice +first copies packets lying in the given range from the first file; it +then increases the starting time of the range to lie just beyond the +timestamp of the last packet in the first file, repeats the process +with the second file, and so on. Thus files with interleaved packets +are +.I not +merged. For a given file, only packets that are newer than any in the +preceding files will be considered. This mechanism avoids any possibility +of a packet occurring more than once in the output. +.SH OPTIONS +.LP +If any of +.B \-R, +.B \-r +or +.B \-t +are specified then +.I tcpslice +reports the timestamps of the first and last packets in each input file +and exits. Only one of these three options may be specified. +.TP +.B \-d +Dump the start and end times specified by the given range and +exit. This option is useful for checking that the given range actually +specifies the times you think it does. If one of +.B \-R, +.B \-r +or +.B \-t +has been specified then the times are dumped in the corresponding +format; otherwise, raw format (\fB \-R\fP) is used. +.TP +.B \-R +Dump the timestamps of the first and last packets in each input file +as raw timestamps (i.e., in the form \fI sssssssss.uuuuuu\fP). +.TP +.B \-r +Same as +.B \-R +except the timestamps are dumped in human-readable format, similar +to that used by \fI date(1)\fP. +.TP +.B \-t +Same as +.B \-R +except the timestamps are dumped in +.I tcpslice +format, i.e., in the +.I ymdhmsu +format discussed above. +.TP +.B \-w +Direct the output to \fIfile\fR rather than \fIstdout\fP. +.SH "SEE ALSO" +tcpdump(l) +.SH AUTHOR +Vern Paxson (vern@ee.lbl.gov), of +Lawrence Berkeley Laboratory, University of California, Berkeley, CA. +.SH BUGS +An input filename that beings with a digit or a `+' can be confused +with a start/end time. Such filenames can be specified with a +leading `./'; for example, specify the file `04Jul76.trace' as +`./04Jul76.trace'. +.LP +.I tcpslice +cannot read its input from \fIstdin\fP, since it uses random-access +to rummage through its input files. +.LP +.I tcpslice +refuses to write to its output if it is a terminal +(as indicated by \fIisatty(3)\fP). This is not a bug but a feature, +to prevent it from spraying binary data to the user's terminal. +Note that this means you must either redirect \fIstdout\fP or specify an +output file via \fB\-w\fP. +.LP +.I tcpslice +will not work properly on \fItcpdump\fP files spanning more than one year; +with files containing portions of packets whose original length was +more than 65,535 bytes; nor with files containing fewer than three packets. +Such files result in +the error message: `couldn't find final packet in file'. These problems +are due to the interpolation scheme used by +.I tcpslice +to greatly speed up its processing when dealing with large trace files. +Note that +.I tcpslice +can efficiently extract slices from the middle of trace files of any +size, and can also work with truncated trace files (i.e., the final packet +in the file is only partially present, typically due to \fItcpdump\fP +being ungracefully killed). diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.c b/usr.sbin/tcpdump/tcpslice/tcpslice.c new file mode 100644 index 0000000..b5b6eea --- /dev/null +++ b/usr.sbin/tcpdump/tcpslice/tcpslice.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 1987-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +char copyright[] = + "@(#) Copyright (c) 1987-1990 The Regents of the University of California.\nAll rights reserved.\n"; +static char rcsid[] = + "@(#)$Header: tcpslice.c,v 1.10 92/06/02 17:57:44 mccanne Exp $ (LBL)"; +#endif + +/* + * tcpslice - extract pieces of and/or glue together tcpdump files + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <netinet/in.h> +#include <varargs.h> + +#include "savefile.h" +#include "version.h" + + +int tflag = 0; /* global that util routines are sensitive to */ + +char *program_name; + +long thiszone; /* gmt to local correction in trace file */ + +/* Length of saved portion of packet. */ +int snaplen; + +/* Length of saved portion of data past link level protocol. */ +int snapdlen; + +/* Precision of clock used to generate trace file. */ +int precision; + +static int linkinfo; + +/* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is + * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format, + * designed to be easy to parse. The default is RAW. + */ +enum stamp_styles { TIMESTAMP_RAW, TIMESTAMP_READABLE, TIMESTAMP_PARSEABLE }; +enum stamp_styles timestamp_style = TIMESTAMP_RAW; + + +time_t gwtm2secs( /* struct tm *tmp */ ); + + +long local_time_zone( /* timestamp */ ); +struct timeval parse_time(/* time_string, base_time*/); +void fill_tm(/* time_string, is_delta, t, usecs_addr */); +void get_file_range( /* filename, first_time, last_time */ ); +struct timeval first_packet_time(/* filename */); +void extract_slice(/* filename, start_time, stop_time */); +char *timestamp_to_string( /* timestamp */ ); +void dump_times(/* filename */); +void usage(); + + +int +main(argc, argv) + int argc; + char **argv; +{ + int op; + int dump_flag = 0; + int report_times = 0; + char *start_time_string = 0; + char *stop_time_string = 0; + char *write_file_name = "-"; /* default is stdout */ + struct timeval first_time, start_time, stop_time; + + extern char *optarg; + extern int optind, opterr; + + program_name = argv[0]; + + opterr = 0; + while ((op = getopt(argc, argv, "dRrtw:")) != EOF) + switch (op) { + + case 'd': + dump_flag = 1; + break; + + case 'R': + ++report_times; + timestamp_style = TIMESTAMP_RAW; + break; + + case 'r': + ++report_times; + timestamp_style = TIMESTAMP_READABLE; + break; + + case 't': + ++report_times; + timestamp_style = TIMESTAMP_PARSEABLE; + break; + + case 'w': + write_file_name = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + + if ( report_times > 1 ) + error( "only one of -R, -r, or -t can be specified" ); + + + if (optind < argc) + /* See if the next argument looks like a possible + * start time, and if so assume it is one. + */ + if (isdigit(argv[optind][0]) || argv[optind][0] == '+') + start_time_string = argv[optind++]; + + if (optind < argc) + if (isdigit(argv[optind][0]) || argv[optind][0] == '+') + stop_time_string = argv[optind++]; + + + if (optind >= argc) + error("at least one input file must be given"); + + + first_time = first_packet_time(argv[optind]); + fclose( sf_readfile ); + + + if (start_time_string) + start_time = parse_time(start_time_string, first_time); + else + start_time = first_time; + + if (stop_time_string) + stop_time = parse_time(stop_time_string, start_time); + + else + { + stop_time = start_time; + stop_time.tv_sec += 86400*3660; /* + 10 years; "forever" */ + } + + + if (report_times) { + for (; optind < argc; ++optind) + dump_times(argv[optind]); + } + + if (dump_flag) { + printf( "start\t%s\nstop\t%s\n", + timestamp_to_string( &start_time ), + timestamp_to_string( &stop_time ) ); + } + + if (! report_times && ! dump_flag) { + if ( ! strcmp( write_file_name, "-" ) && + isatty( fileno(stdout) ) ) + error("stdout is a terminal; redirect or use -w"); + + sf_write_init(write_file_name, linkinfo, thiszone, snaplen, + precision); + + for (; optind < argc; ++optind) + extract_slice(argv[optind], &start_time, &stop_time); + + fclose( sf_writefile ); + } + + return 0; +} + + +/* Returns non-zero if a string matches the format for a timestamp, + * 0 otherwise. + */ +int is_timestamp( str ) +char *str; + { + while ( isdigit(*str) || *str == '.' ) + ++str; + + return *str == '\0'; + } + + +/* Return the correction in seconds for the local time zone with respect + * to Greenwich time. + */ +long local_time_zone(timestamp) +long timestamp; +{ + struct timeval now; + struct timezone tz; + long localzone; + + if (gettimeofday(&now, &tz) < 0) { + perror("tcpslice: gettimeofday"); + exit(1); + } + localzone = tz.tz_minuteswest * -60; + + if (localtime((time_t *) ×tamp)->tm_isdst) + localzone += 3600; + + return localzone; +} + +/* Given a string specifying a time (or a time offset) and a "base time" + * from which to compute offsets and fill in defaults, returns a timeval + * containing the specified time. + */ + +struct timeval +parse_time(time_string, base_time) + char *time_string; + struct timeval base_time; +{ + struct tm *bt = localtime((time_t *) &base_time.tv_sec); + struct tm t; + struct timeval result; + time_t usecs = 0; + int is_delta = (time_string[0] == '+'); + + if ( is_delta ) + ++time_string; /* skip over '+' sign */ + + if ( is_timestamp( time_string ) ) + { /* interpret as a raw timestamp or timestamp offset */ + char *time_ptr; + + result.tv_sec = atoi( time_string ); + time_ptr = strchr( time_string, '.' ); + + if ( time_ptr ) + { /* microseconds are specified, too */ + int num_digits = strlen( time_ptr + 1 ); + result.tv_usec = atoi( time_ptr + 1 ); + + /* turn 123.456 into 123 seconds plus 456000 usec */ + while ( num_digits++ < 6 ) + result.tv_usec *= 10; + } + + else + result.tv_usec = 0; + + if ( is_delta ) + { + result.tv_sec += base_time.tv_sec; + result.tv_usec += base_time.tv_usec; + + if ( result.tv_usec > 1000000 ) + { + result.tv_usec -= 1000000; + ++result.tv_sec; + } + } + + return result; + } + + if (is_delta) { + t = *bt; + usecs = base_time.tv_usec; + } else { + /* Zero struct (easy way around lack of tm_gmtoff/tm_zone + * under older systems) */ + bzero((char *)&t, sizeof(t)); + + /* Set values to "not set" flag so we can later identify + * and default them. + */ + t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon = + t.tm_year = -1; + } + + fill_tm(time_string, is_delta, &t, &usecs); + + /* Now until we reach a field that was specified, fill in the + * missing fields from the base time. + */ +#define CHECK_FIELD(field_name) \ + if (t.field_name < 0) \ + t.field_name = bt->field_name; \ + else \ + break + + do { /* bogus do-while loop so "break" in CHECK_FIELD will work */ + CHECK_FIELD(tm_year); + CHECK_FIELD(tm_mon); + CHECK_FIELD(tm_mday); + CHECK_FIELD(tm_hour); + CHECK_FIELD(tm_min); + CHECK_FIELD(tm_sec); + } while ( 0 ); + + /* Set remaining unspecified fields to 0. */ +#define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \ + if (t.field_name < 0) \ + t.field_name = zero_val + + if (! is_delta) { + ZERO_FIELD_IF_NOT_SET(tm_year,90); /* should never happen */ + ZERO_FIELD_IF_NOT_SET(tm_mon,0); + ZERO_FIELD_IF_NOT_SET(tm_mday,1); + ZERO_FIELD_IF_NOT_SET(tm_hour,0); + ZERO_FIELD_IF_NOT_SET(tm_min,0); + ZERO_FIELD_IF_NOT_SET(tm_sec,0); + } + + result.tv_sec = gwtm2secs(&t); + result.tv_sec -= local_time_zone(result.tv_sec); + result.tv_usec = usecs; + + return result; +} + + +/* Fill in (or add to, if is_delta is true) the time values in the + * tm struct "t" as specified by the time specified in the string + * "time_string". "usecs_addr" is updated with the specified number + * of microseconds, if any. + */ +void +fill_tm(time_string, is_delta, t, usecs_addr) + char *time_string; + int is_delta; /* if true, add times in instead of replacing */ + struct tm *t; /* tm struct to be filled from time_string */ + time_t *usecs_addr; +{ + char *t_start, *t_stop, format_ch; + int val; + +#define SET_VAL(lhs,rhs) \ + if (is_delta) \ + lhs += rhs; \ + else \ + lhs = rhs + + /* Loop through the time string parsing one specification at + * a time. Each specification has the form <number><letter> + * where <number> indicates the amount of time and <letter> + * the units. + */ + for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) { + if (! isdigit(*t_start)) + error("bad date format %s, problem starting at %s", + time_string, t_start); + + while (isdigit(*t_stop)) + ++t_stop; + if (! t_stop) + error("bad date format %s, problem starting at %s", + time_string, t_start); + + val = atoi(t_start); + + format_ch = *t_stop; + if ( isupper( format_ch ) ) + format_ch = tolower( format_ch ); + + switch (format_ch) { + case 'y': + if ( val > 1900 ) + val -= 1900; + SET_VAL(t->tm_year, val); + break; + + case 'm': + if (strchr(t_stop+1, 'D') || + strchr(t_stop+1, 'd')) + /* it's months */ + SET_VAL(t->tm_mon, val - 1); + else /* it's minutes */ + SET_VAL(t->tm_min, val); + break; + + case 'd': + SET_VAL(t->tm_mday, val); + break; + + case 'h': + SET_VAL(t->tm_hour, val); + break; + + case 's': + SET_VAL(t->tm_sec, val); + break; + + case 'u': + SET_VAL(*usecs_addr, val); + break; + + default: + error( + "bad date format %s, problem starting at %s", + time_string, t_start); + } + } +} + + +/* Return in first_time and last_time the timestamps of the first and + * last packets in the given file. + */ +void +get_file_range( filename, first_time, last_time ) + char filename[]; + struct timeval *first_time; + struct timeval *last_time; +{ + *first_time = first_packet_time( filename ); + + if ( ! sf_find_end( first_time, last_time ) ) + error( "couldn't find final packet in file %s", filename ); +} + + +/* Returns the timestamp of the first packet in the given tcpdump save + * file, which as a side-effect is initialized for further save-file + * reading. + */ + +struct timeval +first_packet_time(filename) + char filename[]; +{ + struct packet_header hdr; + u_char *buf; + + if (sf_read_init(filename, &linkinfo, &thiszone, &snaplen, &precision)) + error( "bad tcpdump file %s", filename ); + + buf = (u_char *)malloc((unsigned)snaplen); + + if (sf_next_packet(&hdr, buf, snaplen)) + error( "bad status reading first packet in %s", filename ); + + free((char *)buf); + + return hdr.ts; +} + + +/* Extract from the given file all packets with timestamps between + * the two time values given (inclusive). These packets are written + * to the save file output set up by a previous call to sf_write_init(). + * Upon return, start_time is adjusted to reflect a time just after + * that of the last packet written to the output. + */ + +void +extract_slice(filename, start_time, stop_time) + char filename[]; + struct timeval *start_time; + struct timeval *stop_time; +{ + long start_pos, stop_pos; + struct timeval file_start_time, file_stop_time; + int status; + struct packet_header hdr; + u_char *buf; + + + if (sf_read_init(filename, &linkinfo, &thiszone, &snaplen, &precision)) + error( "bad tcpdump file %s", filename ); + + buf = (u_char *)malloc((unsigned)snaplen); + + start_pos = ftell( sf_readfile ); + + + if ( (status = sf_next_packet( &hdr, buf, snaplen )) ) + error( "bad status %d reading packet in %s", + status, filename ); + + file_start_time = hdr.ts; + + + if ( ! sf_find_end( &file_start_time, &file_stop_time ) ) + error( "problems finding end packet of file %s", + filename ); + + stop_pos = ftell( sf_readfile ); + + + /* sf_find_packet() requires that the time it's passed as its last + * argument be in the range [min_time, max_time], so we enforce + * that constraint here. + */ + if ( sf_timestamp_less_than( start_time, &file_start_time ) ) + *start_time = file_start_time; + + if ( sf_timestamp_less_than( &file_stop_time, start_time ) ) + return; /* there aren't any packets of interest in the file */ + + + sf_find_packet( &file_start_time, start_pos, + &file_stop_time, stop_pos, + start_time ); + + for ( ; ; ) + { + struct timeval *timestamp; + status = sf_next_packet( &hdr, buf, snaplen ); + + if ( status ) + { + if ( status != SFERR_EOF ) + error( "bad status %d reading packet in %s", + status, filename ); + break; + } + + timestamp = &hdr.ts; + + if ( ! sf_timestamp_less_than( timestamp, start_time ) ) + { /* packet is recent enough */ + if ( sf_timestamp_less_than( stop_time, timestamp ) ) + /* We've gone beyond the end of the region + * of interest ... We're done with this file. + */ + break; + + sf_write( buf, timestamp, (int) hdr.len, + (int) hdr.caplen ); + *start_time = *timestamp; + + /* We know that each packet is guaranteed to have + * a unique timestamp, so we push forward the + * allowed minimum time to weed out duplicate + * packets. + */ + ++start_time->tv_usec; + } + } + + fclose( sf_readfile ); + free( (char *) buf ); +} + + +/* Translates a timestamp to the time format specified by the user. + * Returns a pointer to the translation residing in a static buffer. + * There are two such buffers, which are alternated on subseqeuent + * calls, so two calls may be made to this routine without worrying + * about the results of the first call being overwritten by the + * results of the second. + */ + +char * +timestamp_to_string(timestamp) + struct timeval *timestamp; +{ + struct tm *t; +#define NUM_BUFFERS 2 + static char buffers[NUM_BUFFERS][128]; + static int buffer_to_use = 0; + char *buf; + + buf = buffers[buffer_to_use]; + buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS; + + switch ( timestamp_style ) + { + case TIMESTAMP_RAW: + sprintf( buf, "%d.%d", timestamp->tv_sec, timestamp->tv_usec ); + break; + + case TIMESTAMP_READABLE: + t = localtime((time_t *) ×tamp->tv_sec); + strcpy( buf, asctime( t ) ); + buf[24] = '\0'; /* nuke final newline */ + break; + + case TIMESTAMP_PARSEABLE: + t = localtime((time_t *) ×tamp->tv_sec); + sprintf( buf, "%02dy%02dm%02dd%02dh%02dm%02ds%06du", + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, + t->tm_min, t->tm_sec, timestamp->tv_usec ); + break; + + } + + return buf; +} + + +/* Given a tcpdump save filename, reports on the times of the first + * and last packets in the file. + */ + +void +dump_times(filename) + char filename[]; +{ + struct timeval first_time, last_time; + + get_file_range( filename, &first_time, &last_time ); + + printf( "%s\t%s\t%s\n", + filename, + timestamp_to_string( &first_time ), + timestamp_to_string( &last_time ) ); +} + +void +usage() +{ + (void)fprintf(stderr, "tcpslice for tcpdump version %d.%d\n", + VERSION_MAJOR, VERSION_MINOR); + (void)fprintf(stderr, +"Usage: tcpslice [-dRrt] [-w file] [start-time [end-time]] file ... \n"); + + exit(-1); +} |