De-orbit burn tcpslice.
Reviewed by: sam, bmah
This commit is contained in:
parent
81fbc3d1f3
commit
790ca1147e
@ -1,23 +0,0 @@
|
||||
# @(#)Makefile 0.1 (RWGrimes) 3/24/93
|
||||
# $FreeBSD$
|
||||
|
||||
TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
|
||||
|
||||
PROG= tcpslice
|
||||
SRCS= gwtm2secs.c search.c tcpslice.c util.c version.c version.h
|
||||
CLEANFILES= version.c version.h
|
||||
|
||||
CFLAGS+= -I.
|
||||
|
||||
DPADD= ${LIBPCAP}
|
||||
LDADD= -lpcap
|
||||
|
||||
.ORDER: version.c version.h
|
||||
version.c version.h: ${TCPDUMP_DISTDIR}/VERSION
|
||||
rm -f version.c ; \
|
||||
sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION > version.c
|
||||
set `sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \2/' ${TCPDUMP_DISTDIR}/VERSION` ; \
|
||||
{ echo '#define VERSION_MAJOR' $$1 ; \
|
||||
echo '#define VERSION_MINOR' $$2 ; } > version.h
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#if !defined(lint) && !defined(__GNUC__)
|
||||
static char rcsid[] =
|
||||
"@(#)$FreeBSD$ (LBL)";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* gwtm2secs.c - convert "tm" structs for Greenwich time to Unix timestamp
|
||||
*/
|
||||
|
||||
#include "tcpslice.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( 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;
|
||||
|
||||
/* Make sure our year is still >= 1970. We fix 3-digit years
|
||||
* this way, because localtime(3) can return tm_year >= 100,
|
||||
* starting in year 2000.
|
||||
*/
|
||||
if ( year < 1970 )
|
||||
year += 1900;
|
||||
|
||||
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;
|
||||
}
|
@ -1,566 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1990, 1991, 1992, 1993
|
||||
* 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.
|
||||
*/
|
||||
#if !defined(lint) && !defined(__GNUC__)
|
||||
static char rcsid[] =
|
||||
"@(#)$FreeBSD$ (LBL)";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* search.c - supports fast searching through tcpdump files for timestamps
|
||||
*/
|
||||
|
||||
#include "tcpslice.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 pcap_pkthdr ))
|
||||
|
||||
extern int snaplen;
|
||||
|
||||
/* 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)
|
||||
|
||||
|
||||
/* 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( struct pcap_pkthdr *hdr, long first_time, long 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;
|
||||
}
|
||||
|
||||
|
||||
#define SWAPLONG(y) \
|
||||
((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
|
||||
#define SWAPSHORT(y) \
|
||||
( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )
|
||||
|
||||
/* Given a buffer, extracts a (properly aligned) packet header from it. */
|
||||
|
||||
static void
|
||||
extract_header( pcap_t *p, u_char *buf, struct pcap_pkthdr *hdr )
|
||||
{
|
||||
bcopy((char *) buf, (char *) hdr, sizeof(struct pcap_pkthdr));
|
||||
|
||||
if ( pcap_is_swapped( p ) )
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* From bpf/libpcap/savefile.c:
|
||||
*
|
||||
* We interchanged the caplen and len fields at version 2.3,
|
||||
* in order to match the bpf header layout. But unfortunately
|
||||
* some files were written with version 2.3 in their headers
|
||||
* but without the interchanged fields.
|
||||
*/
|
||||
if ( pcap_minor_version( p ) < 3 ||
|
||||
(pcap_minor_version( p ) == 3 && hdr->caplen > hdr->len) )
|
||||
{
|
||||
int t = hdr->caplen;
|
||||
hdr->caplen = hdr->len;
|
||||
hdr->len = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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( pcap_t *p, u_char *buf, int buf_len,
|
||||
long first_time, long last_time,
|
||||
u_char **hdrpos_addr, struct pcap_pkthdr *return_hdr )
|
||||
{
|
||||
u_char *bufptr, *bufend, *last_pos_to_try;
|
||||
struct pcap_pkthdr 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( p, 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( p, 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( pcap_t *p, struct timeval *first_timestamp,
|
||||
struct timeval *last_timestamp )
|
||||
{
|
||||
long first_time = first_timestamp->tv_sec;
|
||||
u_int num_bytes;
|
||||
u_char *buf, *bufpos, *bufend;
|
||||
u_char *hdrpos;
|
||||
struct pcap_pkthdr 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 ( fseeko( pcap_file( p ), (off_t)-num_bytes, 2 ) < 0 )
|
||||
return 0;
|
||||
|
||||
buf = (u_char *)malloc((u_int) num_bytes);
|
||||
if ( ! buf )
|
||||
return 0;
|
||||
|
||||
status = 0;
|
||||
bufpos = buf;
|
||||
bufend = buf + num_bytes;
|
||||
|
||||
if ( fread( (char *) bufpos, num_bytes, 1, pcap_file( p ) ) != 1 )
|
||||
goto done;
|
||||
|
||||
if ( find_header( p, 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( p, 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 ( fseeko( pcap_file( p ), (off_t) -(bufend - hdrpos), 2 ) < 0 )
|
||||
error( "final fseeko() 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( struct timeval *tv1, struct timeval *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( 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);
|
||||
}
|
||||
|
||||
|
||||
/* 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( struct timeval *min_time, long min_pos,
|
||||
struct timeval *max_time, long max_pos,
|
||||
struct timeval *desired_time )
|
||||
{
|
||||
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( pcap_t *p, struct timeval *desired_time )
|
||||
{
|
||||
struct pcap_pkthdr hdr;
|
||||
const u_char *buf;
|
||||
fpos_t pos;
|
||||
int status;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
|
||||
fgetpos( pcap_file( p ), &pos );
|
||||
buf = pcap_next( p, &hdr );
|
||||
|
||||
if ( buf == 0 )
|
||||
{
|
||||
if ( feof( pcap_file( p ) ) )
|
||||
{
|
||||
status = 0;
|
||||
clearerr( pcap_file( p ) );
|
||||
break;
|
||||
}
|
||||
|
||||
error( "bad status in read_up_to()" );
|
||||
}
|
||||
|
||||
timestamp = &hdr.ts;
|
||||
|
||||
if ( ! sf_timestamp_less_than( timestamp, desired_time ) )
|
||||
{
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( fsetpos( pcap_file( p ), &pos ) < 0 )
|
||||
error( "fsetpos() failed in read_up_to()" );
|
||||
|
||||
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( pcap_t *p,
|
||||
struct timeval *min_time, long min_pos,
|
||||
struct timeval *max_time, long max_pos,
|
||||
struct timeval *desired_time )
|
||||
{
|
||||
int status = 1;
|
||||
struct timeval min_time_copy, max_time_copy;
|
||||
u_int num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
|
||||
int num_bytes_read;
|
||||
fpos_t desired_pos, present_pos;
|
||||
u_char *buf, *hdrpos;
|
||||
struct pcap_pkthdr 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;
|
||||
}
|
||||
|
||||
fgetpos( pcap_file( p ), &present_pos );
|
||||
|
||||
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( p, 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 ( fsetpos( pcap_file( p ), &desired_pos ) < 0 )
|
||||
error( "fsetpos() failed in sf_find_packet()" );
|
||||
|
||||
num_bytes_read =
|
||||
fread( (char *) buf, 1, num_bytes, pcap_file( p ) );
|
||||
|
||||
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( p, 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 ( fsetpos( pcap_file( p ), &desired_pos ) < 0 )
|
||||
error( "fsetpos() 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;
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
.\" 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd October 14, 1991
|
||||
.Dt TCPSLICE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm tcpslice
|
||||
.Nd extract pieces of and/or glue together tcpdump files
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl dRrt
|
||||
.Op Fl w Ar file
|
||||
.Op Ar start-time Op end-time
|
||||
.Ar
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility extracts portions of packet-trace files generated using
|
||||
.Xr tcpdump 1 Ns 's
|
||||
.Fl w
|
||||
flag.
|
||||
It can also be used to glue together several such files, as discussed
|
||||
below.
|
||||
.Pp
|
||||
The basic operation of
|
||||
.Nm
|
||||
is to copy to
|
||||
.Pa 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
|
||||
.Em first time .
|
||||
The ending time defaults to ten years after the starting time.
|
||||
Thus, the command
|
||||
.Nm
|
||||
.Ar trace-file
|
||||
simply copies
|
||||
.Ar trace-file
|
||||
to
|
||||
.Pa stdout
|
||||
(assuming the file does not include more than
|
||||
ten years' worth of data).
|
||||
.Pp
|
||||
There are a number of ways to specify times.
|
||||
The first is using
|
||||
Unix timestamps of the form
|
||||
.Em sssssssss.uuuuuu
|
||||
(this is the format specified by
|
||||
.Xr tcpdump 1 Ns 's
|
||||
.Fl tt
|
||||
flag).
|
||||
For example,
|
||||
.Em 654321098.7654
|
||||
specifies 38 seconds and 765,400 microseconds
|
||||
after 8:51PM PDT, Sept.\& 25, 1990.
|
||||
.Pp
|
||||
All examples in this manual are given
|
||||
for PDT times, but when displaying times and interpreting times symbolically
|
||||
as discussed below,
|
||||
.Nm
|
||||
uses the local timezone, regardless of the timezone in which the
|
||||
.Xr tcpdump 1
|
||||
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.
|
||||
.Pp
|
||||
Times may also be specified relative
|
||||
to either the
|
||||
.Em 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
|
||||
.Em +200
|
||||
indicates 200 seconds after the
|
||||
.Em first time ,
|
||||
and the two arguments
|
||||
.Em +200 +300
|
||||
indicate from 200 seconds after the
|
||||
.Em first time
|
||||
through 500 seconds after the
|
||||
.Em first time .
|
||||
.Pp
|
||||
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
|
||||
.Em 90y9m25d20h51m38s765400u .
|
||||
.Pp
|
||||
When specifying times using this style, fields that are omitted default
|
||||
as follows.
|
||||
If the omitted field is a unit
|
||||
.Em greater
|
||||
than that of the first specified field, then its value defaults to
|
||||
the corresponding value taken from either
|
||||
.Em 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
|
||||
.Em less
|
||||
than that of the first specified field, then it defaults to zero.
|
||||
For example, suppose that the input file has a
|
||||
.Em 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
|
||||
.Em 21h36m .
|
||||
To specify a range from 9:36PM PDT through 1:54AM PDT the next day we
|
||||
could use
|
||||
.Em 21h36m 26d1h54m .
|
||||
.Pp
|
||||
Relative times can also be specified when using the
|
||||
.Em ymdhmsu
|
||||
format.
|
||||
Omitted fields then default to 0 if the unit of the field is
|
||||
.Em greater
|
||||
than that of the first specified field, and to the corresponding value
|
||||
taken from either the
|
||||
.Em first time
|
||||
or the starting time if the omitted field's unit is
|
||||
.Em less
|
||||
than that of the first specified field.
|
||||
Given a
|
||||
.Em first time
|
||||
of the Unix timestamp mentioned above,
|
||||
.Em 22h +1h10m
|
||||
specifies a range from 10:00PM PDT on that date through 11:10PM PDT, and
|
||||
.Em +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
|
||||
.Em +0 +1h .
|
||||
.Pp
|
||||
Note that with the
|
||||
.Em ymdhmsu
|
||||
format there is an ambiguity between using
|
||||
.Em m
|
||||
for `month' or for `minute'.
|
||||
The ambiguity is resolved as follows: if an
|
||||
.Em m
|
||||
field is followed by a
|
||||
.Em d
|
||||
field then it is interpreted as specifying months; otherwise it
|
||||
specifies minutes.
|
||||
.Pp
|
||||
If more than one input file is specified then
|
||||
.Nm
|
||||
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
|
||||
.Em 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
|
||||
If any of
|
||||
.Fl R ,
|
||||
.Fl r
|
||||
or
|
||||
.Fl t
|
||||
are specified then
|
||||
.Nm
|
||||
reports the timestamps of the first and last packets in each input file
|
||||
and exits.
|
||||
Only one of these three options may be specified.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Fl 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
|
||||
.Fl R ,
|
||||
.Fl r
|
||||
or
|
||||
.Fl t
|
||||
has been specified then the times are dumped in the corresponding
|
||||
format; otherwise, raw format
|
||||
.Pq Fl R
|
||||
is used.
|
||||
.It Fl R
|
||||
Dump the timestamps of the first and last packets in each input file
|
||||
as raw timestamps (i.e., in the form
|
||||
.Em sssssssss.uuuuuu ) .
|
||||
.It Fl r
|
||||
Same as
|
||||
.Fl R
|
||||
except the timestamps are dumped in human-readable format, similar
|
||||
to that used by
|
||||
.Xr date 1 .
|
||||
.It Fl t
|
||||
Same as
|
||||
.Fl R
|
||||
except the timestamps are dumped in
|
||||
.Nm
|
||||
format, i.e., in the
|
||||
.Em ymdhmsu
|
||||
format discussed above.
|
||||
.It Fl w Ar file
|
||||
Direct the output to
|
||||
.Ar file
|
||||
rather than
|
||||
.Pa stdout .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr tcpdump 1
|
||||
.Sh AUTHORS
|
||||
.An Vern Paxson Aq 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'.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility cannot read its input from
|
||||
.Pa stdin ,
|
||||
since it uses random-access
|
||||
to rummage through its input files.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility refuses to write to its output if it is a terminal
|
||||
(as indicated by
|
||||
.Xr isatty 3 ) .
|
||||
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
|
||||
.Pa stdout
|
||||
or specify an
|
||||
output file via
|
||||
.Fl w .
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility will not work properly on
|
||||
.Xr tcpdump 1
|
||||
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
|
||||
.Nm
|
||||
to greatly speed up its processing when dealing with large trace files.
|
||||
Note that
|
||||
.Nm
|
||||
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
|
||||
.Xr tcpdump 1
|
||||
being ungracefully killed).
|
@ -1,626 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
static const char copyright[] =
|
||||
"@(#) Copyright (c) 1987-1990\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD$";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
* tcpslice - extract pieces of and/or glue together tcpdump files
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include "tcpslice.h"
|
||||
|
||||
int tflag = 0; /* global that util routines are sensitive to */
|
||||
int fddipad; /* XXX: libpcap needs this global */
|
||||
|
||||
/* 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;
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
extern int getopt( int argc, char **argv, char *optstring );
|
||||
#endif
|
||||
|
||||
int is_timestamp( char *str );
|
||||
long local_time_zone(long timestamp);
|
||||
struct timeval parse_time(char *time_string, struct timeval base_time);
|
||||
void fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr);
|
||||
void get_file_range( char filename[], pcap_t **p,
|
||||
struct timeval *first_time, struct timeval *last_time );
|
||||
struct timeval first_packet_time(char filename[], pcap_t **p_addr);
|
||||
void extract_slice(char filename[], char write_file_name[],
|
||||
struct timeval *start_time, struct timeval *stop_time);
|
||||
char *timestamp_to_string(struct timeval *timestamp);
|
||||
void dump_times(pcap_t **p, char filename[]);
|
||||
static void usage(void);
|
||||
|
||||
|
||||
pcap_dumper_t *dumper = 0;
|
||||
|
||||
int
|
||||
main(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;
|
||||
pcap_t *pcap;
|
||||
|
||||
opterr = 0;
|
||||
while ((op = getopt(argc, argv, "dRrtw:")) != -1)
|
||||
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], &pcap);
|
||||
pcap_close(pcap);
|
||||
|
||||
|
||||
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(&pcap, 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");
|
||||
|
||||
for (; optind < argc; ++optind)
|
||||
extract_slice(argv[optind], write_file_name,
|
||||
&start_time, &stop_time);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Returns non-zero if a string matches the format for a timestamp,
|
||||
* 0 otherwise.
|
||||
*/
|
||||
int is_timestamp( 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(long timestamp)
|
||||
{
|
||||
struct timeval now;
|
||||
struct timezone tz;
|
||||
long localzone;
|
||||
time_t t = _long_to_time(timestamp);
|
||||
|
||||
if (gettimeofday(&now, &tz) < 0)
|
||||
err(1, "gettimeofday");
|
||||
localzone = tz.tz_minuteswest * -60;
|
||||
|
||||
if (localtime(&t)->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(char *time_string, struct timeval base_time)
|
||||
{
|
||||
time_t tt = _long_to_time(base_time.tv_sec);
|
||||
struct tm *bt = localtime(&tt);
|
||||
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(char *time_string, int is_delta, struct tm *t, 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;
|
||||
else if (val < 100 && !is_delta) {
|
||||
if (val < 69) /* Same hack as date */
|
||||
val += 100;
|
||||
}
|
||||
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( char filename[], pcap_t **p,
|
||||
struct timeval *first_time, struct timeval *last_time )
|
||||
{
|
||||
*first_time = first_packet_time( filename, p );
|
||||
|
||||
if ( ! sf_find_end( *p, first_time, last_time ) )
|
||||
error( "couldn't find final packet in file %s", filename );
|
||||
}
|
||||
|
||||
int snaplen;
|
||||
|
||||
/* 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(char filename[], pcap_t **p_addr)
|
||||
{
|
||||
struct pcap_pkthdr hdr;
|
||||
pcap_t *p;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
p = *p_addr = pcap_open_offline(filename, errbuf);
|
||||
if (! p)
|
||||
error( "bad tcpdump file %s: %s", filename, errbuf );
|
||||
|
||||
snaplen = pcap_snapshot( p );
|
||||
|
||||
if (pcap_next(p, &hdr) == 0)
|
||||
error( "bad status reading first packet in %s", filename );
|
||||
|
||||
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 given by write_file_name.
|
||||
*
|
||||
* Upon return, start_time is adjusted to reflect a time just after
|
||||
* that of the last packet written to the output.
|
||||
*/
|
||||
|
||||
void
|
||||
extract_slice(char filename[], char write_file_name[],
|
||||
struct timeval *start_time, struct timeval *stop_time)
|
||||
{
|
||||
off_t start_pos, stop_pos;
|
||||
struct timeval file_start_time, file_stop_time;
|
||||
struct pcap_pkthdr hdr;
|
||||
pcap_t *p;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
p = pcap_open_offline(filename, errbuf);
|
||||
if (! p)
|
||||
error( "bad tcpdump file %s: %s", filename, errbuf );
|
||||
|
||||
snaplen = pcap_snapshot( p );
|
||||
fgetpos( pcap_file( p ), &start_pos );
|
||||
|
||||
if ( ! dumper )
|
||||
{
|
||||
dumper = pcap_dump_open(p, write_file_name);
|
||||
if ( ! dumper )
|
||||
error( "error creating output file %s: ",
|
||||
write_file_name, pcap_geterr( p ) );
|
||||
}
|
||||
|
||||
if (pcap_next(p, &hdr) == 0)
|
||||
error( "error reading packet in %s: ",
|
||||
filename, pcap_geterr( p ) );
|
||||
|
||||
file_start_time = hdr.ts;
|
||||
|
||||
|
||||
if ( ! sf_find_end( p, &file_start_time, &file_stop_time ) )
|
||||
error( "problems finding end packet of file %s",
|
||||
filename );
|
||||
|
||||
fgetpos( pcap_file( p ), &stop_pos );
|
||||
|
||||
|
||||
/* 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( p, &file_start_time, start_pos,
|
||||
&file_stop_time, stop_pos,
|
||||
start_time );
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
const u_char *pkt = pcap_next( p, &hdr );
|
||||
|
||||
if ( pkt == 0 )
|
||||
{
|
||||
#ifdef notdef
|
||||
int status;
|
||||
if ( status != SFERR_EOF )
|
||||
error( "bad status %d reading packet in %s",
|
||||
status, filename );
|
||||
#endif
|
||||
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;
|
||||
|
||||
pcap_dump((u_char *) dumper, &hdr, pkt);
|
||||
|
||||
*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;
|
||||
}
|
||||
}
|
||||
|
||||
pcap_close( p );
|
||||
}
|
||||
|
||||
|
||||
/* 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(struct timeval *timestamp)
|
||||
{
|
||||
struct tm *t;
|
||||
time_t tt;
|
||||
#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, "%lu.%06lu", timestamp->tv_sec, timestamp->tv_usec);
|
||||
break;
|
||||
|
||||
case TIMESTAMP_READABLE:
|
||||
tt = _long_to_time(timestamp->tv_sec);
|
||||
t = localtime(&tt);
|
||||
strcpy( buf, asctime( t ) );
|
||||
buf[24] = '\0'; /* nuke final newline */
|
||||
break;
|
||||
|
||||
case TIMESTAMP_PARSEABLE:
|
||||
tt = _long_to_time(timestamp->tv_sec);
|
||||
t = localtime(&tt);
|
||||
if (t->tm_year >= 100)
|
||||
t->tm_year += 1900;
|
||||
sprintf( buf, "%02dy%02dm%02dd%02dh%02dm%02ds%06ldu",
|
||||
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(pcap_t **p, char filename[])
|
||||
{
|
||||
struct timeval first_time, last_time;
|
||||
|
||||
get_file_range( filename, p, &first_time, &last_time );
|
||||
|
||||
printf( "%s\t%s\t%s\n",
|
||||
filename,
|
||||
timestamp_to_string( &first_time ),
|
||||
timestamp_to_string( &last_time ) );
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
(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);
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <net/bpf.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#ifdef SOLARIS
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef __STDC__
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#if __STDC__
|
||||
#include <stdarg.h>
|
||||
#else
|
||||
#include <varargs.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pcap.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
time_t gwtm2secs( struct tm *tm );
|
||||
|
||||
int sf_find_end( struct pcap *p, struct timeval *first_timestamp,
|
||||
struct timeval *last_timestamp );
|
||||
int sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 );
|
||||
int sf_find_packet( struct pcap *p,
|
||||
struct timeval *min_time, long min_pos,
|
||||
struct timeval *max_time, long max_pos,
|
||||
struct timeval *desired_time );
|
||||
|
||||
void error(const char *fmt, ...);
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(__GNUC__)
|
||||
static char rcsid[] =
|
||||
"@(#) $FreeBSD$ (LBL)";
|
||||
#endif
|
||||
|
||||
#include "tcpslice.h"
|
||||
|
||||
/* VARARGS */
|
||||
void
|
||||
#if __STDC__
|
||||
error(const char *fmt, ...)
|
||||
#else
|
||||
error(fmt, va_alist)
|
||||
char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
(void)fprintf(stderr, "tcpslice: ");
|
||||
#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);
|
||||
}
|
||||
exit(1);
|
||||
/* NOTREACHED */
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user