From 34624f128f2e6f937c04317fdd2c0ae6d8883ad5 Mon Sep 17 00:00:00 2001 From: guy Date: Tue, 1 Jul 2008 08:02:33 +0000 Subject: [PATCH] When activating a device, return PCAP_ERROR_IFACE_NOT_UP if the device isn't up, so applications can report that differently from a generic error (the latter could mean there's a bug somewhere in libpcap). When capturing on a device without mmap on Linux, ignore ENETDOWN, so that we can continue to capture traffic if the interface goes down and comes back up again; comments in the kernel indicate that we'll just block waiting for packets if we try to receive from a socket that delivered ENETDOWN, and, if we're using a memory-mapped buffer, we won't even get notified of "network down" events. --- pcap-bpf.c | 33 +++++++++++++++++++----- pcap-linux.c | 63 +++++++++++++++++++++++++++++++++++---------- pcap.c | 5 +++- pcap/pcap.h | 3 ++- pcap_activate.3pcap | 8 +++--- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/pcap-bpf.c b/pcap-bpf.c index 3aa1ccf..c55170a 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.110 2008-04-14 20:40:58 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.111 2008-07-01 08:02:33 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -413,11 +413,23 @@ pcap_can_set_rfmon_bpf(pcap_t *p) */ (void)strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "BIOCSETIF: %s: %s", - p->opt.source, pcap_strerror(errno)); - close(fd); - return (PCAP_ERROR); + if (errno == ENETDOWN) { + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + close(fd); + return (PCAP_ERROR_IFACE_NOT_UP); + } else { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "BIOCSETIF: %s: %s", + p->opt.source, pcap_strerror(errno)); + close(fd); + return (PCAP_ERROR); + } } /* @@ -1016,6 +1028,15 @@ check_setif_failure(pcap_t *p, int error) */ strcpy(p->errbuf, ""); return (PCAP_ERROR_NO_SUCH_DEVICE); + } else if (errno == ENETDOWN) { + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + return (PCAP_ERROR_IFACE_NOT_UP); } else { /* * Some other error; fill in the error string, and diff --git a/pcap-linux.c b/pcap-linux.c index c547711..d9f9f10 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -34,7 +34,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.149 2008-06-24 06:44:32 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.150 2008-07-01 08:02:33 guy Exp $ (LBL)"; #endif /* @@ -646,8 +646,21 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) offset = 0; #endif - /* Receive a single packet from the kernel */ - + /* + * Receive a single packet from the kernel. + * We ignore EINTR, as that might just be due to a signal + * being delivered - if the signal should interrupt the + * loop, the signal handler should call pcap_breakloop() + * to set handle->break_loop (we ignore it on other + * platforms as well). + * We also ignore ENETDOWN, so that we can continue to + * capture traffic if the interface goes down and comes + * back up again; comments in the kernel indicate that + * we'll just block waiting for packets if we try to + * receive from a socket that delivered ENETDOWN, and, + * if we're using a memory-mapped buffer, we won't even + * get notified of "network down" events. + */ bp = handle->buffer + handle->offset; do { /* @@ -667,7 +680,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) handle->fd, bp + offset, handle->bufsize - offset, MSG_TRUNC, (struct sockaddr *) &from, &fromlen); - } while (packet_len == -1 && errno == EINTR); + } while (packet_len == -1 && (errno == EINTR || errno == ENETDOWN)); /* Check if an error occured */ @@ -1722,10 +1735,10 @@ activate_new(pcap_t *handle) } if ((err = iface_bind(sock_fd, handle->md.ifindex, - handle->errbuf)) < 0) { + handle->errbuf)) != 1) { close(sock_fd); - if (err == -2) - return PCAP_ERROR; + if (err < 0) + return err; else return 0; /* try old mechanism */ } @@ -2183,6 +2196,8 @@ iface_get_id(int fd, const char *device, char *ebuf) /* * Bind the socket associated with FD to the given device. + * Return 1 on success, 0 if we should try a SOCK_PACKET socket, + * or a PCAP_ERROR_ value on a hard error. */ static int iface_bind(int fd, int ifindex, char *ebuf) @@ -2197,9 +2212,20 @@ iface_bind(int fd, int ifindex, char *ebuf) sll.sll_protocol = htons(ETH_P_ALL); if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "bind: %s", pcap_strerror(errno)); - return -1; + if (errno == ENETDOWN) { + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + return PCAP_ERROR_IFACE_NOT_UP; + } else { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "bind: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } } /* Any pending errors, e.g., network is down? */ @@ -2207,16 +2233,25 @@ iface_bind(int fd, int ifindex, char *ebuf) if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); - return -2; + return 0; } - if (err > 0) { + if (err == ENETDOWN) { + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + return PCAP_ERROR_IFACE_NOT_UP; + } else if (err > 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(err)); - return -2; + return 0; } - return 0; + return 1; } /* diff --git a/pcap.c b/pcap.c index 01d12d9..3f9fa09 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.122 2008-05-26 19:58:06 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.123 2008-07-01 08:02:33 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -951,6 +951,9 @@ pcap_statustostr(int errnum) case PCAP_ERROR_PERM_DENIED: return ("You don't have permission to capture on that device"); + + case PCAP_ERROR_IFACE_NOT_UP: + return ("That device is not up"); } (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); return(ebuf); diff --git a/pcap/pcap.h b/pcap/pcap.h index c7bc8a0..2175308 100644 --- a/pcap/pcap.h +++ b/pcap/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/pcap.h,v 1.13 2008-05-30 01:35:33 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.14 2008-07-01 08:02:33 guy Exp $ (LBL) */ #ifndef lib_pcap_pcap_h @@ -243,6 +243,7 @@ typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, #define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */ #define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */ #define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */ +#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */ /* * Warning codes for the pcap API. diff --git a/pcap_activate.3pcap b/pcap_activate.3pcap index 0adf6ac..b33fa53 100644 --- a/pcap_activate.3pcap +++ b/pcap_activate.3pcap @@ -1,4 +1,4 @@ -.\" @(#) $Header: /tcpdump/master/libpcap/pcap_activate.3pcap,v 1.4 2008-04-09 21:26:12 guy Exp $ +.\" @(#) $Header: /tcpdump/master/libpcap/pcap_activate.3pcap,v 1.5 2008-07-01 08:02:33 guy Exp $ .\" .\" Copyright (c) 1994, 1996, 1997 .\" The Regents of the University of California. All rights reserved. @@ -54,9 +54,11 @@ exist, if the process doesn't have permission to open the capture source, .B PCAP_ERROR_RFMON_NOTSUP if monitor mode was specified but the capture source doesn't support -monitor mode, and +monitor mode, +.B PCAP_ERROR_IFACE_NOT_UP +if the capture source is not up, and .B PCAP_ERROR -if an error occurred. +if another error occurred. If .B PCAP_WARNING or