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
28 // For fileno() and getopt().
29 #if defined(__SUNPRO_C) && ! defined(__EXTENSIONS__)
30 #define __EXTENSIONS__
33 #include <sys/types.h>
50 #ifdef HAVE_OS_PROTO_H
56 # ifdef HAVE_LIBOOH323C
58 # endif /* HAVE_LIBOOH323C */
59 #endif /* HAVE_LIBNIDS */
62 #include "gmt2local.h"
65 /* For Solaris before 11. */
66 /* compute a + b, store in c */
68 #define timeradd(a, b, c) { \
69 (c)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
70 (c)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
71 if ((c)->tv_usec > 1000000) { \
72 (c)->tv_usec -= 1000000; \
77 /* compute a - b, store in c */
79 #define timersub(a, b, c) { \
80 (c)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
81 if ((a)->tv_usec < (b)->tv_usec) { \
82 (c)->tv_sec -= 1; /* need to borrow */ \
83 (c)->tv_usec = ((a)->tv_usec + 1000000) - (b)->tv_usec; \
85 (c)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
90 /* The structure used to keep track of files being merged. */
92 int64_t start_pos
, /* seek position corresponding to start time */
93 stop_pos
; /* seek position corresponding to stop time */
95 file_start_time
, /* time of first pkt in file */
96 file_stop_time
, /* time of last pkt in file */
97 last_pkt_time
; /* time of most recently read pkt */
99 struct pcap_pkthdr hdr
;
105 /* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
106 * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
107 * designed to be easy to parse. The default is RAW.
109 enum stamp_styles
{ TIMESTAMP_RAW
, TIMESTAMP_READABLE
, TIMESTAMP_PARSEABLE
};
110 static enum stamp_styles timestamp_style
= TIMESTAMP_RAW
;
112 /* Let's for now define that as far as tcpslice command-line argument parsing
113 * of raw timestamps goes, valid Unix time is the non-negative range of a
114 * 32-bit signed integer. This way it is possible to validate input without
115 * knowing the size and signedness of the local time_t type. Someone please
116 * invent a better solution before year 2038.
118 #define TS_RAW_S_MAX_VALUE INT32_MAX
119 #define TS_RAW_US_MAX_DIGITS 6 /* 000000~999999 */
120 #define TS_PARSEABLE_MAX_TOKENS 7 /* ymdhmsu */
122 struct parseable_token_t
{
124 /* Bigger units must have bigger integer values for validation. */
128 MINUTE
, /* the default for "m" */
131 MONTH
, /* potentially after disambiguation */
137 static unsigned char timestamp_input_format_correct(const char *str
);
138 static struct timeval
parse_time(const char *time_string
, struct timeval base_time
);
139 static void fill_tm(const char *time_string
, const int is_delta
, struct tm
*t
, time_t *usecs_addr
);
140 static struct timeval
lowest_start_time(const struct state
*states
, int numfiles
);
141 static struct timeval
latest_end_time(const struct state
*states
, int numfiles
);
142 static struct state
*open_files(char *filenames
[], const int numfiles
);
143 static u_char
validate_files(struct state
[], const int);
144 static void close_files(struct state
[], const int);
145 static void extract_slice(struct state
*states
, const int numfiles
,
146 const char *write_file_name
,
147 const struct timeval
*start_time
, struct timeval
*stop_time
,
148 const int keep_dups
, const int relative_time_merge
,
149 const struct timeval
*base_time
);
150 static void dump_times(const struct state
*states
, int numfiles
);
151 static void print_usage(FILE *);
154 pcap_dumper_t
*global_dumper
= 0;
157 extern int optind
, opterr
;
159 extern int snaplen
; /* needed by search.c, extract_slice() */
163 main(int argc
, char **argv
)
168 int report_times
= 0;
169 int relative_time_merge
= 0;
171 char *start_time_string
= NULL
;
172 char *stop_time_string
= NULL
;
173 const char *write_file_name
= "-"; /* default is stdout */
174 struct timeval first_time
, start_time
, stop_time
;
175 struct state
*states
;
178 while ((op
= getopt(argc
, argv
, "dDe:f:hlRrs:tvw:")) != EOF
)
190 sessions_expiration_delay
= atoi(optarg
);
194 sessions_file_format
= optarg
;
203 relative_time_merge
= 1;
208 timestamp_style
= TIMESTAMP_RAW
;
213 timestamp_style
= TIMESTAMP_READABLE
;
217 timestamp_style
= TIMESTAMP_PARSEABLE
;
218 sessions_init(optarg
);
223 timestamp_style
= TIMESTAMP_PARSEABLE
;
231 write_file_name
= optarg
;
235 (void)fprintf(stderr
, "Error: invalid command-line option and/or argument!\n");
241 if ( report_times
> 1 )
242 error( "only one of -R, -r, or -t can be specified" );
244 /* As far as command-line argument parsing is concerned, iff a string
245 * conforms to a timestamp format, it is a time argument no matter
246 * which format and what value. Whether a parseable time argument
247 * produces a valid date and a valid time is a different test a bit
248 * later with an additional input (base time) and a different error
249 * reporting. This way, the argument "25h70m80s" should always make
250 * tcpslice exit with an invalid time argument error instead of trying
251 * to open a file with that name.
254 if (timestamp_input_format_correct(argv
[optind
]))
255 start_time_string
= argv
[optind
++];
258 if (timestamp_input_format_correct(argv
[optind
]))
259 stop_time_string
= argv
[optind
++];
262 error("at least one input file must be given");
264 numfiles
= argc
- optind
;
267 keep_dups
= 1; /* no dups can occur, so don't do the work */
269 states
= open_files(&argv
[optind
], numfiles
);
270 /* validate_files() might identify multiple issues before returning. */
271 if (validate_files(states
, numfiles
))
273 first_time
= lowest_start_time(states
, numfiles
);
275 if (start_time_string
)
276 start_time
= parse_time(start_time_string
, first_time
);
278 start_time
= first_time
;
280 if (stop_time_string
)
281 stop_time
= parse_time(stop_time_string
, start_time
);
283 stop_time
= latest_end_time(states
, numfiles
);
286 dump_times(states
, numfiles
);
290 printf( "start\t%s\nstop\t%s\n",
291 timestamp_to_string( &start_time
),
292 timestamp_to_string( &stop_time
) );
295 if (! report_times
&& ! dump_flag
) {
296 if ( ! strcmp( write_file_name
, "-" ) &&
297 isatty( fileno(stdout
) ) )
298 error("stdout is a terminal; redirect or use -w");
300 extract_slice(states
, numfiles
, write_file_name
,
301 &start_time
, &stop_time
, keep_dups
, relative_time_merge
,
305 close_files (states
, numfiles
);
309 /* Test if the string has a form of "sssssssss" or "sssssssss.uuuuuu" (as
310 * discussed in the man page) and the integer part does not exceed the upper
311 * limit and the fractional part (if any) does not try to specify more
312 * precision than the format allows. Return 1 on good and 0 on bad.
315 timestamp_raw_format_correct(const char *str
)
317 enum { START
, SECONDS
, POINT
, MICROSECONDS
} fsm_state
= START
;
318 uint64_t s_value
= 0; /* Initialize to squelch a warning. */
319 unsigned us_digits
= 0; /* Initialize to squelch a warning. */
323 case START
: /* Have not seen anything yet. */
324 if (! isdigit((u_char
)*str
))
326 s_value
= *str
- '0';
329 case SECONDS
: /* Have seen one or more digits for the seconds. */
331 return 1; /* "uuuuuuuuu" */
336 if (! isdigit((u_char
)*str
) ||
337 (s_value
= s_value
* 10 + *str
- '0') > TS_RAW_S_MAX_VALUE
)
340 case POINT
: /* Have seen the decimal point. */
341 if (! isdigit((u_char
)*str
))
344 fsm_state
= MICROSECONDS
;
346 case MICROSECONDS
: /* Have seen one or more digits for the microseconds. */
348 return 1; /* "uuuuuuuuu.ssssss" */
349 if (! isdigit((u_char
)*str
) || ++us_digits
> TS_RAW_US_MAX_DIGITS
)
353 error("invalid FSM state in %s()", __func__
);
354 } /* switch (fsm_state) */
359 /* Try to read one complete token (amount, unit) from the given string into
360 * the provided structure by advancing the pointer and consuming characters.
361 * Accept the unit characters in both lowercase and uppercase to be consistent
362 * with the undocumented behaviour of fill_tm(). Do not check whether the
363 * amount is valid for the unit. Return the advanced pointer on success or
367 parse_token(const char *str
, struct parseable_token_t
*token
)
369 enum { START
, AMOUNT
, UNIT
} fsm_state
= START
;
370 uint64_t amount
= 0; /* Initialize to squelch a warning. */
371 char char_unit
= 0; /* Initialize to squelch a warning. */
375 case START
: /* Have not seen anything yet. */
376 if (! isdigit((u_char
)*str
))
381 case AMOUNT
: /* Have seen one or more digits for the amount. */
382 if (isalpha((u_char
)*str
)) {
383 token
->amount
= amount
;
384 char_unit
= tolower((u_char
)*str
);
388 if (! isdigit((u_char
)*str
) ||
389 (amount
= amount
* 10 + *str
- '0') > INT32_MAX
)
392 case UNIT
: /* Have seen a character, could be a valid unit. */
405 token
->unit
= MINUTE
;
408 token
->unit
= SECOND
;
411 token
->unit
= MICROSECOND
;
415 } /* switch (char_unit) */
418 error("invalid FSM state in %s()", __func__
);
419 } /* switch (fsm_state) */
424 /* Test if the string conforms to the "ymdhmsu" format (as discussed in the
425 * man page). Do not test individual amounts to be valid for their time units
426 * or the date to be a valid date or the time to be a valid time. Return 1 on
430 timestamp_parseable_format_correct(const char *str
)
432 struct parseable_token_t token
[TS_PARSEABLE_MAX_TOKENS
];
433 unsigned numtokens
= 0;
435 /* Try to tokenize the full string, fail as early as possible. */
436 while (*str
!= '\0') {
437 if (numtokens
== TS_PARSEABLE_MAX_TOKENS
||
438 (str
= parse_token(str
, token
+ numtokens
)) == NULL
)
445 /* Disambiguate "m". */
446 for (i
= 0; i
< numtokens
- 1; i
++)
447 if (token
[i
].unit
== MINUTE
&& token
[i
+ 1].unit
== DAY
)
448 token
[i
].unit
= MONTH
;
449 /* Require each time unit in the vector to appear at most once
450 * and in the order of strictly decreasing magnitude.
452 for (i
= 0; i
< numtokens
- 1; i
++)
453 if (token
[i
].unit
<= token
[i
+ 1].unit
)
457 return numtokens
> 0;
460 /* Test if the string conforms to a valid input time format (with an optional
461 * leading "+"). Return 1 on good and 0 on bad.
464 timestamp_input_format_correct(const char *str
)
468 return timestamp_parseable_format_correct(str
) ||
469 timestamp_raw_format_correct(str
);
472 /* No-op iff the date and the time in the given broken-down time are valid
473 * and the year is within the [1970, 2069] range declared in the man page.
476 assert_valid_tm(const struct tm t
)
480 /* yy: [70, 99] in tm_year means [1970, 1999]
481 * yy: [0, 69] in tm_year means [2000, 2069]
482 * yyyy: [70, 99] in tm_year means [1970, 1999].
483 * yyyy: [100, 169] in tm_year means [2000, 2069].
485 year
= 1900 + t
.tm_year
;
488 if (year
< 1970 || year
> 2069)
489 error("year %d is not valid\n", year
);
491 if (t
.tm_mon
< 0 || t
.tm_mon
> 11) /* 11 is December */
492 error("month %d is not valid\n", t
.tm_mon
+ 1);
494 maxdays
= days_in_month
[t
.tm_mon
];
495 if (t
.tm_mon
== 1 && IS_LEAP_YEAR(year
)) /* 1 is February */
497 if (t
.tm_mday
< 1 || t
.tm_mday
> maxdays
)
498 error("day %d is not valid\n", t
.tm_mday
);
500 if (t
.tm_hour
< 0 || t
.tm_hour
> 23)
501 error("hour %d is not valid\n", t
.tm_hour
);
503 if (t
.tm_min
< 0 || t
.tm_min
> 59)
504 error("minute %d is not valid\n", t
.tm_min
);
506 if (t
.tm_sec
< 0 || t
.tm_sec
> 59)
507 error("second %d is not valid\n", t
.tm_sec
);
510 /* Given a string specifying a time (or a time offset) and a "base time"
511 * from which to compute offsets and fill in defaults, returns a timeval
512 * containing the specified time.
514 static struct timeval
515 parse_time(const char *time_string
, struct timeval base_time
)
517 struct tm
*bt
= localtime((time_t *) &base_time
.tv_sec
);
519 struct timeval result
;
521 int is_delta
= (time_string
[0] == '+');
524 ++time_string
; /* skip over '+' sign */
526 if (timestamp_raw_format_correct(time_string
))
527 { /* interpret as a raw timestamp or timestamp offset */
530 result
.tv_sec
= atoi( time_string
);
531 time_ptr
= strchr( time_string
, '.' );
534 { /* microseconds are specified, too */
535 int num_digits
= strlen( time_ptr
+ 1 );
536 result
.tv_usec
= atoi( time_ptr
+ 1 );
538 /* turn 123.456 into 123 seconds plus 456000 usec */
539 while ( num_digits
++ < TS_RAW_US_MAX_DIGITS
)
540 result
.tv_usec
*= 10;
547 timeradd(&result
, &base_time
, &result
);
554 usecs
= base_time
.tv_usec
;
556 /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
557 * under older systems) */
558 memset((char *)&t
, 0, sizeof(t
));
560 /* Set values to "not set" flag so we can later identify
563 t
.tm_sec
= t
.tm_min
= t
.tm_hour
= t
.tm_mday
= t
.tm_mon
=
567 /* Terminate the program on error. */
568 fill_tm(time_string
, is_delta
, &t
, &usecs
);
570 /* Now until we reach a field that was specified, fill in the
571 * missing fields from the base time.
573 #define CHECK_FIELD(field_name) \
574 if (t.field_name < 0) \
575 t.field_name = bt->field_name; \
579 do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
580 CHECK_FIELD(tm_year
);
582 CHECK_FIELD(tm_mday
);
583 CHECK_FIELD(tm_hour
);
588 /* Set remaining unspecified fields to 0. */
589 #define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
590 if (t.field_name < 0) \
591 t.field_name = zero_val
594 ZERO_FIELD_IF_NOT_SET(tm_year
,90); /* should never happen */
595 ZERO_FIELD_IF_NOT_SET(tm_mon
,0);
596 ZERO_FIELD_IF_NOT_SET(tm_mday
,1);
597 ZERO_FIELD_IF_NOT_SET(tm_hour
,0);
598 ZERO_FIELD_IF_NOT_SET(tm_min
,0);
599 ZERO_FIELD_IF_NOT_SET(tm_sec
,0);
602 assert_valid_tm(t
); /* Terminate the program on error. */
603 result
.tv_sec
= gwtm2secs(&t
);
604 result
.tv_sec
-= gmt2local(result
.tv_sec
);
605 result
.tv_usec
= usecs
;
610 /* Fill in (or add to, if is_delta is true) the time values in the
611 * tm struct "t" as specified by the time specified in the string
612 * "time_string". "usecs_addr" is updated with the specified number
613 * of microseconds, if any.
616 fill_tm(const char *time_string
, const int is_delta
, struct tm
*t
, time_t *usecs_addr
)
618 const char *t_start
, *t_stop
;
620 #define SET_VAL(lhs,rhs) \
626 /* Loop through the time string parsing one specification at
627 * a time. Each specification has the form <number><letter>
628 * where <number> indicates the amount of time and <letter>
631 for (t_stop
= t_start
= time_string
; *t_start
; t_start
= ++t_stop
) {
632 if (! isdigit((u_char
)*t_start
))
633 error("bad date format %s, problem starting at %s",
634 time_string
, t_start
);
636 while (isdigit((u_char
)*t_stop
))
639 error("bad date format %s, problem starting at %s",
640 time_string
, t_start
);
642 int val
= atoi(t_start
);
644 char format_ch
= *t_stop
;
645 if (isupper((u_char
)format_ch
))
646 format_ch
= tolower((u_char
)format_ch
);
650 if ( val
>= 100 && val
< 1970)
651 error("Can't handle year %d\n", val
);
654 SET_VAL(t
->tm_year
, val
);
658 if (strchr(t_stop
+1, 'D') ||
659 strchr(t_stop
+1, 'd'))
661 SET_VAL(t
->tm_mon
, val
- 1);
662 else /* it's minutes */
663 SET_VAL(t
->tm_min
, val
);
667 SET_VAL(t
->tm_mday
, val
);
671 SET_VAL(t
->tm_hour
, val
);
675 SET_VAL(t
->tm_sec
, val
);
679 SET_VAL(*usecs_addr
, val
);
684 "bad date format %s, problem starting at %s",
685 time_string
, t_start
);
690 /* Of all the files, what is the lowest start time. */
691 static struct timeval
692 lowest_start_time(const struct state
*states
, int numfiles
)
694 struct timeval min_time
= states
->file_start_time
;
697 if (sf_timestamp_less_than(&states
->file_start_time
, &min_time
)) {
698 min_time
= states
->file_start_time
;
705 /* Of all the files, what is the latest end time. */
706 static struct timeval
707 latest_end_time(const struct state
*states
, int numfiles
)
709 struct timeval max_time
= states
->file_stop_time
;
712 if (sf_timestamp_less_than(&max_time
, &states
->file_stop_time
)) {
713 max_time
= states
->file_stop_time
;
720 /* Get the next record in a file. Deal with end of file.
722 * This routine also prevents time from going "backwards"
723 * within a single file.
726 get_next_packet(struct state
*s
)
728 struct timeval tvbuf
;
731 s
->pkt
= pcap_next(s
->p
, &s
->hdr
);
738 TIMEVAL_FROM_PKTHDR_TS(tvbuf
, s
->hdr
.ts
);
739 } while ((! s
->done
) &&
740 sf_timestamp_less_than(&tvbuf
, &s
->last_pkt_time
));
742 s
->last_pkt_time
= tvbuf
;
745 static struct state
*
746 open_files(char *filenames
[], const int numfiles
)
748 struct state
*states
;
750 char errbuf
[PCAP_ERRBUF_SIZE
];
754 error("no input files specified");
756 /* allocate memory for all the files */
757 states
= (struct state
*) calloc(numfiles
, sizeof(struct state
));
759 error("unable to allocate memory for %d input files", numfiles
);
761 for (i
= 0; i
< numfiles
; ++i
) {
763 s
->filename
= filenames
[i
];
764 s
->p
= pcap_open_offline(s
->filename
, errbuf
);
766 error( "bad pcap file %s: %s", s
->filename
, errbuf
);
768 #ifdef HAVE_POSIX_FADVISE
769 FILE *pf
= pcap_file(s
->p
);
771 error("pcap_file() failed");
774 error("fileno() failed: %s", strerror(errno
));
776 if (0 != (padv_err
= posix_fadvise(fd
, 0, 0, POSIX_FADV_RANDOM
)))
777 warning("warning: posix_fadvise() failed: %s", strerror(padv_err
));
778 if (0 != (padv_err
= posix_fadvise(fd
, 0, 0, POSIX_FADV_NOREUSE
)))
779 warning("warning: posix_fadvise() failed: %s", strerror(padv_err
));
783 sessions_nids_init(s
->p
);
785 int this_snap
= pcap_snapshot( s
->p
);
786 if (this_snap
> snaplen
) {
790 s
->start_pos
= ftell64( pcap_file( s
->p
) );
792 if (pcap_next(s
->p
, &s
->hdr
) == NULL
)
793 error( "error reading packet in %s: %s",
794 s
->filename
, pcap_geterr( s
->p
) );
796 TIMEVAL_FROM_PKTHDR_TS(s
->file_start_time
, s
->hdr
.ts
);
798 if ( ! sf_find_end( s
->p
, &s
->file_start_time
,
799 &s
->file_stop_time
) )
800 error( "problems finding end packet of file %s",
803 s
->stop_pos
= ftell64( pcap_file( s
->p
) );
809 /* Return 0 on no errors. */
811 validate_files(struct state states
[], const int numfiles
)
814 int i
, first_dlt
, this_dlt
;
816 for (i
= 0; i
< numfiles
; i
++) {
817 this_dlt
= pcap_datalink(states
[i
].p
);
819 first_dlt
= this_dlt
;
820 else if (first_dlt
!= this_dlt
) {
821 warning("file '%s' uses DLT %d, and the first file '%s' uses DLT %d",
822 states
[i
].filename
, this_dlt
, states
[0].filename
, first_dlt
);
826 /* Do a minimal sanity check of the timestamps. */
827 if (sf_timestamp_less_than(&states
[i
].file_stop_time
,
828 &states
[i
].file_start_time
)) {
829 warning("'%s' has the last timestamp before the first timestamp",
838 close_files(struct state states
[], const int numfiles
)
842 for (i
= 0; i
< numfiles
; i
++)
844 pcap_close(states
[i
].p
);
849 * Extract from a given set of files all packets with timestamps between
850 * the two time values given (inclusive). These packets are written
851 * to the save file given by write_file_name.
853 * Upon return, start_time is adjusted to reflect a time just after
854 * that of the last packet written to the output.
857 extract_slice(struct state
*states
, const int numfiles
, const char *write_file_name
,
858 const struct timeval
*start_time
, struct timeval
*stop_time
,
859 const int keep_dups
, const int relative_time_merge
,
860 const struct timeval
*base_time
)
862 struct state
*s
, *min_state
;
863 struct timeval temp1
, temp2
, relative_start
, relative_stop
;
866 struct state
*last_state
; /* remember the last packet */
867 struct pcap_pkthdr last_hdr
; /* in order to remove duplicates */
871 error("no input files specified");
874 last_hdr
.ts
.tv_sec
= last_hdr
.ts
.tv_usec
= 0;
875 last_hdr
.caplen
= last_hdr
.len
= 0;
876 last_pkt
= (u_char
*) calloc(1, snaplen
);
879 error("out of memory");
881 timersub(start_time
, base_time
, &relative_start
);
882 timersub(stop_time
, base_time
, &relative_stop
);
884 /* Always write the output file, use the first input file's DLT. */
885 global_dumper
= pcap_dump_open(states
[0].p
, write_file_name
);
886 if (!global_dumper
) {
887 error("error creating output file '%s': %s",
888 write_file_name
, pcap_geterr(states
[0].p
));
891 for (i
= 0; i
< numfiles
; ++i
) {
894 /* compute the first packet time within *this* file */
895 if (relative_time_merge
) {
896 /* relative time within this file */
897 timeradd(&s
->file_start_time
, &relative_start
, &temp1
);
903 /* check if this file has *anything* for us ... */
904 if (sf_timestamp_less_than(&s
->file_stop_time
, &temp1
)) {
905 /* there aren't any packets of interest in this file */
912 * sf_find_packet() requires that the time it's passed as
913 * its last argument be in the range [min_time, max_time],
914 * so we enforce that constraint here.
917 if (sf_timestamp_less_than(&temp1
, &s
->file_start_time
)){
918 temp1
= s
->file_start_time
;
921 sf_find_packet(s
->p
, &s
->file_start_time
, s
->start_pos
,
922 &s
->file_stop_time
, s
->stop_pos
,
925 /* get first packet for this file */
931 * Now, loop through all the packets in all the files,
932 * putting packets out in timestamp order.
934 * Quite often, the files will not have overlapping
935 * timestamps, so it would be nice to try to deal
936 * efficiently with that situation. (XXX)
940 struct timeval tvbuf
;
943 for (i
= 0; i
< numfiles
; ++i
) {
949 if (relative_time_merge
) {
950 /* compare *relative* times */
952 &s
->file_start_time
, &temp1
);
953 timersub(&min_state
->hdr
.ts
,
954 &min_state
->file_start_time
, &temp2
);
956 /* compare *absolute* times */
957 TIMEVAL_FROM_PKTHDR_TS(temp1
, s
->hdr
.ts
);
958 TIMEVAL_FROM_PKTHDR_TS(temp2
, min_state
->hdr
.ts
);
960 if (sf_timestamp_less_than( &temp1
, &temp2
))
966 break; /* didn't find any !done files */
968 if (relative_time_merge
) {
969 /* relative time w/in this file */
970 timeradd(&min_state
->file_start_time
, &relative_stop
, &temp1
);
972 /* take absolute times */
975 TIMEVAL_FROM_PKTHDR_TS(tvbuf
, min_state
->hdr
.ts
);
976 if (sf_timestamp_less_than(&temp1
, &tvbuf
)) {
977 if (!sessions_count
) {
978 /* We've gone beyond the end of the region
979 * of interest ... We're done with this file.
984 pcap_close(min_state
->p
);
987 /* We need to wait for the sessions to close */
989 *stop_time
= min_state
->file_stop_time
;
993 if (relative_time_merge
) {
994 timersub(&min_state
->hdr
.ts
, &min_state
->file_start_time
, &temp1
);
995 timeradd(&temp1
, base_time
, &min_state
->hdr
.ts
);
999 /* Keep track of sessions, if specified by the user */
1000 if (track_sessions
&& min_state
->hdr
.caplen
) {
1002 * Copy the packet buffer to deconstify it for the function.
1004 u_char
*pkt_copy
= malloc(min_state
->hdr
.caplen
);
1007 error("malloc() failed in %s()", __func__
);
1008 memcpy(pkt_copy
, min_state
->pkt
, min_state
->hdr
.caplen
);
1009 nids_pcap_handler((u_char
*)min_state
->p
, &min_state
->hdr
, pkt_copy
);
1014 /* Dump it, unless it's a duplicate. */
1017 min_state
== last_state
||
1018 memcmp(&last_hdr
, &min_state
->hdr
, sizeof(last_hdr
)) ||
1019 memcmp(last_pkt
, min_state
->pkt
, last_hdr
.caplen
) ) {
1020 pcap_dump((u_char
*) global_dumper
, &min_state
->hdr
, min_state
->pkt
);
1022 if ( ! keep_dups
) {
1023 last_state
= min_state
;
1024 last_hdr
= min_state
->hdr
;
1025 memcpy(last_pkt
, min_state
->pkt
, min_state
->hdr
.caplen
);
1029 get_next_packet(min_state
);
1032 pcap_dump_close(global_dumper
);
1036 /* Translates a timestamp to the time format specified by the user.
1037 * Returns a pointer to the translation residing in a static buffer.
1038 * There are two such buffers, which are alternated on subsequent
1039 * calls, so two calls may be made to this routine without worrying
1040 * about the results of the first call being overwritten by the
1041 * results of the second.
1044 timestamp_to_string(const struct timeval
*timestamp
)
1047 #define NUM_BUFFERS 2
1048 #define DATEBUFSIZE 128
1049 static char buffers
[NUM_BUFFERS
][DATEBUFSIZE
];
1050 static int buffer_to_use
= 0;
1053 buf
= buffers
[buffer_to_use
];
1054 buffer_to_use
= (buffer_to_use
+ 1) % NUM_BUFFERS
;
1056 switch ( timestamp_style
) {
1059 snprintf( buf
, DATEBUFSIZE
, "%u.%06u",
1060 (u_int32_t
)timestamp
->tv_sec
,
1061 (u_int32_t
)timestamp
->tv_usec
);
1064 case TIMESTAMP_READABLE
:
1065 t
= localtime((const time_t *) ×tamp
->tv_sec
);
1066 /* Mimic asctime() with C99 format specifiers. */
1067 strftime(buf
, DATEBUFSIZE
, "%a %b %e %T %Y", t
);
1070 case TIMESTAMP_PARSEABLE
:
1071 t
= localtime((const time_t *) ×tamp
->tv_sec
);
1072 snprintf( buf
, DATEBUFSIZE
, "%04dy%02dm%02dd%02dh%02dm%02ds%06uu",
1073 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
1074 t
->tm_hour
, t
->tm_min
, t
->tm_sec
,
1075 (u_int32_t
)timestamp
->tv_usec
);
1083 /* Given a pcap save filename, reports on the times of the first
1084 * and last packets in the file.
1087 dump_times(const struct state
*states
, int numfiles
)
1089 for (; numfiles
--; states
++) {
1090 printf( "%s\t%s\t%s\n",
1092 timestamp_to_string( &states
->file_start_time
),
1093 timestamp_to_string( &states
->file_stop_time
) );
1098 print_usage(FILE *f
)
1100 #ifndef HAVE_PCAP_LIB_VERSION
1101 #ifdef HAVE_PCAP_VERSION
1102 extern char pcap_version
[];
1103 #else /* HAVE_PCAP_VERSION */
1104 static char pcap_version
[] = "unknown";
1105 #endif /* HAVE_PCAP_VERSION */
1106 #endif /* HAVE_PCAP_LIB_VERSION */
1108 (void)fprintf(f
, "tcpslice version %s\n", PACKAGE_VERSION
);
1109 #ifdef HAVE_PCAP_LIB_VERSION
1110 (void)fprintf(f
, "%s\n", pcap_lib_version());
1111 #else /* HAVE_PCAP_LIB_VERSION */
1112 (void)fprintf(f
, "libpcap version %s\n", pcap_version
);
1113 #endif /* HAVE_PCAP_LIB_VERSION */
1116 (void)fprintf(f
, "libnids version %u.%u\n", NIDS_MAJOR
, NIDS_MINOR
);
1118 #ifdef HAVE_LIBOSIPPARSER2
1119 (void)fprintf(f
, "libosip2 unknown version\n");
1120 #endif /* HAVE_LIBOSIPPARSER2 */
1122 #ifdef HAVE_LIBOOH323C
1123 (void)fprintf(f
, "libooh323c %s\n", OOH323C_VERSION
);
1124 #endif /* HAVE_LIBOOH323C */
1126 #endif /* HAVE_LIBNIDS */
1128 #if defined(SIZEOF_VOID_P) && defined(SIZEOF_TIME_T)
1129 (void)fprintf (f
, "%u-bit build, %u-bit time_t\n",
1130 SIZEOF_VOID_P
* 8, SIZEOF_TIME_T
* 8);
1134 "Usage: tcpslice [-DdhlRrtv] [-w file]\n"
1135 " [ -s types [ -e seconds ] [ -f format ] ]\n"
1136 " [start-time [end-time]] file ... \n");