]> The Tcpdump Group git mirrors - libpcap/blobdiff - pcap-linux.c
Merge pull request #906 from Oppen/issue_898
[libpcap] / pcap-linux.c
index 3ba8f71026212470d09108850f17c80b29e75c86..9b126a3a68ce30b639ba3902c948ce6133412a0f 100644 (file)
  */
 #include <linux/if_bonding.h>
 
-/*
- * Got Wireless Extensions?
- */
-#ifdef HAVE_LINUX_WIRELESS_H
-#include <linux/wireless.h>
-#endif /* HAVE_LINUX_WIRELESS_H */
-
 /*
  * Got libnl?
  */
@@ -213,8 +206,8 @@ struct pcap_linux {
  * Prototypes for internal functions and methods.
  */
 static int get_if_flags(const char *, bpf_u_int32 *, char *);
-static int is_wifi(int, const char *);
-static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int);
+static int is_wifi(const char *);
+static void map_arphrd_to_dlt(pcap_t *, int, const char *, int);
 static int pcap_activate_linux(pcap_t *);
 static int activate_pf_packet(pcap_t *, int);
 static int setup_mmapped(pcap_t *, int *);
@@ -310,9 +303,6 @@ static int  iface_get_id(int fd, const char *device, char *ebuf);
 static int     iface_get_mtu(int fd, const char *device, char *ebuf);
 static int     iface_get_arptype(int fd, const char *device, char *ebuf);
 static int     iface_bind(int fd, int ifindex, char *ebuf, int protocol);
-#ifdef IW_MODE_MONITOR
-static int     has_wext(int sock_fd, const char *device, char *ebuf);
-#endif /* IW_MODE_MONITOR */
 static int     enter_rfmon_mode(pcap_t *handle, int sock_fd,
     const char *device);
 #if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
@@ -338,7 +328,7 @@ pcap_create_interface(const char *device, char *ebuf)
 {
        pcap_t *handle;
 
-       handle = pcap_create_common(ebuf, sizeof (struct pcap_linux));
+       handle = PCAP_CREATE_COMMON(ebuf, struct pcap_linux);
        if (handle == NULL)
                return NULL;
 
@@ -689,169 +679,8 @@ nla_put_failure:
        nlmsg_free(msg);
        return PCAP_ERROR;
 }
-
-static int
-enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device)
-{
-       struct pcap_linux *handlep = handle->priv;
-       int ret;
-       char phydev_path[PATH_MAX+1];
-       struct nl80211_state nlstate;
-       struct ifreq ifr;
-       u_int n;
-
-       /*
-        * Is this a mac80211 device?
-        */
-       ret = get_mac80211_phydev(handle, device, phydev_path, PATH_MAX);
-       if (ret < 0)
-               return ret;     /* error */
-       if (ret == 0)
-               return 0;       /* no error, but not mac80211 device */
-
-       /*
-        * XXX - is this already a monN device?
-        * If so, we're done.
-        * Is that determined by old Wireless Extensions ioctls?
-        */
-
-       /*
-        * OK, it's apparently a mac80211 device.
-        * Try to find an unused monN device for it.
-        */
-       ret = nl80211_init(handle, &nlstate, device);
-       if (ret != 0)
-               return ret;
-       for (n = 0; n < UINT_MAX; n++) {
-               /*
-                * Try mon{n}.
-                */
-               char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */
-
-               snprintf(mondevice, sizeof mondevice, "mon%u", n);
-               ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice);
-               if (ret == 1) {
-                       /*
-                        * Success.  We don't clean up the libnl state
-                        * yet, as we'll be using it later.
-                        */
-                       goto added;
-               }
-               if (ret < 0) {
-                       /*
-                        * Hard failure.  Just return ret; handle->errbuf
-                        * has already been set.
-                        */
-                       nl80211_cleanup(&nlstate);
-                       return ret;
-               }
-       }
-
-       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-           "%s: No free monN interfaces", device);
-       nl80211_cleanup(&nlstate);
-       return PCAP_ERROR;
-
-added:
-
-#if 0
-       /*
-        * Sleep for .1 seconds.
-        */
-       delay.tv_sec = 0;
-       delay.tv_nsec = 500000000;
-       nanosleep(&delay, NULL);
-#endif
-
-       /*
-        * If we haven't already done so, arrange to have
-        * "pcap_close_all()" called when we exit.
-        */
-       if (!pcap_do_addexit(handle)) {
-               /*
-                * "atexit()" failed; don't put the interface
-                * in rfmon mode, just give up.
-                */
-               del_mon_if(handle, sock_fd, &nlstate, device,
-                   handlep->mondevice);
-               nl80211_cleanup(&nlstate);
-               return PCAP_ERROR;
-       }
-
-       /*
-        * Now configure the monitor interface up.
-        */
-       memset(&ifr, 0, sizeof(ifr));
-       pcap_strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
-       if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
-               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "%s: Can't get flags for %s", device,
-                   handlep->mondevice);
-               del_mon_if(handle, sock_fd, &nlstate, device,
-                   handlep->mondevice);
-               nl80211_cleanup(&nlstate);
-               return PCAP_ERROR;
-       }
-       ifr.ifr_flags |= IFF_UP|IFF_RUNNING;
-       if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
-               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "%s: Can't set flags for %s", device,
-                   handlep->mondevice);
-               del_mon_if(handle, sock_fd, &nlstate, device,
-                   handlep->mondevice);
-               nl80211_cleanup(&nlstate);
-               return PCAP_ERROR;
-       }
-
-       /*
-        * Success.  Clean up the libnl state.
-        */
-       nl80211_cleanup(&nlstate);
-
-       /*
-        * Note that we have to delete the monitor device when we close
-        * the handle.
-        */
-       handlep->must_do_on_close |= MUST_DELETE_MONIF;
-
-       /*
-        * Add this to the list of pcaps to close when we exit.
-        */
-       pcap_add_to_pcaps_to_close(handle);
-
-       return 1;
-}
 #endif /* HAVE_LIBNL */
 
-#ifdef IW_MODE_MONITOR
-/*
- * Bonding devices mishandle unknown ioctls; they fail with ENODEV
- * rather than ENOTSUP, EOPNOTSUPP, or ENOTTY, so Wireless Extensions
- * will fail with ENODEV if we try to do them on a bonding device,
- * making us return a "no such device" indication rather than just
- * saying "no Wireless Extensions".
- *
- * So we check for bonding devices, if we can, before trying those
- * ioctls, by trying a bonding device information query ioctl to see
- * whether it succeeds.
- */
-static int
-is_bonding_device(int fd, const char *device)
-{
-       struct ifreq ifr;
-       ifbond ifb;
-
-       memset(&ifr, 0, sizeof ifr);
-       pcap_strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
-       memset(&ifb, 0, sizeof ifb);
-       ifr.ifr_data = (caddr_t)&ifb;
-       if (ioctl(fd, SIOCBONDINFOQUERY, &ifr) == 0)
-               return 1;       /* success, so it's a bonding device */
-
-       return 0;       /* no, it's not a bonding device */
-}
-#endif /* IW_MODE_MONITOR */
-
 static int pcap_protocol(pcap_t *handle)
 {
        int protocol;
@@ -870,10 +699,6 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
        char phydev_path[PATH_MAX+1];
        int ret;
 #endif
-#ifdef IW_MODE_MONITOR
-       int sock_fd;
-       struct iwreq ireq;
-#endif
 
        if (strcmp(handle->opt.device, "any") == 0) {
                /*
@@ -889,11 +714,6 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
         * we'll just check whether the device appears to be a
         * mac80211 device and, if so, assume the device supports
         * monitor mode.
-        *
-        * wmaster devices don't appear to support the Wireless
-        * Extensions, but we can create a mon device for a
-        * wmaster device, so we don't bother checking whether
-        * a mac80211 device supports the Wireless Extensions.
         */
        ret = get_mac80211_phydev(handle, handle->opt.device, phydev_path,
            PATH_MAX);
@@ -903,51 +723,6 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
                return 1;       /* mac80211 device */
 #endif
 
-#ifdef IW_MODE_MONITOR
-       /*
-        * Bleah.  There doesn't appear to be an ioctl to use to ask
-        * whether a device supports monitor mode; we'll just do
-        * SIOCGIWMODE and, if it succeeds, assume the device supports
-        * monitor mode.
-        *
-        * Open a socket on which to attempt to get the mode.
-        * (We assume that if we have Wireless Extensions support
-        * we also have PF_PACKET support.)
-        */
-       sock_fd = socket(PF_PACKET, SOCK_RAW, pcap_protocol(handle));
-       if (sock_fd == -1) {
-               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "socket");
-               return PCAP_ERROR;
-       }
-
-       if (is_bonding_device(sock_fd, handle->opt.device)) {
-               /* It's a bonding device, so don't even try. */
-               close(sock_fd);
-               return 0;
-       }
-
-       /*
-        * Attempt to get the current mode.
-        */
-       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device,
-           sizeof ireq.ifr_ifrn.ifrn_name);
-       if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) {
-               /*
-                * Well, we got the mode; assume we can set it.
-                */
-               close(sock_fd);
-               return 1;
-       }
-       if (errno == ENODEV) {
-               /* The device doesn't even exist. */
-               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "SIOCGIWMODE failed");
-               close(sock_fd);
-               return PCAP_ERROR_NO_SUCH_DEVICE;
-       }
-       close(sock_fd);
-#endif
        return 0;
 }
 
@@ -1001,15 +776,10 @@ linux_if_drops(const char * if_name)
 static void    pcap_cleanup_linux( pcap_t *handle )
 {
        struct pcap_linux *handlep = handle->priv;
-       struct ifreq    ifr;
 #ifdef HAVE_LIBNL
        struct nl80211_state nlstate;
        int ret;
 #endif /* HAVE_LIBNL */
-#ifdef IW_MODE_MONITOR
-       int oldflags;
-       struct iwreq ireq;
-#endif /* IW_MODE_MONITOR */
 
        if (handlep->must_do_on_close != 0) {
                /*
@@ -1033,68 +803,6 @@ static void       pcap_cleanup_linux( pcap_t *handle )
                }
 #endif /* HAVE_LIBNL */
 
-#ifdef IW_MODE_MONITOR
-               if (handlep->must_do_on_close & 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.
-                        */
-
-                       /*
-                        * First, take the interface down if it's up;
-                        * otherwise, we might get EBUSY.
-                        * If we get errors, just drive on and print
-                        * a warning if we can't restore the mode.
-                        */
-                       oldflags = 0;
-                       memset(&ifr, 0, sizeof(ifr));
-                       pcap_strlcpy(ifr.ifr_name, handlep->device,
-                           sizeof(ifr.ifr_name));
-                       if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) {
-                               if (ifr.ifr_flags & IFF_UP) {
-                                       oldflags = ifr.ifr_flags;
-                                       ifr.ifr_flags &= ~IFF_UP;
-                                       if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1)
-                                               oldflags = 0;   /* didn't set, don't restore */
-                               }
-                       }
-
-                       /*
-                        * Now restore the mode.
-                        */
-                       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, handlep->device,
-                           sizeof ireq.ifr_ifrn.ifrn_name);
-                       ireq.u.mode = handlep->oldmode;
-                       if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) {
-                               /*
-                                * Scientist, you've failed.
-                                */
-                               fprintf(stderr,
-                                   "Can't restore interface %s wireless mode (SIOCSIWMODE failed: %s).\n"
-                                   "Please adjust manually.\n",
-                                   handlep->device, strerror(errno));
-                       }
-
-                       /*
-                        * Now bring the interface back up if we brought
-                        * it down.
-                        */
-                       if (oldflags != 0) {
-                               ifr.ifr_flags = oldflags;
-                               if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) {
-                                       fprintf(stderr,
-                                           "Can't bring interface %s back up (SIOCSIFFLAGS failed: %s).\n"
-                                           "Please adjust manually.\n",
-                                           handlep->device, strerror(errno));
-                               }
-                       }
-               }
-#endif /* IW_MODE_MONITOR */
-
                /*
                 * Take this pcap out of the list of pcaps for which we
                 * have to take the interface out of some mode.
@@ -1817,7 +1525,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
         * OK, what type of network is this?
         * In particular, is it wired or wireless?
         */
-       if (is_wifi(sock, name)) {
+       if (is_wifi(name)) {
                /*
                 * Wi-Fi, hence wireless.
                 */
@@ -2004,17 +1712,10 @@ pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d)
 }
 
 static int
-is_wifi(int sock_fd
-#ifndef IW_MODE_MONITOR
-_U_
-#endif
-, const char *device)
+is_wifi(const char *device)
 {
        char *pathstr;
        struct stat statb;
-#ifdef IW_MODE_MONITOR
-       char errbuf[PCAP_ERRBUF_SIZE];
-#endif
 
        /*
         * See if there's a sysfs wireless directory for it.
@@ -2032,19 +1733,6 @@ _U_
        }
        free(pathstr);
 
-#ifdef IW_MODE_MONITOR
-       /*
-        * OK, maybe it's not wireless, or maybe this kernel doesn't
-        * support sysfs.  Try the wireless extensions.
-        */
-       if (has_wext(sock_fd, device, errbuf) == 1) {
-               /*
-                * It supports the wireless extensions, so it's a Wi-Fi
-                * device.
-                */
-               return 1;
-       }
-#endif
        return 0;
 }
 
@@ -2066,7 +1754,7 @@ _U_
  *
  *  Sets the link type to -1 if unable to map the type.
  */
-static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype,
+static void map_arphrd_to_dlt(pcap_t *handle, int arptype,
                              const char *device, int cooked_ok)
 {
        static const char cdma_rmnet[] = "cdma_rmnet";
@@ -2109,7 +1797,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype,
                 * is_wifi() to check for 802.11 devices; are there any
                 * others?
                 */
-               if (!is_wifi(sock_fd, device)) {
+               if (!is_wifi(device)) {
                        int ret;
 
                        /*
@@ -2676,7 +2364,7 @@ activate_pf_packet(pcap_t *handle, int is_any_device)
                        close(sock_fd);
                        return arptype;
                }
-               map_arphrd_to_dlt(handle, sock_fd, arptype, device, 1);
+               map_arphrd_to_dlt(handle, arptype, device, 1);
                if (handle->linktype == -1 ||
                    handle->linktype == DLT_LINUX_SLL ||
                    handle->linktype == DLT_LINUX_IRDA ||
@@ -4252,6 +3940,13 @@ static int pcap_handle_packet_mmap(
         *
         * Trim the snapshot length to be no longer than the
         * specified snapshot length.
+        *
+        * XXX - an alternative is to put a filter, consisting
+        * of a "ret <snaplen>" instruction, on the socket
+        * in the activate routine, so that the truncation is
+        * done in the kernel even if nobody specified a filter;
+        * that means that less buffer space is consumed in
+        * the memory-mapped buffer.
         */
        if (pcaphdr.caplen > (bpf_u_int32)handle->snapshot)
                pcaphdr.caplen = handle->snapshot;
@@ -4782,394 +4477,83 @@ iface_bind(int fd, int ifindex, char *ebuf, int protocol)
        return 0;
 }
 
-#ifdef IW_MODE_MONITOR
-/*
- * Check whether the device supports the Wireless Extensions.
- * Returns 1 if it does, 0 if it doesn't, PCAP_ERROR_NO_SUCH_DEVICE
- * if the device doesn't even exist.
- */
-static int
-has_wext(int sock_fd, const char *device, char *ebuf)
-{
-       struct iwreq ireq;
-       int ret;
-
-       if (is_bonding_device(sock_fd, device))
-               return 0;       /* bonding device, so don't even try */
-
-       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-           sizeof ireq.ifr_ifrn.ifrn_name);
-       if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0)
-               return 1;       /* yes */
-       if (errno == ENODEV)
-               ret = PCAP_ERROR_NO_SUCH_DEVICE;
-       else
-               ret = 0;
-       pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno,
-           "%s: SIOCGIWNAME", device);
-       return ret;
-}
-
-/*
- * Per me si va ne la citta dolente,
- * Per me si va ne l'etterno dolore,
- *     ...
- * Lasciate ogne speranza, voi ch'intrate.
- *
- * XXX - airmon-ng does special stuff with the Orinoco driver and the
- * wlan-ng driver.
- */
-typedef enum {
-       MONITOR_WEXT,
-       MONITOR_HOSTAP,
-       MONITOR_PRISM,
-       MONITOR_PRISM54,
-       MONITOR_ACX100,
-       MONITOR_RT2500,
-       MONITOR_RT2570,
-       MONITOR_RT73,
-       MONITOR_RTL8XXX
-} monitor_type;
-
 /*
- * Use the Wireless Extensions, if we have them, to try to turn monitor mode
- * on if it's not already on.
- *
- * Returns 1 on success, 0 if we don't support the Wireless Extensions
- * on this device, or a PCAP_ERROR_ value if we do support them but
- * we weren't able to turn monitor mode on.
+ * Try to enter monitor mode.
+ * If we have libnl, try to create a new monitor-mode device and
+ * capture on that; otherwise, just say "not supported".
  */
+#ifdef HAVE_LIBNL
 static int
-enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
+enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
 {
-       /*
-        * XXX - at least some adapters require non-Wireless Extensions
-        * mechanisms to turn monitor mode on.
-        *
-        * Atheros cards might require that a separate "monitor virtual access
-        * point" be created, with later versions of the madwifi driver.
-        * airmon-ng does
-        *
-        *    wlanconfig ath create wlandev {if_name} wlanmode monitor -bssid
-        *
-        * which apparently spits out a line "athN" where "athN" is the
-        * monitor mode device.  To leave monitor mode, it destroys the
-        * monitor mode device.
-        *
-        * Some Intel Centrino adapters might require private ioctls to get
-        * radio headers; the ipw2200 and ipw3945 drivers allow you to
-        * configure a separate "rtapN" interface to capture in monitor
-        * mode without preventing the adapter from operating normally.
-        * (airmon-ng doesn't appear to use that, though.)
-        *
-        * It would be Truly Wonderful if mac80211 and nl80211 cleaned this
-        * up, and if all drivers were converted to mac80211 drivers.
-        *
-        * If interface {if_name} is a mac80211 driver, the file
-        * /sys/class/net/{if_name}/phy80211 is a symlink to
-        * /sys/class/ieee80211/{phydev_name}, for some {phydev_name}.
-        *
-        * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at
-        * least, has a "wmaster0" device and a "wlan0" device; the
-        * latter is the one with the IP address.  Both show up in
-        * "tcpdump -D" output.  Capturing on the wmaster0 device
-        * captures with 802.11 headers.
-        *
-        * airmon-ng searches through /sys/class/net for devices named
-        * monN, starting with mon0; as soon as one *doesn't* exist,
-        * it chooses that as the monitor device name.  If the "iw"
-        * command exists, it does
-        *
-        *    iw dev {if_name} interface add {monif_name} type monitor"
-        *
-        * where {monif_name} is the monitor device.  It then (sigh) sleeps
-        * .1 second, and then configures the device up.  Otherwise, if
-        * /sys/class/ieee80211/{phydev_name}/add_iface is a file, it writes
-        * {mondev_name}, without a newline, to that file, and again (sigh)
-        * sleeps .1 second, and then iwconfig's that device into monitor
-        * mode and configures it up.  Otherwise, you can't do monitor mode.
-        *
-        * All these devices are "glued" together by having the
-        * /sys/class/net/{device_name}/phy80211 links pointing to the same
-        * place, so, given a wmaster, wlan, or mon device, you can
-        * find the other devices by looking for devices with
-        * the same phy80211 link.
-        *
-        * To turn monitor mode off, delete the monitor interface,
-        * either with
-        *
-        *    iw dev {monif_name} interface del
-        *
-        * or by sending {monif_name}, with no NL, down
-        * /sys/class/ieee80211/{phydev_name}/remove_iface
-        *
-        * Note: if you try to create a monitor device named "monN", and
-        * there's already a "monN" device, it fails, as least with
-        * the netlink interface (which is what iw uses), with a return
-        * value of -ENFILE.  (Return values are negative errnos.)  We
-        * could probably use that to find an unused device.
-        */
        struct pcap_linux *handlep = handle->priv;
-       int err;
-       struct iwreq ireq;
-       struct iw_priv_args *priv;
-       monitor_type montype;
-       int i;
-       __u32 cmd;
+       int ret;
+       char phydev_path[PATH_MAX+1];
+       struct nl80211_state nlstate;
        struct ifreq ifr;
-       int oldflags;
-       int args[2];
-       int channel;
+       u_int n;
 
        /*
-        * Does this device *support* the Wireless Extensions?
+        * Is this a mac80211 device?
         */
-       err = has_wext(sock_fd, device, handle->errbuf);
-       if (err <= 0)
-               return err;     /* either it doesn't or the device doesn't even exist */
+       ret = get_mac80211_phydev(handle, device, phydev_path, PATH_MAX);
+       if (ret < 0)
+               return ret;     /* error */
+       if (ret == 0)
+               return 0;       /* no error, but not mac80211 device */
+
        /*
-        * Start out assuming we have no private extensions to control
-        * radio metadata.
+        * XXX - is this already a monN device?
+        * If so, we're done.
         */
-       montype = MONITOR_WEXT;
-       cmd = 0;
 
        /*
-        * Try to get all the Wireless Extensions private ioctls
-        * supported by this device.
-        *
-        * First, get the size of the buffer we need, by supplying no
-        * buffer and a length of 0.  If the device supports private
-        * ioctls, it should return E2BIG, with ireq.u.data.length set
-        * to the length we need.  If it doesn't support them, it should
-        * return EOPNOTSUPP.
+        * OK, it's apparently a mac80211 device.
+        * Try to find an unused monN device for it.
         */
-       memset(&ireq, 0, sizeof ireq);
-       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-           sizeof ireq.ifr_ifrn.ifrn_name);
-       ireq.u.data.pointer = (void *)args;
-       ireq.u.data.length = 0;
-       ireq.u.data.flags = 0;
-       if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) != -1) {
-               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   "%s: SIOCGIWPRIV with a zero-length buffer didn't fail!",
-                   device);
-               return PCAP_ERROR;
-       }
-       if (errno != EOPNOTSUPP) {
+       ret = nl80211_init(handle, &nlstate, device);
+       if (ret != 0)
+               return ret;
+       for (n = 0; n < UINT_MAX; n++) {
                /*
-                * OK, it's not as if there are no private ioctls.
+                * Try mon{n}.
                 */
-               if (errno != E2BIG) {
+               char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */
+
+               snprintf(mondevice, sizeof mondevice, "mon%u", n);
+               ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice);
+               if (ret == 1) {
                        /*
-                        * Failed.
+                        * Success.  We don't clean up the libnl state
+                        * yet, as we'll be using it later.
                         */
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWPRIV", device);
-                       return PCAP_ERROR;
-               }
-
-               /*
-                * OK, try to get the list of private ioctls.
-                */
-               priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args));
-               if (priv == NULL) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "malloc");
-                       return PCAP_ERROR;
-               }
-               ireq.u.data.pointer = (void *)priv;
-               if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWPRIV", device);
-                       free(priv);
-                       return PCAP_ERROR;
+                       goto added;
                }
-
-               /*
-                * Look for private ioctls to turn monitor mode on or, if
-                * monitor mode is on, to set the header type.
-                */
-               for (i = 0; i < ireq.u.data.length; i++) {
-                       if (strcmp(priv[i].name, "monitor_type") == 0) {
-                               /*
-                                * Hostap driver, use this one.
-                                * Set monitor mode first.
-                                * You can set it to 0 to get DLT_IEEE80211,
-                                * 1 to get DLT_PRISM, 2 to get
-                                * DLT_IEEE80211_RADIO_AVS, and, with more
-                                * recent versions of the driver, 3 to get
-                                * DLT_IEEE80211_RADIO.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                                       break;
-                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                                       break;
-                               montype = MONITOR_HOSTAP;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "set_prismhdr") == 0) {
-                               /*
-                                * Prism54 driver, use this one.
-                                * Set monitor mode first.
-                                * You can set it to 2 to get DLT_IEEE80211
-                                * or 3 or get DLT_PRISM.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                                       break;
-                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                                       break;
-                               montype = MONITOR_PRISM54;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "forceprismheader") == 0) {
-                               /*
-                                * RT2570 driver, use this one.
-                                * Do this after turning monitor mode on.
-                                * You can set it to 1 to get DLT_PRISM or 2
-                                * to get DLT_IEEE80211.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                                       break;
-                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                                       break;
-                               montype = MONITOR_RT2570;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "forceprism") == 0) {
-                               /*
-                                * RT73 driver, use this one.
-                                * Do this after turning monitor mode on.
-                                * Its argument is a *string*; you can
-                                * set it to "1" to get DLT_PRISM or "2"
-                                * to get DLT_IEEE80211.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR)
-                                       break;
-                               if (priv[i].set_args & IW_PRIV_SIZE_FIXED)
-                                       break;
-                               montype = MONITOR_RT73;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "prismhdr") == 0) {
-                               /*
-                                * One of the RTL8xxx drivers, use this one.
-                                * It can only be done after monitor mode
-                                * has been turned on.  You can set it to 1
-                                * to get DLT_PRISM or 0 to get DLT_IEEE80211.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                                       break;
-                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1)
-                                       break;
-                               montype = MONITOR_RTL8XXX;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "rfmontx") == 0) {
-                               /*
-                                * RT2500 or RT61 driver, use this one.
-                                * It has one one-byte parameter; set
-                                * u.data.length to 1 and u.data.pointer to
-                                * point to the parameter.
-                                * It doesn't itself turn monitor mode on.
-                                * You can set it to 1 to allow transmitting
-                                * in monitor mode(?) and get DLT_IEEE80211,
-                                * or set it to 0 to disallow transmitting in
-                                * monitor mode(?) and get DLT_PRISM.
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2)
-                                       break;
-                               montype = MONITOR_RT2500;
-                               cmd = priv[i].cmd;
-                               break;
-                       }
-                       if (strcmp(priv[i].name, "monitor") == 0) {
-                               /*
-                                * Either ACX100 or hostap, use this one.
-                                * It turns monitor mode on.
-                                * If it takes two arguments, it's ACX100;
-                                * the first argument is 1 for DLT_PRISM
-                                * or 2 for DLT_IEEE80211, and the second
-                                * argument is the channel on which to
-                                * run.  If it takes one argument, it's
-                                * HostAP, and the argument is 2 for
-                                * DLT_IEEE80211 and 3 for DLT_PRISM.
-                                *
-                                * If we see this, we don't quit, as this
-                                * might be a version of the hostap driver
-                                * that also supports "monitor_type".
-                                */
-                               if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT)
-                                       break;
-                               if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED))
-                                       break;
-                               switch (priv[i].set_args & IW_PRIV_SIZE_MASK) {
-
-                               case 1:
-                                       montype = MONITOR_PRISM;
-                                       cmd = priv[i].cmd;
-                                       break;
-
-                               case 2:
-                                       montype = MONITOR_ACX100;
-                                       cmd = priv[i].cmd;
-                                       break;
-
-                               default:
-                                       break;
-                               }
-                       }
+               if (ret < 0) {
+                       /*
+                        * Hard failure.  Just return ret; handle->errbuf
+                        * has already been set.
+                        */
+                       nl80211_cleanup(&nlstate);
+                       return ret;
                }
-               free(priv);
        }
 
-       /*
-        * XXX - ipw3945?  islism?
-        */
+       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+           "%s: No free monN interfaces", device);
+       nl80211_cleanup(&nlstate);
+       return PCAP_ERROR;
 
-       /*
-        * Get the old mode.
-        */
-       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-           sizeof ireq.ifr_ifrn.ifrn_name);
-       if (ioctl(sock_fd, SIOCGIWMODE, &ireq) == -1) {
-               /*
-                * We probably won't be able to set the mode, either.
-                */
-               return PCAP_ERROR_RFMON_NOTSUP;
-       }
+added:
 
+#if 0
        /*
-        * Is it currently in monitor mode?
-        */
-       if (ireq.u.mode == IW_MODE_MONITOR) {
-               /*
-                * Yes.  Just leave things as they are.
-                * We don't offer multiple link-layer types, as
-                * changing the link-layer type out from under
-                * somebody else capturing in monitor mode would
-                * be considered rude.
-                */
-               return 1;
-       }
-       /*
-        * No.  We have to put the adapter into rfmon mode.
+        * Sleep for .1 seconds.
         */
+       delay.tv_sec = 0;
+       delay.tv_nsec = 500000000;
+       nanosleep(&delay, NULL);
+#endif
 
        /*
         * If we haven't already done so, arrange to have
@@ -5180,277 +4564,47 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
                 * "atexit()" failed; don't put the interface
                 * in rfmon mode, just give up.
                 */
-               return PCAP_ERROR_RFMON_NOTSUP;
-       }
-
-       /*
-        * Save the old mode.
-        */
-       handlep->oldmode = ireq.u.mode;
-
-       /*
-        * Put the adapter in rfmon mode.  How we do this depends
-        * on whether we have a special private ioctl or not.
-        */
-       if (montype == MONITOR_PRISM) {
-               /*
-                * We have the "monitor" private ioctl, but none of
-                * the other private ioctls.  Use this, and select
-                * the Prism header.
-                *
-                * If it fails, just fall back on SIOCSIWMODE.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               ireq.u.data.length = 1; /* 1 argument */
-               args[0] = 3;    /* request Prism header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               if (ioctl(sock_fd, cmd, &ireq) != -1) {
-                       /*
-                        * Success.
-                        * Note that we have to put the old mode back
-                        * when we close the device.
-                        */
-                       handlep->must_do_on_close |= MUST_CLEAR_RFMON;
-
-                       /*
-                        * Add this to the list of pcaps to close
-                        * when we exit.
-                        */
-                       pcap_add_to_pcaps_to_close(handle);
-
-                       return 1;
-               }
-
-               /*
-                * Failure.  Fall back on SIOCSIWMODE.
-                */
+               del_mon_if(handle, sock_fd, &nlstate, device,
+                   handlep->mondevice);
+               nl80211_cleanup(&nlstate);
+               return PCAP_ERROR;
        }
 
        /*
-        * First, take the interface down if it's up; otherwise, we
-        * might get EBUSY.
+        * Now configure the monitor interface up.
         */
        memset(&ifr, 0, sizeof(ifr));
-       pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+       pcap_strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
        if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
                pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
-                   errno, "%s: Can't get flags", device);
+                   errno, "%s: Can't get flags for %s", device,
+                   handlep->mondevice);
+               del_mon_if(handle, sock_fd, &nlstate, device,
+                   handlep->mondevice);
+               nl80211_cleanup(&nlstate);
                return PCAP_ERROR;
        }
-       oldflags = 0;
-       if (ifr.ifr_flags & IFF_UP) {
-               oldflags = ifr.ifr_flags;
-               ifr.ifr_flags &= ~IFF_UP;
-               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags",
-                           device);
-                       return PCAP_ERROR;
-               }
-       }
-
-       /*
-        * Then turn monitor mode on.
-        */
-       pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-           sizeof ireq.ifr_ifrn.ifrn_name);
-       ireq.u.mode = IW_MODE_MONITOR;
-       if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) {
-               /*
-                * Scientist, you've failed.
-                * Bring the interface back up if we shut it down.
-                */
-               ifr.ifr_flags = oldflags;
-               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags",
-                           device);
-                       return PCAP_ERROR;
-               }
-               return PCAP_ERROR_RFMON_NOTSUP;
-       }
-
-       /*
-        * XXX - airmon-ng does "iwconfig {if_name} key off" after setting
-        * monitor mode and setting the channel, and then does
-        * "iwconfig up".
-        */
-
-       /*
-        * Now select the appropriate radio header.
-        */
-       switch (montype) {
-
-       case MONITOR_WEXT:
-               /*
-                * We don't have any private ioctl to set the header.
-                */
-               break;
-
-       case MONITOR_HOSTAP:
-               /*
-                * Try to select the radiotap header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 3;    /* request radiotap header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               if (ioctl(sock_fd, cmd, &ireq) != -1)
-                       break;  /* success */
-
-               /*
-                * That failed.  Try to select the AVS header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 2;    /* request AVS header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               if (ioctl(sock_fd, cmd, &ireq) != -1)
-                       break;  /* success */
-
-               /*
-                * That failed.  Try to select the Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 1;    /* request Prism header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_PRISM:
-               /*
-                * The private ioctl failed.
-                */
-               break;
-
-       case MONITOR_PRISM54:
-               /*
-                * Select the Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 3;    /* request Prism header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_ACX100:
-               /*
-                * Get the current channel.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWFREQ", device);
-                       return PCAP_ERROR;
-               }
-               channel = ireq.u.freq.m;
-
-               /*
-                * Select the Prism header, and set the channel to the
-                * current value.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 1;            /* request Prism header */
-               args[1] = channel;      /* set channel */
-               memcpy(ireq.u.name, args, 2*sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_RT2500:
-               /*
-                * Disallow transmission - that turns on the
-                * Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 0;    /* disallow transmitting */
-               memcpy(ireq.u.name, args, sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_RT2570:
-               /*
-                * Force the Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 1;    /* request Prism header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_RT73:
-               /*
-                * Force the Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               ireq.u.data.length = 1; /* 1 argument */
-               ireq.u.data.pointer = "1";
-               ireq.u.data.flags = 0;
-               ioctl(sock_fd, cmd, &ireq);
-               break;
-
-       case MONITOR_RTL8XXX:
-               /*
-                * Force the Prism header.
-                */
-               memset(&ireq, 0, sizeof ireq);
-               pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
-                   sizeof ireq.ifr_ifrn.ifrn_name);
-               args[0] = 1;    /* request Prism header */
-               memcpy(ireq.u.name, args, sizeof (int));
-               ioctl(sock_fd, cmd, &ireq);
-               break;
+       ifr.ifr_flags |= IFF_UP|IFF_RUNNING;
+       if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
+               pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+                   errno, "%s: Can't set flags for %s", device,
+                   handlep->mondevice);
+               del_mon_if(handle, sock_fd, &nlstate, device,
+                   handlep->mondevice);
+               nl80211_cleanup(&nlstate);
+               return PCAP_ERROR;
        }
 
        /*
-        * Now bring the interface back up if we brought it down.
+        * Success.  Clean up the libnl state.
         */
-       if (oldflags != 0) {
-               ifr.ifr_flags = oldflags;
-               if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
-                       pcap_fmt_errmsg_for_errno(handle->errbuf,
-                           PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags",
-                           device);
-
-                       /*
-                        * At least try to restore the old mode on the
-                        * interface.
-                        */
-                       if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) {
-                               /*
-                                * Scientist, you've failed.
-                                */
-                               fprintf(stderr,
-                                   "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n"
-                                   "Please adjust manually.\n",
-                                   strerror(errno));
-                       }
-                       return PCAP_ERROR;
-               }
-       }
+       nl80211_cleanup(&nlstate);
 
        /*
-        * Note that we have to put the old mode back when we
-        * close the device.
+        * Note that we have to delete the monitor device when we close
+        * the handle.
         */
-       handlep->must_do_on_close |= MUST_CLEAR_RFMON;
+       handlep->must_do_on_close |= MUST_DELETE_MONIF;
 
        /*
         * Add this to the list of pcaps to close when we exit.
@@ -5459,41 +4613,16 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
 
        return 1;
 }
-#endif /* IW_MODE_MONITOR */
-
-/*
- * Try various mechanisms to enter monitor mode.
- */
+#else /* HAVE_LIBNL */
 static int
-enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
+enter_rfmon_mode(pcap_t *handle _U_, int sock_fd _U_, const char *device _U_)
 {
-#if defined(HAVE_LIBNL) || defined(IW_MODE_MONITOR)
-       int ret;
-#endif
-
-#ifdef HAVE_LIBNL
-       ret = enter_rfmon_mode_mac80211(handle, sock_fd, device);
-       if (ret < 0)
-               return ret;     /* error attempting to do so */
-       if (ret == 1)
-               return 1;       /* success */
-#endif /* HAVE_LIBNL */
-
-#ifdef IW_MODE_MONITOR
-       ret = enter_rfmon_mode_wext(handle, sock_fd, device);
-       if (ret < 0)
-               return ret;     /* error attempting to do so */
-       if (ret == 1)
-               return 1;       /* success */
-#endif /* IW_MODE_MONITOR */
-
        /*
-        * Either none of the mechanisms we know about work or none
-        * of those mechanisms are available, so we can't do monitor
-        * mode.
+        * We don't have libnl, so we can't do monitor mode.
         */
        return 0;
 }
+#endif /* HAVE_LIBNL */
 
 #if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
 /*