]> 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 a12e64ddfde86f7d040eeb4de21361d26ea00849..bef5c437858d300e6215615571b6213f344dc782 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static const char rcsid[] _U_ =
 
 #ifndef lint
 static const char rcsid[] _U_ =
-    "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.18 2008-04-14 20:41:52 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
 
 /*
 #endif
 
 /*
@@ -471,7 +471,7 @@ static void pcap_cleanup_linux( pcap_t *handle )
                free(handle->md.device);
                handle->md.device = NULL;
        }
                free(handle->md.device);
                handle->md.device = NULL;
        }
-       pcap_cleanup_live_common(p);
+       pcap_cleanup_live_common(handle);
 }
 
 /*
 }
 
 /*
@@ -625,10 +625,10 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
 #else
        struct sockaddr         from;
 #endif
 #else
        struct sockaddr         from;
 #endif
-       socklen_t               fromlen;
        int                     packet_len, caplen;
        struct pcap_pkthdr      pcap_header;
        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
 #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
 
        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;
        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?
        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;
                }
                        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 */
 
 
        /* Check if an error occured */
 
@@ -1539,6 +1561,17 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok)
                handle->linktype = DLT_LINUX_LAPD;
                break;
 
                handle->linktype = DLT_LINUX_LAPD;
                break;
 
+#ifndef ARPHRD_NONE
+#define ARPHRD_NONE    0xFFFE
+#endif
+       case ARPHRD_NONE:
+               /*
+                * No link-layer header; packets are just IP
+                * packets, so use DLT_RAW.
+                */
+               handle->linktype = DLT_RAW;
+               break;
+
        default:
                handle->linktype = -1;
                break;
        default:
                handle->linktype = -1;
                break;
@@ -1711,10 +1744,10 @@ activate_new(pcap_t *handle)
                }
 
                if ((err = iface_bind(sock_fd, handle->md.ifindex,
                }
 
                if ((err = iface_bind(sock_fd, handle->md.ifindex,
-                   handle->errbuf)) < 0) {
+                   handle->errbuf)) != 1) {
                        close(sock_fd);
                        close(sock_fd);
-                       if (err == -2)
-                               return PCAP_ERROR;
+                       if (err < 0)
+                               return err;
                        else
                                return 0;       /* try old mechanism */
                }
                        else
                                return 0;       /* try old mechanism */
                }
@@ -2172,6 +2205,8 @@ iface_get_id(int fd, const char *device, char *ebuf)
 
 /*
  *  Bind the socket associated with FD to the given device.
 
 /*
  *  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)
  */
 static int
 iface_bind(int fd, int ifindex, char *ebuf)
@@ -2186,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) {
        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? */
        }
 
        /* Any pending errors, e.g., network is down? */
@@ -2196,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));
        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));
                snprintf(ebuf, PCAP_ERRBUF_SIZE,
                        "bind: %s", pcap_strerror(err));
-               return -2;
+               return 0;
        }
 
        }
 
-       return 0;
+       return 1;
 }
 
 /*
 }
 
 /*