From c98ffbcabf992f4925d904e9d4e7960be069ef15 Mon Sep 17 00:00:00 2001 From: guy Date: Fri, 21 Nov 2003 10:19:33 +0000 Subject: [PATCH] Add a "pcap_get_selectable_fd()" API to get an FD on which you can do a "select()" or "poll()" - or -1 if that won't work. --- pcap-bpf.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- pcap-dag.c | 7 ++++++- pcap-dlpi.c | 8 +++++++- pcap-int.h | 3 ++- pcap-linux.c | 8 +++++++- pcap-nit.c | 7 ++++++- pcap-pf.c | 7 ++++++- pcap-snit.c | 8 +++++++- pcap-snoop.c | 7 ++++++- pcap.3 | 4 ++-- pcap.c | 10 ++++++++- pcap.h | 9 +++++++- savefile.c | 8 +++++++- 13 files changed, 130 insertions(+), 14 deletions(-) diff --git a/pcap-bpf.c b/pcap-bpf.c index 82cc7fd..3f6bba2 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.69 2003-11-20 02:02:38 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.70 2003-11-21 10:19:33 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -33,6 +33,7 @@ static const char rcsid[] _U_ = #include #include #include +#include #include @@ -495,6 +496,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, #endif u_int v; pcap_t *p; + struct utsname osinfo; #ifdef HAVE_DAG_API if (strstr(device, "dag")) { @@ -760,6 +762,60 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, memset(p->buffer, 0x0, p->bufsize); #endif + /* + * On most BPF platforms, either you can do a "select()" or + * "poll()" on a BPF file descriptor and it works correctly, + * or you can do it and it will return "readable" if the + * hold buffer is full but not if the timeout expires *and* + * a non-blocking read will, if the hold buffer is empty + * but the store buffer isn't empty, rotate the buffers + * and return what packets are available. + * + * In the latter case, the fact that a non-blocking read + * will give you the available packets means you can work + * around the failure of "select()" and "poll()" to wake up + * and return "readable" when the timeout expires by using + * the timeout as the "select()" or "poll()" timeout, putting + * the BPF descriptor into non-blocking mode, and read from + * it regardless of whether "select()" reports it as readable + * or not. + * + * However, in FreeBSD 4.3 and 4.4, "select()" and "poll()" + * won't wake up and return "readable" if the timer expires + * and non-blocking reads return EWOULDBLOCK if the hold + * buffer is empty, even if the store buffer is non-empty. + * + * This means the workaround in question won't work. + * + * Therefore, on FreeBSD 4.3 and 4.4, we set "p->selectable_fd" + * to -1, which means "sorry, you can't use 'select()' or 'poll()' + * here". On all other BPF platforms, we set it to the FD for + * the BPF device; in NetBSD, OpenBSD, and Darwin, a non-blocking + * read will, if the hold buffer is empty and the store buffer + * isn't empty, rotate the buffers and return what packets are + * there (and in sufficiently recent versions of OpenBSD + * "select()" and "poll()" should work correctly). + * + * XXX - what about AIX? + */ + if (uname(&osinfo) == 0) { + /* + * We can check what OS this is. + */ + if (strcmp(osinfo.sysname, "FreeBSD") == 0 && + (strcmp(osinfo.release, "4.3") == 0 || + strcmp(osinfo.release, "4.4") == 0)) + p->selectable_fd = -1; + else + p->selectable_fd = p->fd; + } else { + /* + * We can't find out what OS this is, so assume we can + * do a "select()" or "poll()". + */ + p->selectable_fd = p->fd; + } + p->read_op = pcap_read_bpf; p->setfilter_op = pcap_setfilter_bpf; p->set_datalink_op = pcap_set_datalink_bpf; diff --git a/pcap-dag.c b/pcap-dag.c index 44b989b..fa01824 100644 --- a/pcap-dag.c +++ b/pcap-dag.c @@ -29,7 +29,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.13 2003-11-20 02:02:38 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.14 2003-11-21 10:19:33 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -469,6 +469,11 @@ pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, c goto fail; } + /* + * "select()" and "poll()" don't (yet) work on DAG device descriptors. + */ + handle->selectable_fd = -1; + #ifdef linux handle->md.device = (char *)device; #else diff --git a/pcap-dlpi.c b/pcap-dlpi.c index 4a9eafd..1e7055b 100644 --- a/pcap-dlpi.c +++ b/pcap-dlpi.c @@ -38,7 +38,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.93 2003-11-20 02:02:38 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.94 2003-11-21 10:19:34 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -704,6 +704,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, goto bad; } + /* + * "p->fd" is an FD for a STREAMS device, so "select()" and + * "poll()" should work on it. + */ + p->selectable_fd = p->fd; + p->read_op = pcap_read_dlpi; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap-int.h b/pcap-int.h index fda8895..996d05a 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.57 2003-11-20 02:02:39 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.58 2003-11-21 10:19:34 guy Exp $ (LBL) */ #ifndef pcap_int_h @@ -101,6 +101,7 @@ struct pcap { int nonblock; #else int fd; + int selectable_fd; #endif /* WIN32 */ int snapshot; int linktype; diff --git a/pcap-linux.c b/pcap-linux.c index b59d4ed..50883b6 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -27,7 +27,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.101 2003-11-20 02:02:39 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.102 2003-11-21 10:19:34 guy Exp $ (LBL)"; #endif /* @@ -397,6 +397,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, return NULL; } + /* + * "handle->fd" is a socket, so "select()" and "poll()" + * should work on it. + */ + handle->selectable_fd = handle->fd; + handle->read_op = pcap_read_linux; handle->setfilter_op = pcap_setfilter_linux; handle->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap-nit.c b/pcap-nit.c index 3f64610..6aebc90 100644 --- a/pcap-nit.c +++ b/pcap-nit.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.52 2003-11-20 02:02:40 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.53 2003-11-21 10:19:35 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -280,6 +280,11 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, goto bad; } + /* + * "handle->fd" is a socket, so "select()" should work on it. + */ + p->selectable_fd = p->fd; + p->read_op = pcap_read_nit; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap-pf.c b/pcap-pf.c index f2bd129..1df6219 100644 --- a/pcap-pf.c +++ b/pcap-pf.c @@ -24,7 +24,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.81 2003-11-20 02:02:40 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.82 2003-11-21 10:19:35 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -434,6 +434,11 @@ your system may not be properly configured; see the packetfilter(4) man page\n", goto bad; } + /* + * "select()" and "poll()" work on packetfilter devices. + */ + p->selectable_fd = p->fd; + p->read_op = pcap_read_pf; p->setfilter_op = pcap_setfilter_pf; p->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap-snit.c b/pcap-snit.c index 4c08b16..20a6889 100644 --- a/pcap-snit.c +++ b/pcap-snit.c @@ -25,7 +25,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.68 2003-11-20 02:02:40 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.69 2003-11-21 10:19:35 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -338,6 +338,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, goto bad; } + /* + * "p->fd" is an FD for a STREAMS device, so "select()" and + * "poll()" should work on it. + */ + p->selectable_fd = p->fd; + p->read_op = pcap_read_snit; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap-snoop.c b/pcap-snoop.c index 0feaf7e..d216ba9 100644 --- a/pcap-snoop.c +++ b/pcap-snoop.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.48 2003-11-20 02:02:40 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.49 2003-11-21 10:19:35 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -323,6 +323,11 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, goto bad; } + /* + * "handle->fd" is a socket, so "select()" should work on it. + */ + p->selectable_fd = p->fd; + p->read_op = pcap_read_snoop; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ diff --git a/pcap.3 b/pcap.3 index 290a994..6f356c0 100644 --- a/pcap.3 +++ b/pcap.3 @@ -1,4 +1,4 @@ -.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.54 2003-11-18 22:14:24 guy Exp $ +.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.55 2003-11-21 10:19:36 guy Exp $ .\" .\" Copyright (c) 1994, 1996, 1997 .\" The Regents of the University of California. All rights reserved. @@ -19,7 +19,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP 3 "18 November 2003" +.TH PCAP 3 "21 November 2003" .SH NAME pcap \- Packet Capture library .SH SYNOPSIS diff --git a/pcap.c b/pcap.c index aa584cb..8dd34b9 100644 --- a/pcap.c +++ b/pcap.c @@ -33,7 +33,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.68 2003-11-20 02:02:41 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.69 2003-11-21 10:19:36 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -500,6 +500,14 @@ pcap_fileno(pcap_t *p) #endif } +#ifndef WIN32 +int +pcap_get_selectable_fd(pcap_t *p) +{ + return (p->selectable_fd); +} +#endif + void pcap_perror(pcap_t *p, char *prefix) { diff --git a/pcap.h b/pcap.h index da05938..0695c1e 100644 --- a/pcap.h +++ b/pcap.h @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.47 2003-11-18 22:14:25 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.48 2003-11-21 10:19:36 guy Exp $ (LBL) */ #ifndef lib_pcap_h @@ -241,6 +241,13 @@ int pcap_setmintocopy(pcap_t *p, int size); #define MODE_CAPT 0 #define MODE_STAT 1 +#else +/* + * UN*X definitions + */ + +int pcap_get_selectable_fd(pcap_t *); + #endif /* WIN32 */ #ifdef __cplusplus diff --git a/savefile.c b/savefile.c index fa0130b..036fc0d 100644 --- a/savefile.c +++ b/savefile.c @@ -30,7 +30,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.97 2003-11-20 02:02:41 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.98 2003-11-21 10:19:37 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -600,6 +600,12 @@ pcap_open_offline(const char *fname, char *errbuf) break; } + /* + * You can do "select()" and "poll()" on plain files on most + * platforms, and should be able to do so on pipes. + */ + p->selectable_fd = fileno(fp); + p->read_op = pcap_offline_read; p->setfilter_op = install_bpf_program; p->set_datalink_op = NULL; /* we don't support munging link-layer headers */