]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-bpf.c
Update config.{guess,sub}, timestamps 2023-01-01,2023-01-21
[libpcap] / pcap-bpf.c
index 60086e42f17dcb2756f68b63595e76dc63bec83c..2898e598d8e3916c598c6535d79c9851b008d6b0 100644 (file)
  */
 
 #ifdef HAVE_CONFIG_H
-#include "config.h"
+#include <config.h>
 #endif
 
 #include <sys/param.h>                 /* optionally get BSD define */
-#ifdef HAVE_ZEROCOPY_BPF
-#include <sys/mman.h>
-#endif
 #include <sys/socket.h>
 #include <time.h>
 /*
  * <net/bpf.h> defines ioctls, but doesn't include <sys/ioccom.h>.
  *
  * We include <sys/ioctl.h> as it might be necessary to declare ioctl();
- * at least on *BSD and Mac OS X, it also defines various SIOC ioctls -
+ * at least on *BSD and macOS, it also defines various SIOC ioctls -
  * we could include <sys/sockio.h>, but if we're already including
  * <sys/ioctl.h>, which includes <sys/sockio.h> on those platforms,
  * there's not much point in doing so.
@@ -57,10 +54,6 @@ static const char usbus_prefix[] = "usbus";
 #include <dirent.h>
 #endif
 
-#ifdef HAVE_ZEROCOPY_BPF
-#include <machine/atomic.h>
-#endif
-
 #include <net/if.h>
 
 #ifdef _AIX
@@ -82,6 +75,16 @@ static const char usbus_prefix[] = "usbus";
 #include <net/bpf.h>
 #define _AIX
 
+/*
+ * If both BIOCROTZBUF and BPF_BUFMODE_ZBUF are defined, we have
+ * zero-copy BPF.
+ */
+#if defined(BIOCROTZBUF) && defined(BPF_BUFMODE_ZBUF)
+  #define HAVE_ZEROCOPY_BPF
+  #include <sys/mman.h>
+  #include <machine/atomic.h>
+#endif
+
 #include <net/if_types.h>              /* for IFT_ values */
 #include <sys/sysconfig.h>
 #include <sys/device.h>
@@ -112,7 +115,6 @@ static int bpf_load(char *errbuf);
 
 #endif /* _AIX */
 
-#include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <netdb.h>
@@ -121,7 +123,7 @@ static int bpf_load(char *errbuf);
 #include <string.h>
 #include <unistd.h>
 
-#ifdef HAVE_NET_IF_MEDIA_H
+#ifdef SIOCGIFMEDIA
 # include <net/if_media.h>
 #endif
 
@@ -150,7 +152,7 @@ struct pcap_bpf {
         * As there is a header on the front size of the mmap'd buffer, only
         * some of the buffer is exposed to libpcap as a whole via bufsize;
         * zbufsize is the true size.  zbuffer tracks the current zbuf
-        * assocated with buffer so that it can be used to decide which the
+        * associated with buffer so that it can be used to decide which the
         * next buffer to read will be.
         */
        u_char *zbuf1, *zbuf2, *zbuffer;
@@ -203,7 +205,7 @@ static int monitor_mode(pcap_t *, int);
 #  endif
 
 #  if defined(__APPLE__)
-static void remove_en(pcap_t *);
+static void remove_non_802_11(pcap_t *);
 static void remove_802_11(pcap_t *);
 #  endif
 
@@ -224,8 +226,9 @@ static void remove_802_11(pcap_t *);
 #endif
 
 /*
- * On OS X, we don't even get any of the 802.11-plus-radio-header DLT_'s
- * defined, even though some of them are used by various Airport drivers.
+ * In some versions of macOS, we might not even get any of the
+ * 802.11-plus-radio-header DLT_'s defined, even though some
+ * of them are used by various Airport drivers in those versions.
  */
 #ifndef DLT_PRISM_HEADER
 #define DLT_PRISM_HEADER       119
@@ -252,7 +255,7 @@ static int pcap_set_datalink_bpf(pcap_t *p, int dlt);
  * blocking mode.
  */
 static int
-pcap_getnonblock_bpf(pcap_t *p, char *errbuf)
+pcap_getnonblock_bpf(pcap_t *p)
 {
 #ifdef HAVE_ZEROCOPY_BPF
        struct pcap_bpf *pb = p->priv;
@@ -260,11 +263,11 @@ pcap_getnonblock_bpf(pcap_t *p, char *errbuf)
        if (pb->zerocopy)
                return (pb->nonblock);
 #endif
-       return (pcap_getnonblock_fd(p, errbuf));
+       return (pcap_getnonblock_fd(p));
 }
 
 static int
-pcap_setnonblock_bpf(pcap_t *p, int nonblock, char *errbuf)
+pcap_setnonblock_bpf(pcap_t *p, int nonblock)
 {
 #ifdef HAVE_ZEROCOPY_BPF
        struct pcap_bpf *pb = p->priv;
@@ -274,7 +277,7 @@ pcap_setnonblock_bpf(pcap_t *p, int nonblock, char *errbuf)
                return (0);
        }
 #endif
-       return (pcap_setnonblock_fd(p, nonblock, errbuf));
+       return (pcap_setnonblock_fd(p, nonblock));
 }
 
 #ifdef HAVE_ZEROCOPY_BPF
@@ -309,7 +312,7 @@ pcap_next_zbuf_shm(pcap_t *p, int *cc)
                    atomic_load_acq_int(&bzh->bzh_kernel_gen)) {
                        pb->bzh = bzh;
                        pb->zbuffer = (u_char *)pb->zbuf2;
-                       p->buffer = pb->zbuffer + sizeof(*bzh);
+                       p->buffer = pb->zbuffer + sizeof(*bzh);
                        *cc = bzh->bzh_kernel_len;
                        return (1);
                }
@@ -365,8 +368,8 @@ pcap_next_zbuf(pcap_t *p, int *cc)
                        if (data)
                                return (data);
                        if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) {
-                               (void) pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "BIOCROTZBUF: %s", strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errno, "BIOCROTZBUF");
                                return (PCAP_ERROR);
                        }
                        return (pcap_next_zbuf_shm(p, cc));
@@ -393,8 +396,8 @@ pcap_next_zbuf(pcap_t *p, int *cc)
                        }
                        return (0);
                } else if (r < 0) {
-                       (void) pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "select: %s", strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "select");
                        return (PCAP_ERROR);
                }
        }
@@ -412,8 +415,8 @@ pcap_next_zbuf(pcap_t *p, int *cc)
         * data.
         */
        if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) {
-               (void) pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "BIOCROTZBUF: %s", strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCROTZBUF");
                return (PCAP_ERROR);
        }
        return (pcap_next_zbuf_shm(p, cc));
@@ -441,7 +444,7 @@ pcap_create_interface(const char *device _U_, char *ebuf)
 {
        pcap_t *p;
 
-       p = pcap_create_common(ebuf, sizeof (struct pcap_bpf));
+       p = PCAP_CREATE_COMMON(ebuf, struct pcap_bpf);
        if (p == NULL)
                return (NULL);
 
@@ -452,16 +455,16 @@ pcap_create_interface(const char *device _U_, char *ebuf)
         * We claim that we support microsecond and nanosecond time
         * stamps.
         */
-       p->tstamp_precision_count = 2;
        p->tstamp_precision_list = malloc(2 * sizeof(u_int));
        if (p->tstamp_precision_list == NULL) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno,
+                   "malloc");
                free(p);
                return (NULL);
        }
        p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO;
        p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO;
+       p->tstamp_precision_count = 2;
 #endif /* BIOCSTSTAMP */
        return (p);
 }
@@ -473,13 +476,11 @@ pcap_create_interface(const char *device _U_, char *ebuf)
 static int
 bpf_open(char *errbuf)
 {
-       int fd;
-#ifdef HAVE_CLONING_BPF
-       static const char device[] = "/dev/bpf";
-#else
-       int n = 0;
+       int fd = -1;
+       static const char cloning_device[] = "/dev/bpf";
+       u_int n = 0;
        char device[sizeof "/dev/bpf0000000000"];
-#endif
+       static int no_cloning_bpf = 0;
 
 #ifdef _AIX
        /*
@@ -491,40 +492,60 @@ bpf_open(char *errbuf)
                return (PCAP_ERROR);
 #endif
 
-#ifdef HAVE_CLONING_BPF
-       if ((fd = open(device, O_RDWR)) == -1 &&
-           (errno != EACCES || (fd = open(device, O_RDONLY)) == -1)) {
-               if (errno == EACCES)
-                       fd = PCAP_ERROR_PERM_DENIED;
-               else
-                       fd = PCAP_ERROR;
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                 "(cannot open device) %s: %s", device, pcap_strerror(errno));
-       }
-#else
        /*
-        * Go through all the minors and find one that isn't in use.
+        * First, unless we've already tried opening /dev/bpf and
+        * gotten ENOENT, try opening /dev/bpf.
+        * If it fails with ENOENT, remember that, so we don't try
+        * again, and try /dev/bpfN.
         */
-       do {
-               (void)pcap_snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+       if (!no_cloning_bpf &&
+           (fd = open(cloning_device, O_RDWR)) == -1 &&
+           ((errno != EACCES && errno != ENOENT) ||
+            (fd = open(cloning_device, O_RDONLY)) == -1)) {
+               if (errno != ENOENT) {
+                       if (errno == EACCES) {
+                               fd = PCAP_ERROR_PERM_DENIED;
+                               snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                                   "Attempt to open %s failed - root privileges may be required",
+                                   cloning_device);
+                       } else {
+                               fd = PCAP_ERROR;
+                               pcap_fmt_errmsg_for_errno(errbuf,
+                                   PCAP_ERRBUF_SIZE, errno,
+                                   "(cannot open device) %s", cloning_device);
+                       }
+                       return (fd);
+               }
+               no_cloning_bpf = 1;
+       }
+
+       if (no_cloning_bpf) {
                /*
-                * Initially try a read/write open (to allow the inject
-                * method to work).  If that fails due to permission
-                * issues, fall back to read-only.  This allows a
-                * non-root user to be granted specific access to pcap
-                * capabilities via file permissions.
-                *
-                * XXX - we should have an API that has a flag that
-                * controls whether to open read-only or read-write,
-                * so that denial of permission to send (or inability
-                * to send, if sending packets isn't supported on
-                * the device in question) can be indicated at open
-                * time.
+                * We don't have /dev/bpf.
+                * Go through all the /dev/bpfN minors and find one
+                * that isn't in use.
                 */
-               fd = open(device, O_RDWR);
-               if (fd == -1 && errno == EACCES)
-                       fd = open(device, O_RDONLY);
-       } while (fd < 0 && errno == EBUSY);
+               do {
+                       (void)snprintf(device, sizeof(device), "/dev/bpf%u", n++);
+                       /*
+                        * Initially try a read/write open (to allow the inject
+                        * method to work).  If that fails due to permission
+                        * issues, fall back to read-only.  This allows a
+                        * non-root user to be granted specific access to pcap
+                        * capabilities via file permissions.
+                        *
+                        * XXX - we should have an API that has a flag that
+                        * controls whether to open read-only or read-write,
+                        * so that denial of permission to send (or inability
+                        * to send, if sending packets isn't supported on
+                        * the device in question) can be indicated at open
+                        * time.
+                        */
+                       fd = open(device, O_RDWR);
+                       if (fd == -1 && errno == EACCES)
+                               fd = open(device, O_RDONLY);
+               } while (fd < 0 && errno == EBUSY);
+       }
 
        /*
         * XXX better message for all minors used
@@ -540,7 +561,7 @@ bpf_open(char *errbuf)
                                 * means we probably have no BPF
                                 * devices.
                                 */
-                               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                               snprintf(errbuf, PCAP_ERRBUF_SIZE,
                                    "(there are no BPF devices)");
                        } else {
                                /*
@@ -549,7 +570,7 @@ bpf_open(char *errbuf)
                                 * devices, but all the ones
                                 * that exist are busy.
                                 */
-                               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                               snprintf(errbuf, PCAP_ERRBUF_SIZE,
                                    "(all BPF devices are busy)");
                        }
                        break;
@@ -561,9 +582,9 @@ bpf_open(char *errbuf)
                         * if any.
                         */
                        fd = PCAP_ERROR_PERM_DENIED;
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "(cannot open BPF device) %s: %s", device,
-                           pcap_strerror(errno));
+                       snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "Attempt to open %s failed - root privileges may be required",
+                           device);
                        break;
 
                default:
@@ -571,50 +592,80 @@ bpf_open(char *errbuf)
                         * Some other problem.
                         */
                        fd = PCAP_ERROR;
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "(cannot open BPF device) %s: %s", device,
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "(cannot open BPF device) %s", device);
                        break;
                }
        }
-#endif
 
        return (fd);
 }
 
 /*
- * Open and bind to a device; used if we're not actually going to use
- * the device, but are just testing whether it can be opened, or opening
- * it to get information about it.
+ * Bind a network adapter to a BPF device, given a descriptor for the
+ * BPF device and the name of the network adapter.
  *
- * Returns an error code on failure (always negative), and an FD for
- * the now-bound BPF device on success (always non-negative).
+ * Use BIOCSETLIF if available (meaning "on Solaris"), as it supports
+ * longer device names.
+ *
+ * If the name is longer than will fit, return PCAP_ERROR_NO_SUCH_DEVICE
+ * before trying to bind the interface, as there cannot be such a device.
+ *
+ * If the attempt succeeds, return BPF_BIND_SUCCEEDED.
+ *
+ * If the attempt fails:
+ *
+ *    if it fails with ENXIO, return PCAP_ERROR_NO_SUCH_DEVICE, as
+ *    the device doesn't exist;
+ *
+ *    if it fails with ENETDOWN, return PCAP_ERROR_IFACE_NOT_UP, as
+ *    the interface exists but isn't up and the OS doesn't allow
+ *    binding to an interface that isn't up;
+ *
+ *    if it fails with ENOBUFS, return BPF_BIND_BUFFER_TOO_BIG, and
+ *    fill in an error message, as the buffer being requested is too
+ *    large;
+ *
+ *    otherwise, return PCAP_ERROR and fill in an error message.
  */
+#define BPF_BIND_SUCCEEDED     0
+#define BPF_BIND_BUFFER_TOO_BIG        1
+
 static int
-bpf_open_and_bind(const char *name, char *errbuf)
+bpf_bind(int fd, const char *name, char *errbuf)
 {
-       int fd;
+       int status;
+#ifdef LIFNAMSIZ
+       struct lifreq ifr;
+
+       if (strlen(name) >= sizeof(ifr.lifr_name)) {
+               /* The name is too long, so it can't possibly exist. */
+               return (PCAP_ERROR_NO_SUCH_DEVICE);
+       }
+       (void)pcap_strlcpy(ifr.lifr_name, name, sizeof(ifr.lifr_name));
+       status = ioctl(fd, BIOCSETLIF, (caddr_t)&ifr);
+#else
        struct ifreq ifr;
 
-       /*
-        * First, open a BPF device.
-        */
-       fd = bpf_open(errbuf);
-       if (fd < 0)
-               return (fd);    /* fd is the appropriate error code */
+       if (strlen(name) >= sizeof(ifr.ifr_name)) {
+               /* The name is too long, so it can't possibly exist. */
+               return (PCAP_ERROR_NO_SUCH_DEVICE);
+       }
+       (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       status = ioctl(fd, BIOCSETIF, (caddr_t)&ifr);
+#endif
 
-       /*
-        * Now bind to the device.
-        */
-       (void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+       if (status < 0) {
                switch (errno) {
 
                case ENXIO:
                        /*
                         * There's no such device.
+                        *
+                        * There's nothing more to say, so clear out the
+                        * error message.
                         */
-                       close(fd);
+                       errbuf[0] = '\0';
                        return (PCAP_ERROR_NO_SUCH_DEVICE);
 
                case ENETDOWN:
@@ -625,15 +676,67 @@ bpf_open_and_bind(const char *name, char *errbuf)
                         * suggest that they report a problem to the
                         * libpcap developers.
                         */
-                       close(fd);
                        return (PCAP_ERROR_IFACE_NOT_UP);
 
+               case ENOBUFS:
+                       /*
+                        * The buffer size is too big.
+                        * Return a special indication so that, if we're
+                        * trying to crank the buffer size down, we know
+                        * we have to continue; add an error message that
+                        * tells the user what needs to be fixed.
+                        */
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "The requested buffer size for %s is too large",
+                           name);
+                       return (BPF_BIND_BUFFER_TOO_BIG);
+
                default:
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "BIOCSETIF: %s: %s", name, pcap_strerror(errno));
-                       close(fd);
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "Binding interface %s to BPF device failed",
+                           name);
+                       return (PCAP_ERROR);
+               }
+       }
+       return (BPF_BIND_SUCCEEDED);
+}
+
+/*
+ * Open and bind to a device; used if we're not actually going to use
+ * the device, but are just testing whether it can be opened, or opening
+ * it to get information about it.
+ *
+ * Returns an error code on failure (always negative), and an FD for
+ * the now-bound BPF device on success (always non-negative).
+ */
+static int
+bpf_open_and_bind(const char *name, char *errbuf)
+{
+       int fd;
+       int status;
+
+       /*
+        * First, open a BPF device.
+        */
+       fd = bpf_open(errbuf);
+       if (fd < 0)
+               return (fd);    /* fd is the appropriate error code */
+
+       /*
+        * Now bind to the device.
+        */
+       status = bpf_bind(fd, name, errbuf);
+       if (status != BPF_BIND_SUCCEEDED) {
+               close(fd);
+               if (status == BPF_BIND_BUFFER_TOO_BIG) {
+                       /*
+                        * We didn't specify a buffer size, so
+                        * this *really* shouldn't fail because
+                        * there's no buffer space.  Fail.
+                        */
                        return (PCAP_ERROR);
                }
+               return (status);
        }
 
        /*
@@ -642,6 +745,47 @@ bpf_open_and_bind(const char *name, char *errbuf)
        return (fd);
 }
 
+#ifdef __APPLE__
+static int
+device_exists(int fd, const char *name, char *errbuf)
+{
+       int status;
+       struct ifreq ifr;
+
+       if (strlen(name) >= sizeof(ifr.ifr_name)) {
+               /* The name is too long, so it can't possibly exist. */
+               return (PCAP_ERROR_NO_SUCH_DEVICE);
+       }
+       (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       status = ioctl(fd, SIOCGIFFLAGS, (caddr_t)&ifr);
+
+       if (status < 0) {
+               if (errno == ENXIO || errno == EINVAL) {
+                       /*
+                        * macOS and *BSD return one of those two
+                        * errors if the device doesn't exist.
+                        * Don't fill in an error, as this is
+                        * an "expected" condition.
+                        */
+                       return (PCAP_ERROR_NO_SUCH_DEVICE);
+               }
+
+               /*
+                * Some other error - provide a message for it, as
+                * it's "unexpected".
+                */
+               pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno,
+                   "Can't get interface flags on %s", name);
+               return (PCAP_ERROR);
+       }
+
+       /*
+        * The device exists.
+        */
+       return (0);
+}
+#endif
+
 #ifdef BIOCGDLTLIST
 static int
 get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
@@ -653,14 +797,14 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
 
                bdlp->bfl_list = (u_int *) malloc(sizeof(u_int) * (bdlp->bfl_len + 1));
                if (bdlp->bfl_list == NULL) {
-                       (void)pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+                           errno, "malloc");
                        return (PCAP_ERROR);
                }
 
                if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) {
-                       (void)pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
-                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCGDLTLIST");
                        free(bdlp->bfl_list);
                        return (PCAP_ERROR);
                }
@@ -714,8 +858,8 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
                 * this device"; don't treat it as an error.
                 */
                if (errno != EINVAL) {
-                       (void)pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE,
-                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCGDLTLIST");
                        return (PCAP_ERROR);
                }
        }
@@ -723,19 +867,19 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
 }
 #endif
 
+#if defined(__APPLE__)
 static int
 pcap_can_set_rfmon_bpf(pcap_t *p)
 {
-#if defined(__APPLE__)
        struct utsname osinfo;
-       struct ifreq ifr;
        int fd;
 #ifdef BIOCGDLTLIST
        struct bpf_dltlist bdl;
+       int err;
 #endif
 
        /*
-        * The joys of monitor mode on OS X.
+        * The joys of monitor mode on Mac OS X/OS X/macOS.
         *
         * Prior to 10.4, it's not supported at all.
         *
@@ -769,6 +913,9 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
                return (0);
        }
        if (osinfo.release[0] == '8' && osinfo.release[1] == '.') {
+               char *wlt_name;
+               int status;
+
                /*
                 * 10.4 (Darwin 8.x).  s/en/wlt/, and check
                 * whether the device exists.
@@ -781,20 +928,28 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
                }
                fd = socket(AF_INET, SOCK_DGRAM, 0);
                if (fd == -1) {
-                       (void)pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "socket: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "socket");
                        return (PCAP_ERROR);
                }
-               strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name));
-               strlcat(ifr.ifr_name, p->opt.device + 2, sizeof(ifr.ifr_name));
-               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
-                       /*
-                        * No such device?
-                        */
+               if (pcap_asprintf(&wlt_name, "wlt%s", p->opt.device + 2) == -1) {
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "malloc");
                        close(fd);
-                       return (0);
+                       return (PCAP_ERROR);
                }
+               status = device_exists(fd, wlt_name, p->errbuf);
+               free(wlt_name);
                close(fd);
+               if (status != 0) {
+                       if (status == PCAP_ERROR_NO_SUCH_DEVICE)
+                               return (0);
+
+                       /*
+                        * Error.
+                        */
+                       return (status);
+               }
                return (1);
        }
 
@@ -813,35 +968,18 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
        /*
         * Now bind to the device.
         */
-       (void)strncpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name));
-       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
-               switch (errno) {
-
-               case ENXIO:
-                       /*
-                        * There's no such device.
-                        */
-                       close(fd);
-                       return (PCAP_ERROR_NO_SUCH_DEVICE);
-
-               case ENETDOWN:
+       err = bpf_bind(fd, p->opt.device, p->errbuf);
+       if (err != BPF_BIND_SUCCEEDED) {
+               close(fd);
+               if (err == BPF_BIND_BUFFER_TOO_BIG) {
                        /*
-                        * 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.
+                        * We didn't specify a buffer size, so
+                        * this *really* shouldn't fail because
+                        * there's no buffer space.  Fail.
                         */
-                       close(fd);
-                       return (PCAP_ERROR_IFACE_NOT_UP);
-
-               default:
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "BIOCSETIF: %s: %s",
-                           p->opt.device, pcap_strerror(errno));
-                       close(fd);
                        return (PCAP_ERROR);
                }
+               return (err);
        }
 
        /*
@@ -867,7 +1005,11 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
        close(fd);
 #endif /* BIOCGDLTLIST */
        return (0);
+}
 #elif defined(HAVE_BSD_IEEE80211)
+static int
+pcap_can_set_rfmon_bpf(pcap_t *p)
+{
        int ret;
 
        ret = monitor_mode(p, 0);
@@ -876,10 +1018,14 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
        if (ret == 0)
                return (1);     /* success */
        return (ret);
+}
 #else
+static int
+pcap_can_set_rfmon_bpf(pcap_t *p _U_)
+{
        return (0);
-#endif
 }
+#endif
 
 static int
 pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps)
@@ -900,8 +1046,8 @@ pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps)
         * by libpcap, and thus not yet seen by the application.
         */
        if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCGSTATS");
                return (PCAP_ERROR);
        }
 
@@ -961,7 +1107,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                } else
 #endif
                {
-                       cc = read(p->fd, p->buffer, p->bufsize);
+                       cc = (int)read(p->fd, p->buffer, p->bufsize);
                }
                if (cc < 0) {
                        /* Don't choke when we get ptraced */
@@ -999,18 +1145,21 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                        case EWOULDBLOCK:
                                return (0);
 
-                       case ENXIO:
+                       case ENXIO:     /* FreeBSD, DragonFly BSD, and Darwin */
+                       case EIO:       /* OpenBSD */
+                                       /* NetBSD appears not to return an error in this case */
                                /*
                                 * The device on which we're capturing
                                 * went away.
                                 *
                                 * XXX - we should really return
-                                * PCAP_ERROR_IFACE_NOT_UP, but
-                                * pcap_dispatch() etc. aren't
-                                * defined to retur that.
+                                * an appropriate error for that,
+                                * but pcap_dispatch() etc. aren't
+                                * documented as having error returns
+                                * other than PCAP_ERROR or PCAP_ERROR_BREAK.
                                 */
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "The interface went down");
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                                   "The interface disappeared");
                                return (PCAP_ERROR);
 
 #if defined(sun) && !defined(BSD) && !defined(__svr4__) && !defined(__SVR4)
@@ -1028,8 +1177,8 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                                /* fall through */
 #endif
                        }
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "read");
                        return (PCAP_ERROR);
                }
                bp = (u_char *)p->buffer;
@@ -1038,6 +1187,9 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 
        /*
         * Loop through each packet.
+        *
+        * This assumes that a single buffer of packets will have
+        * <= INT_MAX packets, so the packet count doesn't overflow.
         */
 #ifdef BIOCSTSTAMP
 #define bhp ((struct bpf_xhdr *)bp)
@@ -1063,7 +1215,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                 */
                if (p->break_loop) {
                        p->bp = bp;
-                       p->cc = ep - bp;
+                       p->cc = (int)(ep - bp);
                        /*
                         * ep is set based on the return value of read(),
                         * but read() from a BPF device doesn't necessarily
@@ -1103,7 +1255,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 #endif
                 */
                if (pb->filtering_in_kernel ||
-                   bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) {
+                   pcap_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) {
                        struct pcap_pkthdr pkthdr;
 #ifdef BIOCSTSTAMP
                        struct bintime bt;
@@ -1153,7 +1305,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
                        bp += BPF_WORDALIGN(caplen + hdrlen);
                        if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) {
                                p->bp = bp;
-                               p->cc = ep - bp;
+                               p->cc = (int)(ep - bp);
                                /*
                                 * See comment above about p->cc < 0.
                                 */
@@ -1174,21 +1326,21 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
 }
 
 static int
-pcap_inject_bpf(pcap_t *p, const void *buf, size_t size)
+pcap_inject_bpf(pcap_t *p, const void *buf, int size)
 {
        int ret;
 
-       ret = write(p->fd, buf, size);
+       ret = (int)write(p->fd, buf, size);
 #ifdef __APPLE__
        if (ret == -1 && errno == EAFNOSUPPORT) {
                /*
-                * In Mac OS X, there's a bug wherein setting the
-                * BIOCSHDRCMPLT flag causes writes to fail; see,
-                * for example:
+                * In some versions of macOS, there's a bug wherein setting
+                * the BIOCSHDRCMPLT flag causes writes to fail; see, for
+                * example:
                 *
                 *      http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/BIOCSHDRCMPLT-10.3.3.patch
                 *
-                * So, if, on OS X, we get EAFNOSUPPORT from the write, we
+                * So, if, on macOS, we get EAFNOSUPPORT from the write, we
                 * assume it's due to that bug, and turn off that flag
                 * and try again.  If we succeed, it either means that
                 * somebody applied the fix from that URL, or other patches
@@ -1197,26 +1349,25 @@ pcap_inject_bpf(pcap_t *p, const void *buf, size_t size)
                 *      http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/
                 *
                 * and are running a Darwin kernel with those fixes, or
-                * that Apple fixed the problem in some OS X release.
+                * that Apple fixed the problem in some macOS release.
                 */
                u_int spoof_eth_src = 0;
 
                if (ioctl(p->fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
-                       (void)pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "send: can't turn off BIOCSHDRCMPLT: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "send: can't turn off BIOCSHDRCMPLT");
                        return (PCAP_ERROR);
                }
 
                /*
                 * Now try the write again.
                 */
-               ret = write(p->fd, buf, size);
+               ret = (int)write(p->fd, buf, size);
        }
 #endif /* __APPLE__ */
        if (ret == -1) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "send");
                return (PCAP_ERROR);
        }
        return (ret);
@@ -1231,7 +1382,7 @@ bpf_odminit(char *errbuf)
        if (odm_initialize() == -1) {
                if (odm_err_msg(odmerrno, &errstr) == -1)
                        errstr = "Unknown error";
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(errbuf, PCAP_ERRBUF_SIZE,
                    "bpf_load: odm_initialize failed: %s",
                    errstr);
                return (PCAP_ERROR);
@@ -1240,7 +1391,7 @@ bpf_odminit(char *errbuf)
        if ((odmlockid = odm_lock("/etc/objrepos/config_lock", ODM_WAIT)) == -1) {
                if (odm_err_msg(odmerrno, &errstr) == -1)
                        errstr = "Unknown error";
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(errbuf, PCAP_ERRBUF_SIZE,
                    "bpf_load: odm_lock of /etc/objrepos/config_lock failed: %s",
                    errstr);
                (void)odm_terminate();
@@ -1259,7 +1410,7 @@ bpf_odmcleanup(char *errbuf)
                if (errbuf != NULL) {
                        if (odm_err_msg(odmerrno, &errstr) == -1)
                                errstr = "Unknown error";
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                       snprintf(errbuf, PCAP_ERRBUF_SIZE,
                            "bpf_load: odm_unlock failed: %s",
                            errstr);
                }
@@ -1270,7 +1421,7 @@ bpf_odmcleanup(char *errbuf)
                if (errbuf != NULL) {
                        if (odm_err_msg(odmerrno, &errstr) == -1)
                                errstr = "Unknown error";
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                       snprintf(errbuf, PCAP_ERRBUF_SIZE,
                            "bpf_load: odm_terminate failed: %s",
                            errstr);
                }
@@ -1304,8 +1455,8 @@ bpf_load(char *errbuf)
 
        major = genmajor(BPF_NAME);
        if (major == -1) {
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "bpf_load: genmajor failed: %s", pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "bpf_load: genmajor failed");
                (void)bpf_odmcleanup(NULL);
                return (PCAP_ERROR);
        }
@@ -1314,9 +1465,8 @@ bpf_load(char *errbuf)
        if (!minors) {
                minors = genminor("bpf", major, 0, BPF_MINORS, 1, 1);
                if (!minors) {
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "bpf_load: genminor failed: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "bpf_load: genminor failed");
                        (void)bpf_odmcleanup(NULL);
                        return (PCAP_ERROR);
                }
@@ -1327,20 +1477,19 @@ bpf_load(char *errbuf)
 
        rc = stat(BPF_NODE "0", &sbuf);
        if (rc == -1 && errno != ENOENT) {
-               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                   "bpf_load: can't stat %s: %s",
-                   BPF_NODE "0", pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "bpf_load: can't stat %s", BPF_NODE "0");
                return (PCAP_ERROR);
        }
 
        if (rc == -1 || getmajor(sbuf.st_rdev) != major) {
                for (i = 0; i < BPF_MINORS; i++) {
-                       sprintf(buf, "%s%d", BPF_NODE, i);
+                       snprintf(buf, sizeof(buf), "%s%d", BPF_NODE, i);
                        unlink(buf);
                        if (mknod(buf, S_IRUSR | S_IFCHR, domakedev(major, i)) == -1) {
-                               pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                                   "bpf_load: can't mknod %s: %s",
-                                   buf, pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(errbuf,
+                                   PCAP_ERRBUF_SIZE, errno,
+                                   "bpf_load: can't mknod %s", buf);
                                return (PCAP_ERROR);
                        }
                }
@@ -1348,15 +1497,14 @@ bpf_load(char *errbuf)
 
        /* Check if the driver is loaded */
        memset(&cfg_ld, 0x0, sizeof(cfg_ld));
+       snprintf(buf, sizeof(buf), "%s/%s", DRIVER_PATH, BPF_NAME);
        cfg_ld.path = buf;
-       sprintf(cfg_ld.path, "%s/%s", DRIVER_PATH, BPF_NAME);
        if ((sysconfig(SYS_QUERYLOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) ||
            (cfg_ld.kmid == 0)) {
                /* Driver isn't loaded, load it now */
                if (sysconfig(SYS_SINGLELOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) {
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "bpf_load: could not load driver: %s",
-                           strerror(errno));
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "bpf_load: could not load driver");
                        return (PCAP_ERROR);
                }
        }
@@ -1369,9 +1517,8 @@ bpf_load(char *errbuf)
        for (i = 0; i < BPF_MINORS; i++) {
                cfg_bpf.devno = domakedev(major, i);
                if (sysconfig(SYS_CFGKMOD, (void *)&cfg_km, sizeof(cfg_km)) == -1) {
-                       pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "bpf_load: could not configure driver: %s",
-                           strerror(errno));
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "bpf_load: could not configure driver");
                        return (PCAP_ERROR);
                }
        }
@@ -1418,7 +1565,7 @@ pcap_cleanup_bpf(pcap_t *p)
                                    strerror(errno));
                        } else {
                                memset(&req, 0, sizeof(req));
-                               strncpy(req.ifm_name, pb->device,
+                               pcap_strlcpy(req.ifm_name, pb->device,
                                    sizeof(req.ifm_name));
                                if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
                                        fprintf(stderr,
@@ -1432,7 +1579,7 @@ pcap_cleanup_bpf(pcap_t *p)
                                                 * turn it off.
                                                 */
                                                memset(&ifr, 0, sizeof(ifr));
-                                               (void)strncpy(ifr.ifr_name,
+                                               (void)pcap_strlcpy(ifr.ifr_name,
                                                    pb->device,
                                                    sizeof(ifr.ifr_name));
                                                ifr.ifr_media =
@@ -1461,7 +1608,7 @@ pcap_cleanup_bpf(pcap_t *p)
 
                                s = socket(AF_LOCAL, SOCK_DGRAM, 0);
                                if (s >= 0) {
-                                       strlcpy(ifr.ifr_name, pb->device,
+                                       pcap_strlcpy(ifr.ifr_name, pb->device,
                                            sizeof(ifr.ifr_name));
                                        ioctl(s, SIOCIFDESTROY, &ifr);
                                        close(s);
@@ -1500,20 +1647,17 @@ pcap_cleanup_bpf(pcap_t *p)
        pcap_cleanup_live_common(p);
 }
 
+#ifdef __APPLE__
 static int
 check_setif_failure(pcap_t *p, int error)
 {
-#ifdef __APPLE__
        int fd;
-       struct ifreq ifr;
        int err;
-#endif
 
-       if (error == ENXIO) {
+       if (error == PCAP_ERROR_NO_SUCH_DEVICE) {
                /*
                 * No such device exists.
                 */
-#ifdef __APPLE__
                if (p->opt.rfmon && strncmp(p->opt.device, "wlt", 3) == 0) {
                        /*
                         * Monitor mode was requested, and we're trying
@@ -1524,32 +1668,38 @@ check_setif_failure(pcap_t *p, int error)
                         */
                        fd = socket(AF_INET, SOCK_DGRAM, 0);
                        if (fd != -1) {
-                               strlcpy(ifr.ifr_name, "en",
-                                   sizeof(ifr.ifr_name));
-                               strlcat(ifr.ifr_name, p->opt.device + 3,
-                                   sizeof(ifr.ifr_name));
-                               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
-                                       /*
-                                        * We assume this failed because
-                                        * the underlying device doesn't
-                                        * exist.
-                                        */
-                                       err = PCAP_ERROR_NO_SUCH_DEVICE;
-                                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                           "SIOCGIFFLAGS on %s failed: %s",
-                                           ifr.ifr_name, pcap_strerror(errno));
-                               } else {
+                               char *en_name;
+
+                               if (pcap_asprintf(&en_name, "en%s",
+                                   p->opt.device + 3) == -1) {
                                        /*
-                                        * The underlying "enN" device
-                                        * exists, but there's no
-                                        * corresponding "wltN" device;
-                                        * that means that the "enN"
-                                        * device doesn't support
-                                        * monitor mode, probably because
-                                        * it's an Ethernet device rather
-                                        * than a wireless device.
+                                        * We can't find out whether there's
+                                        * an underlying "enN" device, so
+                                        * just report "no such device".
                                         */
-                                       err = PCAP_ERROR_RFMON_NOTSUP;
+                                       pcap_fmt_errmsg_for_errno(p->errbuf,
+                                           PCAP_ERRBUF_SIZE, errno,
+                                           "malloc");
+                                       close(fd);
+                                       return (PCAP_ERROR_NO_SUCH_DEVICE);
+                               }
+                               err = device_exists(fd, en_name, p->errbuf);
+                               free(en_name);
+                               if (err != 0) {
+                                       if (err == PCAP_ERROR_NO_SUCH_DEVICE) {
+                                               /*
+                                                * The underlying "enN" device
+                                                * exists, but there's no
+                                                * corresponding "wltN" device;
+                                                * that means that the "enN"
+                                                * device doesn't support
+                                                * monitor mode, probably
+                                                * because it's an Ethernet
+                                                * device rather than a
+                                                * wireless device.
+                                                */
+                                               err = PCAP_ERROR_RFMON_NOTSUP;
+                                       }
                                }
                                close(fd);
                        } else {
@@ -1559,38 +1709,36 @@ check_setif_failure(pcap_t *p, int error)
                                 * just report "no such device".
                                 */
                                err = PCAP_ERROR_NO_SUCH_DEVICE;
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "socket() failed: %s",
-                                   pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   errno, PCAP_ERRBUF_SIZE,
+                                   "socket() failed");
                        }
                        return (err);
                }
-#endif
+
                /*
                 * No such device.
                 */
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF failed: %s",
-                   pcap_strerror(errno));
                return (PCAP_ERROR_NO_SUCH_DEVICE);
-       } else 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 {
-               /*
-                * Some other error; fill in the error string, and
-                * return PCAP_ERROR.
-                */
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
-                   p->opt.device, pcap_strerror(errno));
-               return (PCAP_ERROR);
        }
+
+       /*
+        * Just return the error status; it's what we want, and, if it's
+        * PCAP_ERROR, the error string has been filled in.
+        */
+       return (error);
+}
+#else
+static int
+check_setif_failure(pcap_t *p _U_, int error)
+{
+       /*
+        * Just return the error status; it's what we want, and, if it's
+        * PCAP_ERROR, the error string has been filled in.
+        */
+       return (error);
 }
+#endif
 
 /*
  * Default capture buffer size.
@@ -1616,15 +1764,9 @@ pcap_activate_bpf(pcap_t *p)
        int retv;
 #endif
        int fd;
-#ifdef LIFNAMSIZ
-       char *zonesep;
+#if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid)
        struct lifreq ifr;
-       char *ifrname = ifr.lifr_name;
-       const size_t ifnamsiz = sizeof(ifr.lifr_name);
-#else
-       struct ifreq ifr;
-       char *ifrname = ifr.ifr_name;
-       const size_t ifnamsiz = sizeof(ifr.ifr_name);
+       char *zonesep;
 #endif
        struct bpf_version bv;
 #ifdef __APPLE__
@@ -1659,26 +1801,37 @@ pcap_activate_bpf(pcap_t *p)
        p->fd = fd;
 
        if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCVERSION");
                status = PCAP_ERROR;
                goto bad;
        }
        if (bv.bv_major != BPF_MAJOR_VERSION ||
            bv.bv_minor < BPF_MINOR_VERSION) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "kernel bpf filter out of date");
                status = PCAP_ERROR;
                goto bad;
        }
 
+       /*
+        * Turn a negative snapshot value (invalid), a snapshot value of
+        * 0 (unspecified), or a value bigger than the normal maximum
+        * value, into the maximum allowed value.
+        *
+        * If some application really *needs* a bigger snapshot
+        * length, we should just increase MAXIMUM_SNAPLEN.
+        */
+       if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
+               p->snapshot = MAXIMUM_SNAPLEN;
+
 #if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid)
        /*
         * Retrieve the zoneid of the zone we are currently executing in.
         */
        if ((ifr.lifr_zoneid = getzoneid()) == -1) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "getzoneid(): %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "getzoneid()");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -1695,25 +1848,24 @@ pcap_activate_bpf(pcap_t *p)
                char *lnamep;
 
                if (ifr.lifr_zoneid != GLOBAL_ZONEID) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                            "zonename/linkname only valid in global zone.");
                        status = PCAP_ERROR;
                        goto bad;
                }
                znamelen = zonesep - p->opt.device;
-               (void) strlcpy(path_zname, p->opt.device, znamelen + 1);
+               (void) pcap_strlcpy(path_zname, p->opt.device, znamelen + 1);
                ifr.lifr_zoneid = getzoneidbyname(path_zname);
                if (ifr.lifr_zoneid == -1) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "getzoneidbyname(%s): %s", path_zname,
-                       pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "getzoneidbyname(%s)", path_zname);
                        status = PCAP_ERROR;
                        goto bad;
                }
                lnamep = strdup(zonesep + 1);
                if (lnamep == NULL) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "strdup");
                        status = PCAP_ERROR;
                        goto bad;
                }
@@ -1724,8 +1876,8 @@ pcap_activate_bpf(pcap_t *p)
 
        pb->device = strdup(p->opt.device);
        if (pb->device == NULL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
-                    pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "strdup");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -1767,24 +1919,19 @@ pcap_activate_bpf(pcap_t *p)
                                         */
                                        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                                        if (sockfd != -1) {
-                                               strlcpy(ifrname,
-                                                   p->opt.device, ifnamsiz);
-                                               if (ioctl(sockfd, SIOCGIFFLAGS,
-                                                   (char *)&ifr) < 0) {
+                                               status = device_exists(sockfd,
+                                                   p->opt.device, p->errbuf);
+                                               if (status == 0) {
                                                        /*
-                                                        * We assume this
-                                                        * failed because
-                                                        * the underlying
-                                                        * device doesn't
-                                                        * exist.
+                                                        * The device exists,
+                                                        * but it's not an
+                                                        * enN device; that
+                                                        * means it doesn't
+                                                        * support monitor
+                                                        * mode.
                                                         */
-                                                       status = PCAP_ERROR_NO_SUCH_DEVICE;
-                                                       pcap_snprintf(p->errbuf,
-                                                           PCAP_ERRBUF_SIZE,
-                                                           "SIOCGIFFLAGS failed: %s",
-                                                           pcap_strerror(errno));
-                                               } else
                                                        status = PCAP_ERROR_RFMON_NOTSUP;
+                                               }
                                                close(sockfd);
                                        } else {
                                                /*
@@ -1793,18 +1940,17 @@ pcap_activate_bpf(pcap_t *p)
                                                 * report "no such device".
                                                 */
                                                status = PCAP_ERROR_NO_SUCH_DEVICE;
-                                               pcap_snprintf(p->errbuf,
-                                                   PCAP_ERRBUF_SIZE,
-                                                   "socket() failed: %s",
-                                                   pcap_strerror(errno));
+                                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                                   PCAP_ERRBUF_SIZE, errno,
+                                                   "socket() failed");
                                        }
                                        goto bad;
                                }
                                wltdev = malloc(strlen(p->opt.device) + 2);
                                if (wltdev == NULL) {
-                                       (void)pcap_snprintf(p->errbuf,
-                                           PCAP_ERRBUF_SIZE, "malloc: %s",
-                                           pcap_strerror(errno));
+                                       pcap_fmt_errmsg_for_errno(p->errbuf,
+                                           PCAP_ERRBUF_SIZE, errno,
+                                           "malloc");
                                        status = PCAP_ERROR;
                                        goto bad;
                                }
@@ -1837,6 +1983,7 @@ pcap_activate_bpf(pcap_t *p)
                         * it when the pcap_t is closed.
                         */
                        int s;
+                       struct ifreq ifr;
 
                        /*
                         * Open a socket to use for ioctls to
@@ -1844,9 +1991,9 @@ pcap_activate_bpf(pcap_t *p)
                         */
                        s = socket(AF_LOCAL, SOCK_DGRAM, 0);
                        if (s < 0) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "Can't open socket: %s",
-                                   pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errno,
+                                   "Can't open socket");
                                status = PCAP_ERROR;
                                goto bad;
                        }
@@ -1860,7 +2007,7 @@ pcap_activate_bpf(pcap_t *p)
                                 * "atexit()" failed; don't create the
                                 * interface, just give up.
                                 */
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                                     "atexit failed");
                                close(s);
                                status = PCAP_ERROR;
@@ -1870,16 +2017,17 @@ pcap_activate_bpf(pcap_t *p)
                        /*
                         * Create the interface.
                         */
-                       strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name));
+                       pcap_strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name));
                        if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) {
                                if (errno == EINVAL) {
-                                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                                            "Invalid USB bus interface %s",
                                            p->opt.device);
                                } else {
-                                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                           "Can't create interface for %s: %s",
-                                           p->opt.device, pcap_strerror(errno));
+                                       pcap_fmt_errmsg_for_errno(p->errbuf,
+                                           PCAP_ERRBUF_SIZE, errno,
+                                           "Can't create interface for %s",
+                                           p->opt.device);
                                }
                                close(s);
                                status = PCAP_ERROR;
@@ -1922,8 +2070,8 @@ pcap_activate_bpf(pcap_t *p)
                 * size.
                 */
                if (ioctl(fd, BIOCGETZMAX, (caddr_t)&zbufmax) < 0) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGETZMAX: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCGETZMAX");
                        status = PCAP_ERROR;
                        goto bad;
                }
@@ -1949,8 +2097,8 @@ pcap_activate_bpf(pcap_t *p)
                pb->zbuf2 = mmap(NULL, pb->zbufsize, PROT_READ | PROT_WRITE,
                    MAP_ANON, -1, 0);
                if (pb->zbuf1 == MAP_FAILED || pb->zbuf2 == MAP_FAILED) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "mmap: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "mmap");
                        status = PCAP_ERROR;
                        goto bad;
                }
@@ -1959,16 +2107,24 @@ pcap_activate_bpf(pcap_t *p)
                bz.bz_bufb = pb->zbuf2;
                bz.bz_buflen = pb->zbufsize;
                if (ioctl(fd, BIOCSETZBUF, (caddr_t)&bz) < 0) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETZBUF: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCSETZBUF");
                        status = PCAP_ERROR;
                        goto bad;
                }
-               (void)strncpy(ifrname, p->opt.device, ifnamsiz);
-               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
-                           p->opt.device, pcap_strerror(errno));
-                       status = PCAP_ERROR;
+               status = bpf_bind(fd, p->opt.device, ifnamsiz, p->errbuf);
+               if (status != BPF_BIND_SUCCEEDED) {
+                       if (status == BPF_BIND_BUFFER_TOO_BIG) {
+                               /*
+                                * The requested buffer size
+                                * is too big.  Fail.
+                                *
+                                * XXX - should we do the "keep cutting
+                                * the buffer size in half" loop here if
+                                * we're using the default buffer size?
+                                */
+                               status = PCAP_ERROR;
+                       }
                        goto bad;
                }
                v = pb->zbufsize - sizeof(struct bpf_zbuf_header);
@@ -1985,9 +2141,9 @@ pcap_activate_bpf(pcap_t *p)
                         */
                        if (ioctl(fd, BIOCSBLEN,
                            (caddr_t)&p->opt.buffer_size) < 0) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "BIOCSBLEN: %s: %s", p->opt.device,
-                                   pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errno,
+                                   "BIOCSBLEN: %s", p->opt.device);
                                status = PCAP_ERROR;
                                goto bad;
                        }
@@ -1995,14 +2151,23 @@ pcap_activate_bpf(pcap_t *p)
                        /*
                         * Now bind to the device.
                         */
-                       (void)strncpy(ifrname, p->opt.device, ifnamsiz);
-#ifdef BIOCSETLIF
-                       if (ioctl(fd, BIOCSETLIF, (caddr_t)&ifr) < 0)
-#else
-                       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0)
-#endif
-                       {
-                               status = check_setif_failure(p, errno);
+                       status = bpf_bind(fd, p->opt.device, p->errbuf);
+                       if (status != BPF_BIND_SUCCEEDED) {
+                               if (status == BPF_BIND_BUFFER_TOO_BIG) {
+                                       /*
+                                        * The requested buffer size
+                                        * is too big.  Fail.
+                                        */
+                                       status = PCAP_ERROR;
+                                       goto bad;
+                               }
+
+                               /*
+                                * Special checks on macOS to deal with
+                                * the way monitor mode was done on
+                                * 10.4 Tiger.
+                                */
+                               status = check_setif_failure(p, status);
                                goto bad;
                        }
                } else {
@@ -2028,22 +2193,30 @@ pcap_activate_bpf(pcap_t *p)
                                 */
                                (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
 
-                               (void)strncpy(ifrname, p->opt.device, ifnamsiz);
-#ifdef BIOCSETLIF
-                               if (ioctl(fd, BIOCSETLIF, (caddr_t)&ifr) >= 0)
-#else
-                               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
-#endif
+                               status = bpf_bind(fd, p->opt.device, p->errbuf);
+                               if (status == BPF_BIND_SUCCEEDED)
                                        break;  /* that size worked; we're done */
 
-                               if (errno != ENOBUFS) {
-                                       status = check_setif_failure(p, errno);
+                               /*
+                                * If the attempt failed because the
+                                * buffer was too big, cut the buffer
+                                * size in half and try again.
+                                *
+                                * Otherwise, fail.
+                                */
+                               if (status != BPF_BIND_BUFFER_TOO_BIG) {
+                                       /*
+                                        * Special checks on macOS to deal
+                                        * with the way monitor mode was
+                                        * done on 10.4 Tiger.
+                                        */
+                                       status = check_setif_failure(p, status);
                                        goto bad;
                                }
                        }
 
                        if (v == 0) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                                    "BIOCSBLEN: %s: No buffer size worked",
                                    p->opt.device);
                                status = PCAP_ERROR;
@@ -2054,8 +2227,8 @@ pcap_activate_bpf(pcap_t *p)
 
        /* Get the data link layer type. */
        if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCGDLT");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2087,7 +2260,7 @@ pcap_activate_bpf(pcap_t *p)
                /*
                 * We don't know what to map this to yet.
                 */
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
                    v);
                status = PCAP_ERROR;
                goto bad;
@@ -2147,9 +2320,9 @@ pcap_activate_bpf(pcap_t *p)
         * we try to select DLT_IEEE802_11.
         */
        if (have_osinfo) {
-               if (isdigit((unsigned)osinfo.release[0]) &&
+               if (PCAP_ISDIGIT((unsigned)osinfo.release[0]) &&
                     (osinfo.release[0] == '9' ||
-                    isdigit((unsigned)osinfo.release[1]))) {
+                    PCAP_ISDIGIT((unsigned)osinfo.release[1]))) {
                        /*
                         * 10.5 (Darwin 9.x), or later.
                         */
@@ -2168,7 +2341,7 @@ pcap_activate_bpf(pcap_t *p)
                                         * of link-layer types, as selecting
                                         * it will keep monitor mode off.
                                         */
-                                       remove_en(p);
+                                       remove_non_802_11(p);
 
                                        /*
                                         * If the new mode we want isn't
@@ -2299,8 +2472,8 @@ pcap_activate_bpf(pcap_t *p)
         * BSDs - check CVS log for "bpf.c"?
         */
        if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
-               (void)pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                   "BIOCSHDRCMPLT: %s", pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCSHDRCMPLT");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2341,8 +2514,8 @@ pcap_activate_bpf(pcap_t *p)
                        bpf_to.tv_sec = p->opt.timeout / 1000;
                        bpf_to.tv_usec = (p->opt.timeout * 1000) % 1000000;
                        if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&bpf_to) < 0) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "BIOCSRTIMEOUT: %s", pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   errno, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT");
                                status = PCAP_ERROR;
                                goto bad;
                        }
@@ -2351,8 +2524,8 @@ pcap_activate_bpf(pcap_t *p)
                        to.tv_sec = p->opt.timeout / 1000;
                        to.tv_usec = (p->opt.timeout * 1000) % 1000000;
                        if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                   "BIOCSRTIMEOUT: %s", pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   errno, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT");
                                status = PCAP_ERROR;
                                goto bad;
                        }
@@ -2386,8 +2559,8 @@ pcap_activate_bpf(pcap_t *p)
 #endif /* _AIX */
                v = 1;
                if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "BIOCIMMEDIATE: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCIMMEDIATE");
                        status = PCAP_ERROR;
                        goto bad;
                }
@@ -2399,7 +2572,7 @@ pcap_activate_bpf(pcap_t *p)
                /*
                 * We don't support immediate mode.  Fail.
                 */
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Immediate mode not supported");
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Immediate mode not supported");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2408,8 +2581,8 @@ pcap_activate_bpf(pcap_t *p)
        if (p->opt.promisc) {
                /* set promiscuous mode, just warn if it fails */
                if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) {
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s",
-                           pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "BIOCPROMISC");
                        status = PCAP_WARNING_PROMISC_NOTSUP;
                }
        }
@@ -2417,16 +2590,16 @@ pcap_activate_bpf(pcap_t *p)
 #ifdef BIOCSTSTAMP
        v = BPF_T_BINTIME;
        if (ioctl(p->fd, BIOCSTSTAMP, &v) < 0) {
-               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSTSTAMP: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCSTSTAMP");
                status = PCAP_ERROR;
                goto bad;
        }
 #endif /* BIOCSTSTAMP */
 
        if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCGBLEN");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2436,8 +2609,8 @@ pcap_activate_bpf(pcap_t *p)
 #endif
        p->buffer = malloc(p->bufsize);
        if (p->buffer == NULL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "malloc");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2467,8 +2640,8 @@ pcap_activate_bpf(pcap_t *p)
        total_prog.bf_len = 1;
        total_prog.bf_insns = &total_insn;
        if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCSETF");
                status = PCAP_ERROR;
                goto bad;
        }
@@ -2578,8 +2751,8 @@ check_bpf_bindable(const char *name)
                en_name_len = strlen(name) - 1;
                en_name = malloc(en_name_len + 1);
                if (en_name == NULL) {
-                       (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
-                           "malloc: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "malloc");
                        return (-1);
                }
                strcpy(en_name, "en");
@@ -2622,7 +2795,18 @@ check_bpf_bindable(const char *name)
 
 #if defined(__FreeBSD__) && defined(SIOCIFCREATE2)
 static int
-finddevs_usb(pcap_if_t **alldevsp, char *errbuf)
+get_usb_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_)
+{
+       /*
+        * XXX - if there's a way to determine whether there's something
+        * plugged into a given USB bus, use that to determine whether
+        * this device is "connected" or not.
+        */
+       return (0);
+}
+
+static int
+finddevs_usb(pcap_if_list_t *devlistp, char *errbuf)
 {
        DIR *usbdir;
        struct dirent *usbitem;
@@ -2684,7 +2868,8 @@ finddevs_usb(pcap_if_t **alldevsp, char *errbuf)
                 * so we need to avoid adding multiple capture devices
                 * for each bus.
                 */
-               if (find_or_add_dev(alldevsp, name, PCAP_IF_UP, NULL, errbuf) == NULL) {
+               if (find_or_add_dev(devlistp, name, PCAP_IF_UP,
+                   get_usb_if_flags, NULL, errbuf) == NULL) {
                        free(name);
                        closedir(usbdir);
                        return (PCAP_ERROR);
@@ -2696,17 +2881,141 @@ finddevs_usb(pcap_if_t **alldevsp, char *errbuf)
 }
 #endif
 
+/*
+ * Get additional flags for a device, using SIOCGIFMEDIA.
+ */
+#ifdef SIOCGIFMEDIA
+static int
+get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
+{
+       int sock;
+       struct ifmediareq req;
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock == -1) {
+               pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno,
+                   "Can't create socket to get media information for %s",
+                   name);
+               return (-1);
+       }
+       memset(&req, 0, sizeof(req));
+       pcap_strlcpy(req.ifm_name, name, sizeof(req.ifm_name));
+       if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
+               if (errno == EOPNOTSUPP || errno == EINVAL || errno == ENOTTY ||
+                   errno == ENODEV || errno == EPERM
+#ifdef EPWROFF
+                   || errno == EPWROFF
+#endif
+                   ) {
+                       /*
+                        * Not supported, so we can't provide any
+                        * additional information.  Assume that
+                        * this means that "connected" vs.
+                        * "disconnected" doesn't apply.
+                        *
+                        * The ioctl routine for Apple's pktap devices,
+                        * annoyingly, checks for "are you root?" before
+                        * checking whether the ioctl is valid, so it
+                        * returns EPERM, rather than ENOTSUP, for the
+                        * invalid SIOCGIFMEDIA, unless you're root.
+                        * So, just as we do for some ethtool ioctls
+                        * on Linux, which makes the same mistake, we
+                        * also treat EPERM as meaning "not supported".
+                        *
+                        * And it appears that Apple's llw0 device, which
+                        * appears to be part of the Skywalk subsystem:
+                        *
+                        *    http://newosxbook.com/bonus/vol1ch16.html
+                        *
+                        * can sometimes return EPWROFF ("Device power
+                        * is off") for that ioctl, so we treat *that*
+                        * as another indication that we can't get a
+                        * connection status.  (If it *isn't* "powered
+                        * off", it's reported as a wireless device,
+                        * complete with an active/inactive state.)
+                        */
+                       *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
+                       close(sock);
+                       return (0);
+               }
+               pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno,
+                   "SIOCGIFMEDIA on %s failed", name);
+               close(sock);
+               return (-1);
+       }
+       close(sock);
+
+       /*
+        * OK, what type of network is this?
+        */
+       switch (IFM_TYPE(req.ifm_active)) {
+
+       case IFM_IEEE80211:
+               /*
+                * Wireless.
+                */
+               *flags |= PCAP_IF_WIRELESS;
+               break;
+       }
+
+       /*
+        * Do we know whether it's connected?
+        */
+       if (req.ifm_status & IFM_AVALID) {
+               /*
+                * Yes.
+                */
+               if (req.ifm_status & IFM_ACTIVE) {
+                       /*
+                        * It's connected.
+                        */
+                       *flags |= PCAP_IF_CONNECTION_STATUS_CONNECTED;
+               } else {
+                       /*
+                        * It's disconnected.
+                        */
+                       *flags |= PCAP_IF_CONNECTION_STATUS_DISCONNECTED;
+               }
+       }
+       return (0);
+}
+#else
+static int
+get_if_flags(const char *name _U_, bpf_u_int32 *flags, char *errbuf _U_)
+{
+       /*
+        * Nothing we can do other than mark loopback devices as "the
+        * connected/disconnected status doesn't apply".
+        *
+        * XXX - on Solaris, can we do what the dladm command does,
+        * i.e. get a connected/disconnected indication from a kstat?
+        * (Note that you can also get the link speed, and possibly
+        * other information, from a kstat as well.)
+        */
+       if (*flags & PCAP_IF_LOOPBACK) {
+               /*
+                * Loopback devices aren't wireless, and "connected"/
+                * "disconnected" doesn't apply to them.
+                */
+               *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
+               return (0);
+       }
+       return (0);
+}
+#endif
+
 int
-pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
+pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
 {
        /*
         * Get the list of regular interfaces first.
         */
-       if (pcap_findalldevs_interfaces(alldevsp, errbuf, check_bpf_bindable) == -1)
+       if (pcap_findalldevs_interfaces(devlistp, errbuf, check_bpf_bindable,
+           get_if_flags) == -1)
                return (-1);    /* failure */
 
 #if defined(__FreeBSD__) && defined(SIOCIFCREATE2)
-       if (finddevs_usb(alldevsp, errbuf) == -1)
+       if (finddevs_usb(devlistp, errbuf) == -1)
                return (-1);
 #endif
 
@@ -2727,13 +3036,13 @@ monitor_mode(pcap_t *p, int set)
 
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock == -1) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't open socket: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "can't open socket");
                return (PCAP_ERROR);
        }
 
        memset(&req, 0, sizeof req);
-       strncpy(req.ifm_name, p->opt.device, sizeof req.ifm_name);
+       pcap_strlcpy(req.ifm_name, p->opt.device, sizeof req.ifm_name);
 
        /*
         * Find out how many media types we have.
@@ -2747,7 +3056,11 @@ monitor_mode(pcap_t *p, int set)
                case ENXIO:
                        /*
                         * There's no such device.
+                        *
+                        * There's nothing more to say, so clear the
+                        * error message.
                         */
+                       p->errbuf[0] = '\0';
                        close(sock);
                        return (PCAP_ERROR_NO_SUCH_DEVICE);
 
@@ -2759,8 +3072,8 @@ monitor_mode(pcap_t *p, int set)
                        return (PCAP_ERROR_RFMON_NOTSUP);
 
                default:
-                       pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                           "SIOCGIFMEDIA 1: %s", pcap_strerror(errno));
+                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                           errno, "SIOCGIFMEDIA");
                        close(sock);
                        return (PCAP_ERROR);
                }
@@ -2779,15 +3092,15 @@ monitor_mode(pcap_t *p, int set)
         */
        media_list = malloc(req.ifm_count * sizeof(*media_list));
        if (media_list == NULL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "malloc");
                close(sock);
                return (PCAP_ERROR);
        }
        req.ifm_ulist = media_list;
        if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "SIOCGIFMEDIA");
                free(media_list);
                close(sock);
                return (PCAP_ERROR);
@@ -2843,12 +3156,12 @@ monitor_mode(pcap_t *p, int set)
                                return (PCAP_ERROR);
                        }
                        memset(&ifr, 0, sizeof(ifr));
-                       (void)strncpy(ifr.ifr_name, p->opt.device,
+                       (void)pcap_strlcpy(ifr.ifr_name, p->opt.device,
                            sizeof(ifr.ifr_name));
                        ifr.ifr_media = req.ifm_current | IFM_IEEE80211_MONITOR;
                        if (ioctl(sock, SIOCSIFMEDIA, &ifr) == -1) {
-                               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
-                                    "SIOCSIFMEDIA: %s", pcap_strerror(errno));
+                               pcap_fmt_errmsg_for_errno(p->errbuf,
+                                   PCAP_ERRBUF_SIZE, errno, "SIOCSIFMEDIA");
                                close(sock);
                                return (PCAP_ERROR);
                        }
@@ -2902,8 +3215,12 @@ find_802_11(struct bpf_dltlist *bdlp)
                                new_dlt = bdlp->bfl_list[i];
                        break;
 
+#ifdef DLT_PRISM_HEADER
                case DLT_PRISM_HEADER:
+#endif
+#ifdef DLT_AIRONET_HEADER
                case DLT_AIRONET_HEADER:
+#endif
                case DLT_IEEE802_11_RADIO_AVS:
                        /*
                         * 802.11 with radio, but not radiotap.
@@ -2938,24 +3255,25 @@ find_802_11(struct bpf_dltlist *bdlp)
 
 #if defined(__APPLE__) && defined(BIOCGDLTLIST)
 /*
- * Remove DLT_EN10MB from the list of DLT_ values, as we're in monitor mode,
- * and DLT_EN10MB isn't supported in monitor mode.
+ * Remove non-802.11 header types from the list of DLT_ values, as we're in
+ * monitor mode, and those header types aren't supported in monitor mode.
  */
 static void
-remove_en(pcap_t *p)
+remove_non_802_11(pcap_t *p)
 {
        int i, j;
 
        /*
-        * Scan the list of DLT_ values and discard DLT_EN10MB.
+        * Scan the list of DLT_ values and discard non-802.11 ones.
         */
        j = 0;
        for (i = 0; i < p->dlt_count; i++) {
                switch (p->dlt_list[i]) {
 
                case DLT_EN10MB:
+               case DLT_RAW:
                        /*
-                        * Don't offer this one.
+                        * Not 802.11.  Don't offer this one.
                         */
                        continue;
 
@@ -2997,10 +3315,17 @@ remove_802_11(pcap_t *p)
                switch (p->dlt_list[i]) {
 
                case DLT_IEEE802_11:
+#ifdef DLT_PRISM_HEADER
                case DLT_PRISM_HEADER:
+#endif
+#ifdef DLT_AIRONET_HEADER
                case DLT_AIRONET_HEADER:
+#endif
                case DLT_IEEE802_11_RADIO:
                case DLT_IEEE802_11_RADIO_AVS:
+#ifdef DLT_PPI
+               case DLT_PPI:
+#endif
                        /*
                         * 802.11.  Don't offer this one.
                         */
@@ -3072,8 +3397,8 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp)
         * some kernels.
         */
        if (errno != EINVAL) {
-               pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
-                   pcap_strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "BIOCSETF");
                return (-1);
        }
 
@@ -3092,60 +3417,198 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp)
  * Set direction flag: Which packets do we accept on a forwarding
  * single device? IN, OUT or both?
  */
+#if defined(BIOCSDIRECTION)
 static int
 pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d)
 {
-#if defined(BIOCSDIRECTION)
        u_int direction;
+       const char *direction_name;
+
+       /*
+        * FreeBSD and NetBSD.
+        */
+       switch (d) {
+
+       case PCAP_D_IN:
+               /*
+                * Incoming, but not outgoing, so accept only
+                * incoming packets.
+                */
+               direction = BPF_D_IN;
+               direction_name = "\"incoming only\"";
+               break;
+
+       case PCAP_D_OUT:
+               /*
+                * Outgoing, but not incoming, so accept only
+                * outgoing packets.
+                */
+               direction = BPF_D_OUT;
+               direction_name = "\"outgoing only\"";
+               break;
+
+       default:
+               /*
+                * Incoming and outgoing, so accept both
+                * incoming and outgoing packets.
+                *
+                * It's guaranteed, at this point, that d is a valid
+                * direction value, so we know that this is PCAP_D_INOUT
+                * if it's not PCAP_D_IN or PCAP_D_OUT.
+                */
+               direction = BPF_D_INOUT;
+               direction_name = "\"incoming and outgoing\"";
+               break;
+       }
 
-       direction = (d == PCAP_D_IN) ? BPF_D_IN :
-           ((d == PCAP_D_OUT) ? BPF_D_OUT : BPF_D_INOUT);
        if (ioctl(p->fd, BIOCSDIRECTION, &direction) == -1) {
-               (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf),
-                   "Cannot set direction to %s: %s",
-                       (d == PCAP_D_IN) ? "PCAP_D_IN" :
-                       ((d == PCAP_D_OUT) ? "PCAP_D_OUT" : "PCAP_D_INOUT"),
-                       strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
+                   errno, "Cannot set direction to %s", direction_name);
                return (-1);
        }
        return (0);
+}
+#elif defined(BIOCSDIRFILT)
+static int
+pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d)
+{
+       u_int dirfilt;
+       const char *direction_name;
+
+       /*
+        * OpenBSD; same functionality, different names, different
+        * semantics (the flags mean "*don't* capture packets in
+        * that direction", not "*capture only* packets in that
+        * direction").
+        */
+       switch (d) {
+
+       case PCAP_D_IN:
+               /*
+                * Incoming, but not outgoing, so filter out
+                * outgoing packets.
+                */
+               dirfilt = BPF_DIRECTION_OUT;
+               direction_name = "\"incoming only\"";
+               break;
+
+       case PCAP_D_OUT:
+               /*
+                * Outgoing, but not incoming, so filter out
+                * incoming packets.
+                */
+               dirfilt = BPF_DIRECTION_IN;
+               direction_name = "\"outgoing only\"";
+               break;
+
+       default:
+               /*
+                * Incoming and outgoing, so don't filter out
+                * any packets based on direction.
+                *
+                * It's guaranteed, at this point, that d is a valid
+                * direction value, so we know that this is PCAP_D_INOUT
+                * if it's not PCAP_D_IN or PCAP_D_OUT.
+                */
+               dirfilt = 0;
+               direction_name = "\"incoming and outgoing\"";
+               break;
+       }
+       if (ioctl(p->fd, BIOCSDIRFILT, &dirfilt) == -1) {
+               pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
+                   errno, "Cannot set direction to %s", direction_name);
+               return (-1);
+       }
+       return (0);
+}
 #elif defined(BIOCSSEESENT)
+static int
+pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d)
+{
        u_int seesent;
+       const char *direction_name;
 
        /*
-        * We don't support PCAP_D_OUT.
+        * OS with just BIOCSSEESENT.
         */
-       if (d == PCAP_D_OUT) {
-               pcap_snprintf(p->errbuf, sizeof(p->errbuf),
-                   "Setting direction to PCAP_D_OUT is not supported on BPF");
-               return -1;
+       switch (d) {
+
+       case PCAP_D_IN:
+               /*
+                * Incoming, but not outgoing, so we don't want to
+                * see transmitted packets.
+                */
+               seesent = 0;
+               direction_name = "\"incoming only\"";
+               break;
+
+       case PCAP_D_OUT:
+               /*
+                * Outgoing, but not incoming; we can't specify that.
+                */
+               snprintf(p->errbuf, sizeof(p->errbuf),
+                   "Setting direction to \"outgoing only\" is not supported on this device");
+               return (-1);
+
+       default:
+               /*
+                * Incoming and outgoing, so we want to see transmitted
+                * packets.
+                *
+                * It's guaranteed, at this point, that d is a valid
+                * direction value, so we know that this is PCAP_D_INOUT
+                * if it's not PCAP_D_IN or PCAP_D_OUT.
+                */
+               seesent = 1;
+               direction_name = "\"incoming and outgoing\"";
+               break;
        }
 
-       seesent = (d == PCAP_D_INOUT);
        if (ioctl(p->fd, BIOCSSEESENT, &seesent) == -1) {
-               (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf),
-                   "Cannot set direction to %s: %s",
-                       (d == PCAP_D_INOUT) ? "PCAP_D_INOUT" : "PCAP_D_IN",
-                       strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
+                   errno, "Cannot set direction to %s", direction_name);
                return (-1);
        }
        return (0);
+}
 #else
-       (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf),
-           "This system doesn't support BIOCSSEESENT, so the direction can't be set");
+static int
+pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d _U_)
+{
+       (void) snprintf(p->errbuf, sizeof(p->errbuf),
+           "Setting direction is not supported on this device");
        return (-1);
-#endif
 }
+#endif
 
+#ifdef BIOCSDLT
 static int
 pcap_set_datalink_bpf(pcap_t *p, int dlt)
 {
-#ifdef BIOCSDLT
        if (ioctl(p->fd, BIOCSDLT, &dlt) == -1) {
-               (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf),
-                   "Cannot set DLT %d: %s", dlt, strerror(errno));
+               pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
+                   errno, "Cannot set DLT %d", dlt);
                return (-1);
        }
-#endif
        return (0);
 }
+#else
+static int
+pcap_set_datalink_bpf(pcap_t *p _U_, int dlt _U_)
+{
+       return (0);
+}
+#endif
+
+/*
+ * Platform-specific information.
+ */
+const char *
+pcap_lib_version(void)
+{
+#ifdef HAVE_ZEROCOPY_BPF
+       return (PCAP_VERSION_STRING " (with zerocopy support)");
+#else
+       return (PCAP_VERSION_STRING);
+#endif
+}