1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 20:51:30 +00:00
Files
nmap/libpcap/pcap-usb-linux.c
david 7197d18d52 Cherry-pick changes to require <linux/compiler.h>.
I needed this to build with --with-libpcap=included on Linux 2.6.16,
otherwise I got this error:

gcc -g -O2 -O2 -fpic -I.  -DHAVE_CONFIG_H  -D_U_="__attribute__((unused))" -c ./pcap-usb-linux.c
./pcap-usb-linux.c: In function ‘probe_devices’:
./pcap-usb-linux.c:234: error: storage size of ‘ctrl’ isn’t known
./pcap-usb-linux.c:279: error: ‘USBDEVFS_CONTROL’ undeclared (first use in this function)
./pcap-usb-linux.c:279: error: (Each undeclared identifier is reported only once
./pcap-usb-linux.c:279: error: for each function it appears in.)
make[1]: *** [pcap-usb-linux.o] Error 1

commit a092bafeee07c97352d2c6d06780220286ab2db0
Author: Guy Harris <guy@alum.mit.edu>
Date:   Tue Nov 30 15:52:45 2010 -0800

    In test programs, just as in pcap-usb-linux.c, if we have
    <linux/compiler.h> we (probably need to include it before including
    <linux/usbdevice_fs.h>, otherwise the test may fail when it shouldn't.

commit b019cc06cb4fdc01f99a36581fe7b55e2533d663
Author: Guy Harris <guy@alum.mit.edu>
Date:   Thu Nov 25 14:06:01 2010 -0800

    Check whether we have <linux/compiler.h>.

    Not all distributions have it in /usr/include; I guess the ones that
    don't have it have versions of other kernel headers that don't require
    it.

commit 0920898be2ede72baafa2a02e77ea223ecbe4f9a
Author: Guy Harris <guy@alum.mit.edu>
Date:   Thu Nov 25 13:49:16 2010 -0800

    Include <linux/compiler.h> before including <linux/usbdevice_fs.h>.

    Some versions of the Linux kernel require that.

commit 85089fea5732acc004dcc7e6aebcb0510c37f675
Author: Guy Harris <guy@alum.mit.edu>
Date:   Fri Jun 4 10:48:16 2010 -0700

    Do filtering on USB and Bluetooth capturing.

    Do the standard userland filtering on USB and Bluetooth captures, rather
    than returning "success" when the filter is installed without doing
    anything with the filter.

    Also, squelch some "dereferencing type-punned pointer will break
    strict-aliasing rules" warnings in pcap-bt-linux.c, by using memcpy
    rather than pointer-casting.
2011-10-27 15:38:30 +00:00

884 lines
23 KiB
C

/*
* Copyright (c) 2006 Paolo Abeni (Italy)
* 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*
* USB sniffing API implementation for Linux platform
* By Paolo Abeni <paolo.abeni@email.it>
* Modifications: Kris Katterjohn <katterjohn@gmail.com>
*
*/
#ifndef lint
static const char rcsid[] _U_ =
"@(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.c,v 1.33 2008-12-23 21:38:50 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pcap-int.h"
#include "pcap-usb-linux.h"
#include "pcap/usb.h"
#ifdef NEED_STRERROR_H
#include "strerror.h"
#endif
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <byteswap.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#ifdef HAVE_LINUX_USBDEVICE_FS_H
/*
* We might need <linux/compiler.h> to define __user for
* <linux/usbdevice_fs.h>.
*/
#ifdef HAVE_LINUX_COMPILER_H
#include <linux/compiler.h>
#endif /* HAVE_LINUX_COMPILER_H */
#include <linux/usbdevice_fs.h>
#endif /* HAVE_LINUX_USBDEVICE_FS_H */
#define USB_IFACE "usbmon"
#define USB_TEXT_DIR_OLD "/sys/kernel/debug/usbmon"
#define USB_TEXT_DIR "/sys/kernel/debug/usb/usbmon"
#define SYS_USB_BUS_DIR "/sys/bus/usb/devices"
#define PROC_USB_BUS_DIR "/proc/bus/usb"
#define USB_LINE_LEN 4096
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htols(s) s
#define htoll(l) l
#define htol64(ll) ll
#else
#define htols(s) bswap_16(s)
#define htoll(l) bswap_32(l)
#define htol64(ll) bswap_64(ll)
#endif
struct mon_bin_stats {
u_int32_t queued;
u_int32_t dropped;
};
struct mon_bin_get {
pcap_usb_header *hdr;
void *data;
size_t data_len; /* Length of data (can be zero) */
};
struct mon_bin_mfetch {
int32_t *offvec; /* Vector of events fetched */
int32_t nfetch; /* Number of events to fetch (out: fetched) */
int32_t nflush; /* Number of events to flush */
};
#define MON_IOC_MAGIC 0x92
#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1)
#define MON_IOCX_URB _IOWR(MON_IOC_MAGIC, 2, struct mon_bin_hdr)
#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)
#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4)
#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5)
#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get)
#define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch)
#define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8)
#define MON_BIN_SETUP 0x1 /* setup hdr is present*/
#define MON_BIN_SETUP_ZERO 0x2 /* setup buffer is not available */
#define MON_BIN_DATA_ZERO 0x4 /* data buffer is not available */
#define MON_BIN_ERROR 0x8
/* forward declaration */
static int usb_activate(pcap_t *);
static int usb_stats_linux(pcap_t *, struct pcap_stat *);
static int usb_stats_linux_bin(pcap_t *, struct pcap_stat *);
static int usb_read_linux(pcap_t *, int , pcap_handler , u_char *);
static int usb_read_linux_bin(pcap_t *, int , pcap_handler , u_char *);
static int usb_read_linux_mmap(pcap_t *, int , pcap_handler , u_char *);
static int usb_inject_linux(pcap_t *, const void *, size_t);
static int usb_setdirection_linux(pcap_t *, pcap_direction_t);
static void usb_cleanup_linux_mmap(pcap_t *);
/* facility to add an USB device to the device list*/
static int
usb_dev_add(pcap_if_t** alldevsp, int n, char *err_str)
{
char dev_name[10];
char dev_descr[30];
snprintf(dev_name, 10, USB_IFACE"%d", n);
snprintf(dev_descr, 30, "USB bus number %d", n);
if (pcap_add_if(alldevsp, dev_name, 0,
dev_descr, err_str) < 0)
return -1;
return 0;
}
int
usb_platform_finddevs(pcap_if_t **alldevsp, char *err_str)
{
struct dirent* data;
int ret = 0;
DIR* dir;
int n;
char* name;
size_t len;
/* try scanning sysfs usb bus directory */
dir = opendir(SYS_USB_BUS_DIR);
if (dir != NULL) {
while ((ret == 0) && ((data = readdir(dir)) != 0)) {
name = data->d_name;
if (strncmp(name, "usb", 3) != 0)
continue;
if (sscanf(&name[3], "%d", &n) == 0)
continue;
ret = usb_dev_add(alldevsp, n, err_str);
}
closedir(dir);
return ret;
}
/* that didn't work; try scanning procfs usb bus directory */
dir = opendir(PROC_USB_BUS_DIR);
if (dir != NULL) {
while ((ret == 0) && ((data = readdir(dir)) != 0)) {
name = data->d_name;
len = strlen(name);
/* if this file name does not end with a number it's not of our interest */
if ((len < 1) || !isdigit(name[--len]))
continue;
while (isdigit(name[--len]));
if (sscanf(&name[len+1], "%d", &n) != 1)
continue;
ret = usb_dev_add(alldevsp, n, err_str);
}
closedir(dir);
return ret;
}
/* neither of them worked */
return 0;
}
static
int usb_mmap(pcap_t* handle)
{
int len = ioctl(handle->fd, MON_IOCQ_RING_SIZE);
if (len < 0)
return 0;
handle->md.mmapbuflen = len;
handle->md.mmapbuf = mmap(0, handle->md.mmapbuflen, PROT_READ,
MAP_SHARED, handle->fd, 0);
return handle->md.mmapbuf != MAP_FAILED;
}
#define CTRL_TIMEOUT (5*1000) /* milliseconds */
#define USB_DIR_IN 0x80
#define USB_TYPE_STANDARD 0x00
#define USB_RECIP_DEVICE 0x00
#define USB_REQ_GET_DESCRIPTOR 6
#define USB_DT_DEVICE 1
/* probe the descriptors of the devices attached to the bus */
/* the descriptors will end up in the captured packet stream */
/* and be decoded by external apps like wireshark */
/* without these identifying probes packet data can't be fully decoded */
static void
probe_devices(int bus)
{
struct usbdevfs_ctrltransfer ctrl;
struct dirent* data;
int ret = 0;
char buf[40];
DIR* dir;
/* scan usb bus directories for device nodes */
snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d", bus);
dir = opendir(buf);
if (!dir)
return;
while ((ret >= 0) && ((data = readdir(dir)) != 0)) {
int fd;
char* name = data->d_name;
if (name[0] == '.')
continue;
snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d/%s", bus, data->d_name);
fd = open(buf, O_RDWR);
if (fd == -1)
continue;
/*
* Sigh. Different kernels have different member names
* for this structure.
*/
#ifdef HAVE_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE
ctrl.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
ctrl.wValue = USB_DT_DEVICE << 8;
ctrl.wIndex = 0;
ctrl.wLength = sizeof(buf);
#else
ctrl.requesttype = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
ctrl.request = USB_REQ_GET_DESCRIPTOR;
ctrl.value = USB_DT_DEVICE << 8;
ctrl.index = 0;
ctrl.length = sizeof(buf);
#endif
ctrl.data = buf;
ctrl.timeout = CTRL_TIMEOUT;
ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
close(fd);
}
closedir(dir);
}
pcap_t *
usb_create(const char *device, char *ebuf)
{
pcap_t *p;
p = pcap_create_common(device, ebuf);
if (p == NULL)
return (NULL);
p->activate_op = usb_activate;
return (p);
}
static int
usb_activate(pcap_t* handle)
{
char full_path[USB_LINE_LEN];
/* Initialize some components of the pcap structure. */
handle->bufsize = handle->snapshot;
handle->offset = 0;
handle->linktype = DLT_USB_LINUX;
handle->inject_op = usb_inject_linux;
handle->setfilter_op = install_bpf_program; /* no kernel filtering */
handle->setdirection_op = usb_setdirection_linux;
handle->set_datalink_op = NULL; /* can't change data link type */
handle->getnonblock_op = pcap_getnonblock_fd;
handle->setnonblock_op = pcap_setnonblock_fd;
/*get usb bus index from device name */
if (sscanf(handle->opt.source, USB_IFACE"%d", &handle->md.ifindex) != 1)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't get USB bus index from %s", handle->opt.source);
return PCAP_ERROR;
}
/*now select the read method: try to open binary interface */
snprintf(full_path, USB_LINE_LEN, LINUX_USB_MON_DEV"%d", handle->md.ifindex);
handle->fd = open(full_path, O_RDONLY, 0);
if (handle->fd >= 0)
{
if (handle->opt.rfmon) {
/*
* Monitor mode doesn't apply to USB devices.
*/
close(handle->fd);
return PCAP_ERROR_RFMON_NOTSUP;
}
/* binary api is available, try to use fast mmap access */
if (usb_mmap(handle)) {
handle->linktype = DLT_USB_LINUX_MMAPPED;
handle->stats_op = usb_stats_linux_bin;
handle->read_op = usb_read_linux_mmap;
handle->cleanup_op = usb_cleanup_linux_mmap;
probe_devices(handle->md.ifindex);
/*
* "handle->fd" is a real file, so "select()" and
* "poll()" work on it.
*/
handle->selectable_fd = handle->fd;
return 0;
}
/* can't mmap, use plain binary interface access */
handle->stats_op = usb_stats_linux_bin;
handle->read_op = usb_read_linux_bin;
probe_devices(handle->md.ifindex);
}
else {
/*Binary interface not available, try open text interface */
snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR"/%dt", handle->md.ifindex);
handle->fd = open(full_path, O_RDONLY, 0);
if (handle->fd < 0)
{
if (errno == ENOENT)
{
/*
* Not found at the new location; try
* the old location.
*/
snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%dt", handle->md.ifindex);
handle->fd = open(full_path, O_RDONLY, 0);
}
if (handle->fd < 0) {
/* no more fallback, give it up*/
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't open USB bus file %s: %s", full_path, strerror(errno));
return PCAP_ERROR;
}
}
if (handle->opt.rfmon) {
/*
* Monitor mode doesn't apply to USB devices.
*/
close(handle->fd);
return PCAP_ERROR_RFMON_NOTSUP;
}
handle->stats_op = usb_stats_linux;
handle->read_op = usb_read_linux;
}
/*
* "handle->fd" is a real file, so "select()" and "poll()"
* work on it.
*/
handle->selectable_fd = handle->fd;
/* for plain binary access and text access we need to allocate the read
* buffer */
handle->buffer = malloc(handle->bufsize);
if (!handle->buffer) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"malloc: %s", pcap_strerror(errno));
close(handle->fd);
return PCAP_ERROR;
}
return 0;
}
static inline int
ascii_to_int(char c)
{
return c < 'A' ? c- '0': ((c<'a') ? c - 'A' + 10: c-'a'+10);
}
/*
* see <linux-kernel-source>/Documentation/usb/usbmon.txt and
* <linux-kernel-source>/drivers/usb/mon/mon_text.c for urb string
* format description
*/
static int
usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
/* see:
* /usr/src/linux/Documentation/usb/usbmon.txt
* for message format
*/
unsigned timestamp;
int tag, cnt, ep_num, dev_addr, dummy, ret, urb_len, data_len;
char etype, pipeid1, pipeid2, status[16], urb_tag, line[USB_LINE_LEN];
char *string = line;
u_char * rawdata = handle->buffer;
struct pcap_pkthdr pkth;
pcap_usb_header* uhdr = (pcap_usb_header*)handle->buffer;
u_char urb_transfer=0;
int incoming=0;
/* ignore interrupt system call errors */
do {
ret = read(handle->fd, line, USB_LINE_LEN - 1);
if (handle->break_loop)
{
handle->break_loop = 0;
return -2;
}
} while ((ret == -1) && (errno == EINTR));
if (ret < 0)
{
if (errno == EAGAIN)
return 0; /* no data there */
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't read from fd %d: %s", handle->fd, strerror(errno));
return -1;
}
/* read urb header; %n argument may increment return value, but it's
* not mandatory, so does not count on it*/
string[ret] = 0;
ret = sscanf(string, "%x %d %c %c%c:%d:%d %s%n", &tag, &timestamp, &etype,
&pipeid1, &pipeid2, &dev_addr, &ep_num, status,
&cnt);
if (ret < 8)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't parse USB bus message '%s', too few tokens (expected 8 got %d)",
string, ret);
return -1;
}
uhdr->id = tag;
uhdr->device_address = dev_addr;
uhdr->bus_id = handle->md.ifindex;
uhdr->status = 0;
string += cnt;
/* don't use usbmon provided timestamp, since it have low precision*/
if (gettimeofday(&pkth.ts, NULL) < 0)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't get timestamp for message '%s' %d:%s",
string, errno, strerror(errno));
return -1;
}
uhdr->ts_sec = pkth.ts.tv_sec;
uhdr->ts_usec = pkth.ts.tv_usec;
/* parse endpoint information */
if (pipeid1 == 'C')
urb_transfer = URB_CONTROL;
else if (pipeid1 == 'Z')
urb_transfer = URB_ISOCHRONOUS;
else if (pipeid1 == 'I')
urb_transfer = URB_INTERRUPT;
else if (pipeid1 == 'B')
urb_transfer = URB_BULK;
if (pipeid2 == 'i') {
ep_num |= URB_TRANSFER_IN;
incoming = 1;
}
if (etype == 'C')
incoming = !incoming;
/* direction check*/
if (incoming)
{
if (handle->direction == PCAP_D_OUT)
return 0;
}
else
if (handle->direction == PCAP_D_IN)
return 0;
uhdr->event_type = etype;
uhdr->transfer_type = urb_transfer;
uhdr->endpoint_number = ep_num;
pkth.caplen = sizeof(pcap_usb_header);
rawdata += sizeof(pcap_usb_header);
/* check if this is a setup packet */
ret = sscanf(status, "%d", &dummy);
if (ret != 1)
{
/* this a setup packet, setup data can be filled with underscore if
* usbmon has not been able to read them, so we must parse this fields as
* strings */
pcap_usb_setup* shdr;
char str1[3], str2[3], str3[5], str4[5], str5[5];
ret = sscanf(string, "%s %s %s %s %s%n", str1, str2, str3, str4,
str5, &cnt);
if (ret < 5)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't parse USB bus message '%s', too few tokens (expected 5 got %d)",
string, ret);
return -1;
}
string += cnt;
/* try to convert to corresponding integer */
shdr = &uhdr->setup;
shdr->bmRequestType = strtoul(str1, 0, 16);
shdr->bRequest = strtoul(str2, 0, 16);
shdr->wValue = htols(strtoul(str3, 0, 16));
shdr->wIndex = htols(strtoul(str4, 0, 16));
shdr->wLength = htols(strtoul(str5, 0, 16));
uhdr->setup_flag = 0;
}
else
uhdr->setup_flag = 1;
/* read urb data */
ret = sscanf(string, " %d%n", &urb_len, &cnt);
if (ret < 1)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't parse urb length from '%s'", string);
return -1;
}
string += cnt;
/* urb tag is not present if urb length is 0, so we can stop here
* text parsing */
pkth.len = urb_len+pkth.caplen;
uhdr->urb_len = urb_len;
uhdr->data_flag = 1;
data_len = 0;
if (uhdr->urb_len == 0)
goto got;
/* check for data presence; data is present if and only if urb tag is '=' */
if (sscanf(string, " %c", &urb_tag) != 1)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't parse urb tag from '%s'", string);
return -1;
}
if (urb_tag != '=')
goto got;
/* skip urb tag and following space */
string += 3;
/* if we reach this point we got some urb data*/
uhdr->data_flag = 0;
/* read all urb data; if urb length is greater then the usbmon internal
* buffer length used by the kernel to spool the URB, we get only
* a partial information.
* At least until linux 2.6.17 there is no way to set usbmon intenal buffer
* length and default value is 130. */
while ((string[0] != 0) && (string[1] != 0) && (pkth.caplen < handle->snapshot))
{
rawdata[0] = ascii_to_int(string[0]) * 16 + ascii_to_int(string[1]);
rawdata++;
string+=2;
if (string[0] == ' ')
string++;
pkth.caplen++;
data_len++;
}
got:
uhdr->data_len = data_len;
if (pkth.caplen > handle->snapshot)
pkth.caplen = handle->snapshot;
if (handle->fcode.bf_insns == NULL ||
bpf_filter(handle->fcode.bf_insns, handle->buffer,
pkth.len, pkth.caplen)) {
handle->md.packets_read++;
callback(user, &pkth, handle->buffer);
return 1;
}
return 0; /* didn't pass filter */
}
static int
usb_inject_linux(pcap_t *handle, const void *buf, size_t size)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on "
"USB devices");
return (-1);
}
static int
usb_stats_linux(pcap_t *handle, struct pcap_stat *stats)
{
int dummy, ret, consumed, cnt;
char string[USB_LINE_LEN];
char token[USB_LINE_LEN];
char * ptr = string;
int fd;
snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handle->md.ifindex);
fd = open(string, O_RDONLY, 0);
if (fd < 0)
{
if (errno == ENOENT)
{
/*
* Not found at the new location; try the old
* location.
*/
snprintf(string, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%ds", handle->md.ifindex);
fd = open(string, O_RDONLY, 0);
}
if (fd < 0) {
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't open USB stats file %s: %s",
string, strerror(errno));
return -1;
}
}
/* read stats line */
do {
ret = read(fd, string, USB_LINE_LEN-1);
} while ((ret == -1) && (errno == EINTR));
close(fd);
if (ret < 0)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't read stats from fd %d ", fd);
return -1;
}
string[ret] = 0;
/* extract info on dropped urbs */
for (consumed=0; consumed < ret; ) {
/* from the sscanf man page:
* The C standard says: "Execution of a %n directive does
* not increment the assignment count returned at the completion
* of execution" but the Corrigendum seems to contradict this.
* Do not make any assumptions on the effect of %n conversions
* on the return value and explicitly check for cnt assignmet*/
int ntok;
cnt = -1;
ntok = sscanf(ptr, "%s%n", token, &cnt);
if ((ntok < 1) || (cnt < 0))
break;
consumed += cnt;
ptr += cnt;
if (strcmp(token, "nreaders") == 0)
ret = sscanf(ptr, "%d", &stats->ps_drop);
else
ret = sscanf(ptr, "%d", &dummy);
if (ntok != 1)
break;
consumed += cnt;
ptr += cnt;
}
stats->ps_recv = handle->md.packets_read;
stats->ps_ifdrop = 0;
return 0;
}
static int
usb_setdirection_linux(pcap_t *p, pcap_direction_t d)
{
p->direction = d;
return 0;
}
static int
usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats)
{
int ret;
struct mon_bin_stats st;
ret = ioctl(handle->fd, MON_IOCG_STATS, &st);
if (ret < 0)
{
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't read stats from fd %d:%s ", handle->fd, strerror(errno));
return -1;
}
stats->ps_recv = handle->md.packets_read + st.queued;
stats->ps_drop = st.dropped;
stats->ps_ifdrop = 0;
return 0;
}
/*
* see <linux-kernel-source>/Documentation/usb/usbmon.txt and
* <linux-kernel-source>/drivers/usb/mon/mon_bin.c binary ABI
*/
static int
usb_read_linux_bin(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
struct mon_bin_get info;
int ret;
struct pcap_pkthdr pkth;
int clen = handle->snapshot - sizeof(pcap_usb_header);
/* the usb header is going to be part of 'packet' data*/
info.hdr = (pcap_usb_header*) handle->buffer;
info.data = handle->buffer + sizeof(pcap_usb_header);
info.data_len = clen;
/* ignore interrupt system call errors */
do {
ret = ioctl(handle->fd, MON_IOCX_GET, &info);
if (handle->break_loop)
{
handle->break_loop = 0;
return -2;
}
} while ((ret == -1) && (errno == EINTR));
if (ret < 0)
{
if (errno == EAGAIN)
return 0; /* no data there */
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't read from fd %d: %s", handle->fd, strerror(errno));
return -1;
}
/* we can get less that than really captured from kernel, depending on
* snaplen, so adjust header accordingly */
if (info.hdr->data_len < clen)
clen = info.hdr->data_len;
info.hdr->data_len = clen;
pkth.caplen = clen + sizeof(pcap_usb_header);
pkth.len = info.hdr->data_len + sizeof(pcap_usb_header);
pkth.ts.tv_sec = info.hdr->ts_sec;
pkth.ts.tv_usec = info.hdr->ts_usec;
if (handle->fcode.bf_insns == NULL ||
bpf_filter(handle->fcode.bf_insns, handle->buffer,
pkth.len, pkth.caplen)) {
handle->md.packets_read++;
callback(user, &pkth, handle->buffer);
return 1;
}
return 0; /* didn't pass filter */
}
/*
* see <linux-kernel-source>/Documentation/usb/usbmon.txt and
* <linux-kernel-source>/drivers/usb/mon/mon_bin.c binary ABI
*/
#define VEC_SIZE 32
static int
usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
struct mon_bin_mfetch fetch;
int32_t vec[VEC_SIZE];
struct pcap_pkthdr pkth;
pcap_usb_header* hdr;
int nflush = 0;
int packets = 0;
int clen, max_clen;
max_clen = handle->snapshot - sizeof(pcap_usb_header);
for (;;) {
int i, ret;
int limit = max_packets - packets;
if (limit <= 0)
limit = VEC_SIZE;
if (limit > VEC_SIZE)
limit = VEC_SIZE;
/* try to fetch as many events as possible*/
fetch.offvec = vec;
fetch.nfetch = limit;
fetch.nflush = nflush;
/* ignore interrupt system call errors */
do {
ret = ioctl(handle->fd, MON_IOCX_MFETCH, &fetch);
if (handle->break_loop)
{
handle->break_loop = 0;
return -2;
}
} while ((ret == -1) && (errno == EINTR));
if (ret < 0)
{
if (errno == EAGAIN)
return 0; /* no data there */
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Can't mfetch fd %d: %s", handle->fd, strerror(errno));
return -1;
}
/* keep track of processed events, we will flush them later */
nflush = fetch.nfetch;
for (i=0; i<fetch.nfetch; ++i) {
/* discard filler */
hdr = (pcap_usb_header*) &handle->md.mmapbuf[vec[i]];
if (hdr->event_type == '@')
continue;
/* we can get less that than really captured from kernel, depending on
* snaplen, so adjust header accordingly */
clen = max_clen;
if (hdr->data_len < clen)
clen = hdr->data_len;
/* get packet info from header*/
pkth.caplen = clen + sizeof(pcap_usb_header_mmapped);
pkth.len = hdr->data_len + sizeof(pcap_usb_header_mmapped);
pkth.ts.tv_sec = hdr->ts_sec;
pkth.ts.tv_usec = hdr->ts_usec;
if (handle->fcode.bf_insns == NULL ||
bpf_filter(handle->fcode.bf_insns, (u_char*) hdr,
pkth.len, pkth.caplen)) {
handle->md.packets_read++;
callback(user, &pkth, (u_char*) hdr);
packets++;
}
}
/* with max_packets <= 0 we stop afer the first chunk*/
if ((max_packets <= 0) || (packets == max_packets))
break;
}
/* flush pending events*/
ioctl(handle->fd, MON_IOCH_MFLUSH, nflush);
return packets;
}
static void
usb_cleanup_linux_mmap(pcap_t* handle)
{
/* if we have a memory-mapped buffer, unmap it */
if (handle->md.mmapbuf != NULL) {
munmap(handle->md.mmapbuf, handle->md.mmapbuflen);
handle->md.mmapbuf = NULL;
}
pcap_cleanup_live_common(handle);
}