Add initial support for "two pass analysis" in tshark. This allows tshark to arrive at the same protocol tree as the Wireshark GUI. Before this change tshark only supported a single scan over the file. This effectively means that packets cannot use data that are gathered from frames that appear after the current frame. By scanning twice we give the dissector the opportunity to make forward references.

svn path=/trunk/; revision=30076
This commit is contained in:
Kovarththanan Rajaratnam 2009-09-22 19:28:00 +00:00
parent 2cacda1823
commit 760eb47fc1
2 changed files with 284 additions and 28 deletions

View File

@ -28,6 +28,9 @@ ERR_FILE=$BASE_NAME.err
# Loop this many times (< 1 loops forever)
MAX_PASSES=0
# Perform a two pass analysis on the capture file?
TWO_PASS=
# These may be set to your liking
# Stop the child process, if it's running longer than x seconds
MAX_CPU_TIME=900
@ -44,12 +47,13 @@ ERR_PROB=0.02
# To do: add options for file names and limits
while getopts ":b:d:e:p:" OPTCHAR ; do
while getopts ":b:d:e:Pp:" OPTCHAR ; do
case $OPTCHAR in
b) BIN_DIR=$OPTARG ;;
d) TMP_DIR=$OPTARG ;;
e) ERR_PROB=$OPTARG ;;
p) MAX_PASSES=$OPTARG ;;
P) TWO_PASS="-P " ;;
esac
done
shift $(($OPTIND - 1))
@ -71,7 +75,7 @@ ulimit -c unlimited
# V Print a view of the details of the packet rather than a one-line summary of the packet
# x Cause TShark to print a hex and ASCII dump of the packet data after printing the summary or details
# r Read packet data from the following infile
TSHARK_ARGS="-nVxr"
TSHARK_ARGS="${TWO_PASS}-nVxr"
NOTFOUND=0
for i in "$TSHARK" "$EDITCAP" "$CAPINFOS" "$DATE" "$TMP_DIR" ; do
@ -98,7 +102,7 @@ if [ $FOUND -eq 0 ] ; then
cat <<FIN
Error: No valid capture files found.
Usage: `basename $0` [-p passes] [-d work_dir] [-e error probability] capture file 1 [capture file 2]...
Usage: `basename $0` [-p passes] [-d work_dir] [-P ] [-e error probability] capture file 1 [capture file 2]...
FIN
exit 1
fi

302
tshark.c
View File

@ -112,6 +112,8 @@ static nstime_t prev_cap_ts;
static gboolean print_packet_info; /* TRUE if we're to print packet information */
static gboolean perform_two_pass_analysis;
/*
* The way the packet decode is to be written.
*/
@ -764,7 +766,7 @@ main(int argc, char *argv[])
GLogLevelFlags log_flags;
int optind_initial;
#define OPTSTRING_INIT "a:b:c:C:d:De:E:f:F:G:hi:K:lLnN:o:pqr:R:s:St:T:vVw:xX:y:z:"
#define OPTSTRING_INIT "a:b:c:C:d:De:E:f:F:G:hi:K:lLnN:o:pPqr:R:s:St:T:vVw:xX:y:z:"
#ifdef HAVE_LIBPCAP
#ifdef _WIN32
#define OPTSTRING_WIN32 "B:"
@ -1081,6 +1083,11 @@ main(int argc, char *argv[])
arg_error = TRUE;
#endif
break;
#if GLIB_CHECK_VERSION(2,10,0)
case 'P': /* Perform two pass analysis */
perform_two_pass_analysis = TRUE;
break;
#endif
case 'n': /* No name resolution */
g_resolv_flags = RESOLV_NONE;
break;
@ -1622,6 +1629,11 @@ main(int argc, char *argv[])
#endif
}
#if GLIB_CHECK_VERSION(2,10,0)
if (cfile.plist_start != NULL)
g_slice_free_chain(frame_data, cfile.plist_start, next);
#endif
draw_tap_listeners(TRUE);
funnel_dump_all_text_windows();
epan_cleanup();
@ -2165,6 +2177,181 @@ capture_cleanup(int signum _U_)
#endif /* _WIN32 */
#endif /* HAVE_LIBPCAP */
#if GLIB_CHECK_VERSION(2,10,0)
static gboolean
process_packet_first_pass(capture_file *cf,
gint64 offset, const struct wtap_pkthdr *whdr,
union wtap_pseudo_header *pseudo_header, const guchar *pd)
{
frame_data *fdata = g_slice_new(frame_data);
epan_dissect_t edt;
gboolean passed;
/* Count this packet. */
cf->count++;
/* If we're not running a display filter and we're not printing any
packet information, we don't need to do a dissection. This means
that all packets can be marked as 'passed'. */
passed = TRUE;
frame_data_init(fdata, cf->count, whdr, offset, cum_bytes);
/* If we're going to print packet information, or we're going to
run a read filter, or we're going to process taps, set up to
do a dissection and do so. */
if (do_dissection) {
if (g_resolv_flags)
/* Grab any resolved addresses */
host_name_lookup_process(NULL);
/* The protocol tree will be "visible", i.e., printed, only if we're
printing packet details, which is true if we're printing stuff
("print_packet_info" is true) and we're in verbose mode ("verbose"
is true). */
epan_dissect_init(&edt, FALSE, FALSE);
/* If we're running a read filter, prime the epan_dissect_t with that
filter. */
if (cf->rfcode)
epan_dissect_prime_dfilter(&edt, cf->rfcode);
frame_data_set_before_dissect(fdata, &cf->elapsed_time,
&first_ts, &prev_dis_ts, &prev_cap_ts);
epan_dissect_run(&edt, pseudo_header, pd, fdata, NULL);
/* Run the read filter if we have one. */
if (cf->rfcode)
passed = dfilter_apply_edt(cf->rfcode, &edt);
}
if (passed) {
frame_data_set_after_dissect(fdata, &cum_bytes, &prev_dis_ts);
cap_file_add_fdata(cf, fdata);
}
else
g_slice_free(frame_data, fdata);
if (do_dissection)
epan_dissect_cleanup(&edt);
return passed;
}
static gboolean
process_packet_second_pass(capture_file *cf, frame_data *fdata,
gint64 offset, union wtap_pseudo_header *pseudo_header, const guchar *pd,
gboolean filtering_tap_listeners, guint tap_flags)
{
gboolean create_proto_tree;
column_info *cinfo;
epan_dissect_t edt;
gboolean passed;
/* If we're not running a display filter and we're not printing any
packet information, we don't need to do a dissection. This means
that all packets can be marked as 'passed'. */
passed = TRUE;
/* If we're going to print packet information, or we're going to
run a read filter, or we're going to process taps, set up to
do a dissection and do so. */
if (do_dissection) {
if (g_resolv_flags)
/* Grab any resolved addresses */
host_name_lookup_process(NULL);
if (cf->rfcode || verbose || filtering_tap_listeners ||
(tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo))
create_proto_tree = TRUE;
else
create_proto_tree = FALSE;
/* The protocol tree will be "visible", i.e., printed, only if we're
printing packet details, which is true if we're printing stuff
("print_packet_info" is true) and we're in verbose mode ("verbose"
is true). */
epan_dissect_init(&edt, create_proto_tree, print_packet_info && verbose);
/* If we're running a read filter, prime the epan_dissect_t with that
filter. */
if (cf->rfcode)
epan_dissect_prime_dfilter(&edt, cf->rfcode);
col_custom_prime_edt(&edt, &cf->cinfo);
tap_queue_init(&edt);
/* We only need the columns if either
1) some tap needs the columns
or
2) we're printing packet info but we're *not* verbose; in verbose
mode, we print the protocol tree, not the protocol summary. */
if ((tap_flags & TL_REQUIRES_COLUMNS) || (print_packet_info && !verbose))
cinfo = &cf->cinfo;
else
cinfo = NULL;
epan_dissect_run(&edt, pseudo_header, pd, fdata, cinfo);
tap_push_tapped_queue(&edt);
/* Run the read filter if we have one. */
if (cf->rfcode)
passed = dfilter_apply_edt(cf->rfcode, &edt);
}
if (passed) {
/* Process this packet. */
if (print_packet_info) {
/* We're printing packet information; print the information for
this packet. */
if (do_dissection)
print_packet(cf, &edt);
else
print_packet(cf, NULL);
/* The ANSI C standard does not appear to *require* that a line-buffered
stream be flushed to the host environment whenever a newline is
written, it just says that, on such a stream, characters "are
intended to be transmitted to or from the host environment as a
block when a new-line character is encountered".
The Visual C++ 6.0 C implementation doesn't do what is intended;
even if you set a stream to be line-buffered, it still doesn't
flush the buffer at the end of every line.
So, if the "-l" flag was specified, we flush the standard output
at the end of a packet. This will do the right thing if we're
printing packet summary lines, and, as we print the entire protocol
tree for a single packet without waiting for anything to happen,
it should be as good as line-buffered mode if we're printing
protocol trees. (The whole reason for the "-l" flag in either
tcpdump or TShark is to allow the output of a live capture to
be piped to a program or script and to have that script see the
information for the packet as soon as it's printed, rather than
having to wait until a standard I/O buffer fills up. */
if (line_buffered)
fflush(stdout);
if (ferror(stdout)) {
show_print_file_io_error(errno);
exit(2);
}
}
}
if (do_dissection) {
epan_dissect_cleanup(&edt);
}
return passed;
}
#endif
static int
load_cap_file(capture_file *cf, char *save_file, int out_file_type,
int max_packet_count, gint64 max_byte_count)
@ -2241,34 +2428,99 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
/* Get the union of the flags for all tap listeners. */
tap_flags = union_of_tap_listener_flags();
while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
if (process_packet(cf, data_offset, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
filtering_tap_listeners, tap_flags)) {
/* Either there's no read filtering or this packet passed the
filter, so, if we're writing to a capture file, write
this packet out. */
if (pdh != NULL) {
if (!wtap_dump(pdh, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
&err)) {
/* Error writing to a capture file */
show_capture_file_io_error(save_file, err, FALSE);
wtap_dump_close(pdh, &err);
exit(2);
if (perform_two_pass_analysis) {
#if GLIB_CHECK_VERSION(2,10,0)
frame_data *fdata;
int old_max_packet_count = max_packet_count;
while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
if (process_packet_first_pass(cf, data_offset, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth))) {
/* Stop reading if we have the maximum number of packets;
* When the -c option has not been used, max_packet_count
* starts at 0, which practically means, never stop reading.
* (unless we roll over max_packet_count ?)
*/
if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
err = 0; /* This is not an error */
break;
}
}
/* Stop reading if we have the maximum number of packets;
* When the -c option has not been used, max_packet_count
* starts at 0, which practically means, never stop reading.
* (unless we roll over max_packet_count ?)
*/
if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
err = 0; /* This is not an error */
break;
}
/* Close the sequential I/O side, to free up memory it requires. */
wtap_sequential_close(cf->wth);
/* Allow the protocol dissectors to free up memory that they
* don't need after the sequential run-through of the packets. */
postseq_cleanup_all_protocols();
max_packet_count = old_max_packet_count;
for (fdata = cf->plist_start; err == 0 && fdata != NULL; fdata = fdata->next) {
if (wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
cf->pd, fdata->cap_len, &err, &err_info)) {
if (process_packet_second_pass(cf, fdata, fdata->file_off,
&cf->pseudo_header, cf->pd,
filtering_tap_listeners, tap_flags)) {
/* Either there's no read filtering or this packet passed the
filter, so, if we're writing to a capture file, write
this packet out. */
if (pdh != NULL) {
if (!wtap_dump(pdh, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
&err)) {
/* Error writing to a capture file */
show_capture_file_io_error(save_file, err, FALSE);
wtap_dump_close(pdh, &err);
exit(2);
}
}
/* Stop reading if we have the maximum number of packets;
* When the -c option has not been used, max_packet_count
* starts at 0, which practically means, never stop reading.
* (unless we roll over max_packet_count ?)
*/
if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
err = 0; /* This is not an error */
break;
}
}
}
}
#endif
}
else {
while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
if (process_packet(cf, data_offset, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
filtering_tap_listeners, tap_flags)) {
/* Either there's no read filtering or this packet passed the
filter, so, if we're writing to a capture file, write
this packet out. */
if (pdh != NULL) {
if (!wtap_dump(pdh, wtap_phdr(cf->wth),
wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
&err)) {
/* Error writing to a capture file */
show_capture_file_io_error(save_file, err, FALSE);
wtap_dump_close(pdh, &err);
exit(2);
}
}
/* Stop reading if we have the maximum number of packets;
* When the -c option has not been used, max_packet_count
* starts at 0, which practically means, never stop reading.
* (unless we roll over max_packet_count ?)
*/
if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
err = 0; /* This is not an error */
break;
}
}
}
}
if (err != 0) {
/* Print a message noting that the read failed somewhere along the line. */
switch (err) {
@ -3009,7 +3261,7 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
gchar *err_info;
char err_msg[2048+1];
wth = wtap_open_offline(fname, err, &err_info, FALSE);
wth = wtap_open_offline(fname, err, &err_info, perform_two_pass_analysis);
if (wth == NULL)
goto fail;