diff options
author | Philip Paeps <philip@FreeBSD.org> | 2019-10-06 04:26:37 +0000 |
---|---|---|
committer | Philip Paeps <philip@FreeBSD.org> | 2019-10-06 04:26:37 +0000 |
commit | 30a580a870fabfea51e4b970c488e58bd1515ce4 (patch) | |
tree | cf18fcdc7c1818418ee5fc375ca4ba2094583a41 /pcap-usb-linux.c | |
parent | d109bf9e4b609b5a0626b433e56db4a47dc530bb (diff) | |
download | src-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.c | 273 |
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; |