]> The Tcpdump Group git mirrors - libpcap/commitdiff
Put code to open PF_PACKET sockets into a common routine.
authorGuy Harris <[email protected]>
Sun, 2 Dec 2018 03:00:09 +0000 (19:00 -0800)
committerGuy Harris <[email protected]>
Sun, 2 Dec 2018 03:00:09 +0000 (19:00 -0800)
pcap-linux.c

index 47885cd9e5ae4e2cce678f678cd8386431ecdaff..756ada37e743ad2365d656723634d86fa9a2bf09 100644 (file)
@@ -3618,6 +3618,77 @@ set_dlt_list_cooked(pcap_t *handle _U_, int sock_fd _U_)
 }
 #endif
 
+#ifdef HAVE_PF_PACKET_SOCKETS
+/*
+ * We need a special error return to indicate that PF_PACKET sockets
+ * aren't supported; that's not a fatal error, it's just an indication
+ * that we have a pre-2.2 kernel, and must fall back on PF_INET/SOCK_PACKET
+ * sockets.
+ *
+ * We assume, for now, that we won't have so many PCAP_ERROR_ values that
+ * -128 will be used, and use that as the error (it fits into a byte,
+ * so comparison against it should be doable without too big an immediate
+ * value - yeah, I know, premature optimization is the root of all evil...).
+ */
+#define PCAP_ERROR_NO_PF_PACKET_SOCKETS        -128
+
+/*
+ * Open a PF_PACKET socket.
+ */
+static int
+open_pf_packet_socket(pcap_t *handle, int cooked)
+{
+       int     protocol = pcap_protocol(handle);
+       int     sock_fd, ret;
+
+       /*
+        * Open a socket with protocol family packet. If cooked is true,
+        * we open a SOCK_DGRAM socket for the cooked interface, otherwise
+        * we open a SOCK_RAW socket for the raw interface.
+        */
+       sock_fd = cooked ?
+               socket(PF_PACKET, SOCK_DGRAM, protocol) :
+               socket(PF_PACKET, SOCK_RAW, protocol);
+
+       if (sock_fd == -1) {
+               if (errno == EINVAL || errno == EAFNOSUPPORT) {
+                       /*
+                        * PF_PACKET sockets aren't supported.
+                        *
+                        * If this is the first attempt to open a PF_PACKET
+                        * socket, our caller will just want to try a
+                        * PF_INET/SOCK_PACKET socket; in other cases, we
+                        * already succeeded opening a PF_PACKET socket,
+                        * but are just switching to cooked from raw, in
+                        * which case this is a fatal error (and "can't
+                        * happen", because the kernel isn't going to
+                        * spontaneously drop its support for PF_PACKET
+                        * sockets).
+                        */
+                       pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                           "PF_PACKET sockets not supported (this \"can't happen\"!");
+                       return PCAP_ERROR_NO_PF_PACKET_SOCKETS;
+               }
+               if (errno == EPERM || errno == EACCES) {
+                       /*
+                        * You don't have permission to open the
+                        * socket.
+                        */
+                       ret = PCAP_ERROR_PERM_DENIED;
+               } else {
+                       /*
+                        * Other error.
+                        */
+                       ret = PCAP_ERROR;
+               }
+               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "socket");
+               return ret;
+       }
+       return sock_fd;
+}
+#endif
+
 /*
  * Try to open a packet socket using the new kernel PF_PACKET interface.
  * Returns 1 on success, 0 on an error that means the new interface isn't
@@ -3632,8 +3703,7 @@ activate_new(pcap_t *handle)
        struct pcap_linux *handlep = handle->priv;
        const char              *device = handle->opt.device;
        int                     is_any_device = (strcmp(device, "any") == 0);
-       int                     protocol = pcap_protocol(handle);
-       int                     sock_fd = -1, arptype, ret;
+       int                     sock_fd = -1, arptype;
 #ifdef HAVE_PACKET_AUXDATA
        int                     val;
 #endif
@@ -3645,38 +3715,28 @@ activate_new(pcap_t *handle)
 #endif
 
        /*
-        * Open a socket with protocol family packet. If the
-        * "any" device was specified, we open a SOCK_DGRAM
-        * socket for the cooked interface, otherwise we first
-        * try a SOCK_RAW socket for the raw interface.
+        * Try to open a PF_PACKET socket. If the "any" device was
+        * specified, we open a SOCK_DGRAM socket for the cooked
+        * interface, otherwise we first try a SOCK_RAW socket for
+        * the raw interface.
         */
-       sock_fd = is_any_device ?
-               socket(PF_PACKET, SOCK_DGRAM, protocol) :
-               socket(PF_PACKET, SOCK_RAW, protocol);
-
-       if (sock_fd == -1) {
-               if (errno == EINVAL || errno == EAFNOSUPPORT) {
+       sock_fd = open_pf_packet_socket(handle, is_any_device);
+       if (sock_fd < 0) {
+               if (sock_fd == PCAP_ERROR_NO_PF_PACKET_SOCKETS) {
                        /*
                         * We don't support PF_PACKET/SOCK_whatever
-                        * sockets; try the old mechanism.
+                        * sockets; tell our caller to try the old
+                        * mechanism.
                         */
                        return 0;
                }
-               if (errno == EPERM || errno == EACCES) {
-                       /*
-                        * You don't have permission to open the
-                        * socket.
-                        */
-                       ret = PCAP_ERROR_PERM_DENIED;
-               } else {
-                       /*
-                        * Other error.
-                        */
-                       ret = PCAP_ERROR;
-               }
-               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "socket");
-               return ret;
+
+               /*
+                * Fatal error; the return value is the error code,
+                * and handle->errbuf has been set to an appropriate
+                * error message.
+                */
+               return sock_fd;
        }
 
        /* It seems the kernel supports the new interface. */
@@ -3769,23 +3829,30 @@ activate_new(pcap_t *handle)
                                    PCAP_ERRBUF_SIZE, errno, "close");
                                return PCAP_ERROR;
                        }
-                       sock_fd = socket(PF_PACKET, SOCK_DGRAM, protocol);
-                       if (sock_fd == -1) {
-                               if (errno == EPERM || errno == EACCES) {
+                       sock_fd = open_pf_packet_socket(handle, 1);
+                       if (sock_fd < 0) {
+                               if (sock_fd == PCAP_ERROR_NO_PF_PACKET_SOCKETS) {
                                        /*
-                                        * You don't have permission to
-                                        * open the socket.
-                                        */
-                                       ret = PCAP_ERROR_PERM_DENIED;
-                               } else {
-                                       /*
-                                        * Other error.
+                                        * We don't support PF_PACKET/SOCK_whatever
+                                        * sockets.  This should never happen,
+                                        * because we don't support cooked mode
+                                        * without those sockets, so we
+                                        * shouldn't get called if we're
+                                        * running on a kernel old enough
+                                        * not to support them.
+                                        *
+                                        * The error message has already been
+                                        * filled in appropriately.
                                         */
-                                       ret = PCAP_ERROR;
+                                       sock_fd = PCAP_ERROR;
                                }
-                               pcap_fmt_errmsg_for_errno(handle->errbuf,
-                                   PCAP_ERRBUF_SIZE, errno, "socket");
-                               return ret;
+                               /*
+                                * Fatal error; the return value is the
+                                * error code, and handle->errbuf has
+                                * been set to an appropriate error
+                                * message.
+                                */
+                               return sock_fd;
                        }
                        handlep->cooked = 1;
 
@@ -3835,7 +3902,7 @@ activate_new(pcap_t *handle)
                }
 
                if ((err = iface_bind(sock_fd, handlep->ifindex,
-                   handle->errbuf, protocol)) != 0) {
+                   handle->errbuf, pcap_protocol(handle))) != 0) {
                        close(sock_fd);
                        return err;
                }