diff options
Diffstat (limited to 'pcap-usb-linux.c')
| -rw-r--r-- | pcap-usb-linux.c | 877 |
1 files changed, 229 insertions, 648 deletions
diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c index b306dca9b951..7020577ca6cb 100644 --- a/pcap-usb-linux.c +++ b/pcap-usb-linux.c @@ -39,13 +39,15 @@ #include "pcap-int.h" #include "pcap-usb-linux.h" +#include "pcap-usb-linux-common.h" #include "pcap/usb.h" +#include "extract.h" + #ifdef NEED_STRERROR_H #include "strerror.h" #endif -#include <ctype.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> @@ -69,11 +71,12 @@ #include <linux/usbdevice_fs.h> #endif /* HAVE_LINUX_USBDEVICE_FS_H */ +#include "diag-control.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 USBMON_DEV_PREFIX "usbmon" +#define USBMON_DEV_PREFIX_LEN (sizeof USBMON_DEV_PREFIX - 1) #define USB_LINE_LEN 4096 #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -131,106 +134,20 @@ struct pcap_usb_linux { /* 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_inject_linux(pcap_t *, const void *, int); static int usb_setdirection_linux(pcap_t *, pcap_direction_t); static void usb_cleanup_linux_mmap(pcap_t *); -static int -have_binary_usbmon(void) -{ - struct utsname utsname; - char *version_component, *endp; - int major, minor, subminor; - - if (uname(&utsname) == 0) { - /* - * 2.6.21 is the first release with the binary-mode - * USB monitoring. - */ - version_component = utsname.release; - major = strtol(version_component, &endp, 10); - if (endp != version_component && *endp == '.') { - /* - * OK, that was a valid major version. - * Is it 3 or greater? If so, we have binary - * mode support. - */ - if (major >= 3) - return 1; - - /* - * Is it 1 or less? If so, we don't have binary - * mode support. (In fact, we don't have any - * USB monitoring....) - */ - if (major <= 1) - return 0; - } - - /* - * OK, this is a 2.x kernel. - * What's the minor version? - */ - version_component = endp + 1; - minor = strtol(version_component, &endp, 10); - if (endp != version_component && - (*endp == '.' || *endp == '\0')) { - /* - * OK, that was a valid minor version. - * Is is 2.6 or later? (There shouldn't be a - * "later", as 2.6.x went to 3.x, but we'll - * check anyway.) - */ - if (minor < 6) { - /* - * No, so no binary support (did 2.4 have - * any USB monitoring at all?) - */ - return 0; - } - - /* - * OK, this is a 2.6.x kernel. - * What's the subminor version? - */ - version_component = endp + 1; - subminor = strtol(version_component, &endp, 10); - if (endp != version_component && - (*endp == '.' || *endp == '\0')) { - /* - * OK, that was a valid subminor version. - * Is it 21 or greater? - */ - if (subminor >= 21) { - /* - * Yes - we have binary mode - * support. - */ - return 1; - } - } - } - } - - /* - * Either uname() failed, in which case we just say "no binary - * mode support", or we don't have binary mode support. - */ - return 0; -} - /* facility to add an USB device to the device list*/ static int usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str) { char dev_name[10]; char dev_descr[30]; - pcap_snprintf(dev_name, 10, USB_IFACE"%d", n); + snprintf(dev_name, 10, USB_IFACE"%d", n); /* * XXX - is there any notion of "up" and "running"? */ @@ -251,7 +168,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, "Raw USB traffic, bus number %d", n); + 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; } @@ -262,123 +179,41 @@ usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str) int usb_findalldevs(pcap_if_list_t *devlistp, char *err_str) { - char usb_mon_dir[PATH_MAX]; - char *usb_mon_prefix; - size_t usb_mon_prefix_len; struct dirent* data; int ret = 0; DIR* dir; int n; char* name; - size_t len; - if (have_binary_usbmon()) { - /* - * We have binary-mode support. - * What do the device names look like? - * Split LINUX_USB_MON_DEV into a directory that we'll - * scan and a file name prefix that we'll check for. - */ - 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) { + /* + * We require 2.6.27 or later kernels, so we have binary-mode support. + * The devices are of the form /dev/usbmon{N}. + * Open /dev and scan it. + */ + dir = opendir("/dev"); + if (dir != NULL) { + while ((ret == 0) && ((data = readdir(dir)) != 0)) { + name = data->d_name; + /* - * This "shouldn't happen". Just give up if it - * does. + * Is this a usbmon device? */ - return 0; - } - *usb_mon_prefix++ = '\0'; - usb_mon_prefix_len = strlen(usb_mon_prefix); + if (strncmp(name, USBMON_DEV_PREFIX, + USBMON_DEV_PREFIX_LEN) != 0) + continue; /* no */ - /* - * Open the directory and scan it. - */ - dir = opendir(usb_mon_dir); - if (dir != NULL) { - while ((ret == 0) && ((data = readdir(dir)) != 0)) { - name = data->d_name; - - /* - * Is this a usbmon device? - */ - if (strncmp(name, usb_mon_prefix, usb_mon_prefix_len) != 0) - continue; /* no */ - - /* - * What's the device number? - */ - if (sscanf(&name[usb_mon_prefix_len], "%d", &n) == 0) - continue; /* failed */ - - ret = usb_dev_add(devlistp, n, err_str); - } - - closedir(dir); - } - return 0; - } else { - /* - * We have only text mode support. - * We don't look for the text devices because we can't - * look for them without root privileges, and we don't - * want to require root privileges to enumerate devices - * (we want to let the user to try a device and get - * an error, rather than seeing no devices and asking - * "why am I not seeing devices" and forcing a long - * process of poking to figure out whether it's "no - * privileges" or "your kernel is too old" or "the - * usbmon module isn't loaded" or...). - * - * Instead, we look to see what buses we have. - * If the kernel is so old that it doesn't have - * binary-mode support, it's also so old that - * it doesn't have a "scan all buses" device. - * - * First, 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(devlistp, n, err_str); - } - - closedir(dir); - return 0; - } - - /* 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(devlistp, n, err_str); - } + /* + * What's the device number? + */ + if (sscanf(&name[USBMON_DEV_PREFIX_LEN], "%d", &n) == 0) + continue; /* failed */ - closedir(dir); - return ret; + ret = usb_dev_add(devlistp, n, err_str); } - /* neither of them worked */ - return 0; + closedir(dir); } + return 0; } /* @@ -490,6 +325,10 @@ int usb_mmap(pcap_t* handle) #define USB_REQ_GET_DESCRIPTOR 6 #define USB_DT_DEVICE 1 +#define USB_DT_CONFIG 2 + +#define USB_DEVICE_DESCRIPTOR_SIZE 18 +#define USB_CONFIG_DESCRIPTOR_SIZE 9 /* probe the descriptors of the devices attached to the bus */ /* the descriptors will end up in the captured packet stream */ @@ -501,12 +340,14 @@ probe_devices(int bus) struct usbdevfs_ctrltransfer ctrl; struct dirent* data; int ret = 0; - char buf[sizeof("/dev/bus/usb/000/") + NAME_MAX]; + char busdevpath[sizeof("/dev/bus/usb/000/") + NAME_MAX]; DIR* dir; + uint8_t descriptor[USB_DEVICE_DESCRIPTOR_SIZE]; + uint8_t configdesc[USB_CONFIG_DESCRIPTOR_SIZE]; /* scan usb bus directories for device nodes */ - pcap_snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d", bus); - dir = opendir(buf); + snprintf(busdevpath, sizeof(busdevpath), "/dev/bus/usb/%03d", bus); + dir = opendir(busdevpath); if (!dir) return; @@ -517,9 +358,9 @@ probe_devices(int bus) if (name[0] == '.') continue; - pcap_snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d/%s", bus, data->d_name); + snprintf(busdevpath, sizeof(busdevpath), "/dev/bus/usb/%03d/%s", bus, data->d_name); - fd = open(buf, O_RDWR); + fd = open(busdevpath, O_RDWR); if (fd == -1) continue; @@ -532,19 +373,43 @@ probe_devices(int bus) ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; ctrl.wValue = USB_DT_DEVICE << 8; ctrl.wIndex = 0; - ctrl.wLength = sizeof(buf); + ctrl.wLength = sizeof(descriptor); #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); + ctrl.length = sizeof(descriptor); #endif - ctrl.data = buf; + ctrl.data = descriptor; ctrl.timeout = CTRL_TIMEOUT; ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + /* Request CONFIGURATION descriptor alone to know wTotalLength */ +#ifdef HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE + ctrl.wValue = USB_DT_CONFIG << 8; + ctrl.wLength = sizeof(configdesc); +#else + ctrl.value = USB_DT_CONFIG << 8; + ctrl.length = sizeof(configdesc); +#endif + ctrl.data = configdesc; + ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + if (ret >= 0) { + uint16_t wtotallength; + wtotallength = EXTRACT_LE_U_2(&configdesc[2]); +#ifdef HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE + ctrl.wLength = wtotallength; +#else + ctrl.length = wtotallength; +#endif + ctrl.data = malloc(wtotallength); + if (ctrl.data) { + ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + free(ctrl.data); + } + } close(fd); } closedir(dir); @@ -586,7 +451,7 @@ usb_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_usb_linux)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_usb_linux); if (p == NULL) return (NULL); @@ -599,7 +464,6 @@ 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 @@ -627,171 +491,113 @@ usb_activate(pcap_t* handle) /*get usb bus index from device name */ if (sscanf(handle->opt.device, USB_IFACE"%d", &handlep->bus_index) != 1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get USB bus index from %s", handle->opt.device); return PCAP_ERROR; } - if (have_binary_usbmon()) + /* + * We require 2.6.27 or later kernels, so we have binary-mode support. + * Try to open the binary interface. + */ + snprintf(full_path, USB_LINE_LEN, "/dev/"USBMON_DEV_PREFIX"%d", + handlep->bus_index); + handle->fd = open(full_path, O_RDONLY, 0); + if (handle->fd < 0) { /* - * We have binary-mode support. - * Try to open the binary interface. + * The attempt failed; why? */ - pcap_snprintf(full_path, USB_LINE_LEN, LINUX_USB_MON_DEV"%d", handlep->bus_index); - handle->fd = open(full_path, O_RDONLY, 0); - if (handle->fd < 0) - { + switch (errno) { + + case ENOENT: /* - * The attempt failed; why? + * The device doesn't exist. + * That could either mean that there's + * no support for monitoring USB buses + * (which probably means "the usbmon + * module isn't loaded") or that there + * is but that *particular* device + * doesn't exist (no "scan all buses" + * device if the bus index is 0, no + * such bus if the bus index isn't 0). + * + * For now, don't provide an error message; + * if we can determine what the particular + * problem is, we should report that. */ - switch (errno) { - - case ENOENT: - /* - * The device doesn't exist. - * That could either mean that there's - * no support for monitoring USB buses - * (which probably means "the usbmon - * module isn't loaded") or that there - * is but that *particular* device - * doesn't exist (no "scan all buses" - * device if the bus index is 0, no - * such bus if the bus index isn't 0). - */ - return PCAP_ERROR_NO_SUCH_DEVICE; - - case EACCES: - /* - * We didn't have permission to open it. - */ - return PCAP_ERROR_PERM_DENIED; + handle->errbuf[0] = '\0'; + return PCAP_ERROR_NO_SUCH_DEVICE; - default: - /* - * Something went wrong. - */ - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "Can't open USB bus file %s", full_path); - return PCAP_ERROR; - } - } - - if (handle->opt.rfmon) - { + case EACCES: /* - * Monitor mode doesn't apply to USB devices. + * We didn't have permission to open it. */ - close(handle->fd); - return PCAP_ERROR_RFMON_NOTSUP; - } - - /* 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; - handle->cleanup_op = usb_cleanup_linux_mmap; -#ifdef HAVE_LINUX_USBDEVICE_FS_H - probe_devices(handlep->bus_index); -#endif - +DIAG_OFF_FORMAT_TRUNCATION + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with EACCES - root privileges may be required", + full_path); +DIAG_ON_FORMAT_TRUNCATION + return PCAP_ERROR_PERM_DENIED; + + default: /* - * "handle->fd" is a real file, so - * "select()" and "poll()" work on it. + * Something went wrong. */ - handle->selectable_fd = handle->fd; - return 0; + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, errno, + "Can't open USB bus file %s", full_path); + return PCAP_ERROR; } + } + if (handle->opt.rfmon) + { /* - * 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. + * Monitor mode doesn't apply to USB devices. */ - if (usb_set_ring_size(handle, (int)sizeof(pcap_usb_header)) == -1) { - /* Failed. */ - close(handle->fd); - return PCAP_ERROR; - } + close(handle->fd); + return PCAP_ERROR_RFMON_NOTSUP; + } + + /* 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_bin; + handle->read_op = usb_read_linux_mmap; + handle->cleanup_op = usb_cleanup_linux_mmap; #ifdef HAVE_LINUX_USBDEVICE_FS_H probe_devices(handlep->bus_index); #endif - } - else { + /* - * We don't have binary mode support. - * Try opening the text-mode device. + * "handle->fd" is a real file, so + * "select()" and "poll()" work on it. */ - pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR"/%dt", handlep->bus_index); - 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. - */ - pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%dt", handlep->bus_index); - handle->fd = open(full_path, O_RDONLY, 0); - } - if (handle->fd < 0) { - if (errno == ENOENT) - { - /* - * 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".) - */ - ret = PCAP_ERROR_NO_SUCH_DEVICE; - } - else if (errno == EACCES) - { - /* - * The problem is that we don't - * have sufficient permission to - * open the file. Report that. - */ - ret = PCAP_ERROR_PERM_DENIED; - } - 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; - } - } - - if (handle->opt.rfmon) - { - /* - * Monitor mode doesn't apply to USB devices. - */ - close(handle->fd); - return PCAP_ERROR_RFMON_NOTSUP; - } + handle->selectable_fd = handle->fd; + return 0; + } - handle->stats_op = usb_stats_linux; - handle->read_op = usb_read_linux; + /* + * 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 + probe_devices(handlep->bus_index); +#endif /* * "handle->fd" is a real file, so "select()" and "poll()" @@ -811,308 +617,25 @@ usb_activate(pcap_t* handle) 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 _U_, pcap_handler callback, u_char *user) +usb_inject_linux(pcap_t *handle, const void *buf _U_, int size _U_) { - /* see: - * /usr/src/linux/Documentation/usb/usbmon.txt - * for message format - */ - struct pcap_usb_linux *handlep = handle->priv; - 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 */ - - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't read from fd %d", handle->fd); - 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, ×tamp, &etype, - &pipeid1, &pipeid2, &dev_addr, &ep_num, status, - &cnt); - if (ret < 8) - { - pcap_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 = handlep->bus_index; - uhdr->status = 0; - string += cnt; - - /* don't use usbmon provided timestamp, since it have low precision*/ - if (gettimeofday(&pkth.ts, NULL) < 0) - { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't get timestamp for message '%s'", string); - 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) - { - pcap_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) - { - pcap_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) - { - pcap_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 < (bpf_u_int32)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 > (bpf_u_int32)handle->snapshot) - pkth.caplen = (bpf_u_int32)handle->snapshot; - - if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, handle->buffer, - pkth.len, pkth.caplen)) { - handlep->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 _U_, size_t size _U_) -{ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Packet injection is not supported on USB devices"); return (-1); } static int -usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) -{ - struct pcap_usb_linux *handlep = handle->priv; - int dummy, ret, consumed, cnt; - char string[USB_LINE_LEN]; - char token[USB_LINE_LEN]; - char * ptr = string; - int fd; - - pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handlep->bus_index); - fd = open(string, O_RDONLY, 0); - if (fd < 0) - { - if (errno == ENOENT) - { - /* - * Not found at the new location; try the old - * location. - */ - pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%ds", handlep->bus_index); - fd = open(string, O_RDONLY, 0); - } - if (fd < 0) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "Can't open USB stats file %s", string); - return -1; - } - } - - /* read stats line */ - do { - ret = read(fd, string, USB_LINE_LEN-1); - } while ((ret == -1) && (errno == EINTR)); - close(fd); - - if (ret < 0) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't read stats from fd %d ", fd); - return -1; - } - 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: - * 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, "text_lost") == 0) - ntok = sscanf(ptr, "%d%n", &stats->ps_drop, &cnt); - else - ntok = sscanf(ptr, "%d%n", &dummy, &cnt); - if ((ntok != 1) || (cnt < 0)) - break; - consumed += cnt; - ptr += cnt; - } - - return 0; -} - -static int usb_setdirection_linux(pcap_t *p, pcap_direction_t d) { + /* + * It's guaranteed, at this point, that d is a valid + * direction value. + */ p->direction = d; return 0; } - static int usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats) { @@ -1208,11 +731,11 @@ usb_read_linux_bin(pcap_t *handle, int max_packets _U_, pcap_handler callback, u */ pkth.len = sizeof(pcap_usb_header) + info.hdr->urb_len; } - pkth.ts.tv_sec = info.hdr->ts_sec; + pkth.ts.tv_sec = (time_t)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, + pcap_filter(handle->fcode.bf_insns, handle->buffer, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, handle->buffer); @@ -1234,6 +757,7 @@ 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; + u_char *bp; pcap_usb_header_mmapped* hdr; int nflush = 0; int packets = 0; @@ -1243,13 +767,40 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch for (;;) { int i, ret; - int limit = max_packets - packets; - if (limit <= 0) - limit = VEC_SIZE; - if (limit > VEC_SIZE) + int limit; + + if (PACKET_COUNT_IS_UNLIMITED(max_packets)) { + /* + * There's no limit on the number of packets + * to process, so try to fetch VEC_SIZE packets. + */ limit = VEC_SIZE; + } else { + /* + * Try to fetch as many packets as we have left + * to process, or VEC_SIZE packets, whichever + * is less. + * + * At this point, max_packets > 0 (otherwise, + * PACKET_COUNT_IS_UNLIMITED(max_packets) + * would be true) and max_packets > packets + * (packet starts out as 0, and the test + * at the bottom of the loop exits if + * max_packets <= packets), so limit is + * guaranteed to be > 0. + */ + limit = max_packets - packets; + if (limit > VEC_SIZE) + limit = VEC_SIZE; + } - /* try to fetch as many events as possible*/ + /* + * Try to fetch as many events as possible, up to + * the limit, and flush the events we've processed + * earlier (nflush) - MON_IOCX_MFETCH does both + * (presumably to reduce the number of system + * calls in loops like this). + */ fetch.offvec = vec; fetch.nfetch = limit; fetch.nflush = nflush; @@ -1276,8 +827,27 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch /* keep track of processed events, we will flush them later */ nflush = fetch.nfetch; for (i=0; i<fetch.nfetch; ++i) { + /* + * XXX - we can't check break_loop here, as + * we read the indices of packets into a + * local variable, so if we're later called + * to fetch more packets, those packets will + * not be seen - and won't be flushed, either. + * + * Instead, we would have to keep the array + * of indices in our private data, along + * with the count of packets to flush - or + * would have to flush the already-processed + * packets if we break out of the loop here. + */ + + /* Get a pointer to this packet's buffer */ + bp = &handlep->mmapbuf[vec[i]]; + + /* That begins with a metadata header */ + hdr = (pcap_usb_header_mmapped*) bp; + /* discard filler */ - hdr = (pcap_usb_header_mmapped*) &handlep->mmapbuf[vec[i]]; if (hdr->event_type == '@') continue; @@ -1322,12 +892,19 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch */ pkth.len = sizeof(pcap_usb_header_mmapped) + (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len; + + /* + * Now clean it up if it's a completion + * event for an incoming isochronous + * transfer. + */ + fix_linux_usb_mmapped_length(&pkth, bp); } - pkth.ts.tv_sec = hdr->ts_sec; + pkth.ts.tv_sec = (time_t)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, + pcap_filter(handle->fcode.bf_insns, (u_char*) hdr, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, (u_char*) hdr); @@ -1335,8 +912,12 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch } } - /* with max_packets specifying "unlimited" we stop afer the first chunk*/ - if (PACKET_COUNT_IS_UNLIMITED(max_packets) || (packets == max_packets)) + /* + * If max_packets specifiesg "unlimited", we stop after + * the first chunk. + */ + if (PACKET_COUNT_IS_UNLIMITED(max_packets) || + (packets >= max_packets)) break; } |
