]> 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 4a4d515e40fb577c89f5218156d8956db69d87f8..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:
@@ -601,21 +607,98 @@ bpf_open(char *errbuf)
  *
  * 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_bind(int fd, const char *name)
+bpf_bind(int fd, const char *name, char *errbuf)
 {
+       int status;
 #ifdef LIFNAMSIZ
        struct lifreq ifr;
 
-       (void)strncpy(ifr.lifr_name, name, sizeof(ifr.lifr_name));
-       return (ioctl(fd, BIOCSETLIF, (caddr_t)&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;
 
-       (void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-       return (ioctl(fd, BIOCSETIF, (caddr_t)&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, BIOCSETIF, (caddr_t)&ifr);
 #endif
+
+       if (status < 0) {
+               switch (errno) {
+
+               case ENXIO:
+                       /*
+                        * There's no such device.
+                        *
+                        * There's nothing more to say, so clear out the
+                        * error message.
+                        */
+                       errbuf[0] = '\0';
+                       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.
+                        */
+                       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, "Binding interface %s to BPF device failed",
+                           name);
+                       return (PCAP_ERROR);
+               }
+       }
+       return (BPF_BIND_SUCCEEDED);
 }
 
 /*
@@ -630,6 +713,7 @@ static int
 bpf_open_and_bind(const char *name, char *errbuf)
 {
        int fd;
+       int status;
 
        /*
         * First, open a BPF device.
@@ -641,34 +725,18 @@ bpf_open_and_bind(const char *name, char *errbuf)
        /*
         * Now bind to the device.
         */
-       if (bpf_bind(fd, name) < 0) {
-               switch (errno) {
-
-               case ENXIO:
-                       /*
-                        * There's no such device.
-                        */
-                       close(fd);
-                       return (PCAP_ERROR_NO_SUCH_DEVICE);
-
-               case ENETDOWN:
+       status = bpf_bind(fd, name, errbuf);
+       if (status != BPF_BIND_SUCCEEDED) {
+               close(fd);
+               if (status == 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_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "Binding BPF device to interface failed: %s",
-                           name);
-                       close(fd);
                        return (PCAP_ERROR);
                }
+               return (status);
        }
 
        /*
@@ -677,21 +745,19 @@ 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;
-#ifdef LIFNAMSIZ
-       struct lifreq ifr;
-
-       (void)strncpy(ifr.lifr_name, name, sizeof(ifr.lifr_name));
-       status = ioctl(fd, SIOCGLIFFLAGS, (caddr_t)&ifr);
-#else
        struct ifreq ifr;
 
-       (void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       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);
-#endif
 
        if (status < 0) {
                if (errno == ENXIO || errno == EINVAL) {
@@ -718,6 +784,7 @@ device_exists(int fd, const char *name, char *errbuf)
         */
        return (0);
 }
+#endif
 
 #ifdef BIOCGDLTLIST
 static int
@@ -808,6 +875,7 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
        int fd;
 #ifdef BIOCGDLTLIST
        struct bpf_dltlist bdl;
+       int err;
 #endif
 
        /*
@@ -900,34 +968,18 @@ pcap_can_set_rfmon_bpf(pcap_t *p)
        /*
         * Now bind to the device.
         */
-       if (bpf_bind(fd, p->opt.device) < 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_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "Binding BPF device to interface failed: %s",
-                           p->opt.device);
-                       close(fd);
                        return (PCAP_ERROR);
                }
+               return (err);
        }
 
        /*
@@ -1135,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)
@@ -1510,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,
@@ -1524,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 =
@@ -1592,19 +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;
        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
@@ -1662,31 +1715,30 @@ check_setif_failure(pcap_t *p, int error)
                        }
                        return (err);
                }
-#endif
+
                /*
                 * No such device.
                 */
                return (PCAP_ERROR_NO_SUCH_DEVICE);
-       } else if (error == 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,
-                   error, "Binding BPF device to interface failed: %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.
@@ -1712,7 +1764,8 @@ pcap_activate_bpf(pcap_t *p)
        int retv;
 #endif
        int fd;
-#ifdef LIFNAMSIZ
+#if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid)
+       struct lifreq ifr;
        char *zonesep;
 #endif
        struct bpf_version bv;
@@ -1930,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
@@ -2058,11 +2112,19 @@ pcap_activate_bpf(pcap_t *p)
                        status = PCAP_ERROR;
                        goto bad;
                }
-               if (bpf_bind(fd, p->opt.device, ifnamsiz) < 0) {
-                       pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
-                           errno, "Binding BPF device to interface failed: %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);
@@ -2089,8 +2151,23 @@ pcap_activate_bpf(pcap_t *p)
                        /*
                         * Now bind to the device.
                         */
-                       if (bpf_bind(fd, p->opt.device) < 0) {
-                               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 {
@@ -2116,11 +2193,24 @@ pcap_activate_bpf(pcap_t *p)
                                 */
                                (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
 
-                               if (bpf_bind(fd, p->opt.device) >= 0)
+                               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;
                                }
                        }
@@ -2809,7 +2899,7 @@ 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
@@ -2891,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
@@ -2952,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.
@@ -2966,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);
 
@@ -3062,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) {