2 * Copyright (c) 1991, 1992, 1993, 1995, 1996, 1997, 1999, 2000
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * tcpslice - extract pieces of and/or glue together pcap files
30 #include <sys/types.h>
52 #define INT32_MAX (2147483647)
55 #ifdef HAVE_OS_PROTO_H
61 # ifdef HAVE_LIBOOH323C
63 # endif /* HAVE_LIBOOH323C */
64 #endif /* HAVE_LIBNIDS */
67 #include "gmt2local.h"
71 /* For Solaris before 11. */
72 /* compute a + b, store in c */
74 #define timeradd(a, b, c) { \
75 (c)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
76 (c)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
77 if ((c)->tv_usec > 1000000) { \
78 (c)->tv_usec -= 1000000; \
83 /* compute a - b, store in c */
85 #define timersub(a, b, c) { \
86 (c)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
87 if ((a)->tv_usec < (b)->tv_usec) { \
88 (c)->tv_sec -= 1; /* need to borrow */ \
89 (c)->tv_usec = ((a)->tv_usec + 1000000) - (b)->tv_usec; \
91 (c)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
96 /* The structure used to keep track of files being merged. */
98 int64_t start_pos
, /* seek position corresponding to start time */
99 stop_pos
; /* seek position corresponding to stop time */
101 file_start_time
, /* time of first pkt in file */
102 file_stop_time
, /* time of last pkt in file */
103 last_pkt_time
; /* time of most recently read pkt */
105 struct pcap_pkthdr hdr
;
111 /* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
112 * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
113 * designed to be easy to parse. The default is RAW.
115 enum stamp_styles
{ TIMESTAMP_RAW
, TIMESTAMP_READABLE
, TIMESTAMP_PARSEABLE
};
116 static enum stamp_styles timestamp_style
= TIMESTAMP_RAW
;
118 /* Let's for now define that as far as tcpslice command-line argument parsing
119 * of raw timestamps goes, valid Unix time is the non-negative range of a
120 * 32-bit signed integer. This way it is possible to validate input without
121 * knowing the size and signedness of the local time_t type. Someone please
122 * invent a better solution before year 2038.
124 #define TS_RAW_S_MAX_VALUE INT32_MAX
125 #define TS_RAW_US_MAX_DIGITS 6 /* 000000~999999 */
126 #define TS_PARSEABLE_MAX_TOKENS 7 /* ymdhmsu */
128 struct parseable_token_t
{
130 /* Bigger units must have bigger integer values for validation. */
134 MINUTE
, /* the default for "m" */
137 MONTH
, /* potentially after disambiguation */
143 static unsigned char timestamp_input_format_correct(const char *str
);
144 static struct timeval
parse_time(const char *time_string
, struct timeval base_time
);
145 static void fill_tm(const char *time_string
, const int is_delta
, struct tm
*t
, time_t *usecs_addr
);
146 static struct timeval
lowest_start_time(const struct state
*states
, int numfiles
);
147 static struct timeval
latest_end_time(const struct state
*states
, int numfiles
);
148 static struct state
*open_files(char *filenames
[], const int numfiles
);
149 static u_char
validate_files(struct state
[], const int);
150 static void close_files(struct state
[], const int);
151 static void extract_slice(struct state
*states
, const int numfiles
,
152 const char *write_file_name
,
153 const struct timeval
*start_time
, struct timeval
*stop_time
,
154 const int keep_dups
, const int relative_time_merge
,
155 const struct timeval
*base_time
);
156 static void dump_times(const struct state
*states
, int numfiles
);
157 static void print_usage(FILE *);
160 pcap_dumper_t
*global_dumper
= 0;
163 extern int optind
, opterr
;
165 extern int snaplen
; /* needed by search.c, extract_slice() */
169 main(int argc
, char **argv
)
174 int report_times
= 0;
175 int relative_time_merge
= 0;
177 char *start_time_string
= NULL
;
178 char *stop_time_string
= NULL
;
179 const char *write_file_name
= "-"; /* default is stdout */
180 struct timeval first_time
, start_time
, stop_time
;
181 char ebuf
[PCAP_ERRBUF_SIZE
];
182 struct state
*states
;
185 * On platforms where the CPU doesn't support unaligned loads,
186 * force unaligned accesses to abort with SIGBUS, rather than
187 * being fixed up (slowly) by the OS kernel; on those platforms,
188 * misaligned accesses are bugs, and we want tcpslice to crash so
189 * that the bugs are reported.
191 if (abort_on_misalignment(ebuf
, sizeof(ebuf
)) < 0)
195 while ((op
= getopt(argc
, argv
, "dDe:f:hlRrs:tvw:")) != EOF
)
207 sessions_expiration_delay
= atoi(optarg
);
211 sessions_file_format
= optarg
;
220 relative_time_merge
= 1;
225 timestamp_style
= TIMESTAMP_RAW
;
230 timestamp_style
= TIMESTAMP_READABLE
;
234 timestamp_style
= TIMESTAMP_PARSEABLE
;
235 sessions_init(optarg
);
240 timestamp_style
= TIMESTAMP_PARSEABLE
;
248 write_file_name
= optarg
;
252 (void)fprintf(stderr
, "Error: invalid command-line option and/or argument!\n");
258 if ( report_times
> 1 )
259 error( "only one of -R, -r, or -t can be specified" );
261 /* As far as command-line argument parsing is concerned, iff a string
262 * conforms to a timestamp format, it is a time argument no matter
263 * which format and what value. Whether a parseable time argument
264 * produces a valid date and a valid time is a different test a bit
265 * later with an additional input (base time) and a different error
266 * reporting. This way, the argument "25h70m80s" should always make
267 * tcpslice exit with an invalid time argument error instead of trying
268 * to open a file with that name.
271 if (timestamp_input_format_correct(argv
[optind
]))
272 start_time_string
= argv
[optind
++];
275 if (timestamp_input_format_correct(argv
[optind
]))
276 stop_time_string
= argv
[optind
++];
279 error("at least one input file must be given");
281 numfiles
= argc
- optind
;
284 keep_dups
= 1; /* no dups can occur, so don't do the work */
286 states
= open_files(&argv
[optind
], numfiles
);
287 /* validate_files() might identify multiple issues before returning. */
288 if (validate_files(states
, numfiles
))
290 first_time
= lowest_start_time(states
, numfiles
);
292 if (start_time_string
)
293 start_time
= parse_time(start_time_string
, first_time
);
295 start_time
= first_time
;
297 if (stop_time_string
)
298 stop_time
= parse_time(stop_time_string
, start_time
);
300 stop_time
= latest_end_time(states
, numfiles
);
303 dump_times(states
, numfiles
);
307 printf( "start\t%s\nstop\t%s\n",
308 timestamp_to_string( &start_time
),
309 timestamp_to_string( &stop_time
) );
312 if (! report_times
&& ! dump_flag
) {
313 if ( ! strcmp( write_file_name
, "-" ) &&
314 isatty( fileno(stdout
) ) )
315 error("stdout is a terminal; redirect or use -w");
317 extract_slice(states
, numfiles
, write_file_name
,
318 &start_time
, &stop_time
, keep_dups
, relative_time_merge
,
322 close_files (states
, numfiles
);
326 /* Test if the string has a form of "sssssssss" or "sssssssss.uuuuuu" (as
327 * discussed in the man page) and the integer part does not exceed the upper
328 * limit and the fractional part (if any) does not try to specify more
329 * precision than the format allows. Return 1 on good and 0 on bad.
332 timestamp_raw_format_correct(const char *str
)
334 enum { START
, SECONDS
, POINT
, MICROSECONDS
} fsm_state
= START
;
335 uint64_t s_value
= 0; /* Initialize to squelch a warning. */
336 unsigned us_digits
= 0; /* Initialize to squelch a warning. */
340 case START
: /* Have not seen anything yet. */
341 if (! isdigit((u_char
)*str
))
343 s_value
= *str
- '0';
346 case SECONDS
: /* Have seen one or more digits for the seconds. */
348 return 1; /* "uuuuuuuuu" */
353 if (! isdigit((u_char
)*str
) ||
354 (s_value
= s_value
* 10 + *str
- '0') > TS_RAW_S_MAX_VALUE
)
357 case POINT
: /* Have seen the decimal point. */
358 if (! isdigit((u_char
)*str
))
361 fsm_state
= MICROSECONDS
;
363 case MICROSECONDS
: /* Have seen one or more digits for the microseconds. */
365 return 1; /* "uuuuuuuuu.ssssss" */
366 if (! isdigit((u_char
)*str
) || ++us_digits
> TS_RAW_US_MAX_DIGITS
)
370 error("invalid FSM state in %s()", __func__
);
371 } /* switch (fsm_state) */
376 /* Try to read one complete token (amount, unit) from the given string into
377 * the provided structure by advancing the pointer and consuming characters.
378 * Accept the unit characters in both lowercase and uppercase to be consistent
379 * with the undocumented behaviour of fill_tm(). Do not check whether the
380 * amount is valid for the unit. Return the advanced pointer on success or
384 parse_token(const char *str
, struct parseable_token_t
*token
)
386 enum { START
, AMOUNT
, UNIT
} fsm_state
= START
;
387 uint64_t amount
= 0; /* Initialize to squelch a warning. */
388 char char_unit
= 0; /* Initialize to squelch a warning. */
392 case START
: /* Have not seen anything yet. */
393 if (! isdigit((u_char
)*str
))
398 case AMOUNT
: /* Have seen one or more digits for the amount. */
399 if (isalpha((u_char
)*str
)) {
400 token
->amount
= amount
;
401 char_unit
= tolower((u_char
)*str
);
405 if (! isdigit((u_char
)*str
) ||
406 (amount
= amount
* 10 + *str
- '0') > INT32_MAX
)
409 case UNIT
: /* Have seen a character, could be a valid unit. */
422 token
->unit
= MINUTE
;
425 token
->unit
= SECOND
;
428 token
->unit
= MICROSECOND
;
432 } /* switch (char_unit) */
435 error("invalid FSM state in %s()", __func__
);
436 } /* switch (fsm_state) */
441 /* Test if the string conforms to the "ymdhmsu" format (as discussed in the
442 * man page). Do not test individual amounts to be valid for their time units
443 * or the date to be a valid date or the time to be a valid time. Return 1 on
447 timestamp_parseable_format_correct(const char *str
)
449 struct parseable_token_t token
[TS_PARSEABLE_MAX_TOKENS
];
450 unsigned numtokens
= 0;
452 /* Try to tokenize the full string, fail as early as possible. */
453 while (*str
!= '\0') {
454 if (numtokens
== TS_PARSEABLE_MAX_TOKENS
||
455 (str
= parse_token(str
, token
+ numtokens
)) == NULL
)
462 /* Disambiguate "m". */
463 for (i
= 0; i
< numtokens
- 1; i
++)
464 if (token
[i
].unit
== MINUTE
&& token
[i
+ 1].unit
== DAY
)
465 token
[i
].unit
= MONTH
;
466 /* Require each time unit in the vector to appear at most once
467 * and in the order of strictly decreasing magnitude.
469 for (i
= 0; i
< numtokens
- 1; i
++)
470 if (token
[i
].unit
<= token
[i
+ 1].unit
)
474 return numtokens
> 0;
477 /* Test if the string conforms to a valid input time format (with an optional
478 * leading "+"). Return 1 on good and 0 on bad.
481 timestamp_input_format_correct(const char *str
)
485 return timestamp_parseable_format_correct(str
) ||
486 timestamp_raw_format_correct(str
);
489 /* No-op iff the date and the time in the given broken-down time are valid
490 * and the year is within the [1970, 2069] range declared in the man page.
493 assert_valid_tm(const struct tm t
)
497 /* yy: [70, 99] in tm_year means [1970, 1999]
498 * yy: [0, 69] in tm_year means [2000, 2069]
499 * yyyy: [70, 99] in tm_year means [1970, 1999].
500 * yyyy: [100, 169] in tm_year means [2000, 2069].
502 year
= 1900 + t
.tm_year
;
505 if (year
< 1970 || year
> 2069)
506 error("year %d is not valid\n", year
);
508 if (t
.tm_mon
< 0 || t
.tm_mon
> 11) /* 11 is December */
509 error("month %d is not valid\n", t
.tm_mon
+ 1);
511 maxdays
= days_in_month
[t
.tm_mon
];
512 if (t
.tm_mon
== 1 && IS_LEAP_YEAR(year
)) /* 1 is February */
514 if (t
.tm_mday
< 1 || t
.tm_mday
> maxdays
)
515 error("day %d is not valid\n", t
.tm_mday
);
517 if (t
.tm_hour
< 0 || t
.tm_hour
> 23)
518 error("hour %d is not valid\n", t
.tm_hour
);
520 if (t
.tm_min
< 0 || t
.tm_min
> 59)
521 error("minute %d is not valid\n", t
.tm_min
);
523 if (t
.tm_sec
< 0 || t
.tm_sec
> 59)
524 error("second %d is not valid\n", t
.tm_sec
);
527 /* Given a string specifying a time (or a time offset) and a "base time"
528 * from which to compute offsets and fill in defaults, returns a timeval
529 * containing the specified time.
531 static struct timeval
532 parse_time(const char *time_string
, struct timeval base_time
)
534 struct tm
*bt
= localtime((time_t *) &base_time
.tv_sec
);
536 struct timeval result
;
538 int is_delta
= (time_string
[0] == '+');
541 ++time_string
; /* skip over '+' sign */
543 if (timestamp_raw_format_correct(time_string
))
544 { /* interpret as a raw timestamp or timestamp offset */
547 result
.tv_sec
= atoi( time_string
);
548 time_ptr
= strchr( time_string
, '.' );
551 { /* microseconds are specified, too */
552 int num_digits
= strlen( time_ptr
+ 1 );
553 result
.tv_usec
= atoi( time_ptr
+ 1 );
555 /* turn 123.456 into 123 seconds plus 456000 usec */
556 while ( num_digits
++ < 6 )
557 result
.tv_usec
*= 10;
564 timeradd(&result
, &base_time
, &result
);
571 usecs
= base_time
.tv_usec
;
573 /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
574 * under older systems) */
575 memset((char *)&t
, 0, sizeof(t
));
577 /* Set values to "not set" flag so we can later identify
580 t
.tm_sec
= t
.tm_min
= t
.tm_hour
= t
.tm_mday
= t
.tm_mon
=
584 /* Terminate the program on error. */
585 fill_tm(time_string
, is_delta
, &t
, &usecs
);
587 /* Now until we reach a field that was specified, fill in the
588 * missing fields from the base time.
590 #define CHECK_FIELD(field_name) \
591 if (t.field_name < 0) \
592 t.field_name = bt->field_name; \
596 do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
597 CHECK_FIELD(tm_year
);
599 CHECK_FIELD(tm_mday
);
600 CHECK_FIELD(tm_hour
);
605 /* Set remaining unspecified fields to 0. */
606 #define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
607 if (t.field_name < 0) \
608 t.field_name = zero_val
611 ZERO_FIELD_IF_NOT_SET(tm_year
,90); /* should never happen */
612 ZERO_FIELD_IF_NOT_SET(tm_mon
,0);
613 ZERO_FIELD_IF_NOT_SET(tm_mday
,1);
614 ZERO_FIELD_IF_NOT_SET(tm_hour
,0);
615 ZERO_FIELD_IF_NOT_SET(tm_min
,0);
616 ZERO_FIELD_IF_NOT_SET(tm_sec
,0);
619 assert_valid_tm(t
); /* Terminate the program on error. */
620 result
.tv_sec
= gwtm2secs(&t
);
621 result
.tv_sec
-= gmt2local(result
.tv_sec
);
622 result
.tv_usec
= usecs
;
627 /* Fill in (or add to, if is_delta is true) the time values in the
628 * tm struct "t" as specified by the time specified in the string
629 * "time_string". "usecs_addr" is updated with the specified number
630 * of microseconds, if any.
633 fill_tm(const char *time_string
, const int is_delta
, struct tm
*t
, time_t *usecs_addr
)
635 const char *t_start
, *t_stop
;
637 #define SET_VAL(lhs,rhs) \
643 /* Loop through the time string parsing one specification at
644 * a time. Each specification has the form <number><letter>
645 * where <number> indicates the amount of time and <letter>
648 for (t_stop
= t_start
= time_string
; *t_start
; t_start
= ++t_stop
) {
649 if (! isdigit((u_char
)*t_start
))
650 error("bad date format %s, problem starting at %s",
651 time_string
, t_start
);
653 while (isdigit((u_char
)*t_stop
))
656 error("bad date format %s, problem starting at %s",
657 time_string
, t_start
);
659 int val
= atoi(t_start
);
661 char format_ch
= *t_stop
;
662 if (isupper((u_char
)format_ch
))
663 format_ch
= tolower((u_char
)format_ch
);
667 if ( val
>= 100 && val
< 1970)
668 error("Can't handle year %d\n", val
);
671 SET_VAL(t
->tm_year
, val
);
675 if (strchr(t_stop
+1, 'D') ||
676 strchr(t_stop
+1, 'd'))
678 SET_VAL(t
->tm_mon
, val
- 1);
679 else /* it's minutes */
680 SET_VAL(t
->tm_min
, val
);
684 SET_VAL(t
->tm_mday
, val
);
688 SET_VAL(t
->tm_hour
, val
);
692 SET_VAL(t
->tm_sec
, val
);
696 SET_VAL(*usecs_addr
, val
);
701 "bad date format %s, problem starting at %s",
702 time_string
, t_start
);
707 /* Of all the files, what is the lowest start time. */
708 static struct timeval
709 lowest_start_time(const struct state
*states
, int numfiles
)
711 struct timeval min_time
= states
->file_start_time
;
714 if (sf_timestamp_less_than(&states
->file_start_time
, &min_time
)) {
715 min_time
= states
->file_start_time
;
722 /* Of all the files, what is the latest end time. */
723 static struct timeval
724 latest_end_time(const struct state
*states
, int numfiles
)
726 struct timeval max_time
= states
->file_stop_time
;
729 if (sf_timestamp_less_than(&max_time
, &states
->file_stop_time
)) {
730 max_time
= states
->file_stop_time
;
737 /* Get the next record in a file. Deal with end of file.
739 * This routine also prevents time from going "backwards"
740 * within a single file.
743 get_next_packet(struct state
*s
)
745 struct timeval tvbuf
;
748 s
->pkt
= pcap_next(s
->p
, &s
->hdr
);
755 TIMEVAL_FROM_PKTHDR_TS(tvbuf
, s
->hdr
.ts
);
756 } while ((! s
->done
) &&
757 sf_timestamp_less_than(&tvbuf
, &s
->last_pkt_time
));
759 s
->last_pkt_time
= tvbuf
;
762 static struct state
*
763 open_files(char *filenames
[], const int numfiles
)
765 struct state
*states
;
767 char errbuf
[PCAP_ERRBUF_SIZE
];
771 error("no input files specified");
773 /* allocate memory for all the files */
774 states
= (struct state
*) malloc(sizeof(struct state
) * numfiles
);
776 error("unable to allocate memory for %d input files", numfiles
);
777 memset(states
, 0, sizeof(struct state
) * numfiles
);
779 for (i
= 0; i
< numfiles
; ++i
) {
781 s
->filename
= filenames
[i
];
782 s
->p
= pcap_open_offline(s
->filename
, errbuf
);
784 error( "bad pcap file %s: %s", s
->filename
, errbuf
);
786 #ifdef HAVE_POSIX_FADVISE
787 FILE *pf
= pcap_file(s
->p
);
789 error("pcap_file() failed");
792 error("fileno() failed: %s", strerror(errno
));
794 if (0 != (padv_err
= posix_fadvise(fd
, 0, 0, POSIX_FADV_RANDOM
)))
795 warning("warning: posix_fadvise() failed: %s", strerror(padv_err
));
796 if (0 != (padv_err
= posix_fadvise(fd
, 0, 0, POSIX_FADV_NOREUSE
)))
797 warning("warning: posix_fadvise() failed: %s", strerror(padv_err
));
801 sessions_nids_init(s
->p
);
803 int this_snap
= pcap_snapshot( s
->p
);
804 if (this_snap
> snaplen
) {
808 s
->start_pos
= ftell64( pcap_file( s
->p
) );
810 if (pcap_next(s
->p
, &s
->hdr
) == NULL
)
811 error( "error reading packet in %s: %s",
812 s
->filename
, pcap_geterr( s
->p
) );
814 TIMEVAL_FROM_PKTHDR_TS(s
->file_start_time
, s
->hdr
.ts
);
816 if ( ! sf_find_end( s
->p
, &s
->file_start_time
,
817 &s
->file_stop_time
) )
818 error( "problems finding end packet of file %s",
821 s
->stop_pos
= ftell64( pcap_file( s
->p
) );
827 /* Return 0 on no errors. */
829 validate_files(struct state states
[], const int numfiles
)
832 int i
, first_dlt
, this_dlt
;
834 for (i
= 0; i
< numfiles
; i
++) {
835 this_dlt
= pcap_datalink(states
[i
].p
);
837 first_dlt
= this_dlt
;
838 else if (first_dlt
!= this_dlt
) {
839 warning("file '%s' uses DLT %d, and the first file '%s' uses DLT %d",
840 states
[i
].filename
, this_dlt
, states
[0].filename
, first_dlt
);
844 /* Do a minimal sanity check of the timestamps. */
845 if (sf_timestamp_less_than(&states
[i
].file_stop_time
,
846 &states
[i
].file_start_time
)) {
847 warning("'%s' has the last timestamp before the first timestamp",
856 close_files(struct state states
[], const int numfiles
)
860 for (i
= 0; i
< numfiles
; i
++)
862 pcap_close(states
[i
].p
);
867 * Extract from a given set of files all packets with timestamps between
868 * the two time values given (inclusive). These packets are written
869 * to the save file given by write_file_name.
871 * Upon return, start_time is adjusted to reflect a time just after
872 * that of the last packet written to the output.
875 extract_slice(struct state
*states
, const int numfiles
, const char *write_file_name
,
876 const struct timeval
*start_time
, struct timeval
*stop_time
,
877 const int keep_dups
, const int relative_time_merge
,
878 const struct timeval
*base_time
)
880 struct state
*s
, *min_state
;
881 struct timeval temp1
, temp2
, relative_start
, relative_stop
;
884 struct state
*last_state
; /* remember the last packet */
885 struct pcap_pkthdr last_hdr
; /* in order to remove duplicates */
889 error("no input files specified");
892 last_hdr
.ts
.tv_sec
= last_hdr
.ts
.tv_usec
= 0;
893 last_hdr
.caplen
= last_hdr
.len
= 0;
894 last_pkt
= (u_char
*) malloc(snaplen
);
897 error("out of memory");
899 memset(last_pkt
, 0, snaplen
);
901 timersub(start_time
, base_time
, &relative_start
);
902 timersub(stop_time
, base_time
, &relative_stop
);
904 /* Always write the output file, use the first input file's DLT. */
905 global_dumper
= pcap_dump_open(states
[0].p
, write_file_name
);
906 if (!global_dumper
) {
907 error("error creating output file '%s': %s",
908 write_file_name
, pcap_geterr(states
[0].p
));
911 for (i
= 0; i
< numfiles
; ++i
) {
914 /* compute the first packet time within *this* file */
915 if (relative_time_merge
) {
916 /* relative time within this file */
917 timeradd(&s
->file_start_time
, &relative_start
, &temp1
);
923 /* check if this file has *anything* for us ... */
924 if (sf_timestamp_less_than(&s
->file_stop_time
, &temp1
)) {
925 /* there aren't any packets of interest in this file */
932 * sf_find_packet() requires that the time it's passed as
933 * its last argument be in the range [min_time, max_time],
934 * so we enforce that constraint here.
937 if (sf_timestamp_less_than(&temp1
, &s
->file_start_time
)){
938 temp1
= s
->file_start_time
;
941 sf_find_packet(s
->p
, &s
->file_start_time
, s
->start_pos
,
942 &s
->file_stop_time
, s
->stop_pos
,
945 /* get first packet for this file */
951 * Now, loop through all the packets in all the files,
952 * putting packets out in timestamp order.
954 * Quite often, the files will not have overlapping
955 * timestamps, so it would be nice to try to deal
956 * efficiently with that situation. (XXX)
960 struct timeval tvbuf
;
963 for (i
= 0; i
< numfiles
; ++i
) {
969 if (relative_time_merge
) {
970 /* compare *relative* times */
972 &s
->file_start_time
, &temp1
);
973 timersub(&min_state
->hdr
.ts
,
974 &min_state
->file_start_time
, &temp2
);
976 /* compare *absolute* times */
977 TIMEVAL_FROM_PKTHDR_TS(temp1
, s
->hdr
.ts
);
978 TIMEVAL_FROM_PKTHDR_TS(temp2
, min_state
->hdr
.ts
);
980 if (sf_timestamp_less_than( &temp1
, &temp2
))
986 break; /* didn't find any !done files */
988 if (relative_time_merge
) {
989 /* relative time w/in this file */
990 timeradd(&min_state
->file_start_time
, &relative_stop
, &temp1
);
992 /* take absolute times */
995 TIMEVAL_FROM_PKTHDR_TS(tvbuf
, min_state
->hdr
.ts
);
996 if (sf_timestamp_less_than(&temp1
, &tvbuf
)) {
997 if (!sessions_count
) {
998 /* We've gone beyond the end of the region
999 * of interest ... We're done with this file.
1003 min_state
->done
= 1;
1004 pcap_close(min_state
->p
);
1007 /* We need to wait for the sessions to close */
1009 *stop_time
= min_state
->file_stop_time
;
1013 if (relative_time_merge
) {
1014 timersub(&min_state
->hdr
.ts
, &min_state
->file_start_time
, &temp1
);
1015 timeradd(&temp1
, base_time
, &min_state
->hdr
.ts
);
1019 /* Keep track of sessions, if specified by the user */
1020 if (track_sessions
&& min_state
->hdr
.caplen
) {
1022 * Copy the packet buffer to deconstify it for the function.
1024 u_char
*pkt_copy
= malloc(min_state
->hdr
.caplen
);
1027 error("malloc() failed in %s()", __func__
);
1028 memcpy(pkt_copy
, min_state
->pkt
, min_state
->hdr
.caplen
);
1029 nids_pcap_handler((u_char
*)min_state
->p
, &min_state
->hdr
, pkt_copy
);
1034 /* Dump it, unless it's a duplicate. */
1037 min_state
== last_state
||
1038 memcmp(&last_hdr
, &min_state
->hdr
, sizeof(last_hdr
)) ||
1039 memcmp(last_pkt
, min_state
->pkt
, last_hdr
.caplen
) ) {
1040 pcap_dump((u_char
*) global_dumper
, &min_state
->hdr
, min_state
->pkt
);
1042 if ( ! keep_dups
) {
1043 last_state
= min_state
;
1044 last_hdr
= min_state
->hdr
;
1045 memcpy(last_pkt
, min_state
->pkt
, min_state
->hdr
.caplen
);
1049 get_next_packet(min_state
);
1052 pcap_dump_close(global_dumper
);
1056 /* Translates a timestamp to the time format specified by the user.
1057 * Returns a pointer to the translation residing in a static buffer.
1058 * There are two such buffers, which are alternated on subsequent
1059 * calls, so two calls may be made to this routine without worrying
1060 * about the results of the first call being overwritten by the
1061 * results of the second.
1064 timestamp_to_string(const struct timeval
*timestamp
)
1067 #define NUM_BUFFERS 2
1068 #define DATEBUFSIZE 128
1069 static char buffers
[NUM_BUFFERS
][DATEBUFSIZE
];
1070 static int buffer_to_use
= 0;
1073 buf
= buffers
[buffer_to_use
];
1074 buffer_to_use
= (buffer_to_use
+ 1) % NUM_BUFFERS
;
1076 switch ( timestamp_style
) {
1079 snprintf( buf
, DATEBUFSIZE
, "%u.%06u",
1080 (u_int32_t
)timestamp
->tv_sec
,
1081 (u_int32_t
)timestamp
->tv_usec
);
1084 case TIMESTAMP_READABLE
:
1085 t
= localtime((const time_t *) ×tamp
->tv_sec
);
1086 /* Mimic asctime() with C99 format specifiers. */
1087 strftime(buf
, DATEBUFSIZE
, "%a %b %e %T %Y", t
);
1090 case TIMESTAMP_PARSEABLE
:
1091 t
= localtime((const time_t *) ×tamp
->tv_sec
);
1092 snprintf( buf
, DATEBUFSIZE
, "%04dy%02dm%02dd%02dh%02dm%02ds%06uu",
1093 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
1094 t
->tm_hour
, t
->tm_min
, t
->tm_sec
,
1095 (u_int32_t
)timestamp
->tv_usec
);
1103 /* Given a pcap save filename, reports on the times of the first
1104 * and last packets in the file.
1107 dump_times(const struct state
*states
, int numfiles
)
1109 for (; numfiles
--; states
++) {
1110 printf( "%s\t%s\t%s\n",
1112 timestamp_to_string( &states
->file_start_time
),
1113 timestamp_to_string( &states
->file_stop_time
) );
1118 print_usage(FILE *f
)
1120 #ifndef HAVE_PCAP_LIB_VERSION
1121 #ifdef HAVE_PCAP_VERSION
1122 extern char pcap_version
[];
1123 #else /* HAVE_PCAP_VERSION */
1124 static char pcap_version
[] = "unknown";
1125 #endif /* HAVE_PCAP_VERSION */
1126 #endif /* HAVE_PCAP_LIB_VERSION */
1128 (void)fprintf(f
, "tcpslice version %s\n", PACKAGE_VERSION
);
1129 #ifdef HAVE_PCAP_LIB_VERSION
1130 (void)fprintf(f
, "%s\n", pcap_lib_version());
1131 #else /* HAVE_PCAP_LIB_VERSION */
1132 (void)fprintf(f
, "libpcap version %s\n", pcap_version
);
1133 #endif /* HAVE_PCAP_LIB_VERSION */
1136 (void)fprintf(f
, "libnids version %u.%u\n", NIDS_MAJOR
, NIDS_MINOR
);
1138 #ifdef HAVE_LIBOSIPPARSER2
1139 (void)fprintf(f
, "libosip2 unknown version\n");
1140 #endif /* HAVE_LIBOSIPPARSER2 */
1142 #ifdef HAVE_LIBOOH323C
1143 (void)fprintf(f
, "libooh323c %s\n", OOH323C_VERSION
);
1144 #endif /* HAVE_LIBOOH323C */
1146 #endif /* HAVE_LIBNIDS */
1149 "Usage: tcpslice [-DdhlRrtv] [-w file]\n"
1150 " [ -s types [ -e seconds ] [ -f format ] ]\n"
1151 " [start-time [end-time]] file ... \n");