X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/62ff2e48b35f29af145cd083543773d601f072ff..refs/heads/libpcap-1.0:/pcap-linux.c?ds=sidebyside diff --git a/pcap-linux.c b/pcap-linux.c index e96c0479..e14de811 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -34,7 +34,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129.2.23 2008-08-06 07:45:59 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 /* @@ -93,7 +93,7 @@ static const char rcsid[] _U_ = #include #include #include -#include +#include #include #include #include @@ -177,6 +177,11 @@ static const char rcsid[] _U_ = * 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 */ @@ -240,11 +245,18 @@ static int pcap_setfilter_linux(pcap_t *, struct bpf_program *); static int pcap_setdirection_linux(pcap_t *, pcap_direction_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 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 *); @@ -262,10 +274,12 @@ static int iface_get_mtu(int fd, const char *device, char *ebuf); 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 @@ -285,6 +299,12 @@ pcap_create(const char *device, char *ebuf) { 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); @@ -326,10 +346,9 @@ pcap_can_set_rfmon_linux(pcap_t *p) 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; } @@ -491,7 +510,6 @@ pcap_activate_linux(pcap_t *handle) { const char *device; int status = 0; - int activate_ok = 0; device = handle->opt.source; @@ -506,12 +524,11 @@ pcap_activate_linux(pcap_t *handle) 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. */ @@ -519,10 +536,9 @@ pcap_activate_linux(pcap_t *handle) "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) ); @@ -540,28 +556,57 @@ pcap_activate_linux(pcap_t *handle) */ 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. */ @@ -629,8 +674,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #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; @@ -638,6 +682,12 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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 @@ -672,6 +722,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) */ 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; @@ -682,6 +733,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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 { /* @@ -697,7 +749,15 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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 */ @@ -784,7 +844,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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; @@ -812,8 +872,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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 @@ -1630,19 +1690,21 @@ static int 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", @@ -1677,7 +1739,7 @@ activate_new(pcap_t *handle) * 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; @@ -1792,15 +1854,23 @@ activate_new(pcap_t *handle) } } 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 @@ -1825,10 +1895,13 @@ activate_new(pcap_t *handle) /* * 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; @@ -1897,8 +1970,11 @@ activate_mmap(pcap_t *handle) /* 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 @@ -1918,6 +1994,56 @@ activate_mmap(pcap_t *handle) } #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) @@ -1944,7 +2070,9 @@ create_ring(pcap_t *handle) * (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; @@ -1962,10 +2090,16 @@ retry: 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 */ @@ -1973,28 +2107,32 @@ retry: 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; ibp[i*req.tp_block_size]; + void *base = &handle->bp[i*req.tp_block_size]; for (j=0; joffset) { - RING_GET_FRAME(handle) = (struct tpacket_hdr*) base; + RING_GET_FRAME(handle) = base; base += req.tp_frame_size; } } @@ -2055,6 +2193,29 @@ pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf) 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) @@ -2062,7 +2223,8 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 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; @@ -2094,16 +2256,46 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 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; } @@ -2116,25 +2308,25 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, * 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) { @@ -2156,7 +2348,9 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, * 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; @@ -2178,6 +2372,23 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 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); @@ -2185,7 +2396,16 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 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; @@ -2219,7 +2439,7 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) 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; } @@ -2316,6 +2536,7 @@ iface_bind(int fd, int ifindex, char *ebuf) 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 @@ -2324,7 +2545,6 @@ iface_bind(int fd, int ifindex, char *ebuf) 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, @@ -2336,15 +2556,18 @@ has_wext(int sock_fd, const char *device, char *ebuf) "%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, @@ -2376,14 +2599,57 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * * 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; @@ -2709,6 +2975,12 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) 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. */ @@ -2904,7 +3176,7 @@ activate_old(pcap_t *handle) /* 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;