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
This commit is contained in:
Guy Harris 2006-05-20 23:18:44 +00:00
parent 37a570600e
commit 58a0b10698
2 changed files with 142 additions and 30 deletions

View File

@ -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 */
#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 */
#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) {
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");
#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} */
/* 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++;
}
if(in < 0) {
inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *) ld);
if (inpkts < 0) {
ld->pcap_err = TRUE;
ld->go = FALSE;
inpkts = in;
}
}
#else
{
#ifdef LOG_CAPTURE_VERBOSE
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} */
/* 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++;
}
if(in < 0) {
ld->pcap_err = TRUE;
ld->go = FALSE;
inpkts = in;
}
}
#endif
}
}
#ifdef LOG_CAPTURE_VERBOSE
@ -1018,6 +1101,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
ld.pdh = NULL;
#ifndef _WIN32
ld.cap_pipe_fd = -1;
#endif
#ifdef MUST_DO_SELECT
ld.pcap_fd = 0;
#endif
ld.packet_cb = capture_loop_packet_cb;

View File

@ -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 */