#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.25 2008-08-06 07:51:43 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.35 2008-12-14 22:00:04 guy Exp $ (LBL)";
#endif
/*
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <sys/mman.h>
-#include <net/if.h>
+#include <linux/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <net/if_arp.h>
static int iface_get_arptype(int fd, const char *device, char *ebuf);
#ifdef HAVE_PF_PACKET_SOCKETS
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,
const char *device);
-#endif
+#endif /* HAVE_PF_PACKET_SOCKETS */
static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
{
pcap_t *handle;
+ /*
+ * A null device name is equivalent to the "any" device.
+ */
+ if (device == NULL)
+ device = "any";
+
#ifdef HAVE_DAG_API
if (strstr(device, "dag")) {
return dag_create(device, ebuf);
struct iwreq ireq;
#endif
- if (p->opt.source == NULL) {
+ if (strcmp(p->opt.source, "any") == 0) {
/*
- * This is equivalent to the "any" device, and we don't
- * support monitor mode on it.
+ * Monitor mode makes no sense on the "any" device.
*/
return 0;
}
{
const char *device;
int status = 0;
- int activate_ok = 0;
device = handle->opt.source;
handle->stats_op = pcap_stats_linux;
/*
- * NULL and "any" are special devices which give us the hint to
- * monitor all devices.
+ * The "any" device is a special device which causes us not
+ * to bind to a particular device and thus to look at all
+ * devices.
*/
- if (!device || strcmp(device, "any") == 0) {
- device = NULL;
- handle->md.device = strdup("any");
+ if (strcmp(device, "any") == 0) {
if (handle->opt.promisc) {
handle->opt.promisc = 0;
/* Just a warning. */
"Promiscuous mode not supported on the \"any\" device");
status = PCAP_WARNING_PROMISC_NOTSUP;
}
+ }
- } else
- handle->md.device = strdup(device);
-
+ handle->md.device = strdup(device);
if (handle->md.device == NULL) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
pcap_strerror(errno) );
*/
if ((status = activate_new(handle)) == 1) {
- activate_ok = 1;
/*
+ * Success.
* Try to use memory-mapped access.
*/
- if (activate_mmap(handle) == 1)
- return 0; /* we succeeded; nothing more to do */
+ switch (activate_mmap(handle)) {
+
+ case 1:
+ /* we succeeded; nothing more to do */
+ return 0;
+
+ case 0:
+ /*
+ * Kernel doesn't support it - just continue
+ * with non-memory-mapped access.
+ */
+ break;
+
+ case -1:
+ /*
+ * We failed to set up to use it, or kernel
+ * supports it, but we failed to enable it;
+ * return an error. handle->errbuf contains
+ * an error message.
+ */
+ status = PCAP_ERROR;
+ goto fail;
+ }
}
else if (status == 0) {
/* Non-fatal error; try old way */
- if ((status = activate_old(handle)) == 1)
- activate_ok = 1;
- }
- if (!activate_ok) {
+ if ((status = activate_old(handle)) != 1) {
+ /*
+ * Both methods to open the packet socket failed.
+ * Tidy up and report our failure (handle->errbuf
+ * is expected to be set by the functions above).
+ */
+ goto fail;
+ }
+ } else {
/*
- * Both methods to open the packet socket failed. Tidy
- * up and report our failure (ebuf is expected to be
- * set by the functions above).
+ * Fatal error with the new way; just fail.
+ * status has the error return; if it's PCAP_ERROR,
+ * handle->errbuf has been set appropriately.
*/
goto fail;
}
- if (handle->opt.buffer_size == 0) {
+ /*
+ * We set up the socket, but not with memory-mapped access.
+ */
+ if (handle->opt.buffer_size != 0) {
/*
* Set the socket buffer size to the specified value.
*/
#else
struct sockaddr from;
#endif
- int packet_len, caplen;
- struct pcap_pkthdr pcap_header;
+#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
struct cmsghdr cmsg;
char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
} cmsg_buf;
+#else /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
+ socklen_t fromlen;
+#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
+ int packet_len, caplen;
+ struct pcap_pkthdr pcap_header;
+
#ifdef HAVE_PF_PACKET_SOCKETS
/*
* If this is a cooked device, leave extra room for a
*/
bp = handle->buffer + handle->offset;
+#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &iov;
iov.iov_len = handle->bufsize - offset;
iov.iov_base = bp + offset;
+#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
do {
/*
return -2;
}
+#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
packet_len = recvmsg(handle->fd, &msg, MSG_TRUNC);
+#else /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
+ fromlen = sizeof(from);
+ packet_len = recvfrom(
+ handle->fd, bp + offset,
+ handle->bufsize - offset, MSG_TRUNC,
+ (struct sockaddr *) &from, &fromlen);
+#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
} while (packet_len == -1 && (errno == EINTR || errno == ENETDOWN));
/* Check if an error occured */
hdrp->sll_protocol = from.sll_protocol;
}
-#ifdef HAVE_PACKET_AUXDATA
+#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
struct tpacket_auxdata *aux;
unsigned int len;
packet_len += VLAN_TAG_LEN;
}
-#endif /* HAVE_PACKET_AUXDATA */
-#endif
+#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
+#endif /* HAVE_PF_PACKET_SOCKETS */
/*
* XXX: According to the kernel source we should get the real
activate_new(pcap_t *handle)
{
#ifdef HAVE_PF_PACKET_SOCKETS
+ const char *device = handle->opt.source;
+ int is_any_device = (strcmp(device, "any") == 0);
int sock_fd = -1, arptype, val;
int err = 0;
struct packet_mreq mr;
- const char* device = handle->opt.source;
/*
- * Open a socket with protocol family packet. If a device is
- * given we try to open it in raw mode otherwise we use
- * the cooked interface.
+ * Open a socket with protocol family packet. If the
+ * "any" device was specified, we open a SOCK_DGRAM
+ * socket for the cooked interface, otherwise we first
+ * try a SOCK_RAW socket for the raw interface.
*/
- sock_fd = device ?
- socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
- : socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
+ sock_fd = is_any_device ?
+ socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) :
+ socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd == -1) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s",
* to cooked mode if we have an unknown interface type
* or a type we know doesn't work well in raw mode.
*/
- if (device) {
+ if (!is_any_device) {
/* Assume for now we don't need cooked mode. */
handle->md.cooked = 0;
}
} else {
/*
- * This is cooked mode.
+ * The "any" device.
+ */
+ if (handle->opt.rfmon) {
+ /*
+ * It doesn't support monitor mode.
+ */
+ return PCAP_ERROR_RFMON_NOTSUP;
+ }
+
+ /*
+ * It uses cooked mode.
*/
handle->md.cooked = 1;
handle->linktype = DLT_LINUX_SLL;
/*
* We're not bound to a device.
- * XXX - true? Or true only if we're using
- * the "any" device?
* For now, we're using this as an indication
* that we can't transmit; stop doing that only
* if we figure out how to transmit in cooked
/*
* Hmm, how can we set promiscuous mode on all interfaces?
- * I am not sure if that is possible at all.
+ * I am not sure if that is possible at all. For now, we
+ * silently ignore attempts to turn promiscuous mode on
+ * for the "any" device (so you don't have to explicitly
+ * disable it in programs such as tcpdump).
*/
- if (device && handle->opt.promisc) {
+ if (!is_any_device && handle->opt.promisc) {
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = handle->md.ifindex;
mr.mr_type = PACKET_MR_PROMISC;
handle->opt.buffer_size = 2*1024*1024;
}
ret = prepare_tpacket_socket(handle);
- if (ret == 0)
+ if (ret != 1)
return ret;
ret = create_ring(handle);
- if (ret == 0)
+ if (ret != 1)
return ret;
/* override some defaults and inherit the other fields from
static int
prepare_tpacket_socket(pcap_t *handle)
{
+#ifdef HAVE_TPACKET2
socklen_t len;
int val;
+#endif
handle->md.tp_version = TPACKET_V1;
handle->md.tp_hdrlen = sizeof(struct tpacket_hdr);
len = sizeof(val);
if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
if (errno == ENOPROTOOPT)
- return 1;
+ return 1; /* no - just drive on */
+
+ /* Yes - treat as a failure. */
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't get TPACKET_V2 header len on socket %d: %d-%s",
- handle->fd, errno, pcap_strerror(errno));
- return 0;
+ "can't get TPACKET_V2 header len on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
}
handle->md.tp_hdrlen = val;
if (setsockopt(handle->fd, SOL_PACKET, PACKET_VERSION, &val,
sizeof(val)) < 0) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't activate TPACKET_V2 on socket %d: %d-%s",
- handle->fd, errno, pcap_strerror(errno));
- return 0;
+ "can't activate TPACKET_V2 on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
}
handle->md.tp_version = TPACKET_V2;
if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val,
sizeof(val)) < 0) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "can't set up reserve on socket %d: %d-%s",
- handle->fd, errno, pcap_strerror(errno));
- return 0;
+ "can't set up reserve on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
}
#endif /* HAVE_TPACKET2 */
req.tp_block_nr = req.tp_frame_nr/frames_per_block;
goto retry;
}
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't create rx ring on "
- "packet socket %d: %d-%s", handle->fd, errno,
- pcap_strerror(errno));
- return 0;
+ if (errno == ENOPROTOOPT) {
+ /*
+ * We don't have ring buffer support in this kernel.
+ */
+ return 0;
+ }
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't create rx ring on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
}
/* memory map the rx ring */
handle->bp = mmap(0, ringsize, PROT_READ| PROT_WRITE, MAP_SHARED,
handle->fd, 0);
if (handle->bp == MAP_FAILED) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't mmap rx ring: %d-%s",
- errno, pcap_strerror(errno));
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't mmap rx ring: %s", pcap_strerror(errno));
/* clear the allocated ring on error*/
destroy_ring(handle);
- return 0;
+ return -1;
}
/* allocate a ring for each frame header pointer*/
handle->cc = req.tp_frame_nr;
handle->buffer = malloc(handle->cc * sizeof(union thdr *));
if (!handle->buffer) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't allocate ring of frame headers: %s",
+ pcap_strerror(errno));
+
destroy_ring(handle);
- return 0;
+ return -1;
}
/* fill the header ring with proper frame ptr*/
tp_usec = h.h2->tp_nsec / 1000;
break;
#endif
+ default:
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "unsupported tpacket version %d",
+ handle->md.tp_version);
+ return -1;
}
/* perform sanity check on internal offset. */
if (tp_mac + tp_snaplen > handle->bufsize) {
return 1;
}
+#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
static int
has_wext(int sock_fd, const char *device, char *ebuf)
{
-#ifdef IW_MODE_MONITOR
struct iwreq ireq;
strncpy(ireq.ifr_ifrn.ifrn_name, device,
"%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno));
if (errno == ENODEV)
return PCAP_ERROR_NO_SUCH_DEVICE;
-#endif
return 0;
}
+#endif
/*
* 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,
*
* 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} 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} 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.
*/
int err;
struct iwreq ireq;
return PCAP_ERROR_RFMON_NOTSUP;
}
+ /*
+ * XXX - airmon-ng does "iwconfig {if} key off" after setting
+ * monitor mode and setting the channel, and then does
+ * "iwconfig up".
+ */
+
/*
* Now select the appropriate radio header.
*/
/* Bind to the given device */
- if (!device) {
+ if (strcmp(device, "any") == 0) {
strncpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems",
PCAP_ERRBUF_SIZE);
return PCAP_ERROR;