X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/65deb394b2e22cdb893fd8b12dbd841b9104753a..83b51abcee9dffe2a58c564fb390ce3a3e772f1a:/pcap-linux.c diff --git a/pcap-linux.c b/pcap-linux.c index db561367..bef5c437 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.129.2.20 2008-06-24 06:45:04 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.22 2008-08-06 07:40:20 guy Exp $ (LBL)"; #endif /* @@ -625,10 +625,10 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #else struct sockaddr from; #endif - socklen_t fromlen; int packet_len, caplen; struct pcap_pkthdr pcap_header; - + struct iovec iov; + struct msghdr msg; #ifdef HAVE_PF_PACKET_SOCKETS /* * If this is a cooked device, leave extra room for a @@ -646,9 +646,34 @@ 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; + + msg.msg_name = &from; + msg.msg_namelen = sizeof(from); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + iov.iov_len = handle->bufsize - offset; + iov.iov_base = bp + offset; + do { /* * Has "pcap_breakloop()" been called? @@ -662,12 +687,9 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) handle->break_loop = 0; return -2; } - fromlen = sizeof(from); - packet_len = recvfrom( - handle->fd, bp + offset, - handle->bufsize - offset, MSG_TRUNC, - (struct sockaddr *) &from, &fromlen); - } while (packet_len == -1 && errno == EINTR); + + packet_len = recvmsg(handle->fd, &msg, MSG_TRUNC); + } while (packet_len == -1 && (errno == EINTR || errno == ENETDOWN)); /* Check if an error occured */ @@ -1722,10 +1744,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 +2205,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 +2221,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 +2242,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; } /*