|
|
|
@ -30,6 +30,39 @@
|
|
|
|
|
* based on previous works of:
|
|
|
|
|
* Simon Patarin <patarin@cs.unibo.it>
|
|
|
|
|
* Phil Wood <cpw@lanl.gov>
|
|
|
|
|
*
|
|
|
|
|
* Monitor-mode support for mac80211 includes code taken from the iw
|
|
|
|
|
* command; the copyright notice for that code is
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2007, 2008 Johannes Berg
|
|
|
|
|
* Copyright (c) 2007 Andy Lutomirski
|
|
|
|
|
* Copyright (c) 2007 Mike Kershaw
|
|
|
|
|
* Copyright (c) 2008 Gábor Stefanik
|
|
|
|
|
*
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
|
* are met:
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
@ -80,15 +113,19 @@ static const char rcsid[] _U_ =
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
@ -104,7 +141,21 @@ static const char rcsid[] _U_ =
|
|
|
|
|
*/
|
|
|
|
|
#ifdef HAVE_LINUX_WIRELESS_H
|
|
|
|
|
#include <linux/wireless.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Got libnl?
|
|
|
|
|
*/
|
|
|
|
|
#ifdef HAVE_LIBNL
|
|
|
|
|
#include <linux/nl80211.h>
|
|
|
|
|
|
|
|
|
|
#include <netlink/genl/genl.h>
|
|
|
|
|
#include <netlink/genl/family.h>
|
|
|
|
|
#include <netlink/genl/ctrl.h>
|
|
|
|
|
#include <netlink/msg.h>
|
|
|
|
|
#include <netlink/attr.h>
|
|
|
|
|
#endif /* HAVE_LIBNL */
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_LINUX_WIRELESS_H */
|
|
|
|
|
|
|
|
|
|
#include "pcap-int.h"
|
|
|
|
|
#include "pcap/sll.h"
|
|
|
|
@ -271,7 +322,7 @@ static int iface_bind(int fd, int ifindex, char *ebuf);
|
|
|
|
|
#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_wext(pcap_t *handle, int sock_fd,
|
|
|
|
|
static int enter_rfmon_mode(pcap_t *handle, int sock_fd,
|
|
|
|
|
const char *device);
|
|
|
|
|
#endif /* HAVE_PF_PACKET_SOCKETS */
|
|
|
|
|
static int iface_bind_old(int fd, const char *device, char *ebuf);
|
|
|
|
@ -357,6 +408,10 @@ pcap_can_set_rfmon_linux(pcap_t *p)
|
|
|
|
|
* 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.)
|
|
|
|
|
*
|
|
|
|
|
* This also presumes that the mac80211 framework supports the
|
|
|
|
|
* Wireless Extensions, which appears to be the case at least
|
|
|
|
|
* as far back as the 2.6.22.6 kernel.
|
|
|
|
|
*/
|
|
|
|
|
sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
|
|
|
|
if (sock_fd == -1) {
|
|
|
|
@ -388,6 +443,225 @@ pcap_can_set_rfmon_linux(pcap_t *p)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(IW_MODE_MONITOR) && defined(HAVE_LIBNL)
|
|
|
|
|
|
|
|
|
|
struct nl80211_state {
|
|
|
|
|
struct nl_handle *nl_handle;
|
|
|
|
|
struct nl_cache *nl_cache;
|
|
|
|
|
struct genl_family *nl80211;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
|
|
|
|
|
{
|
|
|
|
|
state->nl_handle = nl_handle_alloc();
|
|
|
|
|
if (!state->nl_handle) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: failed to allocate netlink handle", device);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (genl_connect(state->nl_handle)) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: failed to connect to generic netlink", device);
|
|
|
|
|
goto out_handle_destroy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state->nl_cache = genl_ctrl_alloc_cache(state->nl_handle);
|
|
|
|
|
if (!state->nl_cache) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: failed to allocate generic netlink cache", device);
|
|
|
|
|
goto out_handle_destroy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
|
|
|
|
|
if (!state->nl80211) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl80211 not found", device);
|
|
|
|
|
goto out_cache_free;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_cache_free:
|
|
|
|
|
nl_cache_free(state->nl_cache);
|
|
|
|
|
out_handle_destroy:
|
|
|
|
|
nl_handle_destroy(state->nl_handle);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nl80211_cleanup(struct nl80211_state *state)
|
|
|
|
|
{
|
|
|
|
|
genl_family_put(state->nl80211);
|
|
|
|
|
nl_cache_free(state->nl_cache);
|
|
|
|
|
nl_handle_destroy(state->nl_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
|
|
|
|
|
const char *device, const char *mondevice)
|
|
|
|
|
{
|
|
|
|
|
int ifindex;
|
|
|
|
|
struct nl_msg *msg;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ifindex = iface_get_id(sock_fd, device, handle->errbuf);
|
|
|
|
|
if (ifindex == -1)
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
|
|
|
|
|
msg = nlmsg_alloc();
|
|
|
|
|
if (!msg) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: failed to allocate netlink msg", device);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
|
|
|
|
|
0, NL80211_CMD_NEW_INTERFACE, 0);
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
|
|
|
|
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
|
|
|
|
|
|
|
|
|
|
err = nl_send_auto_complete(state->nl_handle, msg);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err == -ENFILE) {
|
|
|
|
|
/*
|
|
|
|
|
* Device not available; our caller should just
|
|
|
|
|
* keep trying.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Real failure, not just "that device is not
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_send_auto_complete failed adding %s interface: %s",
|
|
|
|
|
device, mondevice, strerror(-err));
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err = nl_wait_for_ack(state->nl_handle);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err == -ENFILE) {
|
|
|
|
|
/*
|
|
|
|
|
* Device not available; our caller should just
|
|
|
|
|
* keep trying.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Real failure, not just "that device is not
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_wait_for_ack failed adding %s interface: %s",
|
|
|
|
|
device, mondevice, strerror(-err));
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Success.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_put failed adding %s interface",
|
|
|
|
|
device, mondevice);
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
|
|
|
|
|
const char *device, const char *mondevice)
|
|
|
|
|
{
|
|
|
|
|
int ifindex;
|
|
|
|
|
struct nl_msg *msg;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ifindex = iface_get_id(sock_fd, mondevice, handle->errbuf);
|
|
|
|
|
if (ifindex == -1)
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
|
|
|
|
|
msg = nlmsg_alloc();
|
|
|
|
|
if (!msg) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: failed to allocate netlink msg", device);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
|
|
|
|
|
0, NL80211_CMD_DEL_INTERFACE, 0);
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
|
|
|
|
|
|
|
|
|
err = nl_send_auto_complete(state->nl_handle, msg);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err == -ENFILE) {
|
|
|
|
|
/*
|
|
|
|
|
* Device not available; our caller should just
|
|
|
|
|
* keep trying.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Real failure, not just "that device is not
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_send_auto_complete failed deleting %s interface: %s",
|
|
|
|
|
device, mondevice, strerror(-err));
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err = nl_wait_for_ack(state->nl_handle);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err == -ENFILE) {
|
|
|
|
|
/*
|
|
|
|
|
* Device not available; our caller should just
|
|
|
|
|
* keep trying.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Real failure, not just "that device is not
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_wait_for_ack failed adding %s interface: %s",
|
|
|
|
|
device, mondevice, strerror(-err));
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Success.
|
|
|
|
|
*/
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: nl_put failed deleting %s interface",
|
|
|
|
|
device, mondevice);
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* defined(IW_MODE_MONITOR) && defined(HAVE_LIBNL) */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* With older kernels promiscuous mode is kind of interesting because we
|
|
|
|
|
* have to reset the interface before exiting. The problem can't really
|
|
|
|
@ -404,15 +678,19 @@ static void pcap_cleanup_linux( pcap_t *handle )
|
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
#ifdef IW_MODE_MONITOR
|
|
|
|
|
#ifdef HAVE_LIBNL
|
|
|
|
|
struct nl80211_state nlstate;
|
|
|
|
|
int ret;
|
|
|
|
|
#endif /* HAVE_LIBNL */
|
|
|
|
|
struct iwreq ireq;
|
|
|
|
|
#endif
|
|
|
|
|
#endif /* IW_MODE_MONITOR */
|
|
|
|
|
|
|
|
|
|
if (handle->md.must_clear != 0) {
|
|
|
|
|
if (handle->md.must_do_on_close != 0) {
|
|
|
|
|
/*
|
|
|
|
|
* There's something we have to do when closing this
|
|
|
|
|
* pcap_t.
|
|
|
|
|
*/
|
|
|
|
|
if (handle->md.must_clear & MUST_CLEAR_PROMISC) {
|
|
|
|
|
if (handle->md.must_do_on_close & MUST_CLEAR_PROMISC) {
|
|
|
|
|
/*
|
|
|
|
|
* We put the interface into promiscuous mode;
|
|
|
|
|
* take it out of promiscuous mode.
|
|
|
|
@ -451,7 +729,24 @@ static void pcap_cleanup_linux( pcap_t *handle )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef IW_MODE_MONITOR
|
|
|
|
|
if (handle->md.must_clear & MUST_CLEAR_RFMON) {
|
|
|
|
|
#ifdef HAVE_LIBNL
|
|
|
|
|
if (handle->md.must_do_on_close & MUST_DELETE_MONIF) {
|
|
|
|
|
ret = nl80211_init(handle, &nlstate, handle->md.device);
|
|
|
|
|
if (ret >= 0) {
|
|
|
|
|
ret = del_mon_if(handle, handle->fd, &nlstate,
|
|
|
|
|
handle->md.device, handle->md.mondevice);
|
|
|
|
|
nl80211_cleanup(&nlstate);
|
|
|
|
|
}
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"Can't delete monitor interface %s (%s).\n"
|
|
|
|
|
"Please delete manually.\n",
|
|
|
|
|
handle->md.mondevice, handle->errbuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_LIBNL */
|
|
|
|
|
|
|
|
|
|
if (handle->md.must_do_on_close & MUST_CLEAR_RFMON) {
|
|
|
|
|
/*
|
|
|
|
|
* We put the interface into rfmon mode;
|
|
|
|
|
* take it out of rfmon mode.
|
|
|
|
@ -475,7 +770,7 @@ static void pcap_cleanup_linux( pcap_t *handle )
|
|
|
|
|
strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#endif /* IW_MODE_MONITOR */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Take this pcap out of the list of pcaps for which we
|
|
|
|
@ -484,6 +779,10 @@ static void pcap_cleanup_linux( pcap_t *handle )
|
|
|
|
|
pcap_remove_from_pcaps_to_close(handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (handle->md.mondevice != NULL) {
|
|
|
|
|
free(handle->md.mondevice);
|
|
|
|
|
handle->md.mondevice = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (handle->md.device != NULL) {
|
|
|
|
|
free(handle->md.device);
|
|
|
|
|
handle->md.device = NULL;
|
|
|
|
@ -1745,7 +2044,7 @@ activate_new(pcap_t *handle)
|
|
|
|
|
* because entering monitor mode could change
|
|
|
|
|
* the link-layer type.
|
|
|
|
|
*/
|
|
|
|
|
err = enter_rfmon_mode_wext(handle, sock_fd, device);
|
|
|
|
|
err = enter_rfmon_mode(handle, sock_fd, device);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
/* Hard failure */
|
|
|
|
|
close(sock_fd);
|
|
|
|
@ -1759,6 +2058,15 @@ activate_new(pcap_t *handle)
|
|
|
|
|
close(sock_fd);
|
|
|
|
|
return PCAP_ERROR_RFMON_NOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Either monitor mode has been turned on for
|
|
|
|
|
* the device, or we've been given a different
|
|
|
|
|
* device to open for monitor mode. If we've
|
|
|
|
|
* been given a different device, use it.
|
|
|
|
|
*/
|
|
|
|
|
if (handle->md.mondevice != NULL)
|
|
|
|
|
device = handle->md.mondevice;
|
|
|
|
|
}
|
|
|
|
|
arptype = iface_get_arptype(sock_fd, device, handle->errbuf);
|
|
|
|
|
if (arptype < 0) {
|
|
|
|
@ -2624,7 +2932,6 @@ has_wext(int sock_fd, const char *device, char *ebuf)
|
|
|
|
|
return PCAP_ERROR_NO_SUCH_DEVICE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Per me si va ne la citta dolente,
|
|
|
|
@ -2647,6 +2954,207 @@ typedef enum {
|
|
|
|
|
MONITOR_RTL8XXX
|
|
|
|
|
} monitor_type;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* If interface {if} is a mac80211 driver, the file
|
|
|
|
|
* /sys/class/net/{if}/phy80211 is a symlink to
|
|
|
|
|
* /sys/class/ieee80211/{phydev}, for some {phydev}.
|
|
|
|
|
*
|
|
|
|
|
* 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} interface add {monif}
|
|
|
|
|
* type monitor", where {monif} is the monitor device. It
|
|
|
|
|
* then (sigh) sleeps .1 second, and then configures the
|
|
|
|
|
* device up. Otherwise, if /sys/class/ieee80211/{phydev}/add_iface
|
|
|
|
|
* is a file, it writes {mondev}, 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}/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} interface del" or by sending
|
|
|
|
|
* {monif}, with no NL, down /sys/class/ieee80211/{phydev}/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.
|
|
|
|
|
*
|
|
|
|
|
* Yes, you can have multiple monitor devices for a given
|
|
|
|
|
* physical device.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBNL
|
|
|
|
|
/*
|
|
|
|
|
* Is this a mac80211 device? If so, fill in the physical device path and
|
|
|
|
|
* return 1; if not, return 0. On an error, fill in handle->errbuf and
|
|
|
|
|
* return PCAP_ERROR.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
|
|
|
|
|
size_t phydev_max_pathlen)
|
|
|
|
|
{
|
|
|
|
|
char *pathstr;
|
|
|
|
|
ssize_t bytes_read;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Generate the path string for the symlink to the physical device.
|
|
|
|
|
*/
|
|
|
|
|
if (asprintf(&pathstr, "/sys/class/net/%s/phy80211", device) == -1) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: Can't generate path name string for /sys/class/net device",
|
|
|
|
|
device);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
bytes_read = readlink(pathstr, phydev_path, phydev_max_pathlen);
|
|
|
|
|
if (bytes_read == -1) {
|
|
|
|
|
if (errno == ENOENT || errno == EINVAL) {
|
|
|
|
|
/*
|
|
|
|
|
* Doesn't exist, or not a symlink; assume that
|
|
|
|
|
* means it's not a mac80211 device.
|
|
|
|
|
*/
|
|
|
|
|
free(pathstr);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: Can't readlink %s: %s", device, pathstr,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
free(pathstr);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
free(pathstr);
|
|
|
|
|
phydev_path[bytes_read] = '\0';
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
{
|
|
|
|
|
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) {
|
|
|
|
|
handle->md.mondevice = strdup(mondevice);
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now configure the monitor interface up.
|
|
|
|
|
*/
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
|
strncpy(ifr.ifr_name, handle->md.mondevice, sizeof(ifr.ifr_name));
|
|
|
|
|
if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: Can't get flags for %s: %s", device,
|
|
|
|
|
handle->md.mondevice, strerror(errno));
|
|
|
|
|
del_mon_if(handle, sock_fd, &nlstate, device,
|
|
|
|
|
handle->md.mondevice);
|
|
|
|
|
nl80211_cleanup(&nlstate);
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
ifr.ifr_flags |= IFF_UP|IFF_RUNNING;
|
|
|
|
|
if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
|
|
|
|
|
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
|
|
|
|
|
"%s: Can't set flags for %s: %s", device,
|
|
|
|
|
handle->md.mondevice, strerror(errno));
|
|
|
|
|
del_mon_if(handle, sock_fd, &nlstate, device,
|
|
|
|
|
handle->md.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.
|
|
|
|
|
*/
|
|
|
|
|
handle->md.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 */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Use the Wireless Extensions, if we have them, to try to turn monitor mode
|
|
|
|
|
* on if it's not already on.
|
|
|
|
@ -2658,7 +3166,6 @@ typedef enum {
|
|
|
|
|
static int
|
|
|
|
|
enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
{
|
|
|
|
|
#ifdef IW_MODE_MONITOR
|
|
|
|
|
/*
|
|
|
|
|
* XXX - at least some adapters require non-Wireless Extensions
|
|
|
|
|
* mechanisms to turn monitor mode on.
|
|
|
|
@ -3013,7 +3520,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
* Note that we have to put the old mode back
|
|
|
|
|
* when we close the device.
|
|
|
|
|
*/
|
|
|
|
|
handle->md.must_clear |= MUST_CLEAR_RFMON;
|
|
|
|
|
handle->md.must_do_on_close |= MUST_CLEAR_RFMON;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add this to the list of pcaps to close
|
|
|
|
@ -3205,7 +3712,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
* Note that we have to put the old mode back when we
|
|
|
|
|
* close the device.
|
|
|
|
|
*/
|
|
|
|
|
handle->md.must_clear |= MUST_CLEAR_RFMON;
|
|
|
|
|
handle->md.must_do_on_close |= MUST_CLEAR_RFMON;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add this to the list of pcaps to close when we exit.
|
|
|
|
@ -3213,13 +3720,39 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
pcap_add_to_pcaps_to_close(handle);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
#else
|
|
|
|
|
}
|
|
|
|
|
#endif /* IW_MODE_MONITOR */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Try various mechanisms to enter monitor mode.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device)
|
|
|
|
|
{
|
|
|
|
|
#ifdef IW_MODE_MONITOR
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
#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 */
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We don't have the Wireless Extensions available, so we can't
|
|
|
|
|
* do monitor mode.
|
|
|
|
|
* Either none of the mechanisms we know about work or none
|
|
|
|
|
* of those mechanisms are available, so we can't do monitor
|
|
|
|
|
* mode.
|
|
|
|
|
*/
|
|
|
|
|
return 0;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_PF_PACKET_SOCKETS */
|
|
|
|
@ -3321,7 +3854,7 @@ activate_old(pcap_t *handle)
|
|
|
|
|
pcap_strerror(errno));
|
|
|
|
|
return PCAP_ERROR;
|
|
|
|
|
}
|
|
|
|
|
handle->md.must_clear |= MUST_CLEAR_PROMISC;
|
|
|
|
|
handle->md.must_do_on_close |= MUST_CLEAR_PROMISC;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add this to the list of pcaps
|
|
|
|
|