From ffb13dc7231051a50bce0461dc100e0aaae4dbfa Mon Sep 17 00:00:00 2001 From: Afshin Paydar Date: Wed, 24 Sep 2025 11:44:13 +0800 Subject: [PATCH 1/4] Fix interface existence check on Linux before privileged operations When attempting to open a non-existent network interface on Linux, libpcap would return PCAP_ERROR_PERM_DENIED instead of the correct PCAP_ERROR_NO_SUCH_DEVICE. This occurred because the privileged socket(PF_PACKET, SOCK_RAW, 0) call in setup_socket() would fail with permission errors before interface validation could occur. This fix adds an interface existence check using an unprivileged AF_INET socket and SIOCGIFINDEX ioctl before attempting to create the privileged packet socket. Non-existent interfaces now correctly return PCAP_ERROR_NO_SUCH_DEVICE. This resolves the issue where applications like tcpdump would report "Permission denied" instead of "No such device exists" for non-existent interfaces, breaking their fallback logic from interface names to indices. Note: BSD/macOS platforms have the same underlying issue where BPF device access fails with permission errors before interface validation. This should be addressed in a separate commit. Fixes: libpcap issue #1538 Related: tcpdump issue #1334 Signed-off-by: Afshin Paydar --- pcap-linux.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/pcap-linux.c b/pcap-linux.c index d1278cc5bb..3e56fcd1f5 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -345,6 +345,7 @@ static const struct timeval netdown_timeout = { static int iface_get_id(int fd, const char *device, char *ebuf); static int iface_get_mtu(int fd, const char *device, char *ebuf); static int iface_get_arptype(int fd, const char *device, char *ebuf); +static int iface_exists(const char *device, char *ebuf); static int iface_bind(int fd, int ifindex, char *ebuf, int protocol); static int enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device); @@ -2426,6 +2427,17 @@ setup_socket(pcap_t *handle, int is_any_device) int err = 0; struct packet_mreq mr; + /* + * Check if the interface exists before attempting to open + * the privileged packet socket, so we can return the correct + * error for non-existent interfaces. + */ + if (!is_any_device) { + status = iface_exists(device, handle->errbuf); + if (status != 0) + return status; + } + /* * Open a socket with protocol family packet. If cooked is true, * we open a SOCK_DGRAM socket for the cooked interface, otherwise @@ -4812,6 +4824,50 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) return 0; } +/* + * Check if the given interface exists. Return 0 on success, + * PCAP_ERROR_NO_SUCH_DEVICE if the interface doesn't exist, + * or PCAP_ERROR on other failures. + */ +static int +iface_exists(const char *device, char *ebuf) +{ + int fd; + struct ifreq ifr; + + if (strlen(device) >= sizeof(ifr.ifr_name)) { + ebuf[0] = '\0'; + return PCAP_ERROR_NO_SUCH_DEVICE; + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + pcapint_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "socket"); + return PCAP_ERROR; + } + + memset(&ifr, 0, sizeof(ifr)); + pcapint_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + int save_errno = errno; + close(fd); + + if (save_errno == ENODEV) { + ebuf[0] = '\0'; + return PCAP_ERROR_NO_SUCH_DEVICE; + } else { + pcapint_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + save_errno, "SIOCGIFINDEX on %s", device); + return PCAP_ERROR; + } + } + + close(fd); + return 0; +} + /* * Return the index of the given device name. Fill ebuf and return * -1 on failure. @@ -5164,13 +5220,13 @@ iface_get_ts_types(const char *device, pcap_t *handle, char *ebuf) case ENODEV: /* - * OK, no such device. - * The user will find that out when they try to - * activate the device; just return an empty - * list of time stamp types. + * No such device. + * + * There's nothing more to say, so clear the + * error message. */ - handle->tstamp_type_list = NULL; - return 0; + ebuf[0] = '\0'; + return PCAP_ERROR_NO_SUCH_DEVICE; default: /* From 25df52d9b66a693d5305aa3b47637cfb4a111e11 Mon Sep 17 00:00:00 2001 From: Afshin Paydar Date: Mon, 24 Nov 2025 18:13:01 +0800 Subject: [PATCH 2/4] reverted the changes to iface_get_ts_types() --- pcap-linux.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcap-linux.c b/pcap-linux.c index d59e5b11cc..e4a10d69ac 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -5220,13 +5220,13 @@ iface_get_ts_types(const char *device, pcap_t *handle, char *ebuf) case ENODEV: /* - * No such device. - * - * There's nothing more to say, so clear the - * error message. + * OK, no such device. + * The user will find that out when they try to + * activate the device; just return an empty + * list of time stamp types. */ - ebuf[0] = '\0'; - return PCAP_ERROR_NO_SUCH_DEVICE; + handle->tstamp_type_list = NULL; + return 0; default: /* From b1a4099bcc6acf6b78e688f0801904cfc9dcc331 Mon Sep 17 00:00:00 2001 From: Afshin Paydar Date: Mon, 24 Nov 2025 18:26:09 +0800 Subject: [PATCH 3/4] removed iface_exists and used the existing iface_get_arptype() --- pcap-linux.c | 65 ++++++++++++++-------------------------------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/pcap-linux.c b/pcap-linux.c index e4a10d69ac..c33920fde7 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -345,7 +345,6 @@ static const struct timeval netdown_timeout = { static int iface_get_id(int fd, const char *device, char *ebuf); static int iface_get_mtu(int fd, const char *device, char *ebuf); static int iface_get_arptype(int fd, const char *device, char *ebuf); -static int iface_exists(const char *device, char *ebuf); static int iface_bind(int fd, int ifindex, char *ebuf, int protocol); static int enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device); @@ -2433,9 +2432,23 @@ setup_socket(pcap_t *handle, int is_any_device) * error for non-existent interfaces. */ if (!is_any_device) { - status = iface_exists(device, handle->errbuf); - if (status != 0) - return status; + int fd; + int arptype; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + pcapint_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, errno, "socket"); + return PCAP_ERROR; + } + arptype = iface_get_arptype(fd, device, handle->errbuf); + close(fd); + if (arptype < 0) { + /* + * Fatal error, including PCAP_ERROR_NO_SUCH_DEVICE. + */ + return arptype; + } } /* @@ -4824,50 +4837,6 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) return 0; } -/* - * Check if the given interface exists. Return 0 on success, - * PCAP_ERROR_NO_SUCH_DEVICE if the interface doesn't exist, - * or PCAP_ERROR on other failures. - */ -static int -iface_exists(const char *device, char *ebuf) -{ - int fd; - struct ifreq ifr; - - if (strlen(device) >= sizeof(ifr.ifr_name)) { - ebuf[0] = '\0'; - return PCAP_ERROR_NO_SUCH_DEVICE; - } - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - pcapint_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - errno, "socket"); - return PCAP_ERROR; - } - - memset(&ifr, 0, sizeof(ifr)); - pcapint_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); - - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - int save_errno = errno; - close(fd); - - if (save_errno == ENODEV) { - ebuf[0] = '\0'; - return PCAP_ERROR_NO_SUCH_DEVICE; - } else { - pcapint_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - save_errno, "SIOCGIFINDEX on %s", device); - return PCAP_ERROR; - } - } - - close(fd); - return 0; -} - /* * Return the index of the given device name. Fill ebuf and return * -1 on failure. From 092b20ef6658e3cdb1242cb0335b5a0f95ec836b Mon Sep 17 00:00:00 2001 From: Afshin Paydar Date: Mon, 24 Nov 2025 20:48:40 +0800 Subject: [PATCH 4/4] fix shadowing --- pcap-linux.c | 1 - 1 file changed, 1 deletion(-) diff --git a/pcap-linux.c b/pcap-linux.c index c33920fde7..df87091677 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -2433,7 +2433,6 @@ setup_socket(pcap_t *handle, int is_any_device) */ if (!is_any_device) { int fd; - int arptype; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) {