aboutsummaryrefslogtreecommitdiff
path: root/pcap-usb-linux.c
diff options
context:
space:
mode:
authorPhilip Paeps <philip@FreeBSD.org>2019-10-06 04:26:37 +0000
committerPhilip Paeps <philip@FreeBSD.org>2019-10-06 04:26:37 +0000
commit30a580a870fabfea51e4b970c488e58bd1515ce4 (patch)
treecf18fcdc7c1818418ee5fc375ca4ba2094583a41 /pcap-usb-linux.c
parentd109bf9e4b609b5a0626b433e56db4a47dc530bb (diff)
downloadsrc-vendor/libpcap/1.9.1.tar.gz
src-vendor/libpcap/1.9.1.zip
Import libpcap 1.9.1vendor/libpcap/1.9.1
Diffstat (limited to 'pcap-usb-linux.c')
-rw-r--r--pcap-usb-linux.c273
1 files changed, 219 insertions, 54 deletions
diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c
index 6f8adf65eab5..b306dca9b951 100644
--- a/pcap-usb-linux.c
+++ b/pcap-usb-linux.c
@@ -50,6 +50,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
#include <string.h>
#include <dirent.h>
#include <byteswap.h>
@@ -241,7 +242,7 @@ usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str)
*/
if (add_dev(devlistp, dev_name,
PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
- "All USB buses", err_str) == NULL)
+ "Raw USB traffic, all USB buses", err_str) == NULL)
return -1;
} else {
/*
@@ -250,7 +251,7 @@ usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str)
* PCAP_IF_CONNECTION_STATUS_CONNECTED or
* PCAP_IF_CONNECTION_STATUS_DISCONNECTED?
*/
- pcap_snprintf(dev_descr, 30, "USB bus number %d", n);
+ pcap_snprintf(dev_descr, 30, "Raw USB traffic, bus number %d", n);
if (add_dev(devlistp, dev_name, 0, dev_descr, err_str) == NULL)
return -1;
}
@@ -278,7 +279,7 @@ usb_findalldevs(pcap_if_list_t *devlistp, char *err_str)
* Split LINUX_USB_MON_DEV into a directory that we'll
* scan and a file name prefix that we'll check for.
*/
- strlcpy(usb_mon_dir, LINUX_USB_MON_DEV, sizeof usb_mon_dir);
+ pcap_strlcpy(usb_mon_dir, LINUX_USB_MON_DEV, sizeof usb_mon_dir);
usb_mon_prefix = strrchr(usb_mon_dir, '/');
if (usb_mon_prefix == NULL) {
/*
@@ -380,18 +381,102 @@ usb_findalldevs(pcap_if_list_t *devlistp, char *err_str)
}
}
+/*
+ * Matches what's in mon_bin.c in the Linux kernel.
+ */
+#define MIN_RING_SIZE (8*1024)
+#define MAX_RING_SIZE (1200*1024)
+
+static int
+usb_set_ring_size(pcap_t* handle, int header_size)
+{
+ /*
+ * A packet from binary usbmon has:
+ *
+ * 1) a fixed-length header, of size header_size;
+ * 2) descriptors, for isochronous transfers;
+ * 3) the payload.
+ *
+ * The kernel buffer has a size, defaulting to 300KB, with a
+ * minimum of 8KB and a maximum of 1200KB. The size is set with
+ * the MON_IOCT_RING_SIZE ioctl; the size passed in is rounded up
+ * to a page size.
+ *
+ * No more than {buffer size}/5 bytes worth of payload is saved.
+ * Therefore, if we subtract the fixed-length size from the
+ * snapshot length, we have the biggest payload we want (we
+ * don't worry about the descriptors - if we have descriptors,
+ * we'll just discard the last bit of the payload to get it
+ * to fit). We multiply that result by 5 and set the buffer
+ * size to that value.
+ */
+ int ring_size;
+
+ if (handle->snapshot < header_size)
+ handle->snapshot = header_size;
+ /* The maximum snapshot size is small enough that this won't overflow */
+ ring_size = (handle->snapshot - header_size) * 5;
+
+ /*
+ * Will this get an error?
+ * (There's no wqy to query the minimum or maximum, so we just
+ * copy the value from the kernel source. We don't round it
+ * up to a multiple of the page size.)
+ */
+ if (ring_size > MAX_RING_SIZE) {
+ /*
+ * Yes. Lower the ring size to the maximum, and set the
+ * snapshot length to the value that would give us a
+ * maximum-size ring.
+ */
+ ring_size = MAX_RING_SIZE;
+ handle->snapshot = header_size + (MAX_RING_SIZE/5);
+ } else if (ring_size < MIN_RING_SIZE) {
+ /*
+ * Yes. Raise the ring size to the minimum, but leave
+ * the snapshot length unchanged, so we show the
+ * callback no more data than specified by the
+ * snapshot length.
+ */
+ ring_size = MIN_RING_SIZE;
+ }
+
+ if (ioctl(handle->fd, MON_IOCT_RING_SIZE, ring_size) == -1) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "Can't set ring size from fd %d", handle->fd);
+ return -1;
+ }
+ return ring_size;
+}
+
static
int usb_mmap(pcap_t* handle)
{
struct pcap_usb_linux *handlep = handle->priv;
- int len = ioctl(handle->fd, MON_IOCQ_RING_SIZE);
- if (len < 0)
+ int len;
+
+ /*
+ * Attempt to set the ring size as appropriate for the snapshot
+ * length, reducing the snapshot length if that'd make the ring
+ * bigger than the kernel supports.
+ */
+ len = usb_set_ring_size(handle, (int)sizeof(pcap_usb_header_mmapped));
+ if (len == -1) {
+ /* Failed. Fall back on non-memory-mapped access. */
return 0;
+ }
handlep->mmapbuflen = len;
handlep->mmapbuf = mmap(0, handlep->mmapbuflen, PROT_READ,
MAP_SHARED, handle->fd, 0);
- return handlep->mmapbuf != MAP_FAILED;
+ if (handlep->mmapbuf == MAP_FAILED) {
+ /*
+ * Failed. We don't treat that as a fatal error, we
+ * just try to fall back on non-memory-mapped access.
+ */
+ return 0;
+ }
+ return 1;
}
#ifdef HAVE_LINUX_USBDEVICE_FS_H
@@ -514,6 +599,7 @@ usb_activate(pcap_t* handle)
{
struct pcap_usb_linux *handlep = handle->priv;
char full_path[USB_LINE_LEN];
+ int ret;
/*
* Turn a negative snapshot value (invalid), a snapshot value of
@@ -604,6 +690,7 @@ usb_activate(pcap_t* handle)
/* try to use fast mmap access */
if (usb_mmap(handle))
{
+ /* We succeeded. */
handle->linktype = DLT_USB_LINUX_MMAPPED;
handle->stats_op = usb_stats_linux_bin;
handle->read_op = usb_read_linux_mmap;
@@ -620,7 +707,19 @@ usb_activate(pcap_t* handle)
return 0;
}
- /* can't mmap, use plain binary interface access */
+ /*
+ * We failed; try plain binary interface access.
+ *
+ * Attempt to set the ring size as appropriate for
+ * the snapshot length, reducing the snapshot length
+ * if that'd make the ring bigger than the kernel
+ * supports.
+ */
+ if (usb_set_ring_size(handle, (int)sizeof(pcap_usb_header)) == -1) {
+ /* Failed. */
+ close(handle->fd);
+ return PCAP_ERROR;
+ }
handle->stats_op = usb_stats_linux_bin;
handle->read_op = usb_read_linux_bin;
#ifdef HAVE_LINUX_USBDEVICE_FS_H
@@ -646,38 +745,38 @@ usb_activate(pcap_t* handle)
handle->fd = open(full_path, O_RDONLY, 0);
}
if (handle->fd < 0) {
- /*
- * Is the problem that we didn't have
- * sufficient permission to open it?
- */
- if (errno == EACCES) {
+ if (errno == ENOENT)
+ {
/*
- * Yes - return that error.
+ * The problem is that the file
+ * doesn't exist. Report that as
+ * "no such device". (That could
+ * mean "no such USB bus" or
+ * "monitoring not supported".)
*/
- return PCAP_ERROR_PERM_DENIED;
+ ret = PCAP_ERROR_NO_SUCH_DEVICE;
}
-
- /*
- * No - was the problem something other
- * than "it doesn't exist"?
- */
- if (errno != ENOENT) {
+ else if (errno == EACCES)
+ {
/*
- * Yes - return *that* error.
+ * The problem is that we don't
+ * have sufficient permission to
+ * open the file. Report that.
*/
- pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno,
- "Can't open USB bus file %s",
- full_path);
- return PCAP_ERROR;
+ ret = PCAP_ERROR_PERM_DENIED;
}
-
- /*
- * No. Report that as "no such device".
- * (That could mean "no such USB bus"
- * or "monitoring not supported".)
- */
- return PCAP_ERROR_NO_SUCH_DEVICE;
+ else
+ {
+ /*
+ * Some other error.
+ */
+ ret = PCAP_ERROR;
+ }
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "Can't open USB bus file %s",
+ full_path);
+ return ret;
}
}
@@ -923,8 +1022,8 @@ got:
static int
usb_inject_linux(pcap_t *handle, const void *buf _U_, size_t size _U_)
{
- pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on "
- "USB devices");
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "Packet injection is not supported on USB devices");
return (-1);
}
@@ -973,6 +1072,10 @@ usb_stats_linux(pcap_t *handle, struct pcap_stat *stats)
}
string[ret] = 0;
+ stats->ps_recv = handlep->packets_read;
+ stats->ps_drop = 0; /* unless we find text_lost */
+ stats->ps_ifdrop = 0;
+
/* extract info on dropped urbs */
for (consumed=0; consumed < ret; ) {
/* from the sscanf man page:
@@ -989,18 +1092,16 @@ usb_stats_linux(pcap_t *handle, struct pcap_stat *stats)
break;
consumed += cnt;
ptr += cnt;
- if (strcmp(token, "nreaders") == 0)
- ret = sscanf(ptr, "%d", &stats->ps_drop);
+ if (strcmp(token, "text_lost") == 0)
+ ntok = sscanf(ptr, "%d%n", &stats->ps_drop, &cnt);
else
- ret = sscanf(ptr, "%d", &dummy);
- if (ntok != 1)
+ ntok = sscanf(ptr, "%d%n", &dummy, &cnt);
+ if ((ntok != 1) || (cnt < 0))
break;
consumed += cnt;
ptr += cnt;
}
- stats->ps_recv = handlep->packets_read;
- stats->ps_ifdrop = 0;
return 0;
}
@@ -1069,13 +1170,44 @@ usb_read_linux_bin(pcap_t *handle, int max_packets _U_, pcap_handler callback, u
return -1;
}
- /* we can get less that than really captured from kernel, depending on
- * snaplen, so adjust header accordingly */
+ /*
+ * info.hdr->data_len is the number of bytes of isochronous
+ * descriptors (if any) plus the number of bytes of data
+ * provided. There are no isochronous descriptors here,
+ * because we're using the old 48-byte header.
+ *
+ * If info.hdr->data_flag is non-zero, there's no URB data;
+ * info.hdr->urb_len is the size of the buffer into which
+ * data is to be placed; it does not represent the amount
+ * of data transferred. If info.hdr->data_flag is zero,
+ * there is URB data, and info.hdr->urb_len is the number
+ * of bytes transmitted or received; it doesn't include
+ * isochronous descriptors.
+ *
+ * The kernel may give us more data than the snaplen; if it did,
+ * reduce the data length so that the total number of bytes we
+ * tell our client we have is not greater than the snaplen.
+ */
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.caplen = sizeof(pcap_usb_header) + clen;
+ if (info.hdr->data_flag) {
+ /*
+ * No data; just base the on-the-wire length on
+ * info.hdr->data_len (so that it's >= the captured
+ * length).
+ */
+ pkth.len = sizeof(pcap_usb_header) + info.hdr->data_len;
+ } else {
+ /*
+ * We got data; base the on-the-wire length on
+ * info.hdr->urb_len, so that it includes data
+ * discarded by the USB monitor device due to
+ * its buffer being too small.
+ */
+ pkth.len = sizeof(pcap_usb_header) + info.hdr->urb_len;
+ }
pkth.ts.tv_sec = info.hdr->ts_sec;
pkth.ts.tv_usec = info.hdr->ts_usec;
@@ -1102,12 +1234,12 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch
struct mon_bin_mfetch fetch;
int32_t vec[VEC_SIZE];
struct pcap_pkthdr pkth;
- pcap_usb_header* hdr;
+ pcap_usb_header_mmapped* hdr;
int nflush = 0;
int packets = 0;
u_int clen, max_clen;
- max_clen = handle->snapshot - sizeof(pcap_usb_header);
+ max_clen = handle->snapshot - sizeof(pcap_usb_header_mmapped);
for (;;) {
int i, ret;
@@ -1145,19 +1277,52 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch
nflush = fetch.nfetch;
for (i=0; i<fetch.nfetch; ++i) {
/* discard filler */
- hdr = (pcap_usb_header*) &handlep->mmapbuf[vec[i]];
+ hdr = (pcap_usb_header_mmapped*) &handlep->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 */
+ /*
+ * hdr->data_len is the number of bytes of
+ * isochronous descriptors (if any) plus the
+ * number of bytes of data provided.
+ *
+ * If hdr->data_flag is non-zero, there's no
+ * URB data; hdr->urb_len is the size of the
+ * buffer into which data is to be placed; it does
+ * not represent the amount of data transferred.
+ * If hdr->data_flag is zero, there is URB data,
+ * and hdr->urb_len is the number of bytes
+ * transmitted or received; it doesn't include
+ * isochronous descriptors.
+ *
+ * The kernel may give us more data than the
+ * snaplen; if it did, reduce the data length
+ * so that the total number of bytes we
+ * tell our client we have is not greater than
+ * the snaplen.
+ */
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.caplen = sizeof(pcap_usb_header_mmapped) + clen;
+ if (hdr->data_flag) {
+ /*
+ * No data; just base the on-the-wire length
+ * on hdr->data_len (so that it's >= the
+ * captured length).
+ */
+ pkth.len = sizeof(pcap_usb_header_mmapped) +
+ hdr->data_len;
+ } else {
+ /*
+ * We got data; base the on-the-wire length
+ * on hdr->urb_len, so that it includes
+ * data discarded by the USB monitor device
+ * due to its buffer being too small.
+ */
+ pkth.len = sizeof(pcap_usb_header_mmapped) +
+ (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len;
+ }
pkth.ts.tv_sec = hdr->ts_sec;
pkth.ts.tv_usec = hdr->ts_usec;