]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-linux.c
From Patrick McHardy: Convert pcap-linux to use recvmsg() as preparation
[libpcap] / pcap-linux.c
index db561367bd2e82bc2d816bb8be37cdd4d7938431..bef5c437858d300e6215615571b6213f344dc782 100644 (file)
@@ -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;
 }
 
 /*