+#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
+