diff options
Diffstat (limited to 'pcap-usb-linux.c')
-rw-r--r-- | pcap-usb-linux.c | 254 |
1 files changed, 196 insertions, 58 deletions
diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c index 830abc775658d..fea527f4f9e1d 100644 --- a/pcap-usb-linux.c +++ b/pcap-usb-linux.c @@ -34,7 +34,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.c,v 1.16.2.8 2008-04-14 21:06:29 guy Exp $ (LBL)"; + "@(#) $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 @@ -60,19 +60,17 @@ static const char rcsid[] _U_ = #include <netinet/in.h> #include <sys/ioctl.h> #include <sys/mman.h> +#ifdef HAVE_LINUX_USBDEVICE_FS_H +#include <linux/usbdevice_fs.h> +#endif -#define USB_IFACE "usb" -#define USB_TEXT_DIR "/sys/kernel/debug/usbmon" -#define USB_BUS_DIR "/proc/bus/usb" +#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 - -#define PIPE_IN 0x80 -#define PIPE_ISOCHRONOUS 0 -#define PIPE_INTERRUPT 1 -#define PIPE_CONTROL 2 -#define PIPE_BULK 3 - #if __BYTE_ORDER == __LITTLE_ENDIAN #define htols(s) s #define htoll(l) l @@ -149,27 +147,52 @@ 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; - /* scan procfs usb bus directory */ - dir = opendir(USB_BUS_DIR); - if (!dir) return 0; - while ((ret == 0) && ((data = readdir(dir)) != 0)) { - int n; - char* name = data->d_name; - int len = strlen(name); + /* 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 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; + if (strncmp(name, "usb", 3) != 0) + continue; + + if (sscanf(&name[3], "%d", &n) == 0) + continue; + + ret = usb_dev_add(alldevsp, n, err_str); + } - ret = usb_dev_add(alldevsp, n, err_str); + closedir(dir); + return ret; } - 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 @@ -179,8 +202,79 @@ int usb_mmap(pcap_t* handle) if (len < 0) return 0; - handle->buffer = mmap(0, len, PROT_READ, MAP_SHARED, handle->fd, 0); - return handle->buffer != MAP_FAILED; + 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 * @@ -230,14 +324,17 @@ usb_activate(pcap_t* handle) /* * 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 @@ -250,6 +347,7 @@ usb_activate(pcap_t* handle) /* 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 */ @@ -257,22 +355,35 @@ usb_activate(pcap_t* handle) 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 (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; } - if (handle->opt.rfmon) { - /* - * Monitor mode doesn't apply to USB devices. - */ - return PCAP_ERROR_RFMON_NOTSUP; - } - /* * "handle->fd" is a real file, so "select()" and "poll()" * work on it. @@ -285,6 +396,7 @@ usb_activate(pcap_t* handle) if (!handle->buffer) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + close(handle->fd); return PCAP_ERROR; } return 0; @@ -351,7 +463,6 @@ usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u return -1; } uhdr->id = tag; - uhdr->endpoint_number = ep_num; uhdr->device_address = dev_addr; uhdr->bus_id = handle->md.ifindex; uhdr->status = 0; @@ -378,7 +489,7 @@ usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u else if (pipeid1 == 'B') urb_transfer = URB_BULK; if (pipeid2 == 'i') { - urb_transfer |= URB_TRANSFER_IN; + ep_num |= URB_TRANSFER_IN; incoming = 1; } if (etype == 'C') @@ -395,6 +506,7 @@ usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u 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); @@ -447,7 +559,7 @@ usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u uhdr->urb_len = urb_len; uhdr->data_flag = 1; data_len = 0; - if (uhdr->urb_len == pkth.caplen) + if (uhdr->urb_len == 0) goto got; /* check for data presence; data is present if and only if urb tag is '=' */ @@ -508,15 +620,27 @@ usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) char string[USB_LINE_LEN]; char token[USB_LINE_LEN]; char * ptr = string; - snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handle->md.ifindex); + int fd; - int fd = open(string, O_RDONLY, 0); + snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%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; + 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 */ @@ -541,8 +665,10 @@ usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) * 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; - int ntok = sscanf(ptr, "%s%n", token, &cnt); + ntok = sscanf(ptr, "%s%n", token, &cnt); if ((ntok < 1) || (cnt < 0)) break; consumed += cnt; @@ -590,7 +716,8 @@ usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats) } stats->ps_recv = handle->md.packets_read + st.queued; - stats->ps_ifdrop = st.dropped; + stats->ps_drop = st.dropped; + stats->ps_ifdrop = 0; return 0; } @@ -636,7 +763,7 @@ usb_read_linux_bin(pcap_t *handle, int max_packets, pcap_handler callback, u_cha clen = info.hdr->data_len; info.hdr->data_len = clen; pkth.caplen = clen + sizeof(pcap_usb_header); - pkth.len = info.hdr->urb_len + 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; @@ -659,6 +786,9 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch 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; @@ -695,13 +825,19 @@ 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*) &handle->buffer[vec[i]]; + 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 = hdr->data_len + sizeof(pcap_usb_header); - pkth.len = hdr->urb_len + sizeof(pcap_usb_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; @@ -723,8 +859,10 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch static void usb_cleanup_linux_mmap(pcap_t* handle) { - /* buffer must not be freed because it's memory mapped */ - /* XXX - does it need to be unmapped? */ - handle->buffer = NULL; + /* 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); } |