diff options
-rw-r--r-- | Makefile.nmake | 1 | ||||
-rw-r--r-- | acinclude.m4 | 2 | ||||
-rw-r--r-- | capture-wpcap.c | 23 | ||||
-rw-r--r-- | capture_loop.c | 258 | ||||
-rw-r--r-- | capture_loop.h | 49 | ||||
-rw-r--r-- | config.h.win32 | 1 | ||||
-rw-r--r-- | config.nmake | 7 |
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 |