aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2006-05-20 23:18:44 +0000
committerGuy Harris <guy@alum.mit.edu>2006-05-20 23:18:44 +0000
commit58a0b106989d8278670d78fba86dd4585c3be52d (patch)
tree2fe596603e8f0e72050cb1a141aba73cfec7b0f4
parent37a570600e7188e9a0d3ef5bbdf66dec431689be (diff)
The timeout is needed if you don't have pcap_breakloop(), so we'll put
it back for now; I'll fix it later not to do the timeout if we have pcap_breakloop(). svn path=/trunk/; revision=18195
-rw-r--r--capture_loop.c138
-rw-r--r--capture_loop.h26
2 files changed, 138 insertions, 26 deletions
diff --git a/capture_loop.c b/capture_loop.c
index d87854f534..16d32bb350 100644
--- a/capture_loop.c
+++ b/capture_loop.c
@@ -628,6 +628,17 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
#endif
}
+/* XXX - will this work for tethereal? */
+#ifdef MUST_DO_SELECT
+ if (!ld->from_cap_pipe) {
+#ifdef HAVE_PCAP_GET_SELECTABLE_FD
+ ld->pcap_fd = pcap_get_selectable_fd(ld->pcap_h);
+#else
+ ld->pcap_fd = pcap_fileno(ld->pcap_h);
+#endif
+ }
+#endif
+
/* Does "open_err_str" contain a non-empty string? If so, "pcap_open_live()"
returned a warning; print it, but keep capturing. */
if (open_err_str[0] != '\0') {
@@ -832,43 +843,115 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld,
#endif /* _WIN32 */
{
/* dispatch from pcap */
-#if 1
+#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");
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select");
#endif
- inpkts = pcap_dispatch(ld->pcap_h, -1, ld->packet_cb, (u_char *) ld);
- if (inpkts < 0) {
- ld->pcap_err = TRUE;
- ld->go = FALSE;
+ 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 */
+#if 1
+#ifdef LOG_CAPTURE_VERBOSE
+ 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) {
+ ld->pcap_err = TRUE;
+ ld->go = FALSE;
+ }
#else
+ {
#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() */
+ /* 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} */
+ /* 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} */
- /* emulate dispatch from pcap */
- {
- int in;
- struct pcap_pkthdr *pkt_header;
- u_char *pkt_data;
-
- 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;
- if(in < 0) {
- ld->pcap_err = TRUE;
- ld->go = FALSE;
- inpkts = in;
+ 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++;
+ }
+
+ if(in < 0) {
+ ld->pcap_err = TRUE;
+ ld->go = FALSE;
+ inpkts = in;
+ }
}
- }
#endif
+ }
}
#ifdef LOG_CAPTURE_VERBOSE
@@ -1019,6 +1102,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
#ifndef _WIN32
ld.cap_pipe_fd = -1;
#endif
+#ifdef MUST_DO_SELECT
+ ld.pcap_fd = 0;
+#endif
ld.packet_cb = capture_loop_packet_cb;
diff --git a/capture_loop.h b/capture_loop.h
index 4d2778793b..c0a91e2e24 100644
--- a/capture_loop.h
+++ b/capture_loop.h
@@ -51,6 +51,29 @@ extern void capture_loop_stop(void);
/*** the following is internal only (should be moved to capture_loop_int.h) ***/
+/*
+ * 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 *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.
+ *
+ * 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__) && \
+ !defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \
+ !defined(__CYGWIN__)
+# define MUST_DO_SELECT
+#endif
+
typedef void (*capture_packet_cb_fct)(u_char *, const struct pcap_pkthdr *, const u_char *);
@@ -72,6 +95,9 @@ typedef struct _loop_data {
/* pcap "input file" */
pcap_t *pcap_h; /* pcap handle */
gboolean pcap_err; /* E: TRUE if error from pcap */
+#ifdef MUST_DO_SELECT
+ int pcap_fd; /* pcap file descriptor */
+#endif
/* capture pipe (unix only "input file") */
gboolean from_cap_pipe; /* TRUE if we are capturing data from a capture pipe */