aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.nmake1
-rw-r--r--acinclude.m42
-rw-r--r--capture-wpcap.c23
-rw-r--r--capture_loop.c258
-rw-r--r--capture_loop.h49
-rw-r--r--config.h.win321
-rw-r--r--config.nmake7
7 files changed, 204 insertions, 137 deletions
diff --git a/Makefile.nmake b/Makefile.nmake
index 5ab4818480..ef6bf13272 100644
--- a/Makefile.nmake
+++ b/Makefile.nmake
@@ -229,6 +229,7 @@ config.h : config.h.win32 config.nmake
-e "s/@HAVE_PCAP_FINDALLDEVS@/$(PCAP_FINDALLDEVS_CONFIG)/" \
-e "s/@HAVE_PCAP_DATALINK_NAME_TO_VAL@/$(PCAP_DATALINK_NAME_TO_VAL_CONFIG)/" \
-e "s/@HAVE_PCAP_DATALINK_VAL_TO_NAME@/$(PCAP_DATALINK_VAL_TO_NAME_CONFIG)/" \
+ -e "s/@HAVE_PCAP_BREAKLOOP@/$(PCAP_BREAKLOOP_CONFIG)/" \
-e "s/@HAVE_LIBETHEREALDLL@/$(LIBETHEREAL_CONFIG)/" \
-e "s/@WPCAP_CONSTIFIED@/$(WPCAP_CONSTIFIED_CONFIG)/" \
-e "s/@HAVE_GNUTLS@/$(GNUTLS_CONFIG)/" \
diff --git a/acinclude.m4 b/acinclude.m4
index e831c7372b..df4e712543 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -437,7 +437,7 @@ and did you also install that package?]]))
else
AC_MSG_RESULT(no)
fi
- AC_CHECK_FUNCS(pcap_open_dead pcap_freecode)
+ AC_CHECK_FUNCS(pcap_open_dead pcap_freecode pcap_breakloop)
#
# Later versions of Mac OS X 10.3[.x] ship a pcap.h that
# doesn't define pcap_if_t but ship an 0.8[.x] libpcap,
diff --git a/capture-wpcap.c b/capture-wpcap.c
index ebff52e05e..bb06067352 100644
--- a/capture-wpcap.c
+++ b/capture-wpcap.c
@@ -49,6 +49,11 @@ gboolean has_wpcap = FALSE;
#ifdef HAVE_LIBPCAP
+/*
+ * XXX - should we require at least WinPcap 3.1 both for building an
+ * for using Wireshark?
+ */
+
static char* (*p_pcap_lookupdev) (char *);
static void (*p_pcap_close) (pcap_t *);
static int (*p_pcap_stats) (pcap_t *, struct pcap_stat *);
@@ -80,6 +85,9 @@ static int (*p_pcap_datalink_name_to_val) (const char *);
#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
static const char *(*p_pcap_datalink_val_to_name) (int);
#endif
+#ifdef HAVE_PCAP_BREAKLOOP
+static void (*p_pcap_breakloop) (pcap_t *);
+#endif
static const char *(*p_pcap_lib_version) (void);
static int (*p_pcap_setbuff) (pcap_t *, int dim);
static int (*p_pcap_next_ex) (pcap_t *, struct pcap_pkthdr **pkt_header, const u_char **pkt_data);
@@ -121,6 +129,14 @@ load_wpcap(void)
#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
SYM(pcap_datalink_val_to_name, TRUE),
#endif
+#ifdef HAVE_PCAP_BREAKLOOP
+ /*
+ * We don't try to work around the lack of this at
+ * run time; it's present in WinPcap 3.1, which is
+ * the version we build with and ship with.
+ */
+ SYM(pcap_breakloop, FALSE),
+#endif
SYM(pcap_lib_version, TRUE),
SYM(pcap_setbuff, TRUE),
SYM(pcap_next_ex, TRUE),
@@ -422,6 +438,13 @@ pcap_datalink_val_to_name(int dlt)
}
#endif
+#ifdef HAVE_PCAP_BREAKLOOP
+void pcap_breakloop(pcap_t *a)
+{
+ p_pcap_breakloop(a);
+}
+#endif
+
/* setbuff is win32 specific! */
int pcap_setbuff(pcap_t *a, int b)
{
diff --git a/capture_loop.c b/capture_loop.c
index 16d32bb350..02508fc46d 100644
--- a/capture_loop.c
+++ b/capture_loop.c
@@ -811,154 +811,154 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld,
#endif
#ifndef _WIN32
- if (ld->from_cap_pipe) {
- /* dispatch from capture pipe */
+ if (ld->from_cap_pipe) {
+ /* dispatch from capture pipe */
#ifdef LOG_CAPTURE_VERBOSE
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe");
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe");
#endif
+ FD_ZERO(&set1);
+ FD_SET(ld->cap_pipe_fd, &set1);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ sel_ret = select(ld->cap_pipe_fd+1, &set1, NULL, NULL, &timeout);
+ if (sel_ret <= 0) {
+ inpkts = 0;
+ if (sel_ret < 0 && errno != EINTR) {
+ g_snprintf(errmsg, errmsg_len,
+ "Unexpected error from select: %s", strerror(errno));
+ report_capture_error(errmsg, please_report);
+ ld->go = FALSE;
+ }
+ } else {
+ /*
+ * "select()" says we can read from the pipe without blocking
+ */
+ inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len);
+ if (inpkts < 0) {
+ ld->go = FALSE;
+ }
+ }
+ }
+ else
+#endif /* _WIN32 */
+ {
+ /* dispatch from pcap */
+#ifdef MUST_DO_SELECT
+ /*
+ * If we have "pcap_get_selectable_fd()", we use it to get the
+ * descriptor on which to select; if that's -1, it means there
+ * is no descriptor on which you can do a "select()" (perhaps
+ * because you're capturing on a special device, and that device's
+ * driver unfortunately doesn't support "select()", in which case
+ * we don't do the select - which means it might not be possible
+ * to stop a capture until a packet arrives. If that's unacceptable,
+ * plead with whoever supplies the software for that device to add
+ * "select()" support, or upgrade to libpcap 0.8.1 or later, and
+ * rebuild Ethereal or get a version built with libpcap 0.8.1 or
+ * later, so it can use pcap_breakloop().
+ */
+#ifdef LOG_CAPTURE_VERBOSE
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select");
+#endif
+ if (ld->pcap_fd != -1) {
FD_ZERO(&set1);
- FD_SET(ld->cap_pipe_fd, &set1);
- timeout.tv_sec = 0;
- timeout.tv_usec = CAP_READ_TIMEOUT*1000;
- sel_ret = select(ld->cap_pipe_fd+1, &set1, NULL, NULL, &timeout);
- if (sel_ret <= 0) {
- inpkts = 0;
+ FD_SET(ld->pcap_fd, &set1);
+ sel_ret = select(ld->pcap_fd+1, &set1, NULL, NULL, NULL);
+ if (sel_ret > 0) {
+ /*
+ * "select()" says we can read from it without blocking; go for
+ * it.
+ *
+ * We don't have pcap_breakloop(), so we only process one packet
+ * per pcap_dispatch() call, to allow a signal to stop the
+ * processing immediately, rather than processing all packets
+ * in a batch before quitting.
+ */
+ inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *)ld);
+ if (inpkts < 0) {
+ ld->pcap_err = TRUE;
+ ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */
+ }
+ } else {
+ inpkts = 0;
if (sel_ret < 0 && errno != EINTR) {
g_snprintf(errmsg, errmsg_len,
"Unexpected error from select: %s", strerror(errno));
report_capture_error(errmsg, please_report);
ld->go = FALSE;
}
- } else {
- /*
- * "select()" says we can read from the pipe without blocking
- */
- inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len);
- if (inpkts < 0) {
- ld->go = FALSE;
- }
}
}
else
-#endif /* _WIN32 */
- {
- /* dispatch from pcap */
-#ifdef MUST_DO_SELECT
- /*
- * Sigh. The semantics of the read timeout argument to
- * "pcap_open_live()" aren't particularly well specified by
- * the "pcap" man page - at least with the BSD BPF code, the
- * intent appears to be, at least in part, a way of cutting
- * down the number of reads done on a capture, by blocking
- * until the buffer fills or a timer expires - and the Linux
- * libpcap doesn't actually support it, so we can't use it
- * to break out of the "pcap_dispatch()" every 1/4 of a second
- * or so. Linux's libpcap is not the only libpcap that doesn't
- * support the read timeout.
- *
- * Furthermore, at least on Solaris, the bufmod STREAMS module's
- * read timeout won't go off if no data has arrived, i.e. it cannot
- * be used to guarantee that a read from a DLPI stream will return
- * within a specified amount of time regardless of whether any
- * data arrives or not.
- *
- * Thus, on all platforms other than BSD, we do a "select()" on the
- * file descriptor for the capture, with a timeout of CAP_READ_TIMEOUT
- * milliseconds, or CAP_READ_TIMEOUT*1000 microseconds.
- *
- * "select()", on BPF devices, doesn't work as you might expect;
- * at least on some versions of some flavors of BSD, the timer
- * doesn't start until a read is done, so it won't expire if
- * only a "select()" or "poll()" is posted.
- *
- * If we have "pcap_get_selectable_fd()", we use it to get the
- * descriptor on which to select; if that's -1, it means there
- * is no descriptor on which you can do a "select()" (perhaps
- * because you're capturing on a special device, and that device's
- * driver unfortunately doesn't support "select()", in which case
- * we don't do the select - which means Ethereal might block,
- * unable to accept user input, until a packet arrives. If
- * that's unacceptable, plead with whoever supplies the software
- * for that device to add "select()" support.
- */
-#ifdef LOG_CAPTURE_VERBOSE
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select");
-#endif
- if (ld->pcap_fd != -1) {
- FD_ZERO(&set1);
- FD_SET(ld->pcap_fd, &set1);
- timeout.tv_sec = 0;
- timeout.tv_usec = CAP_READ_TIMEOUT*1000;
- sel_ret = select(ld->pcap_fd+1, &set1, NULL, NULL, &timeout);
- if (sel_ret > 0) {
- /*
- * "select()" says we can read from it without blocking; go for
- * it.
- */
- inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *)ld);
- if (inpkts < 0) {
- ld->pcap_err = TRUE;
- ld->go = FALSE;
- }
- } else {
- inpkts = 0;
- if (sel_ret < 0 && errno != EINTR) {
- g_snprintf(errmsg, errmsg_len,
- "Unexpected error from select: %s", strerror(errno));
- report_capture_error(errmsg, please_report);
- ld->go = FALSE;
- }
- }
- }
- else
#endif /* MUST_DO_SELECT */
- {
- /* dispatch from pcap without select */
+ {
+ /* dispatch from pcap without select */
#if 1
#ifdef LOG_CAPTURE_VERBOSE
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch");
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch");
#endif
- inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *) ld);
- if (inpkts < 0) {
+#ifdef _WIN32
+ /*
+ * On Windows, we don't support asynchronously telling a process to
+ * stop capturing; instead, we check for an indication on a pipe
+ * after processing packets. We therefore process only one packet
+ * at a time, so that we can check the pipe after every packet.
+ */
+ inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *) ld);
+#else
+ inpkts = pcap_dispatch(ld->pcap_h, -1, ld->packet_cb, (u_char *) ld);
+#endif
+ if (inpkts < 0) {
+ if (inpkts == -1) {
+ /* Error, rather than pcap_breakloop(). */
ld->pcap_err = TRUE;
- ld->go = FALSE;
}
-#else
- {
+ ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */
+ }
+#else /* pcap_next_ex */
#ifdef LOG_CAPTURE_VERBOSE
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_next_ex");
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_next_ex");
#endif
- /* XXX - this is currently unused, as there is some confusion with pcap_next_ex() vs. pcap_dispatch() */
-
- /* WinPcap's remote capturing feature doesn't work, see http://wiki.ethereal.com/CaptureSetup_2fWinPcapRemote */
- /* for reference, an example remote interface: rpcap://[1.2.3.4]/\Device\NPF_{39993D68-7C9B-4439-A329-F2D888DA7C5C} */
+ /* XXX - this is currently unused, as there is some confusion with pcap_next_ex() vs. pcap_dispatch() */
- /* emulate dispatch from pcap */
- int in;
- struct pcap_pkthdr *pkt_header;
- u_char *pkt_data;
+ /*
+ * WinPcap's remote capturing feature doesn't work with pcap_dispatch(),
+ * see http://wiki.ethereal.com/CaptureSetup_2fWinPcapRemote
+ * This should be fixed in the WinPcap 4.0 alpha release.
+ *
+ * For reference, an example remote interface:
+ * rpcap://[1.2.3.4]/\Device\NPF_{39993D68-7C9B-4439-A329-F2D888DA7C5C}
+ */
- inpkts = 0;
- while( (in = pcap_next_ex(ld->pcap_h, &pkt_header, &pkt_data)) == 1) {
- ld->packet_cb( (u_char *) ld, pkt_header, pkt_data);
- inpkts++;
- }
+ /* emulate dispatch from pcap */
+ {
+ int in;
+ struct pcap_pkthdr *pkt_header;
+ u_char *pkt_data;
+
+ inpkts = 0;
+ in = 0;
+ while(ld->go &&
+ (in = pcap_next_ex(ld->pcap_h, &pkt_header, &pkt_data)) == 1) {
+ ld->packet_cb( (u_char *) ld, pkt_header, pkt_data);
+ inpkts++;
+ }
- if(in < 0) {
- ld->pcap_err = TRUE;
- ld->go = FALSE;
- inpkts = in;
- }
+ if(in < 0) {
+ ld->pcap_err = TRUE;
+ ld->go = FALSE;
+ inpkts = in;
}
-#endif
}
+#endif /* pcap_next_ex */
}
+ }
#ifdef LOG_CAPTURE_VERBOSE
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s"));
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s"));
#endif
- return inpkts;
+ return inpkts;
}
@@ -1069,6 +1069,9 @@ capture_loop_stop_signal_handler(int signo _U_)
int
capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
{
+#ifndef _WIN32
+ struct sigaction act;
+#endif
time_t upd_time, cur_time;
time_t start_time;
int err_close;
@@ -1116,8 +1119,16 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
* Catch SIGUSR1, so that we exit cleanly if the parent process
* kills us with it due to the user selecting "Capture->Stop".
*/
- signal(SIGUSR1, capture_loop_stop_signal_handler);
-#endif
+ act.sa_handler = capture_loop_stop_signal_handler;
+ /*
+ * Arrange that system calls not get restarted, because when
+ * our signal handler returns we don't want to restart
+ * a call that was waiting for packets to arrive.
+ */
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, NULL);
+#endif /* _WIN32 */
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop starting ...");
capture_opts_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, capture_opts);
@@ -1452,7 +1463,11 @@ error:
void capture_loop_stop(void)
{
- ld.go = FALSE;
+#ifdef HAVE_PCAP_BREAKLOOP
+ pcap_breakloop(ld.pcap_h);
+#else
+ ld.go = FALSE;
+#endif
}
@@ -1542,4 +1557,3 @@ capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr,
}
#endif /* HAVE_LIBPCAP */
-
diff --git a/capture_loop.h b/capture_loop.h
index c0a91e2e24..839ba528b9 100644
--- a/capture_loop.h
+++ b/capture_loop.h
@@ -51,28 +51,49 @@ extern void capture_loop_stop(void);
/*** the following is internal only (should be moved to capture_loop_int.h) ***/
+#ifndef HAVE_PCAP_BREAKLOOP
/*
- * We don't want to do a "select()" on the pcap_t's file descriptor on
- * BSD (because "select()" doesn't work correctly on BPF devices on at
- * least some releases of some flavors of BSD), and we don't want to do
- * it on Windows (because "select()" is something for sockets, not for
- * arbitrary handles). (Note that "Windows" here includes Cygwin;
- * even in its pretend-it's-UNIX environment, we're using WinPcap, not
- * a UNIX libpcap.)
+ * We don't have pcap_breakloop(), which is the only way to ensure that
+ * pcap_dispatch(), pcap_loop(), or even pcap_next() or pcap_next_ex()
+ * won't, if the call to read the next packet or batch of packets is
+ * is interrupted by a signal on UN*X, just go back and try again to
+ * read again.
*
- * We *do* want to do it on other platforms, as, on other platforms (with
- * the possible exception of Ultrix and Digital UNIX), the read timeout
- * doesn't expire if no packets have arrived, so a "pcap_dispatch()" call
- * will block until packets arrive, causing the UI to hang.
+ * On UN*X, we catch SIGUSR1 as a "stop capturing" signal, and, in
+ * the signal handler, set a flag to stop capturing; however, without
+ * a guarantee of that sort, we can't guarantee that we'll stop capturing
+ * if the read will be retried and won't time out if no packets arrive.
+ *
+ * Therefore, on at least some platforms, we work around the lack of
+ * pcap_breakloop() by doing a select() on the pcap_t's file descriptor
+ * to wait for packets to arrive, so that we're probably going to be
+ * blocked in the select() when the signal arrives, and can just bail
+ * out of the loop at that point.
+ *
+ * However, we don't want to that on BSD (because "select()" doesn't work
+ * correctly on BPF devices on at least some releases of some flavors of
+ * BSD), and we don't want to do it on Windows (because "select()" is
+ * something for sockets, not for arbitrary handles). (Note that "Windows"
+ * here includes Cygwin; even in its pretend-it's-UNIX environment, we're
+ * using WinPcap, not a UNIX libpcap.)
+ *
+ * Fortunately, we don't need to do it on BSD, because the libpcap timeout
+ * on BSD times out even if no packets have arrived, so we'll eventually
+ * exit pcap_dispatch() with an indication that no packets have arrived,
+ * and will break out of the capture loop at that point.
+ *
+ * On Windows, we can't send a SIGUSR1 to stop capturing, so none of this
+ * applies in any case.
*
* XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
* want to include it if it's not present on this platform, however.
*/
-#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \
+# if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \
!defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \
!defined(__CYGWIN__)
-# define MUST_DO_SELECT
-#endif
+# define MUST_DO_SELECT
+# endif /* avoid select */
+#endif /* HAVE_PCAP_BREAKLOOP */
typedef void (*capture_packet_cb_fct)(u_char *, const struct pcap_pkthdr *, const u_char *);
diff --git a/config.h.win32 b/config.h.win32
index a78f6254bc..46f0ccdbaf 100644
--- a/config.h.win32
+++ b/config.h.win32
@@ -56,6 +56,7 @@
#define NEED_MKSTEMP 1
@HAVE_LIBPCAP@
+@HAVE_PCAP_BREAKLOOP@
@HAVE_PCAP_FINDALLDEVS@
@HAVE_PCAP_DATALINK_NAME_TO_VAL@
@HAVE_PCAP_DATALINK_VAL_TO_NAME@
diff --git a/config.nmake b/config.nmake
index b02347ec71..c78fd8b933 100644
--- a/config.nmake
+++ b/config.nmake
@@ -354,10 +354,16 @@ WINPCAP_CONFIG=^#define HAVE_LIBPCAP 1
PCAP_FINDALLDEVS_CONFIG=^#define HAVE_PCAP_FINDALLDEVS 1
PCAP_DATALINK_NAME_TO_VAL_CONFIG=^#define HAVE_PCAP_DATALINK_NAME_TO_VAL 1
PCAP_DATALINK_VAL_TO_NAME_CONFIG=^#define HAVE_PCAP_DATALINK_VAL_TO_NAME 1
+!IF "$(WINPCAP_VERSION)" == "3.1"
+PCAP_BREAKLOOP_CONFIG=^#define HAVE_PCAP_BREAKLOOP 1
+!ELSE
+PCAP_BREAKLOOP_CONFIG=
+!ENDIF
WPCAP_CONSTIFIED_CONFIG=^#define WPCAP_CONSTIFIED 1
!ELSE
PCAP_FINDALLDEVS_CONFIG=
PCAP_DATALINK_VAL_TO_NAME_CONFIG=
+PCAP_BREAKLOOP_CONFIG=
WPCAP_CONSTIFIED=
!ENDIF
!ELSE
@@ -365,6 +371,7 @@ WINPCAP_CONFIG=
PCAP_FINDALLDEVS_CONFIG=
PCAP_DATALINK_NAME_TO_VAL_CONFIG=
PCAP_DATALINK_VAL_TO_NAME_CONFIG=
+PCAP_BREAKLOOP_CONFIG=
WPCAP_CONSTIFIED=
!ENDIF