]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-bpf.c
From Paolo Abeni and me: split pcap_open_live() into a "get a pcap_t
[libpcap] / pcap-bpf.c
index aab48247b1573812070adcfbeb9c38ea163ee119..d5d0fe1c167926a8727a5c5e8a354d550c14a423 100644 (file)
@@ -20,7 +20,7 @@
  */
 #ifndef lint
 static const char rcsid[] _U_ =
-    "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.99.2.4 2008-01-29 10:13:11 guy Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.99.2.5 2008-04-04 19:39:05 guy Exp $ (LBL)";
 #endif
 
 #ifdef HAVE_CONFIG_H
@@ -92,6 +92,10 @@ static int odmlockid = 0;
 #include <string.h>
 #include <unistd.h>
 
+#ifdef HAVE_NET_IF_MEDIA_H
+# include <net/if_media.h>
+#endif
+
 #include "pcap-int.h"
 
 #ifdef HAVE_DAG_API
@@ -102,10 +106,346 @@ static int odmlockid = 0;
 #include "os-proto.h"
 #endif
 
+#ifdef BIOCGDLTLIST
+# if (defined(HAVE_NET_IF_MEDIA_H) && defined(IFM_IEEE80211)) && !defined(__APPLE__)
+#define HAVE_BSD_IEEE80211
+# endif
+
+# if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)
+static int find_802_11(struct bpf_dltlist *);
+
+#  ifdef HAVE_BSD_IEEE80211
+static int monitor_mode(pcap_t *, int);
+#  endif
+
+#  if defined(__APPLE__)
+static void remove_en(pcap_t *);
+static void remove_802_11(pcap_t *);
+#  endif
+
+# endif /* defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) */
+
+#endif /* BIOCGDLTLIST */
+
+/*
+ * We include the OS's <net/bpf.h>, not our "pcap/bpf.h", so we probably
+ * don't get DLT_DOCSIS defined.
+ */
+#ifndef DLT_DOCSIS
+#define DLT_DOCSIS     143
+#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.
+ */
+#ifndef DLT_PRISM_HEADER
+#define DLT_PRISM_HEADER       119
+#endif
+#ifndef DLT_AIRONET_HEADER
+#define DLT_AIRONET_HEADER     120
+#endif
+#ifndef DLT_IEEE802_11_RADIO
+#define DLT_IEEE802_11_RADIO   127
+#endif
+#ifndef DLT_IEEE802_11_RADIO_AVS
+#define DLT_IEEE802_11_RADIO_AVS 163
+#endif
+
+static int pcap_can_set_rfmon_bpf(pcap_t *p);
+static int pcap_activate_bpf(pcap_t *p);
 static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp);
 static int pcap_setdirection_bpf(pcap_t *, pcap_direction_t);
 static int pcap_set_datalink_bpf(pcap_t *p, int dlt);
 
+pcap_t *
+pcap_create(const char *device, char *ebuf)
+{
+       pcap_t *p;
+
+#ifdef HAVE_DAG_API
+       if (strstr(device, "dag"))
+               return (dag_create(device, ebuf));
+#endif /* HAVE_DAG_API */
+
+       p = pcap_create_common(device, ebuf);
+       if (p == NULL)
+               return (NULL);
+
+       p->activate_op = pcap_activate_bpf;
+       p->can_set_rfmon_op = pcap_can_set_rfmon_bpf;
+       return (p);
+}
+
+static int
+bpf_open(pcap_t *p)
+{
+       int fd;
+#ifdef HAVE_CLONING_BPF
+       static const char device[] = "/dev/bpf";
+#else
+       int n = 0;
+       char device[sizeof "/dev/bpf0000000000"];
+#endif
+
+#ifdef _AIX
+       /*
+        * Load the bpf driver, if it isn't already loaded,
+        * and create the BPF device entries, if they don't
+        * already exist.
+        */
+       if (bpf_load(p->errbuf) == -1)
+               return (-1);
+#endif
+
+#ifdef HAVE_CLONING_BPF
+       if ((fd = open(device, O_RDWR)) == -1 &&
+           (errno != EACCES || (fd = open(device, O_RDONLY)) == -1))
+               snprintf(p->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.
+        */
+       do {
+               (void)snprintf(device, sizeof(device), "/dev/bpf%d", 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
+        */
+       if (fd < 0)
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s",
+                   device, pcap_strerror(errno));
+#endif
+
+       return (fd);
+}
+
+#ifdef BIOCGDLTLIST
+static int
+get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
+{
+       memset(bdlp, 0, sizeof(*bdlp));
+       if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) == 0) {
+               u_int i;
+               int is_ethernet;
+
+               bdlp->bfl_list = (u_int *) malloc(sizeof(u_int) * (bdlp->bfl_len + 1));
+               if (bdlp->bfl_list == NULL) {
+                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+                           pcap_strerror(errno));
+                       return (-1);
+               }
+
+               if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) {
+                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
+                       free(bdlp->bfl_list);
+                       return (-1);
+               }
+
+               /*
+                * OK, for real Ethernet devices, add DLT_DOCSIS to the
+                * list, so that an application can let you choose it,
+                * in case you're capturing DOCSIS traffic that a Cisco
+                * Cable Modem Termination System is putting out onto
+                * an Ethernet (it doesn't put an Ethernet header onto
+                * the wire, it puts raw DOCSIS frames out on the wire
+                * inside the low-level Ethernet framing).
+                *
+                * A "real Ethernet device" is defined here as a device
+                * that has a link-layer type of DLT_EN10MB and that has
+                * no alternate link-layer types; that's done to exclude
+                * 802.11 interfaces (which might or might not be the
+                * right thing to do, but I suspect it is - Ethernet <->
+                * 802.11 bridges would probably badly mishandle frames
+                * that don't have Ethernet headers).
+                */
+               if (v == DLT_EN10MB) {
+                       is_ethernet = 1;
+                       for (i = 0; i < bdlp->bfl_len; i++) {
+                               if (bdlp->bfl_list[i] != DLT_EN10MB) {
+                                       is_ethernet = 0;
+                                       break;
+                               }
+                       }
+                       if (is_ethernet) {
+                               /*
+                                * We reserved one more slot at the end of
+                                * the list.
+                                */
+                               bdlp->bfl_list[bdlp->bfl_len] = DLT_DOCSIS;
+                               bdlp->bfl_len++;
+                       }
+               }
+       } else {
+               /*
+                * EINVAL just means "we don't support this ioctl on
+                * this device"; don't treat it as an error.
+                */
+               if (errno != EINVAL) {
+                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
+                       return (-1);
+               }
+       }
+       return (0);
+}
+#endif
+
+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;
+#endif
+
+       /*
+        * The joys of monitor mode on OS X.
+        *
+        * Prior to 10.4, it's not supported at all.
+        *
+        * In 10.4, if adapter enN supports monitor mode, there's a
+        * wltN adapter corresponding to it; you open it, instead of
+        * enN, to get monitor mode.  You get whatever link-layer
+        * headers it supplies.
+        *
+        * In 10.5, and, we assume, later releases, if adapter enN
+        * supports monitor mode, it offers, among its selectable
+        * DLT_ values, values that let you get the 802.11 header;
+        * selecting one of those values puts the adapter into monitor
+        * mode (i.e., you can't get 802.11 headers except in monitor
+        * mode, and you can't get Ethernet headers in monitor mode).
+        */
+       if (uname(&osinfo) == -1) {
+               /*
+                * Can't get the OS version; just say "no".
+                */
+               return (0);
+       }
+       /*
+        * We assume osinfo.sysname is "Darwin", because
+        * __APPLE__ is defined.  We just check the version.
+        */
+       if (osinfo.release[0] < '8' && osinfo.release[1] == '.') {
+               /*
+                * 10.3 (Darwin 7.x) or earlier.
+                * Monitor mode not supported.
+                */
+               return (0);
+       }
+       if (osinfo.release[0] == '8' && osinfo.release[1] == '.') {
+               /*
+                * 10.4 (Darwin 8.x).  s/en/wlt/, and check
+                * whether the device exists.
+                */
+               if (strncmp(p->opt.source, "en", 2) != 0) {
+                       /*
+                        * Not an enN device; no monitor mode.
+                        */
+                       return (0);
+               }
+               fd = socket(AF_INET, SOCK_DGRAM, 0);
+               if (fd == -1) {
+                       (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                           "socket: %s", pcap_strerror(errno));
+                       return (PCAP_ERROR);
+               }
+               strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name));
+               strlcat(ifr.ifr_name, p->opt.source + 2, sizeof(ifr.ifr_name));
+               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+                       /*
+                        * No such device?
+                        */
+                       close(fd);
+                       return (0);
+               }
+               close(fd);
+               return (1);
+       }
+
+#ifdef BIOCGDLTLIST
+       /*
+        * Everything else is 10.5 or later; for those,
+        * we just open the enN device, and check whether
+        * we have any 802.11 devices.
+        *
+        * First, open a BPF device.
+        */
+       fd = bpf_open(p);
+       if (fd < 0)
+               return (PCAP_ERROR);
+
+       /*
+        * Now bind to the device.
+        */
+       (void)strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name));
+       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                   "BIOCSETIF: %s: %s",
+                   p->opt.source, pcap_strerror(errno));
+               close(fd);
+               return (PCAP_ERROR);
+       }
+
+       /*
+        * We know the default link type -- now determine all the DLTs
+        * this interface supports.  If this fails with EINVAL, it's
+        * not fatal; we just don't get to use the feature later.
+        * (We don't care about DLT_DOCSIS, so we pass DLT_NULL
+        * as the default DLT for this adapter.)
+        */
+       if (get_dlt_list(fd, DLT_NULL, &bdl, p->errbuf) == -1) {
+               close(fd);
+               return (PCAP_ERROR);
+       }
+       if (find_802_11(&bdl) != -1) {
+               /*
+                * We have an 802.11 DLT, so we can set monitor mode.
+                */
+               free(bdl.bfl_list);
+               close(fd);
+               return (1);
+       }
+       free(bdl.bfl_list);
+#endif /* BIOCGDLTLIST */
+       return (0);
+#elif defined(HAVE_BSD_IEEE80211)
+       int ret;
+
+       ret = monitor_mode(p, 0);
+       if (ret == PCAP_ERROR_RFMON_NOTSUP)
+               return (0);     /* not an error, just a "can't do" */
+       if (ret == 0)
+               return (1);     /* success */
+       return (ret);
+#else
+       return (0);
+#endif
+}
+
 static int
 pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps)
 {
@@ -516,169 +856,348 @@ bpf_load(char *errbuf)
 }
 #endif
 
-static inline int
-bpf_open(pcap_t *p, char *errbuf)
+/*
+ * Turn off rfmon mode if necessary.
+ */
+static void
+pcap_close_bpf(pcap_t *p)
 {
-       int fd;
-#ifdef HAVE_CLONING_BPF
-       static const char device[] = "/dev/bpf";
-#else
-       int n = 0;
-       char device[sizeof "/dev/bpf0000000000"];
+#ifdef HAVE_BSD_IEEE80211
+       int sock;
+       struct ifmediareq req;
+       struct ifreq ifr;
 #endif
 
-#ifdef _AIX
-       /*
-        * Load the bpf driver, if it isn't already loaded,
-        * and create the BPF device entries, if they don't
-        * already exist.
-        */
-       if (bpf_load(errbuf) == -1)
-               return (-1);
-#endif
+       if (p->md.must_clear != 0) {
+               /*
+                * There's something we have to do when closing this
+                * pcap_t.
+                */
+#ifdef HAVE_BSD_IEEE80211
+               if (p->md.must_clear & MUST_CLEAR_RFMON) {
+                       /*
+                        * We put the interface into rfmon mode;
+                        * take it out of rfmon mode.
+                        *
+                        * XXX - if somebody else wants it in rfmon
+                        * mode, this code cannot know that, so it'll take
+                        * it out of rfmon mode.
+                        */
+                       sock = socket(AF_INET, SOCK_DGRAM, 0);
+                       if (sock == -1) {
+                               fprintf(stderr,
+                                   "Can't restore interface flags (socket() failed: %s).\n"
+                                   "Please adjust manually.\n",
+                                   strerror(errno));
+                       } else {
+                               memset(&req, 0, sizeof(req));
+                               strncpy(req.ifm_name, p->md.device,
+                                   sizeof(req.ifm_name));
+                               if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
+                                       fprintf(stderr,
+                                           "Can't restore interface flags (SIOCGIFMEDIA failed: %s).\n"
+                                           "Please adjust manually.\n",
+                                           strerror(errno));
+                               } else {
+                                       if (req.ifm_current & IFM_IEEE80211_MONITOR) {
+                                               /*
+                                                * Rfmon mode is currently on;
+                                                * turn it off.
+                                                */
+                                               memset(&ifr, 0, sizeof(ifr));
+                                               (void)strncpy(ifr.ifr_name,
+                                                   p->md.device,
+                                                   sizeof(ifr.ifr_name));
+                                               ifr.ifr_media =
+                                                   req.ifm_current & ~IFM_IEEE80211_MONITOR;
+                                               if (ioctl(sock, SIOCSIFMEDIA,
+                                                   &ifr) == -1) {
+                                                       fprintf(stderr,
+                                                           "Can't restore interface flags (SIOCSIFMEDIA failed: %s).\n"
+                                                           "Please adjust manually.\n",
+                                                           strerror(errno));
+                                               }
+                                       }
+                               }
+                               close(sock);
+                       }
+               }
+#endif /* HAVE_BSD_IEEE80211 */
 
-#ifdef HAVE_CLONING_BPF
-       if ((fd = open(device, O_RDWR)) == -1 &&
-           (errno != EACCES || (fd = open(device, O_RDONLY)) == -1))
-               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.
-        */
-       do {
-               (void)snprintf(device, sizeof(device), "/dev/bpf%d", 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.
+                * Take this pcap out of the list of pcaps for which we
+                * have to take the interface out of some mode.
                 */
-               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
-        */
-       if (fd < 0)
-               snprintf(errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s",
-                   device, pcap_strerror(errno));
-#endif
+               pcap_remove_from_pcaps_to_close(p);
+       }
 
-       return (fd);
+       if (p->md.device != NULL)
+               free(p->md.device);
+       p->md.device = NULL;
+       pcap_close_common(p);
 }
 
-/*
- * We include the OS's <net/bpf.h>, not our "pcap/bpf.h", so we probably
- * don't get DLT_DOCSIS defined.
- */
-#ifndef DLT_DOCSIS
-#define DLT_DOCSIS     143
+static int
+check_setif_failure(pcap_t *p, int error)
+{
+#ifdef __APPLE__
+       int fd;
+       struct ifreq ifr;
+       int err;
 #endif
 
-pcap_t *
-pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
-    char *ebuf)
+       if (error == ENXIO) {
+               /*
+                * No such device exists.
+                */
+#ifdef __APPLE__
+               if (p->opt.rfmon && strncmp(p->opt.source, "wlt", 3) == 0) {
+                       /*
+                        * Monitor mode was requested, and we're trying
+                        * to open a "wltN" device.  Assume that this
+                        * is 10.4 and that we were asked to open an
+                        * "enN" device; if that device exists, return
+                        * "monitor mode not supported on the device".
+                        */
+                       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.source + 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;
+                               } 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;
+                               }
+                               close(fd);
+                       } else {
+                               /*
+                                * We can't find out whether there's
+                                * an underlying "enN" device, so
+                                * just report "no such device".
+                                */
+                               err = PCAP_ERROR_NO_SUCH_DEVICE;
+                       }
+                       return (err);
+               }
+#endif
+               /*
+                * No such device.
+                */
+               return (PCAP_ERROR_NO_SUCH_DEVICE);
+       } else {
+               /*
+                * Some other error; fill in the error string, and
+                * return PCAP_ERROR.
+                */
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
+                   p->opt.source, pcap_strerror(errno));
+               return (PCAP_ERROR);
+       }
+}
+
+static int
+pcap_activate_bpf(pcap_t *p)
 {
+       int err;
        int fd;
        struct ifreq ifr;
        struct bpf_version bv;
+#ifdef __APPLE__
+       char *wltdev = NULL;
+#endif
 #ifdef BIOCGDLTLIST
        struct bpf_dltlist bdl;
+#if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)
+       int new_dlt;
 #endif
+#endif /* BIOCGDLTLIST */
 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
        u_int spoof_eth_src = 1;
 #endif
        u_int v;
-       pcap_t *p;
        struct bpf_insn total_insn;
        struct bpf_program total_prog;
        struct utsname osinfo;
+       int have_osinfo = 0;
 
-#ifdef HAVE_DAG_API
-       if (strstr(device, "dag")) {
-               return dag_open_live(device, snaplen, promisc, to_ms, ebuf);
-       }
-#endif /* HAVE_DAG_API */
-
-       p = (pcap_t *)malloc(sizeof(*p));
-       if (p == NULL) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                   pcap_strerror(errno));
-               return (NULL);
-       }
-       memset(p, 0, sizeof(*p));
-       fd = bpf_open(p, ebuf);
-       if (fd < 0)
+       fd = bpf_open(p);
+       if (fd < 0) {
+               err = PCAP_ERROR;
                goto bad;
+       }
 
        p->fd = fd;
-       p->snapshot = snaplen;
 
        if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
        if (bv.bv_major != BPF_MAJOR_VERSION ||
            bv.bv_minor < BPF_MINOR_VERSION) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE,
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "kernel bpf filter out of date");
+               err = PCAP_ERROR;
                goto bad;
        }
 
+       p->md.device = strdup(p->opt.source);
+       if (p->md.device == NULL) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
+                    pcap_strerror(errno));
+               return PCAP_ERROR;
+       }
+
        /*
-        * Try finding a good size for the buffer; 32768 may be too
-        * big, so keep cutting it in half until we find a size
-        * that works, or run out of sizes to try.  If the default
-        * is larger, don't make it smaller.
-        *
-        * XXX - there should be a user-accessible hook to set the
-        * initial buffer size.
+        * Attempt to find out the version of the OS on which we're running.
         */
-       if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768)
-               v = 32768;
-       for ( ; v != 0; v >>= 1) {
-               /* Ignore the return value - this is because the call fails
-                * on BPF systems that don't have kernel malloc.  And if
-                * the call fails, it's no big deal, we just continue to
-                * use the standard buffer size.
-                */
-               (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
+       if (uname(&osinfo) == 0)
+               have_osinfo = 1;
+
+#ifdef __APPLE__
+       /*
+        * See comment in pcap_can_set_rfmon_bpf() for an explanation
+        * of why we check the version number.
+        */
+       if (p->opt.rfmon) {
+               if (have_osinfo) {
+                       /*
+                        * We assume osinfo.sysname is "Darwin", because
+                        * __APPLE__ is defined.  We just check the version.
+                        */
+                       if (osinfo.release[0] < '8' &&
+                           osinfo.release[1] == '.') {
+                               /*
+                                * 10.3 (Darwin 7.x) or earlier.
+                                */
+                               err = PCAP_ERROR_RFMON_NOTSUP;
+                               goto bad;
+                       }
+                       if (osinfo.release[0] == '8' &&
+                           osinfo.release[1] == '.') {
+                               /*
+                                * 10.4 (Darwin 8.x).  s/en/wlt/
+                                */
+                               if (strncmp(p->opt.source, "en", 2) != 0) {
+                                       /*
+                                        * Not an enN device; no monitor
+                                        * mode.
+                                        */
+                                       err = PCAP_ERROR_RFMON_NOTSUP;
+                                       goto bad;
+                               }
+                               wltdev = malloc(strlen(p->opt.source) + 2);
+                               if (wltdev == NULL) {
+                                       (void)snprintf(p->errbuf,
+                                           PCAP_ERRBUF_SIZE, "malloc: %s",
+                                           pcap_strerror(errno));
+                                       err = PCAP_ERROR;
+                                       goto bad;
+                               }
+                               strcpy(wltdev, "wlt");
+                               strcat(wltdev, p->opt.source + 2);
+                               free(p->opt.source);
+                               p->opt.source = wltdev;
+                       }
+                       /*
+                        * Everything else is 10.5 or later; for those,
+                        * we just open the enN device, and set the DLT.
+                        */
+               }
+       }
+#endif /* __APPLE__ */
 
-               (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
-               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
-                       break;  /* that size worked; we're done */
+       /*
+        * Set the buffer size.
+        */
+       if (p->opt.buffer_size != 0) {
+               /*
+                * A buffer size was explicitly specified; use it.
+                */
+               if (ioctl(fd, BIOCSBLEN, (caddr_t)&p->opt.buffer_size) < 0) {
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                           "BIOCSBLEN: %s: %s", p->opt.source,
+                           pcap_strerror(errno));
+                       err = PCAP_ERROR;
+                       goto bad;
+               }
 
-               if (errno != ENOBUFS) {
-                       snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
-                           device, pcap_strerror(errno));
+               /*
+                * Now bind to the device.
+                */
+               (void)strncpy(ifr.ifr_name, p->opt.source,
+                   sizeof(ifr.ifr_name));
+               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+                       err = check_setif_failure(p, errno);
                        goto bad;
                }
-       }
+       } else {
+               /*
+                * No buffer size was explicitly specified.
+                *
+                * Try finding a good size for the buffer; 32768 may
+                * be too big, so keep cutting it in half until we
+                * find a size that works, or run out of sizes to try.
+                * If the default is larger, don't make it smaller.
+                */
+               if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768)
+                       v = 32768;
+               for ( ; v != 0; v >>= 1) {
+                       /*
+                        * Ignore the return value - this is because the
+                        * call fails on BPF systems that don't have
+                        * kernel malloc.  And if the call fails, it's
+                        * no big deal, we just continue to use the
+                        * standard buffer size.
+                        */
+                       (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
 
-       if (v == 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE,
-                        "BIOCSBLEN: %s: No buffer size worked", device);
-               goto bad;
+                       (void)strncpy(ifr.ifr_name, p->opt.source,
+                           sizeof(ifr.ifr_name));
+                       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
+                               break;  /* that size worked; we're done */
+
+                       if (errno != ENOBUFS) {
+                               err = check_setif_failure(p, errno);
+                               goto bad;
+                       }
+               }
+
+               if (v == 0) {
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                           "BIOCSBLEN: %s: No buffer size worked",
+                           p->opt.source);
+                       err = PCAP_ERROR;
+                       goto bad;
+               }
        }
 
        /* Get the data link layer type. */
        if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
+
 #ifdef _AIX
        /*
         * AIX's BPF returns IFF_ types, not DLT_ types, in BIOCGDLT.
@@ -706,8 +1225,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
                /*
                 * We don't know what to map this to yet.
                 */
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
                    v);
+               err = PCAP_ERROR;
                goto bad;
        }
 #endif
@@ -732,13 +1252,6 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
                break;
        }
 #endif
-#ifdef PCAP_FDDIPAD
-       if (v == DLT_FDDI)
-               p->fddipad = PCAP_FDDIPAD;
-       else
-               p->fddipad = 0;
-#endif
-       p->linktype = v;
 
 #ifdef BIOCGDLTLIST
        /*
@@ -746,69 +1259,144 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
         * this interface supports.  If this fails with EINVAL, it's
         * not fatal; we just don't get to use the feature later.
         */
-       memset(&bdl, 0, sizeof(bdl));
-       if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) {
-               u_int i;
-               int is_ethernet;
+       if (get_dlt_list(fd, v, &bdl, p->errbuf) == -1) {
+               err = PCAP_ERROR;
+               goto bad;
+       }
+       p->dlt_count = bdl.bfl_len;
+       p->dlt_list = bdl.bfl_list;
 
-               bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * (bdl.bfl_len + 1));
-               if (bdl.bfl_list == NULL) {
-                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
-                           pcap_strerror(errno));
-                       goto bad;
+#ifdef __APPLE__
+       /*
+        * Monitor mode fun, continued.
+        *
+        * For 10.5 and, we're assuming, later releases, as noted above,
+        * 802.1 adapters that support monitor mode offer both DLT_EN10MB,
+        * DLT_IEEE802_11, and possibly some 802.11-plus-radio-information
+        * DLT_ value.  Choosing one of the 802.11 DLT_ values will turn
+        * monitor mode on.
+        *
+        * Therefore, if the user asked for monitor mode, we filter out
+        * the DLT_EN10MB value, as you can't get that in monitor mode,
+        * and, if the user didn't ask for monitor mode, we filter out
+        * the 802.11 DLT_ values, because selecting those will turn
+        * monitor mode on.  Then, for monitor mode, if an 802.11-plus-
+        * radio DLT_ value is offered, we try to select that, otherwise
+        * we try to select DLT_IEEE802_11.
+        */
+       if (have_osinfo) {
+               if (isdigit((unsigned)osinfo.release[0]) &&
+                    (osinfo.release[0] == '9' ||
+                    isdigit((unsigned)osinfo.release[1]))) {
+                       /*
+                        * 10.5 (Darwin 9.x), or later.
+                        */
+                       new_dlt = find_802_11(&bdl);
+                       if (new_dlt != -1) {
+                               /*
+                                * We have at least one 802.11 DLT_ value,
+                                * so this is an 802.11 interface.
+                                * new_dlt is the best of the 802.11
+                                * DLT_ values in the list.
+                                */
+                               if (p->opt.rfmon) {
+                                       /*
+                                        * Our caller wants monitor mode.
+                                        * Purge DLT_EN10MB from the list
+                                        * of link-layer types, as selecting
+                                        * it will keep monitor mode off.
+                                        */
+                                       remove_en(p);
+
+                                       /*
+                                        * If the new mode we want isn't
+                                        * the default mode, attempt to
+                                        * select the new mode.
+                                        */
+                                       if (new_dlt != v) {
+                                               if (ioctl(p->fd, BIOCSDLT,
+                                                   &new_dlt) != -1) {
+                                                       /*
+                                                        * We succeeded;
+                                                        * make this the
+                                                        * new DLT_ value.
+                                                        */
+                                                       v = new_dlt;
+                                               }
+                                       }
+                               } else {
+                                       /*
+                                        * Our caller doesn't want
+                                        * monitor mode.  Unless this
+                                        * is being done by pcap_open_live(),
+                                        * purge the 802.11 link-layer types
+                                        * from the list, as selecting
+                                        * one of them will turn monitor
+                                        * mode on.
+                                        */
+                                       if (!p->oldstyle)
+                                               remove_802_11(p);
+                               }
+                       } else {
+                               if (p->opt.rfmon) {
+                                       /*
+                                        * The caller requested monitor
+                                        * mode, but we have no 802.11
+                                        * link-layer types, so they
+                                        * can't have it.
+                                        */
+                                       err = PCAP_ERROR_RFMON_NOTSUP;
+                                       goto bad;
+                               }
+                       }
                }
-
-               if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) {
-                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
-                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
-                       free(bdl.bfl_list);
+       }
+#elif defined(HAVE_BSD_IEEE80211)
+       /*
+        * *BSD with the new 802.11 ioctls.
+        * Do we want monitor mode?
+        */
+       if (p->opt.rfmon) {
+               /*
+                * Try to put the interface into monitor mode.
+                */
+               err = monitor_mode(p, 1);
+               if (err != 0) {
+                       /*
+                        * We failed.
+                        */
                        goto bad;
                }
 
                /*
-                * OK, for real Ethernet devices, add DLT_DOCSIS to the
-                * list, so that an application can let you choose it,
-                * in case you're capturing DOCSIS traffic that a Cisco
-                * Cable Modem Termination System is putting out onto
-                * an Ethernet (it doesn't put an Ethernet header onto
-                * the wire, it puts raw DOCSIS frames out on the wire
-                * inside the low-level Ethernet framing).
-                *
-                * A "real Ethernet device" is defined here as a device
-                * that has a link-layer type of DLT_EN10MB and that has
-                * no alternate link-layer types; that's done to exclude
-                * 802.11 interfaces (which might or might not be the
-                * right thing to do, but I suspect it is - Ethernet <->
-                * 802.11 bridges would probably badly mishandle frames
-                * that don't have Ethernet headers).
+                * We're in monitor mode.
+                * Try to find the best 802.11 DLT_ value and, if we
+                * succeed, try to switch to that mode if we're not
+                * already in that mode.
                 */
-               if (p->linktype == DLT_EN10MB) {
-                       is_ethernet = 1;
-                       for (i = 0; i < bdl.bfl_len; i++) {
-                               if (bdl.bfl_list[i] != DLT_EN10MB) {
-                                       is_ethernet = 0;
-                                       break;
+               new_dlt = find_802_11(&bdl);
+               if (new_dlt != -1) {
+                       /*
+                        * We have at least one 802.11 DLT_ value.
+                        * new_dlt is the best of the 802.11
+                        * DLT_ values in the list.
+                        *
+                        * If the new mode we want isn't the default mode,
+                        * attempt to select the new mode.
+                        */
+                       if (new_dlt != v) {
+                               if (ioctl(p->fd, BIOCSDLT, &new_dlt) != -1) {
+                                       /*
+                                        * We succeeded; make this the
+                                        * new DLT_ value.
+                                        */
+                                       v = new_dlt;
                                }
                        }
-                       if (is_ethernet) {
-                               /*
-                                * We reserved one more slot at the end of
-                                * the list.
-                                */
-                               bdl.bfl_list[bdl.bfl_len] = DLT_DOCSIS;
-                               bdl.bfl_len++;
-                       }
-               }
-               p->dlt_count = bdl.bfl_len;
-               p->dlt_list = bdl.bfl_list;
-       } else {
-               if (errno != EINVAL) {
-                       (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
-                           "BIOCGDLTLIST: %s", pcap_strerror(errno));
-                       goto bad;
                }
        }
-#endif
+#endif /* various platforms */
+#endif /* BIOCGDLTLIST */
 
        /*
         * If this is an Ethernet device, and we don't have a DLT_ list,
@@ -818,7 +1406,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
         * some other way of determining whether it's an Ethernet or 802.11
         * device.)
         */
-       if (p->linktype == DLT_EN10MB && p->dlt_count == 0) {
+       if (v == DLT_EN10MB && p->dlt_count == 0) {
                p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
                /*
                 * If that fails, just leave the list empty.
@@ -829,6 +1417,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
                        p->dlt_count = 2;
                }
        }
+#ifdef PCAP_FDDIPAD
+       if (v == DLT_FDDI)
+               p->fddipad = PCAP_FDDIPAD;
+       else
+               p->fddipad = 0;
+#endif
+       p->linktype = v;
 
 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
        /*
@@ -841,24 +1436,26 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
         * BSDs - check CVS log for "bpf.c"?
         */
        if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
-               (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+               (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
                    "BIOCSHDRCMPLT: %s", pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
 #endif
        /* set timeout */
-       if (to_ms != 0) {
+       if (p->md.timeout != 0) {
                /*
                 * XXX - is this seconds/nanoseconds in AIX?
                 * (Treating it as such doesn't fix the timeout
                 * problem described below.)
                 */
                struct timeval to;
-               to.tv_sec = to_ms / 1000;
-               to.tv_usec = (to_ms * 1000) % 1000000;
+               to.tv_sec = p->md.timeout / 1000;
+               to.tv_usec = (p->md.timeout * 1000) % 1000000;
                if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
-                       snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
                            pcap_strerror(errno));
+                       err = PCAP_ERROR;
                        goto bad;
                }
        }
@@ -913,31 +1510,34 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
         */
        v = 1;
        if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
 #endif /* BIOCIMMEDIATE */
 #endif /* _AIX */
 
-       if (promisc) {
+       if (p->opt.promisc) {
                /* set promiscuous mode, okay if it fails */
                if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) {
-                       snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s",
+                       snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s",
                            pcap_strerror(errno));
                }
        }
 
        if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
        p->bufsize = v;
        p->buffer = (u_char *)malloc(p->bufsize);
        if (p->buffer == NULL) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
 #ifdef _AIX
@@ -958,13 +1558,14 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
        total_insn.code = (u_short)(BPF_RET | BPF_K);
        total_insn.jt = 0;
        total_insn.jf = 0;
-       total_insn.k = snaplen;
+       total_insn.k = p->snapshot;
 
        total_prog.bf_len = 1;
        total_prog.bf_insns = &total_insn;
        if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
-               snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
                    pcap_strerror(errno));
+               err = PCAP_ERROR;
                goto bad;
        }
 
@@ -1005,7 +1606,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
         * XXX - what about AIX?
         */
        p->selectable_fd = p->fd;       /* assume select() works until we know otherwise */
-       if (uname(&osinfo) == 0) {
+       if (have_osinfo) {
                /*
                 * We can check what OS this is.
                 */
@@ -1024,15 +1625,16 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
        p->getnonblock_op = pcap_getnonblock_fd;
        p->setnonblock_op = pcap_setnonblock_fd;
        p->stats_op = pcap_stats_bpf;
-       p->close_op = pcap_close_common;
+       p->close_op = pcap_close_bpf;
 
-       return (p);
+       return (0);
  bad:
        (void)close(fd);
-       if (p->dlt_list != NULL)
-               free(p->dlt_list);
-       free(p);
-       return (NULL);
+       if (p->buffer != NULL) {
+               free(p->buffer);
+               p->buffer = NULL;
+       }
+       return (err);
 }
 
 int
@@ -1046,6 +1648,303 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf)
        return (0);
 }
 
+#ifdef HAVE_BSD_IEEE80211
+static int
+monitor_mode(pcap_t *p, int set)
+{
+       int sock;
+       struct ifmediareq req;
+       int *media_list;
+       int i;
+       int can_do;
+       struct ifreq ifr;
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock == -1) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't open socket: %s",
+                   pcap_strerror(errno));
+               return (PCAP_ERROR);
+       }
+
+       memset(&req, 0, sizeof req);
+       strncpy(req.ifm_name, p->opt.source, sizeof req.ifm_name);
+
+       /*
+        * Find out how many media types we have.
+        */
+       if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
+               /*
+                * Can't get the media types.
+                */
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA: %s",
+                   pcap_strerror(errno));
+               close(sock);
+               return (PCAP_ERROR);
+       }
+       if (req.ifm_count == 0) {
+               /*
+                * No media types.
+                */
+               close(sock);
+               return (PCAP_ERROR_RFMON_NOTSUP);
+       }
+
+       /*
+        * Allocate a buffer to hold all the media types, and
+        * get the media types.
+        */
+       media_list = malloc(req.ifm_count * sizeof(int));
+       if (media_list == NULL) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+                   pcap_strerror(errno));
+               close(sock);
+               return (PCAP_ERROR);
+       }
+       req.ifm_ulist = media_list;
+       if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA: %s",
+                   pcap_strerror(errno));
+               free(media_list);
+               close(sock);
+               return (PCAP_ERROR);
+       }
+
+       /*
+        * Look for an 802.11 "automatic" media type.
+        * We assume that all 802.11 adapters have that media type,
+        * and that it will carry the monitor mode supported flag.
+        */
+       can_do = 0;
+       for (i = 0; i < req.ifm_count; i++) {
+               if (IFM_TYPE(media_list[i]) == IFM_IEEE80211
+                   && IFM_SUBTYPE(media_list[i]) == IFM_AUTO) {
+                       /* OK, does it do monitor mode? */
+                       if (media_list[i] & IFM_IEEE80211_MONITOR) {
+                               can_do = 1;
+                               break;
+                       }
+               }
+       }
+       free(media_list);
+       if (!can_do) {
+               /*
+                * This adapter doesn't support monitor mode.
+                */
+               close(sock);
+               return (PCAP_ERROR_RFMON_NOTSUP);
+       }
+
+       if (set) {
+               /*
+                * Don't just check whether we can enable monitor mode,
+                * do so, if it's not already enabled.
+                */
+               if ((req.ifm_current & IFM_IEEE80211_MONITOR) == 0) {
+                       /*
+                        * Monitor mode isn't currently on, so turn it on,
+                        * and remember that we should turn it off when the
+                        * pcap_t is closed.
+                        */
+
+                       /*
+                        * If we haven't already done so, arrange to have
+                        * "pcap_close_all()" called when we exit.
+                        */
+                       if (!pcap_do_addexit(p)) {
+                               /*
+                                * "atexit()" failed; don't put the interface
+                                * in monitor mode, just give up.
+                                */
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                                    "atexit failed");
+                               close(sock);
+                               return (PCAP_ERROR);
+                       }
+                       memset(&ifr, 0, sizeof(ifr));
+                       (void)strncpy(ifr.ifr_name, p->opt.source,
+                           sizeof(ifr.ifr_name));
+                       ifr.ifr_media = req.ifm_current | IFM_IEEE80211_MONITOR;
+                       if (ioctl(sock, SIOCSIFMEDIA, &ifr) == -1) {
+                               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                                    "SIOCSIFMEDIA: %s", pcap_strerror(errno));
+                               close(sock);
+                               return (PCAP_ERROR);
+                       }
+
+                       p->md.must_clear |= MUST_CLEAR_RFMON;
+
+                       /*
+                        * Add this to the list of pcaps to close when we exit.
+                        */
+                       pcap_add_to_pcaps_to_close(p);
+               }
+       }
+       return (0);
+}
+#endif /* HAVE_BSD_IEEE80211 */
+
+#if defined(BIOCGDLTLIST) && (defined(__APPLE__) || defined(HAVE_BSD_IEEE80211))
+/*
+ * Check whether we have any 802.11 link-layer types; return the best
+ * of the 802.11 link-layer types if we find one, and return -1
+ * otherwise.
+ *
+ * DLT_IEEE802_11_RADIO, with the radiotap header, is considered the
+ * best 802.11 link-layer type; any of the other 802.11-plus-radio
+ * headers are second-best; 802.11 with no radio information is
+ * the least good.
+ */
+static int
+find_802_11(struct bpf_dltlist *bdlp)
+{
+       int new_dlt;
+       int i;
+
+       /*
+        * Scan the list of DLT_ values, looking for 802.11 values,
+        * and, if we find any, choose the best of them.
+        */
+       new_dlt = -1;
+       for (i = 0; i < bdlp->bfl_len; i++) {
+               switch (bdlp->bfl_list[i]) {
+
+               case DLT_IEEE802_11:
+                       /*
+                        * 802.11, but no radio.
+                        *
+                        * Offer this, and select it as the new mode
+                        * unless we've already found an 802.11
+                        * header with radio information.
+                        */
+                       if (new_dlt == -1)
+                               new_dlt = bdlp->bfl_list[i];
+                       break;
+
+               case DLT_PRISM_HEADER:
+               case DLT_AIRONET_HEADER:
+               case DLT_IEEE802_11_RADIO_AVS:
+                       /*
+                        * 802.11 with radio, but not radiotap.
+                        *
+                        * Offer this, and select it as the new mode
+                        * unless we've already found the radiotap DLT_.
+                        */
+                       if (new_dlt != DLT_IEEE802_11_RADIO)
+                               new_dlt = bdlp->bfl_list[i];
+                       break;
+
+               case DLT_IEEE802_11_RADIO:
+                       /*
+                        * 802.11 with radiotap.
+                        *
+                        * Offer this, and select it as the new mode.
+                        */
+                       new_dlt = bdlp->bfl_list[i];
+                       break;
+
+               default:
+                       /*
+                        * Not 802.11.
+                        */
+                       break;
+               }
+       }
+
+       return (new_dlt);
+}
+#endif /* defined(BIOCGDLTLIST) && (defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)) */
+
+#if defined(__APPLE__) && defined(BIOCGDLTLIST)
+/*
+ * Remove DLT_EN10MB from the list of DLT_ values.
+ */
+static void
+remove_en(pcap_t *p)
+{
+       int i, j;
+
+       /*
+        * Scan the list of DLT_ values and discard DLT_EN10MB.
+        */
+       j = 0;
+       for (i = 0; i < p->dlt_count; i++) {
+               switch (p->dlt_list[i]) {
+
+               case DLT_EN10MB:
+                       /*
+                        * Don't offer this one.
+                        */
+                       continue;
+
+               default:
+                       /*
+                        * Just copy this mode over.
+                        */
+                       break;
+               }
+
+               /*
+                * Copy this DLT_ value to its new position.
+                */
+               p->dlt_list[j] = p->dlt_list[i];
+               j++;
+       }
+
+       /*
+        * Set the DLT_ count to the number of entries we copied.
+        */
+       p->dlt_count = j;
+}
+
+/*
+ * Remove DLT_EN10MB from the list of DLT_ values, and look for the
+ * best 802.11 link-layer type in that list and return it.
+ * Radiotap is better than anything else; 802.11 with any other radio
+ * header is better than 802.11 with no radio header.
+ */
+static void
+remove_802_11(pcap_t *p)
+{
+       int i, j;
+
+       /*
+        * Scan the list of DLT_ values and discard 802.11 values.
+        */
+       j = 0;
+       for (i = 0; i < p->dlt_count; i++) {
+               switch (p->dlt_list[i]) {
+
+               case DLT_IEEE802_11:
+               case DLT_PRISM_HEADER:
+               case DLT_AIRONET_HEADER:
+               case DLT_IEEE802_11_RADIO:
+               case DLT_IEEE802_11_RADIO_AVS:
+                       /*
+                        * 802.11.  Don't offer this one.
+                        */
+                       continue;
+
+               default:
+                       /*
+                        * Just copy this mode over.
+                        */
+                       break;
+               }
+
+               /*
+                * Copy this DLT_ value to its new position.
+                */
+               p->dlt_list[j] = p->dlt_list[i];
+               j++;
+       }
+
+       /*
+        * Set the DLT_ count to the number of entries we copied.
+        */
+       p->dlt_count = j;
+}
+#endif /* defined(__APPLE__) && defined(BIOCGDLTLIST) */
+
 static int
 pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp)
 {