1998-09-16 02:39:15 +00:00
|
|
|
/* capture.c
|
2005-03-28 18:04:09 +00:00
|
|
|
* Routines for packet capture
|
1998-09-16 02:39:15 +00:00
|
|
|
*
|
2004-07-18 00:24:25 +00:00
|
|
|
* $Id$
|
1998-09-16 03:22:19 +00:00
|
|
|
*
|
1998-09-16 02:39:15 +00:00
|
|
|
* Ethereal - Network traffic analyzer
|
2001-10-25 06:41:48 +00:00
|
|
|
* By Gerald Combs <gerald@ethereal.com>
|
1998-09-16 02:39:15 +00:00
|
|
|
* Copyright 1998 Gerald Combs
|
2002-08-28 21:04:11 +00:00
|
|
|
*
|
1998-09-16 02:39:15 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2002-08-28 21:04:11 +00:00
|
|
|
*
|
1998-09-16 02:39:15 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2002-08-28 21:04:11 +00:00
|
|
|
*
|
1998-09-16 02:39:15 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
1999-07-09 04:18:36 +00:00
|
|
|
#ifdef HAVE_LIBPCAP
|
|
|
|
|
1998-12-17 05:42:33 +00:00
|
|
|
#include <stdlib.h>
|
1998-09-16 02:39:15 +00:00
|
|
|
#include <string.h>
|
2005-02-06 22:18:15 +00:00
|
|
|
#include <ctype.h>
|
2000-08-11 13:37:21 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_FCNTL_H
|
1999-10-02 06:26:53 +00:00
|
|
|
#include <fcntl.h>
|
2000-08-11 13:37:21 +00:00
|
|
|
#endif
|
1999-08-03 20:51:41 +00:00
|
|
|
|
2004-10-30 23:26:47 +00:00
|
|
|
#ifdef HAVE_IO_H
|
|
|
|
# include <io.h>
|
1999-08-03 20:51:41 +00:00
|
|
|
#endif
|
|
|
|
|
1999-05-11 18:51:10 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <errno.h>
|
1998-09-16 02:39:15 +00:00
|
|
|
|
2004-10-30 23:26:47 +00:00
|
|
|
#include <pcap.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
2002-01-21 07:37:49 +00:00
|
|
|
#include <epan/packet.h>
|
2003-10-11 21:49:56 +00:00
|
|
|
#include <epan/dfilter/dfilter.h>
|
1998-09-16 02:39:15 +00:00
|
|
|
#include "file.h"
|
|
|
|
#include "capture.h"
|
2004-10-30 11:44:06 +00:00
|
|
|
#include "capture_sync.h"
|
2005-01-16 02:56:28 +00:00
|
|
|
#include "capture_ui_utils.h"
|
1998-09-16 02:39:15 +00:00
|
|
|
#include "util.h"
|
2001-11-09 07:44:51 +00:00
|
|
|
#include "pcap-util.h"
|
2004-02-11 01:23:25 +00:00
|
|
|
#include "alert_box.h"
|
2000-01-03 06:59:25 +00:00
|
|
|
#include "simple_dialog.h"
|
2004-09-27 22:55:15 +00:00
|
|
|
#include <epan/prefs.h>
|
2001-12-04 07:32:05 +00:00
|
|
|
#include "conditions.h"
|
2001-12-04 08:26:00 +00:00
|
|
|
#include "ringbuffer.h"
|
1998-09-16 02:39:15 +00:00
|
|
|
|
2002-12-31 21:12:55 +00:00
|
|
|
#ifdef _WIN32
|
2001-11-09 07:44:51 +00:00
|
|
|
#include "capture-wpcap.h"
|
|
|
|
#endif
|
2004-01-22 18:13:57 +00:00
|
|
|
#include "ui_util.h"
|
2001-11-09 07:44:51 +00:00
|
|
|
|
2000-06-15 08:02:43 +00:00
|
|
|
|
2004-02-21 14:04:17 +00:00
|
|
|
|
2005-03-28 18:04:09 +00:00
|
|
|
/**
|
|
|
|
* Start a capture.
|
|
|
|
*
|
|
|
|
* @return TRUE if the capture starts successfully, FALSE otherwise.
|
|
|
|
*/
|
2004-12-29 09:09:35 +00:00
|
|
|
gboolean
|
2005-03-28 18:04:09 +00:00
|
|
|
capture_start(capture_options *capture_opts)
|
2004-12-29 09:09:35 +00:00
|
|
|
{
|
|
|
|
gboolean ret;
|
|
|
|
|
2005-02-27 19:59:03 +00:00
|
|
|
|
|
|
|
/* close the currently loaded capture file */
|
|
|
|
cf_close(capture_opts->cf);
|
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
/* try to start the capture child process */
|
|
|
|
ret = sync_pipe_do_capture(capture_opts, capture_opts->save_file == NULL);
|
|
|
|
|
|
|
|
if(ret) {
|
|
|
|
/* tell callbacks (menu, ...) that capture is running now */
|
|
|
|
cf_callback_invoke(cf_cb_live_capture_prepare, capture_opts);
|
2004-02-21 12:58:42 +00:00
|
|
|
} else {
|
2005-03-28 14:39:31 +00:00
|
|
|
if(capture_opts->save_file != NULL) {
|
|
|
|
g_free(capture_opts->save_file);
|
|
|
|
capture_opts->save_file = NULL;
|
|
|
|
}
|
2004-02-21 12:58:42 +00:00
|
|
|
}
|
2004-06-20 13:39:44 +00:00
|
|
|
|
|
|
|
return ret;
|
2004-02-21 12:58:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-28 18:04:09 +00:00
|
|
|
/* We've succeeded a (non real-time) capture, try to read it into a new capture file */
|
2005-03-28 14:39:31 +00:00
|
|
|
static gboolean
|
|
|
|
capture_input_read_all(capture_options *capture_opts, gboolean is_tempfile, gboolean drops_known,
|
2005-02-28 22:46:49 +00:00
|
|
|
guint32 drops)
|
2004-02-21 12:58:42 +00:00
|
|
|
{
|
2005-04-10 08:55:56 +00:00
|
|
|
int err;
|
2004-02-21 12:58:42 +00:00
|
|
|
|
2005-02-27 19:59:03 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* Capture succeeded; attempt to open the capture file. */
|
|
|
|
if (cf_open(capture_opts->cf, capture_opts->save_file, is_tempfile, &err) != CF_OK) {
|
|
|
|
/* We're not doing a capture any more, so we don't have a save
|
|
|
|
file. */
|
|
|
|
return FALSE;
|
|
|
|
}
|
2004-02-21 12:58:42 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* Set the read filter to NULL. */
|
|
|
|
/* XXX - this is odd here, try to put it somewhere, where it fits better */
|
|
|
|
cf_set_rfcode(capture_opts->cf, NULL);
|
|
|
|
|
|
|
|
/* Get the packet-drop statistics.
|
|
|
|
|
|
|
|
XXX - there are currently no packet-drop statistics stored
|
|
|
|
in libpcap captures, and that's what we're reading.
|
|
|
|
|
|
|
|
At some point, we will add support in Wiretap to return
|
|
|
|
packet-drop statistics for capture file formats that store it,
|
|
|
|
and will make "cf_read()" get those statistics from Wiretap.
|
|
|
|
We clear the statistics (marking them as "not known") in
|
|
|
|
"cf_open()", and "cf_read()" will only fetch them and mark
|
|
|
|
them as known if Wiretap supplies them, so if we get the
|
|
|
|
statistics now, after calling "cf_open()" but before calling
|
|
|
|
"cf_read()", the values we store will be used by "cf_read()".
|
|
|
|
|
|
|
|
If a future libpcap capture file format stores the statistics,
|
|
|
|
we'll put them into the capture file that we write, and will
|
|
|
|
thus not have to set them here - "cf_read()" will get them from
|
|
|
|
the file and use them. */
|
|
|
|
if (drops_known) {
|
|
|
|
cf_set_drops_known(capture_opts->cf, TRUE);
|
|
|
|
|
|
|
|
/* XXX - on some systems, libpcap doesn't bother filling in
|
|
|
|
"ps_ifdrop" - it doesn't even set it to zero - so we don't
|
|
|
|
bother looking at it.
|
|
|
|
|
|
|
|
Ideally, libpcap would have an interface that gave us
|
|
|
|
several statistics - perhaps including various interface
|
|
|
|
error statistics - and would tell us which of them it
|
|
|
|
supplies, allowing us to display only the ones it does. */
|
|
|
|
cf_set_drops(capture_opts->cf, drops);
|
|
|
|
}
|
2004-02-21 12:58:42 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* read in the packet data */
|
|
|
|
switch (cf_read(capture_opts->cf)) {
|
|
|
|
|
|
|
|
case CF_READ_OK:
|
|
|
|
case CF_READ_ERROR:
|
|
|
|
/* Just because we got an error, that doesn't mean we were unable
|
|
|
|
to read any of the file; we handle what we could get from the
|
|
|
|
file. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CF_READ_ABORTED:
|
|
|
|
/* User wants to quit program. Exit by leaving the main loop,
|
|
|
|
so that any quit functions we registered get called. */
|
|
|
|
main_window_nested_quit();
|
|
|
|
return FALSE;
|
|
|
|
}
|
2004-02-21 12:58:42 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* if we didn't captured even a single packet, close the file again */
|
|
|
|
if(cf_packet_count(capture_opts->cf) == 0) {
|
|
|
|
simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
|
|
|
|
"%sNo packets captured!%s\n\n"
|
|
|
|
"As no data was captured, closing the %scapture file!",
|
|
|
|
simple_dialog_primary_start(), simple_dialog_primary_end(),
|
|
|
|
(cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
|
|
|
|
cf_close(capture_opts->cf);
|
|
|
|
}
|
2004-02-21 12:58:42 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
Enough is enough. Requiring anybody who uses Ethereal on Linux to
update their libpcap probably isn't going to scale - the increasing
frequency with which "Ethereal hangs when I try to capture packets"
shows up on "ethereal-dev" suggests that, unless and until a libpcap
with the "select()" in it becomes ubiquitous on Linux, that'll be the
source of a constant support burden - so we'll just put the "select()"
in Ethereal if it's being built for Linux.
(Putting it in for platforms where the read timeout argument to
"pcap_open_live()" works adds an extra useless system call at best and,
at worst, could make Ethereal not work - "select()" doesn't work on
"/dev/bpf" devices on FreeBSD 3.3, at least, unless you're in "immediate
mode", and, whilst "immediate mode" would make Ethereal respond more
quickly when packets arrive, it might cause Ethereal to respond too
quickly, doing reads for every new packet rather than waiting for
multiple packets to arrive and reading them all with one "read()", which
appears to be at least part of the intent of the read timeout on
"/dev/bpf" devices in BSD.)
svn path=/trunk/; revision=1451
2000-01-12 06:56:32 +00:00
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
/* capture child tells us, we have a new (or the first) capture file */
|
|
|
|
gboolean
|
|
|
|
capture_input_new_file(capture_options *capture_opts, gchar *new_file)
|
2005-02-27 19:59:03 +00:00
|
|
|
{
|
2005-03-28 14:39:31 +00:00
|
|
|
gboolean is_tempfile;
|
|
|
|
int err;
|
2005-02-27 19:59:03 +00:00
|
|
|
|
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/*g_warning("New capture file: %s", new_file);*/
|
2005-03-28 14:39:31 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* free the old filename */
|
|
|
|
if(capture_opts->save_file != NULL) {
|
|
|
|
/* we start a new capture file, simply close the old one */
|
|
|
|
/* XXX - is it enough to call cf_close here? */
|
|
|
|
/* XXX - is it safe to call cf_close even if the file is close before? */
|
|
|
|
cf_close(capture_opts->cf);
|
|
|
|
g_free(capture_opts->save_file);
|
|
|
|
is_tempfile = FALSE;
|
|
|
|
} else {
|
|
|
|
/* we didn't had a save_file before, must be a tempfile */
|
|
|
|
is_tempfile = TRUE;
|
|
|
|
cf_set_tempfile(capture_opts->cf, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save the new filename */
|
|
|
|
capture_opts->save_file = g_strdup(new_file);
|
2005-03-28 18:04:09 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
/* if we are in real-time mode, open the new file now */
|
|
|
|
if(capture_opts->real_time_mode) {
|
|
|
|
/* Attempt to open the capture file and set up to read from it. */
|
|
|
|
switch(cf_start_tail(capture_opts->cf, capture_opts->save_file, is_tempfile, &err)) {
|
|
|
|
case CF_OK:
|
|
|
|
break;
|
|
|
|
case CF_ERROR:
|
|
|
|
/* Don't unlink (delete) the save file - leave it around,
|
|
|
|
for debugging purposes. */
|
|
|
|
g_free(capture_opts->save_file);
|
|
|
|
capture_opts->save_file = NULL;
|
|
|
|
return FALSE;
|
|
|
|
break;
|
2005-02-27 19:59:03 +00:00
|
|
|
}
|
2005-04-10 19:36:56 +00:00
|
|
|
|
2005-04-10 21:03:31 +00:00
|
|
|
cf_callback_invoke(cf_cb_live_capture_update_started, capture_opts);
|
2005-04-10 19:36:56 +00:00
|
|
|
} else {
|
2005-04-10 21:03:31 +00:00
|
|
|
cf_callback_invoke(cf_cb_live_capture_fixed_started, capture_opts);
|
2005-04-10 08:55:56 +00:00
|
|
|
}
|
2005-03-28 14:39:31 +00:00
|
|
|
|
2005-03-28 21:35:21 +00:00
|
|
|
|
2005-04-10 08:55:56 +00:00
|
|
|
return TRUE;
|
2005-03-28 14:39:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* capture child tells us, we have new packets to read */
|
|
|
|
void
|
|
|
|
capture_input_new_packets(capture_options *capture_opts, int to_read)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
2005-03-28 16:14:34 +00:00
|
|
|
if(capture_opts->real_time_mode) {
|
2005-04-10 08:55:56 +00:00
|
|
|
/* Read from the capture file the number of records the child told us
|
|
|
|
it added.
|
|
|
|
XXX - do something if this fails? */
|
|
|
|
switch (cf_continue_tail(capture_opts->cf, to_read, &err)) {
|
|
|
|
|
|
|
|
case CF_READ_OK:
|
|
|
|
case CF_READ_ERROR:
|
|
|
|
/* Just because we got an error, that doesn't mean we were unable
|
|
|
|
to read any of the file; we handle what we could get from the
|
|
|
|
file.
|
|
|
|
|
|
|
|
XXX - abort on a read error? */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CF_READ_ABORTED:
|
|
|
|
/* Kill the child capture process; the user wants to exit, and we
|
|
|
|
shouldn't just leave it running. */
|
|
|
|
capture_kill_child(capture_opts);
|
|
|
|
break;
|
|
|
|
}
|
2005-03-28 14:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* capture child closed it's side ot the pipe, do the required cleanup */
|
|
|
|
void
|
|
|
|
capture_input_closed(capture_options *capture_opts)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
2005-03-28 16:14:34 +00:00
|
|
|
if(capture_opts->real_time_mode) {
|
2005-04-10 19:36:56 +00:00
|
|
|
/* first of all, we are not doing a capture any more */
|
|
|
|
cf_callback_invoke(cf_cb_live_capture_update_finished, capture_opts->cf);
|
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
/* Read what remains of the capture file, and finish the capture.
|
|
|
|
XXX - do something if this fails? */
|
|
|
|
switch (cf_finish_tail(capture_opts->cf, &err)) {
|
|
|
|
|
|
|
|
case CF_READ_OK:
|
|
|
|
if(cf_packet_count(capture_opts->cf) == 0) {
|
|
|
|
simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
|
|
|
|
"%sNo packets captured!%s\n\n"
|
|
|
|
"As no data was captured, closing the %scapture file!",
|
|
|
|
simple_dialog_primary_start(), simple_dialog_primary_end(),
|
|
|
|
cf_is_tempfile(capture_opts->cf) ? "temporary " : "");
|
|
|
|
cf_close(capture_opts->cf);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CF_READ_ERROR:
|
|
|
|
/* Just because we got an error, that doesn't mean we were unable
|
|
|
|
to read any of the file; we handle what we could get from the
|
|
|
|
file. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CF_READ_ABORTED:
|
|
|
|
/* Exit by leaving the main loop, so that any quit functions
|
|
|
|
we registered get called. */
|
|
|
|
main_window_quit();
|
|
|
|
}
|
2005-03-28 21:05:53 +00:00
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
} else {
|
2005-04-10 19:36:56 +00:00
|
|
|
/* first of all, we are not doing a capture any more */
|
|
|
|
cf_callback_invoke(cf_cb_live_capture_fixed_finished, capture_opts->cf);
|
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
/* this is a normal mode capture, read in the capture file data */
|
|
|
|
capture_input_read_all(capture_opts, cf_is_tempfile(capture_opts->cf),
|
|
|
|
cf_get_drops_known(capture_opts->cf), cf_get_drops(capture_opts->cf));
|
2005-04-10 17:36:36 +00:00
|
|
|
}
|
|
|
|
|
2005-03-28 14:39:31 +00:00
|
|
|
/* We're not doing a capture any more, so we don't have a save file. */
|
|
|
|
g_assert(capture_opts->save_file);
|
|
|
|
g_free(capture_opts->save_file);
|
|
|
|
capture_opts->save_file = NULL;
|
2005-02-27 19:59:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-28 15:31:13 +00:00
|
|
|
void
|
|
|
|
capture_stop(capture_options *capture_opts)
|
|
|
|
{
|
2005-03-28 18:04:09 +00:00
|
|
|
/* stop the capture child gracefully, if we have one */
|
2005-03-28 15:31:13 +00:00
|
|
|
sync_pipe_stop(capture_opts);
|
|
|
|
}
|
|
|
|
|
Add a routine to kill a capture child if it exists, so that if we exit
(by deleting the main window or selecting File->Quit or typing ^Q) while
an "Update list of packets in real time" capture is in progress, we can
abort the capture.
Arrange that "fork_child" is -1 when there is no capture child, so said
routine knows when it can kill the child.
When we exit, kill off any capture child, using that routine, and, if
we're exiting due to a request to delete the main window and, if a read
is in progress (from an "Update list of packets in real time" capture),
don't delete the main window - just set the "Read aborted" flag, so that
the code doing the read will see that flag (it will be called because
the pipe to the capture child is closed due to the child exiting) will
see that and clean up and exit itself.
svn path=/trunk/; revision=4498
2002-01-08 09:32:15 +00:00
|
|
|
void
|
2005-02-05 15:35:25 +00:00
|
|
|
capture_kill_child(capture_options *capture_opts)
|
Add a routine to kill a capture child if it exists, so that if we exit
(by deleting the main window or selecting File->Quit or typing ^Q) while
an "Update list of packets in real time" capture is in progress, we can
abort the capture.
Arrange that "fork_child" is -1 when there is no capture child, so said
routine knows when it can kill the child.
When we exit, kill off any capture child, using that routine, and, if
we're exiting due to a request to delete the main window and, if a read
is in progress (from an "Update list of packets in real time" capture),
don't delete the main window - just set the "Read aborted" flag, so that
the code doing the read will see that flag (it will be called because
the pipe to the capture child is closed due to the child exiting) will
see that and clean up and exit itself.
svn path=/trunk/; revision=4498
2002-01-08 09:32:15 +00:00
|
|
|
{
|
2005-02-27 17:30:33 +00:00
|
|
|
/* kill the capture child, if we have one */
|
2005-03-28 14:39:31 +00:00
|
|
|
sync_pipe_kill(capture_opts);
|
Add a routine to kill a capture child if it exists, so that if we exit
(by deleting the main window or selecting File->Quit or typing ^Q) while
an "Update list of packets in real time" capture is in progress, we can
abort the capture.
Arrange that "fork_child" is -1 when there is no capture child, so said
routine knows when it can kill the child.
When we exit, kill off any capture child, using that routine, and, if
we're exiting due to a request to delete the main window and, if a read
is in progress (from an "Update list of packets in real time" capture),
don't delete the main window - just set the "Read aborted" flag, so that
the code doing the read will see that flag (it will be called because
the pipe to the capture child is closed due to the child exiting) will
see that and clean up and exit itself.
svn path=/trunk/; revision=4498
2002-01-08 09:32:15 +00:00
|
|
|
}
|
|
|
|
|
1999-07-09 04:18:36 +00:00
|
|
|
|
|
|
|
#endif /* HAVE_LIBPCAP */
|