From: Guy Harris Date: Sun, 2 Dec 2018 03:00:09 +0000 (-0800) Subject: Put code to open PF_PACKET sockets into a common routine. X-Git-Tag: libpcap-1.10-bp~707 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/818751a975fbd1894b975c5d5211c361fcb51f22?ds=inline Put code to open PF_PACKET sockets into a common routine. --- diff --git a/pcap-linux.c b/pcap-linux.c index 47885cd9..756ada37 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -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; }