#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.16 2008-04-09 21:26:37 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>
#include "pcap-int.h"
#include "pcap/sll.h"
+#include "pcap/vlan.h"
#ifdef HAVE_DAG_API
#include "pcap-dag.h"
*/
# ifdef PACKET_HOST
# define HAVE_PF_PACKET_SOCKETS
+# ifdef PACKET_AUXDATA
+# define HAVE_PACKET_AUXDATA
+# endif /* PACKET_AUXDATA */
# endif /* PACKET_HOST */
* uses many ring related structs and macros */
# ifdef TPACKET_HDRLEN
# define HAVE_PACKET_RING
+# ifdef TPACKET2_HDRLEN
+# define HAVE_TPACKET2
+# else
+# define TPACKET_V1 0
+# endif /* TPACKET2_HDRLEN */
# endif /* TPACKET_HDRLEN */
#endif /* PF_PACKET */
static int pcap_stats_linux(pcap_t *, struct pcap_stat *);
static int pcap_setfilter_linux(pcap_t *, struct bpf_program *);
static int pcap_setdirection_linux(pcap_t *, pcap_direction_t);
-static void pcap_close_linux(pcap_t *);
+static void pcap_cleanup_linux(pcap_t *);
+
+union thdr {
+ struct tpacket_hdr *h1;
+ struct tpacket2_hdr *h2;
+ void *raw;
+};
#ifdef HAVE_PACKET_RING
-#define RING_GET_FRAME(h) (((struct tpacket_hdr**)h->buffer)[h->offset])
+#define RING_GET_FRAME(h) (((union thdr **)h->buffer)[h->offset])
static void destroy_ring(pcap_t *handle);
static int create_ring(pcap_t *handle);
-static void pcap_close_linux_mmap(pcap_t *);
+static int prepare_tpacket_socket(pcap_t *handle);
+static void pcap_cleanup_linux_mmap(pcap_t *);
static int pcap_read_linux_mmap(pcap_t *, int, pcap_handler , u_char *);
static int pcap_setfilter_linux_mmap(pcap_t *, struct bpf_program *);
static int pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf);
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;
}
* Even with newer kernels, we have the same issue with rfmon mode.
*/
-static void pcap_close_linux( pcap_t *handle )
+static void pcap_cleanup_linux( pcap_t *handle )
{
struct ifreq ifr;
#ifdef IW_MODE_MONITOR
free(handle->md.device);
handle->md.device = NULL;
}
- pcap_close_common(handle);
+ pcap_cleanup_live_common(handle);
}
/*
{
const char *device;
int status = 0;
- int activate_ok = 0;
device = handle->opt.source;
handle->set_datalink_op = NULL; /* can't change data link type */
handle->getnonblock_op = pcap_getnonblock_fd;
handle->setnonblock_op = pcap_setnonblock_fd;
- handle->close_op = pcap_close_linux;
+ handle->cleanup_op = pcap_cleanup_linux;
handle->read_op = pcap_read_linux;
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.
*/
return status;
fail:
- close(handle->fd);
- if (handle->md.device != NULL) {
- free(handle->md.device);
- handle->md.device = NULL;
- }
+ pcap_cleanup_linux(handle);
return status;
}
#else
struct sockaddr from;
#endif
+#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ 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;
offset = 0;
#endif
- /* Receive a single packet from the kernel */
-
+ /*
+ * Receive a single packet from the kernel.
+ * We ignore EINTR, as that might just be due to a signal
+ * being delivered - if the signal should interrupt the
+ * loop, the signal handler should call pcap_breakloop()
+ * to set handle->break_loop (we ignore it on other
+ * platforms as well).
+ * We also ignore ENETDOWN, so that we can continue to
+ * capture traffic if the interface goes down and comes
+ * back up again; comments in the kernel indicate that
+ * we'll just block waiting for packets if we try to
+ * receive from a socket that delivered ENETDOWN, and,
+ * if we're using a memory-mapped buffer, we won't even
+ * get notified of "network down" events.
+ */
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;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ msg.msg_flags = 0;
+
+ 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 {
/*
* Has "pcap_breakloop()" been called?
handle->break_loop = 0;
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);
- } while (packet_len == -1 && errno == EINTR);
+#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 */
from.sll_halen);
hdrp->sll_protocol = from.sll_protocol;
}
-#endif
+
+#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;
+ struct vlan_tag *tag;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) ||
+ cmsg->cmsg_level != SOL_PACKET ||
+ cmsg->cmsg_type != PACKET_AUXDATA)
+ continue;
+
+ aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
+ if (aux->tp_vlan_tci == 0)
+ continue;
+
+ len = packet_len > iov.iov_len ? iov.iov_len : packet_len;
+ if (len < 2 * ETH_ALEN)
+ break;
+
+ bp -= VLAN_TAG_LEN;
+ memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN);
+
+ tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN);
+ tag->vlan_tpid = htons(ETH_P_8021Q);
+ tag->vlan_tci = htons(aux->tp_vlan_tci);
+
+ packet_len += VLAN_TAG_LEN;
+ }
+#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
handle->linktype = DLT_LINUX_LAPD;
break;
+#ifndef ARPHRD_NONE
+#define ARPHRD_NONE 0xFFFE
+#endif
+ case ARPHRD_NONE:
+ /*
+ * No link-layer header; packets are just IP
+ * packets, so use DLT_RAW.
+ */
+ handle->linktype = DLT_RAW;
+ break;
+
default:
handle->linktype = -1;
break;
activate_new(pcap_t *handle)
{
#ifdef HAVE_PF_PACKET_SOCKETS
- int sock_fd = -1, arptype;
+ 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;
}
if ((err = iface_bind(sock_fd, handle->md.ifindex,
- handle->errbuf)) < 0) {
+ handle->errbuf)) != 1) {
close(sock_fd);
- if (err == -2)
- return PCAP_ERROR;
+ if (err < 0)
+ return err;
else
return 0; /* try old mechanism */
}
} 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;
}
}
+ /* Enable auxillary data if supported and reserve room for
+ * reconstructing VLAN headers. */
+#ifdef HAVE_PACKET_AUXDATA
+ val = 1;
+ if (setsockopt(sock_fd, SOL_PACKET, PACKET_AUXDATA, &val,
+ sizeof(val)) == -1 && errno != ENOPROTOOPT) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "setsockopt: %s", pcap_strerror(errno));
+ close(sock_fd);
+ return PCAP_ERROR;
+ }
+ handle->offset += VLAN_TAG_LEN;
+#endif /* HAVE_PACKET_AUXDATA */
+
/*
* This is a 2.2[.x] or later kernel (we know that
* because we're not using a SOCK_PACKET socket -
/* by default request 2M for the ring buffer */
handle->opt.buffer_size = 2*1024*1024;
}
+ ret = prepare_tpacket_socket(handle);
+ 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
* handle->offset is used to get the current position into the rx ring
* handle->cc is used to store the ring size */
handle->read_op = pcap_read_linux_mmap;
- handle->close_op = pcap_close_linux_mmap;
+ handle->cleanup_op = pcap_cleanup_linux_mmap;
handle->setfilter_op = pcap_setfilter_linux_mmap;
handle->setnonblock_op = pcap_setnonblock_mmap;
handle->getnonblock_op = pcap_getnonblock_mmap;
}
#ifdef HAVE_PACKET_RING
+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);
+
+#ifdef HAVE_TPACKET2
+ /* Probe whether kernel supports TPACKET_V2 */
+ val = TPACKET_V2;
+ len = sizeof(val);
+ if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
+ if (errno == ENOPROTOOPT)
+ 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 packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
+ }
+ handle->md.tp_hdrlen = val;
+
+ val = TPACKET_V2;
+ if (setsockopt(handle->fd, SOL_PACKET, PACKET_VERSION, &val,
+ sizeof(val)) < 0) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't activate TPACKET_V2 on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
+ }
+ handle->md.tp_version = TPACKET_V2;
+
+ /* Reserve space for VLAN tag reconstruction */
+ val = VLAN_TAG_LEN;
+ if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val,
+ sizeof(val)) < 0) {
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "can't set up reserve on packet socket: %s",
+ pcap_strerror(errno));
+ return -1;
+ }
+
+#endif /* HAVE_TPACKET2 */
+ return 1;
+}
static void
compute_ring_block(int frame_size, unsigned *block_size, unsigned *frames_per_block)
* (and a lot of memory will be unused).
* The snap len should be carefully chosen to achive best
* performance */
- req.tp_frame_size = TPACKET_ALIGN(handle->snapshot+TPACKET_HDRLEN);
+ req.tp_frame_size = TPACKET_ALIGN(handle->snapshot +
+ TPACKET_ALIGN(handle->md.tp_hdrlen) +
+ sizeof(struct sockaddr_ll));
req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
compute_ring_block(req.tp_frame_size, &req.tp_block_size, &frames_per_block);
req.tp_block_nr = req.tp_frame_nr / frames_per_block;
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(struct tpacket_hdr*));
+ 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*/
handle->offset = 0;
for (i=0; i<req.tp_block_nr; ++i) {
- u_char *base = &handle->bp[i*req.tp_block_size];
+ void *base = &handle->bp[i*req.tp_block_size];
for (j=0; j<frames_per_block; ++j, ++handle->offset) {
- RING_GET_FRAME(handle) = (struct tpacket_hdr*) base;
+ RING_GET_FRAME(handle) = base;
base += req.tp_frame_size;
}
}
munmap(handle->bp, block_size * handle->cc / frames_per_block);
handle->bp = 0;
}
-
- /* if the header ring is allocated, clear it*/
- if (handle->buffer) {
- free(handle->buffer);
- handle->buffer = 0;
- }
}
static void
-pcap_close_linux_mmap( pcap_t *handle )
+pcap_cleanup_linux_mmap( pcap_t *handle )
{
destroy_ring(handle);
- pcap_close_linux(handle);
+ pcap_cleanup_linux(handle);
}
return 0;
}
+static inline union thdr *
+pcap_get_ring_frame(pcap_t *handle, int status)
+{
+ union thdr h;
+
+ h.raw = RING_GET_FRAME(handle);
+ switch (handle->md.tp_version) {
+ case TPACKET_V1:
+ if (status != (h.h1->tp_status ? TP_STATUS_USER :
+ TP_STATUS_KERNEL))
+ return NULL;
+ break;
+#ifdef HAVE_TPACKET2
+ case TPACKET_V2:
+ if (status != (h.h2->tp_status ? TP_STATUS_USER :
+ TP_STATUS_KERNEL))
+ return NULL;
+ break;
+#endif
+ }
+ return h.raw;
+}
+
static int
pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
u_char *user)
int pkts = 0;
/* wait for frames availability.*/
- if ((handle->md.timeout >= 0) && !(RING_GET_FRAME(handle)->tp_status)) {
+ if ((handle->md.timeout >= 0) &&
+ !pcap_get_ring_frame(handle, TP_STATUS_USER)) {
struct pollfd pollinfo;
int ret;
struct sockaddr_ll *sll;
struct pcap_pkthdr pcaphdr;
unsigned char *bp;
- struct tpacket_hdr* thdr = RING_GET_FRAME(handle);
- if (thdr->tp_status == TP_STATUS_KERNEL)
+ union thdr h;
+ unsigned int tp_len;
+ unsigned int tp_mac;
+ unsigned int tp_snaplen;
+ unsigned int tp_sec;
+ unsigned int tp_usec;
+
+ h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
+ if (!h.raw)
break;
+ switch (handle->md.tp_version) {
+ case TPACKET_V1:
+ tp_len = h.h1->tp_len;
+ tp_mac = h.h1->tp_mac;
+ tp_snaplen = h.h1->tp_snaplen;
+ tp_sec = h.h1->tp_sec;
+ tp_usec = h.h1->tp_usec;
+ break;
+#ifdef HAVE_TPACKET2
+ case TPACKET_V2:
+ tp_len = h.h2->tp_len;
+ tp_mac = h.h2->tp_mac;
+ tp_snaplen = h.h2->tp_snaplen;
+ tp_sec = h.h2->tp_sec;
+ 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 (thdr->tp_mac+thdr->tp_snaplen > handle->bufsize) {
+ if (tp_mac + tp_snaplen > handle->bufsize) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"corrupted frame on kernel ring mac "
"offset %d + caplen %d > frame len %d",
- thdr->tp_mac, thdr->tp_snaplen, handle->bufsize);
+ tp_mac, tp_snaplen, handle->bufsize);
return -1;
}
* Note: alternatively it could be possible to stop applying
* the filter when the ring became empty, but it can possibly
* happen a lot later... */
- bp = (unsigned char*)thdr + thdr->tp_mac;
+ bp = (unsigned char*)h.raw + tp_mac;
run_bpf = (!handle->md.use_bpf) ||
((handle->md.use_bpf>1) && handle->md.use_bpf--);
if (run_bpf && handle->fcode.bf_insns &&
(bpf_filter(handle->fcode.bf_insns, bp,
- thdr->tp_len, thdr->tp_snaplen) == 0))
+ tp_len, tp_snaplen) == 0))
goto skip;
/* check direction and interface index */
- sll = (void*)thdr + TPACKET_ALIGN(sizeof(*thdr));
+ sll = (void *)h.raw + TPACKET_ALIGN(handle->md.tp_hdrlen);
if ((sll->sll_ifindex == handle->md.lo_ifindex) &&
(sll->sll_pkttype == PACKET_OUTGOING))
goto skip;
/* get required packet info from ring header */
- pcaphdr.ts.tv_sec = thdr->tp_sec;
- pcaphdr.ts.tv_usec = thdr->tp_usec;
- pcaphdr.caplen = thdr->tp_snaplen;
- pcaphdr.len = thdr->tp_len;
+ pcaphdr.ts.tv_sec = tp_sec;
+ pcaphdr.ts.tv_usec = tp_usec;
+ pcaphdr.caplen = tp_snaplen;
+ pcaphdr.len = tp_len;
/* if required build in place the sll header*/
if (handle->md.cooked) {
* don't step on the header when we construct
* the sll header.
*/
- if (bp < (u_char *)thdr + TPACKET_HDRLEN) {
+ if (bp < (u_char *)h.raw +
+ TPACKET_ALIGN(handle->md.tp_hdrlen) +
+ sizeof(struct sockaddr_ll)) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"cooked-mode frame doesn't have room for sll header");
return -1;
pcaphdr.len += SLL_HDR_LEN;
}
+#ifdef HAVE_TPACKET2
+ if (handle->md.tp_version == TPACKET_V2 && h.h2->tp_vlan_tci &&
+ tp_snaplen >= 2 * ETH_ALEN) {
+ struct vlan_tag *tag;
+
+ bp -= VLAN_TAG_LEN;
+ memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN);
+
+ tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN);
+ tag->vlan_tpid = htons(ETH_P_8021Q);
+ tag->vlan_tci = htons(h.h2->tp_vlan_tci);
+
+ pcaphdr.caplen += VLAN_TAG_LEN;
+ pcaphdr.len += VLAN_TAG_LEN;
+ }
+#endif
+
/* pass the packet to the user */
pkts++;
callback(user, &pcaphdr, bp);
skip:
/* next packet */
- thdr->tp_status = TP_STATUS_KERNEL;
+ switch (handle->md.tp_version) {
+ case TPACKET_V1:
+ h.h1->tp_status = TP_STATUS_KERNEL;
+ break;
+#ifdef HAVE_TPACKET2
+ case TPACKET_V2:
+ h.h2->tp_status = TP_STATUS_KERNEL;
+ break;
+#endif
+ }
if (++handle->offset >= handle->cc)
handle->offset = 0;
for (n=0; n < handle->cc; ++n) {
if (--handle->offset < 0)
handle->offset = handle->cc - 1;
- if (RING_GET_FRAME(handle)->tp_status != TP_STATUS_KERNEL)
+ if (!pcap_get_ring_frame(handle, TP_STATUS_KERNEL))
break;
}
/*
* Bind the socket associated with FD to the given device.
+ * Return 1 on success, 0 if we should try a SOCK_PACKET socket,
+ * or a PCAP_ERROR_ value on a hard error.
*/
static int
iface_bind(int fd, int ifindex, char *ebuf)
sll.sll_protocol = htons(ETH_P_ALL);
if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "bind: %s", pcap_strerror(errno));
- return -1;
+ if (errno == ENETDOWN) {
+ /*
+ * Return a "network down" indication, so that
+ * the application can report that rather than
+ * saying we had a mysterious failure and
+ * suggest that they report a problem to the
+ * libpcap developers.
+ */
+ return PCAP_ERROR_IFACE_NOT_UP;
+ } else {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "bind: %s", pcap_strerror(errno));
+ return PCAP_ERROR;
+ }
}
/* Any pending errors, e.g., network is down? */
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"getsockopt: %s", pcap_strerror(errno));
- return -2;
+ return 0;
}
- if (err > 0) {
+ if (err == ENETDOWN) {
+ /*
+ * Return a "network down" indication, so that
+ * the application can report that rather than
+ * saying we had a mysterious failure and
+ * suggest that they report a problem to the
+ * libpcap developers.
+ */
+ return PCAP_ERROR_IFACE_NOT_UP;
+ } else if (err > 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"bind: %s", pcap_strerror(err));
- return -2;
+ return 0;
}
- return 0;
+ 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.
*/
if (handle->fd == -1) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"socket: %s", pcap_strerror(errno));
- return PCAP_ERROR;
+ return PCAP_ERROR_PERM_DENIED;
}
/* It worked - we are using the old interface */
/* 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;