]> 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 0d8f2d6e9c207cb0c8c3bc69df154b9e86619e07..2898e598d8e3916c598c6535d79c9851b008d6b0 100644 (file)
@@ -152,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;
@@ -312,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);
                }
@@ -444,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);
 
@@ -455,7 +455,6 @@ 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) {
                pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno,
@@ -465,6 +464,7 @@ pcap_create_interface(const char *device _U_, char *ebuf)
        }
        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);
 }
@@ -478,7 +478,7 @@ bpf_open(char *errbuf)
 {
        int fd = -1;
        static const char cloning_device[] = "/dev/bpf";
-       int n = 0;
+       u_int n = 0;
        char device[sizeof "/dev/bpf0000000000"];
        static int no_cloning_bpf = 0;
 
@@ -503,12 +503,17 @@ bpf_open(char *errbuf)
            ((errno != EACCES && errno != ENOENT) ||
             (fd = open(cloning_device, O_RDONLY)) == -1)) {
                if (errno != ENOENT) {
-                       if (errno == EACCES)
+                       if (errno == EACCES) {
                                fd = PCAP_ERROR_PERM_DENIED;
-                       else
+                               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);
+                               pcap_fmt_errmsg_for_errno(errbuf,
+                                   PCAP_ERRBUF_SIZE, errno,
+                                   "(cannot open device) %s", cloning_device);
+                       }
                        return (fd);
                }
                no_cloning_bpf = 1;
@@ -521,7 +526,7 @@ bpf_open(char *errbuf)
                 * that isn't in use.
                 */
                do {
-                       (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+                       (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
@@ -577,8 +582,9 @@ bpf_open(char *errbuf)
                         * if any.
                         */
                        fd = PCAP_ERROR_PERM_DENIED;
-                       pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "(cannot open BPF device) %s", device);
+                       snprintf(errbuf, PCAP_ERRBUF_SIZE,
+                           "Attempt to open %s failed - root privileges may be required",
+                           device);
                        break;
 
                default:
@@ -596,38 +602,70 @@ bpf_open(char *errbuf)
 }
 
 /*
- * 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:
@@ -638,16 +676,68 @@ 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_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "BIOCSETIF: %s", name);
-                       close(fd);
+                           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);
+       }
 
        /*
         * Success.
@@ -655,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)
@@ -741,10 +872,10 @@ static int
 pcap_can_set_rfmon_bpf(pcap_t *p)
 {
        struct utsname osinfo;
-       struct ifreq ifr;
        int fd;
 #ifdef BIOCGDLTLIST
        struct bpf_dltlist bdl;
+       int err;
 #endif
 
        /*
@@ -782,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.
@@ -798,16 +932,24 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
                            errno, "socket");
                        return (PCAP_ERROR);
                }
-               pcap_strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name));
-               pcap_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);
        }
 
@@ -826,34 +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:
+       err = bpf_bind(fd, p->opt.device, p->errbuf);
+       if (err != BPF_BIND_SUCCEEDED) {
+               close(fd);
+               if (err == BPF_BIND_BUFFER_TOO_BIG) {
                        /*
-                        * There's no such device.
+                        * 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_NO_SUCH_DEVICE);
-
-               case 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.
-                        */
-                       close(fd);
-                       return (PCAP_ERROR_IFACE_NOT_UP);
-
-               default:
-                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "BIOCSETIF: %s", p->opt.device);
-                       close(fd);
                        return (PCAP_ERROR);
                }
+               return (err);
        }
 
        /*
@@ -1061,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)
@@ -1436,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,
@@ -1450,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 =
@@ -1518,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
@@ -1542,33 +1668,38 @@ check_setif_failure(pcap_t *p, int error)
                         */
                        fd = socket(AF_INET, SOCK_DGRAM, 0);
                        if (fd != -1) {
-                               pcap_strlcpy(ifr.ifr_name, "en",
-                                   sizeof(ifr.ifr_name));
-                               pcap_strlcat(ifr.ifr_name, p->opt.device + 3,
-                                   sizeof(ifr.ifr_name));
-                               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+                               char *en_name;
+
+                               if (pcap_asprintf(&en_name, "en%s",
+                                   p->opt.device + 3) == -1) {
                                        /*
-                                        * We assume this failed because
-                                        * the underlying device doesn't
-                                        * exist.
+                                        * We can't find out whether there's
+                                        * an underlying "enN" device, so
+                                        * just report "no such device".
                                         */
-                                       err = PCAP_ERROR_NO_SUCH_DEVICE;
                                        pcap_fmt_errmsg_for_errno(p->errbuf,
                                            PCAP_ERRBUF_SIZE, errno,
-                                           "SIOCGIFFLAGS on %s failed",
-                                           ifr.ifr_name);
-                               } else {
-                                       /*
-                                        * 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;
+                                           "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 {
@@ -1584,32 +1715,30 @@ check_setif_failure(pcap_t *p, int error)
                        }
                        return (err);
                }
-#endif
+
                /*
                 * No such device.
                 */
-               pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "BIOCSETIF failed");
                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_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "BIOCSETIF: %s", p->opt.device);
-               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.
@@ -1635,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__
@@ -1796,24 +1919,19 @@ pcap_activate_bpf(pcap_t *p)
                                         */
                                        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                                        if (sockfd != -1) {
-                                               pcap_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_fmt_errmsg_for_errno(p->errbuf,
-                                                           PCAP_ERRBUF_SIZE,
-                                                           errno,
-                                                           "SIOCGIFFLAGS failed");
-                                               } else
                                                        status = PCAP_ERROR_RFMON_NOTSUP;
+                                               }
                                                close(sockfd);
                                        } else {
                                                /*
@@ -1865,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
@@ -1993,11 +2112,19 @@ pcap_activate_bpf(pcap_t *p)
                        status = PCAP_ERROR;
                        goto bad;
                }
-               (void)strncpy(ifrname, p->opt.device, ifnamsiz);
-               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
-                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "BIOCSETIF: %s", p->opt.device);
-                       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);
@@ -2024,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 {
@@ -2057,16 +2193,24 @@ 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;
                                }
                        }
@@ -2755,10 +2899,14 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
                return (-1);
        }
        memset(&req, 0, sizeof(req));
-       strncpy(req.ifm_name, name, sizeof(req.ifm_name));
+       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) {
+                   errno == ENODEV || errno == EPERM
+#ifdef EPWROFF
+                   || errno == EPWROFF
+#endif
+                   ) {
                        /*
                         * Not supported, so we can't provide any
                         * additional information.  Assume that
@@ -2773,6 +2921,18 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
                         * 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);
@@ -2821,7 +2981,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
 }
 #else
 static int
-get_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_)
+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
@@ -2882,7 +3042,7 @@ monitor_mode(pcap_t *p, int set)
        }
 
        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.
@@ -2896,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);
 
@@ -2992,7 +3156,7 @@ 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) {
@@ -3296,7 +3460,7 @@ pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d)
                direction_name = "\"incoming and outgoing\"";
                break;
        }
-               
+
        if (ioctl(p->fd, BIOCSDIRECTION, &direction) == -1) {
                pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
                    errno, "Cannot set direction to %s", direction_name);
@@ -3399,7 +3563,7 @@ pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d)
                direction_name = "\"incoming and outgoing\"";
                break;
        }
-               
+
        if (ioctl(p->fd, BIOCSSEESENT, &seesent) == -1) {
                pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
                    errno, "Cannot set direction to %s", direction_name);