nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright (c) 2006 Paolo Abeni (Italy) |
||
3 | * All rights reserved. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions |
||
7 | * are met: |
||
8 | * |
||
9 | * 1. Redistributions of source code must retain the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer. |
||
11 | * 2. Redistributions in binary form must reproduce the above copyright |
||
12 | * notice, this list of conditions and the following disclaimer in the |
||
13 | * documentation and/or other materials provided with the distribution. |
||
14 | * 3. The name of the author may not be used to endorse or promote |
||
15 | * products derived from this software without specific prior written |
||
16 | * permission. |
||
17 | * |
||
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
29 | * |
||
30 | * USB sniffing API implementation for Linux platform |
||
31 | * By Paolo Abeni <paolo.abeni@email.it> |
||
32 | * Modifications: Kris Katterjohn <katterjohn@gmail.com> |
||
33 | * |
||
34 | */ |
||
35 | |||
36 | #ifdef HAVE_CONFIG_H |
||
37 | #include "config.h" |
||
38 | #endif |
||
39 | |||
40 | #include "pcap-int.h" |
||
41 | #include "pcap-usb-linux.h" |
||
42 | #include "pcap/usb.h" |
||
43 | |||
44 | #ifdef NEED_STRERROR_H |
||
45 | #include "strerror.h" |
||
46 | #endif |
||
47 | |||
48 | #include <ctype.h> |
||
49 | #include <errno.h> |
||
50 | #include <stdlib.h> |
||
51 | #include <unistd.h> |
||
52 | #include <fcntl.h> |
||
53 | #include <string.h> |
||
54 | #include <dirent.h> |
||
55 | #include <byteswap.h> |
||
56 | #include <netinet/in.h> |
||
57 | #include <sys/ioctl.h> |
||
58 | #include <sys/mman.h> |
||
59 | #ifdef HAVE_LINUX_USBDEVICE_FS_H |
||
60 | /* |
||
61 | * We might need <linux/compiler.h> to define __user for |
||
62 | * <linux/usbdevice_fs.h>. |
||
63 | */ |
||
64 | #ifdef HAVE_LINUX_COMPILER_H |
||
65 | #include <linux/compiler.h> |
||
66 | #endif /* HAVE_LINUX_COMPILER_H */ |
||
67 | #include <linux/usbdevice_fs.h> |
||
68 | #endif /* HAVE_LINUX_USBDEVICE_FS_H */ |
||
69 | |||
70 | #define USB_IFACE "usbmon" |
||
71 | #define USB_TEXT_DIR_OLD "/sys/kernel/debug/usbmon" |
||
72 | #define USB_TEXT_DIR "/sys/kernel/debug/usb/usbmon" |
||
73 | #define SYS_USB_BUS_DIR "/sys/bus/usb/devices" |
||
74 | #define PROC_USB_BUS_DIR "/proc/bus/usb" |
||
75 | #define USB_LINE_LEN 4096 |
||
76 | |||
77 | #if __BYTE_ORDER == __LITTLE_ENDIAN |
||
78 | #define htols(s) s |
||
79 | #define htoll(l) l |
||
80 | #define htol64(ll) ll |
||
81 | #else |
||
82 | #define htols(s) bswap_16(s) |
||
83 | #define htoll(l) bswap_32(l) |
||
84 | #define htol64(ll) bswap_64(ll) |
||
85 | #endif |
||
86 | |||
87 | struct mon_bin_stats { |
||
88 | u_int32_t queued; |
||
89 | u_int32_t dropped; |
||
90 | }; |
||
91 | |||
92 | struct mon_bin_get { |
||
93 | pcap_usb_header *hdr; |
||
94 | void *data; |
||
95 | size_t data_len; /* Length of data (can be zero) */ |
||
96 | }; |
||
97 | |||
98 | struct mon_bin_mfetch { |
||
99 | int32_t *offvec; /* Vector of events fetched */ |
||
100 | int32_t nfetch; /* Number of events to fetch (out: fetched) */ |
||
101 | int32_t nflush; /* Number of events to flush */ |
||
102 | }; |
||
103 | |||
104 | #define MON_IOC_MAGIC 0x92 |
||
105 | |||
106 | #define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1) |
||
107 | #define MON_IOCX_URB _IOWR(MON_IOC_MAGIC, 2, struct mon_bin_hdr) |
||
108 | #define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats) |
||
109 | #define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4) |
||
110 | #define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5) |
||
111 | #define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) |
||
112 | #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) |
||
113 | #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) |
||
114 | |||
115 | #define MON_BIN_SETUP 0x1 /* setup hdr is present*/ |
||
116 | #define MON_BIN_SETUP_ZERO 0x2 /* setup buffer is not available */ |
||
117 | #define MON_BIN_DATA_ZERO 0x4 /* data buffer is not available */ |
||
118 | #define MON_BIN_ERROR 0x8 |
||
119 | |||
120 | /* |
||
121 | * Private data for capturing on Linux USB. |
||
122 | */ |
||
123 | struct pcap_usb_linux { |
||
124 | u_char *mmapbuf; /* memory-mapped region pointer */ |
||
125 | size_t mmapbuflen; /* size of region */ |
||
126 | int bus_index; |
||
127 | u_int packets_read; |
||
128 | }; |
||
129 | |||
130 | /* forward declaration */ |
||
131 | static int usb_activate(pcap_t *); |
||
132 | static int usb_stats_linux(pcap_t *, struct pcap_stat *); |
||
133 | static int usb_stats_linux_bin(pcap_t *, struct pcap_stat *); |
||
134 | static int usb_read_linux(pcap_t *, int , pcap_handler , u_char *); |
||
135 | static int usb_read_linux_bin(pcap_t *, int , pcap_handler , u_char *); |
||
136 | static int usb_read_linux_mmap(pcap_t *, int , pcap_handler , u_char *); |
||
137 | static int usb_inject_linux(pcap_t *, const void *, size_t); |
||
138 | static int usb_setdirection_linux(pcap_t *, pcap_direction_t); |
||
139 | static void usb_cleanup_linux_mmap(pcap_t *); |
||
140 | |||
141 | /* facility to add an USB device to the device list*/ |
||
142 | static int |
||
143 | usb_dev_add(pcap_if_t** alldevsp, int n, char *err_str) |
||
144 | { |
||
145 | char dev_name[10]; |
||
146 | char dev_descr[30]; |
||
147 | snprintf(dev_name, 10, USB_IFACE"%d", n); |
||
148 | snprintf(dev_descr, 30, "USB bus number %d", n); |
||
149 | |||
150 | if (pcap_add_if(alldevsp, dev_name, 0, |
||
151 | dev_descr, err_str) < 0) |
||
152 | return -1; |
||
153 | return 0; |
||
154 | } |
||
155 | |||
156 | int |
||
157 | usb_findalldevs(pcap_if_t **alldevsp, char *err_str) |
||
158 | { |
||
159 | struct dirent* data; |
||
160 | int ret = 0; |
||
161 | DIR* dir; |
||
162 | int n; |
||
163 | char* name; |
||
164 | size_t len; |
||
165 | |||
166 | /* try scanning sysfs usb bus directory */ |
||
167 | dir = opendir(SYS_USB_BUS_DIR); |
||
168 | if (dir != NULL) { |
||
169 | while ((ret == 0) && ((data = readdir(dir)) != 0)) { |
||
170 | name = data->d_name; |
||
171 | |||
172 | if (strncmp(name, "usb", 3) != 0) |
||
173 | continue; |
||
174 | |||
175 | if (sscanf(&name[3], "%d", &n) == 0) |
||
176 | continue; |
||
177 | |||
178 | ret = usb_dev_add(alldevsp, n, err_str); |
||
179 | } |
||
180 | |||
181 | closedir(dir); |
||
182 | return ret; |
||
183 | } |
||
184 | |||
185 | /* that didn't work; try scanning procfs usb bus directory */ |
||
186 | dir = opendir(PROC_USB_BUS_DIR); |
||
187 | if (dir != NULL) { |
||
188 | while ((ret == 0) && ((data = readdir(dir)) != 0)) { |
||
189 | name = data->d_name; |
||
190 | len = strlen(name); |
||
191 | |||
192 | /* if this file name does not end with a number it's not of our interest */ |
||
193 | if ((len < 1) || !isdigit(name[--len])) |
||
194 | continue; |
||
195 | while (isdigit(name[--len])); |
||
196 | if (sscanf(&name[len+1], "%d", &n) != 1) |
||
197 | continue; |
||
198 | |||
199 | ret = usb_dev_add(alldevsp, n, err_str); |
||
200 | } |
||
201 | |||
202 | closedir(dir); |
||
203 | return ret; |
||
204 | } |
||
205 | |||
206 | /* neither of them worked */ |
||
207 | return 0; |
||
208 | } |
||
209 | |||
210 | static |
||
211 | int usb_mmap(pcap_t* handle) |
||
212 | { |
||
213 | struct pcap_usb_linux *handlep = handle->priv; |
||
214 | int len = ioctl(handle->fd, MON_IOCQ_RING_SIZE); |
||
215 | if (len < 0) |
||
216 | return 0; |
||
217 | |||
218 | handlep->mmapbuflen = len; |
||
219 | handlep->mmapbuf = mmap(0, handlep->mmapbuflen, PROT_READ, |
||
220 | MAP_SHARED, handle->fd, 0); |
||
221 | return handlep->mmapbuf != MAP_FAILED; |
||
222 | } |
||
223 | |||
224 | #ifdef HAVE_LINUX_USBDEVICE_FS_H |
||
225 | |||
226 | #define CTRL_TIMEOUT (5*1000) /* milliseconds */ |
||
227 | |||
228 | #define USB_DIR_IN 0x80 |
||
229 | #define USB_TYPE_STANDARD 0x00 |
||
230 | #define USB_RECIP_DEVICE 0x00 |
||
231 | |||
232 | #define USB_REQ_GET_DESCRIPTOR 6 |
||
233 | |||
234 | #define USB_DT_DEVICE 1 |
||
235 | |||
236 | /* probe the descriptors of the devices attached to the bus */ |
||
237 | /* the descriptors will end up in the captured packet stream */ |
||
238 | /* and be decoded by external apps like wireshark */ |
||
239 | /* without these identifying probes packet data can't be fully decoded */ |
||
240 | static void |
||
241 | probe_devices(int bus) |
||
242 | { |
||
243 | struct usbdevfs_ctrltransfer ctrl; |
||
244 | struct dirent* data; |
||
245 | int ret = 0; |
||
246 | char buf[40]; |
||
247 | DIR* dir; |
||
248 | |||
249 | /* scan usb bus directories for device nodes */ |
||
250 | snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d", bus); |
||
251 | dir = opendir(buf); |
||
252 | if (!dir) |
||
253 | return; |
||
254 | |||
255 | while ((ret >= 0) && ((data = readdir(dir)) != 0)) { |
||
256 | int fd; |
||
257 | char* name = data->d_name; |
||
258 | |||
259 | if (name[0] == '.') |
||
260 | continue; |
||
261 | |||
262 | snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d/%s", bus, data->d_name); |
||
263 | |||
264 | fd = open(buf, O_RDWR); |
||
265 | if (fd == -1) |
||
266 | continue; |
||
267 | |||
268 | /* |
||
269 | * Sigh. Different kernels have different member names |
||
270 | * for this structure. |
||
271 | */ |
||
272 | #ifdef HAVE_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE |
||
273 | ctrl.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; |
||
274 | ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; |
||
275 | ctrl.wValue = USB_DT_DEVICE << 8; |
||
276 | ctrl.wIndex = 0; |
||
277 | ctrl.wLength = sizeof(buf); |
||
278 | #else |
||
279 | ctrl.requesttype = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; |
||
280 | ctrl.request = USB_REQ_GET_DESCRIPTOR; |
||
281 | ctrl.value = USB_DT_DEVICE << 8; |
||
282 | ctrl.index = 0; |
||
283 | ctrl.length = sizeof(buf); |
||
284 | #endif |
||
285 | ctrl.data = buf; |
||
286 | ctrl.timeout = CTRL_TIMEOUT; |
||
287 | |||
288 | ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); |
||
289 | |||
290 | close(fd); |
||
291 | } |
||
292 | closedir(dir); |
||
293 | } |
||
294 | #endif /* HAVE_LINUX_USBDEVICE_FS_H */ |
||
295 | |||
296 | pcap_t * |
||
297 | usb_create(const char *device, char *ebuf, int *is_ours) |
||
298 | { |
||
299 | const char *cp; |
||
300 | char *cpend; |
||
301 | long devnum; |
||
302 | pcap_t *p; |
||
303 | |||
304 | /* Does this look like a USB monitoring device? */ |
||
305 | cp = strrchr(device, '/'); |
||
306 | if (cp == NULL) |
||
307 | cp = device; |
||
308 | /* Does it begin with USB_IFACE? */ |
||
309 | if (strncmp(cp, USB_IFACE, sizeof USB_IFACE - 1) != 0) { |
||
310 | /* Nope, doesn't begin with USB_IFACE */ |
||
311 | *is_ours = 0; |
||
312 | return NULL; |
||
313 | } |
||
314 | /* Yes - is USB_IFACE followed by a number? */ |
||
315 | cp += sizeof USB_IFACE - 1; |
||
316 | devnum = strtol(cp, &cpend, 10); |
||
317 | if (cpend == cp || *cpend != '\0') { |
||
318 | /* Not followed by a number. */ |
||
319 | *is_ours = 0; |
||
320 | return NULL; |
||
321 | } |
||
322 | if (devnum < 0) { |
||
323 | /* Followed by a non-valid number. */ |
||
324 | *is_ours = 0; |
||
325 | return NULL; |
||
326 | } |
||
327 | |||
328 | /* OK, it's probably ours. */ |
||
329 | *is_ours = 1; |
||
330 | |||
331 | p = pcap_create_common(device, ebuf, sizeof (struct pcap_usb_linux)); |
||
332 | if (p == NULL) |
||
333 | return (NULL); |
||
334 | |||
335 | p->activate_op = usb_activate; |
||
336 | return (p); |
||
337 | } |
||
338 | |||
339 | static int |
||
340 | usb_activate(pcap_t* handle) |
||
341 | { |
||
342 | struct pcap_usb_linux *handlep = handle->priv; |
||
343 | char full_path[USB_LINE_LEN]; |
||
344 | |||
345 | /* Initialize some components of the pcap structure. */ |
||
346 | handle->bufsize = handle->snapshot; |
||
347 | handle->offset = 0; |
||
348 | handle->linktype = DLT_USB_LINUX; |
||
349 | |||
350 | handle->inject_op = usb_inject_linux; |
||
351 | handle->setfilter_op = install_bpf_program; /* no kernel filtering */ |
||
352 | handle->setdirection_op = usb_setdirection_linux; |
||
353 | handle->set_datalink_op = NULL; /* can't change data link type */ |
||
354 | handle->getnonblock_op = pcap_getnonblock_fd; |
||
355 | handle->setnonblock_op = pcap_setnonblock_fd; |
||
356 | |||
357 | /*get usb bus index from device name */ |
||
358 | if (sscanf(handle->opt.source, USB_IFACE"%d", &handlep->bus_index) != 1) |
||
359 | { |
||
360 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
361 | "Can't get USB bus index from %s", handle->opt.source); |
||
362 | return PCAP_ERROR; |
||
363 | } |
||
364 | |||
365 | /*now select the read method: try to open binary interface */ |
||
366 | snprintf(full_path, USB_LINE_LEN, LINUX_USB_MON_DEV"%d", handlep->bus_index); |
||
367 | handle->fd = open(full_path, O_RDONLY, 0); |
||
368 | if (handle->fd >= 0) |
||
369 | { |
||
370 | if (handle->opt.rfmon) { |
||
371 | /* |
||
372 | * Monitor mode doesn't apply to USB devices. |
||
373 | */ |
||
374 | close(handle->fd); |
||
375 | return PCAP_ERROR_RFMON_NOTSUP; |
||
376 | } |
||
377 | |||
378 | /* binary api is available, try to use fast mmap access */ |
||
379 | if (usb_mmap(handle)) { |
||
380 | handle->linktype = DLT_USB_LINUX_MMAPPED; |
||
381 | handle->stats_op = usb_stats_linux_bin; |
||
382 | handle->read_op = usb_read_linux_mmap; |
||
383 | handle->cleanup_op = usb_cleanup_linux_mmap; |
||
384 | #ifdef HAVE_LINUX_USBDEVICE_FS_H |
||
385 | probe_devices(handlep->bus_index); |
||
386 | #endif |
||
387 | |||
388 | /* |
||
389 | * "handle->fd" is a real file, so "select()" and |
||
390 | * "poll()" work on it. |
||
391 | */ |
||
392 | handle->selectable_fd = handle->fd; |
||
393 | return 0; |
||
394 | } |
||
395 | |||
396 | /* can't mmap, use plain binary interface access */ |
||
397 | handle->stats_op = usb_stats_linux_bin; |
||
398 | handle->read_op = usb_read_linux_bin; |
||
399 | #ifdef HAVE_LINUX_USBDEVICE_FS_H |
||
400 | probe_devices(handlep->bus_index); |
||
401 | #endif |
||
402 | } |
||
403 | else { |
||
404 | /*Binary interface not available, try open text interface */ |
||
405 | snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR"/%dt", handlep->bus_index); |
||
406 | handle->fd = open(full_path, O_RDONLY, 0); |
||
407 | if (handle->fd < 0) |
||
408 | { |
||
409 | if (errno == ENOENT) |
||
410 | { |
||
411 | /* |
||
412 | * Not found at the new location; try |
||
413 | * the old location. |
||
414 | */ |
||
415 | snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%dt", handlep->bus_index); |
||
416 | handle->fd = open(full_path, O_RDONLY, 0); |
||
417 | } |
||
418 | if (handle->fd < 0) { |
||
419 | /* no more fallback, give it up*/ |
||
420 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
421 | "Can't open USB bus file %s: %s", full_path, strerror(errno)); |
||
422 | return PCAP_ERROR; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | if (handle->opt.rfmon) { |
||
427 | /* |
||
428 | * Monitor mode doesn't apply to USB devices. |
||
429 | */ |
||
430 | close(handle->fd); |
||
431 | return PCAP_ERROR_RFMON_NOTSUP; |
||
432 | } |
||
433 | |||
434 | handle->stats_op = usb_stats_linux; |
||
435 | handle->read_op = usb_read_linux; |
||
436 | } |
||
437 | |||
438 | /* |
||
439 | * "handle->fd" is a real file, so "select()" and "poll()" |
||
440 | * work on it. |
||
441 | */ |
||
442 | handle->selectable_fd = handle->fd; |
||
443 | |||
444 | /* for plain binary access and text access we need to allocate the read |
||
445 | * buffer */ |
||
446 | handle->buffer = malloc(handle->bufsize); |
||
447 | if (!handle->buffer) { |
||
448 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
449 | "malloc: %s", pcap_strerror(errno)); |
||
450 | close(handle->fd); |
||
451 | return PCAP_ERROR; |
||
452 | } |
||
453 | return 0; |
||
454 | } |
||
455 | |||
456 | static inline int |
||
457 | ascii_to_int(char c) |
||
458 | { |
||
459 | return c < 'A' ? c- '0': ((c<'a') ? c - 'A' + 10: c-'a'+10); |
||
460 | } |
||
461 | |||
462 | /* |
||
463 | * see <linux-kernel-source>/Documentation/usb/usbmon.txt and |
||
464 | * <linux-kernel-source>/drivers/usb/mon/mon_text.c for urb string |
||
465 | * format description |
||
466 | */ |
||
467 | static int |
||
468 | usb_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) |
||
469 | { |
||
470 | /* see: |
||
471 | * /usr/src/linux/Documentation/usb/usbmon.txt |
||
472 | * for message format |
||
473 | */ |
||
474 | struct pcap_usb_linux *handlep = handle->priv; |
||
475 | unsigned timestamp; |
||
476 | int tag, cnt, ep_num, dev_addr, dummy, ret, urb_len, data_len; |
||
477 | char etype, pipeid1, pipeid2, status[16], urb_tag, line[USB_LINE_LEN]; |
||
478 | char *string = line; |
||
479 | u_char * rawdata = handle->buffer; |
||
480 | struct pcap_pkthdr pkth; |
||
481 | pcap_usb_header* uhdr = (pcap_usb_header*)handle->buffer; |
||
482 | u_char urb_transfer=0; |
||
483 | int incoming=0; |
||
484 | |||
485 | /* ignore interrupt system call errors */ |
||
486 | do { |
||
487 | ret = read(handle->fd, line, USB_LINE_LEN - 1); |
||
488 | if (handle->break_loop) |
||
489 | { |
||
490 | handle->break_loop = 0; |
||
491 | return -2; |
||
492 | } |
||
493 | } while ((ret == -1) && (errno == EINTR)); |
||
494 | if (ret < 0) |
||
495 | { |
||
496 | if (errno == EAGAIN) |
||
497 | return 0; /* no data there */ |
||
498 | |||
499 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
500 | "Can't read from fd %d: %s", handle->fd, strerror(errno)); |
||
501 | return -1; |
||
502 | } |
||
503 | |||
504 | /* read urb header; %n argument may increment return value, but it's |
||
505 | * not mandatory, so does not count on it*/ |
||
506 | string[ret] = 0; |
||
507 | ret = sscanf(string, "%x %d %c %c%c:%d:%d %s%n", &tag, ×tamp, &etype, |
||
508 | &pipeid1, &pipeid2, &dev_addr, &ep_num, status, |
||
509 | &cnt); |
||
510 | if (ret < 8) |
||
511 | { |
||
512 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
513 | "Can't parse USB bus message '%s', too few tokens (expected 8 got %d)", |
||
514 | string, ret); |
||
515 | return -1; |
||
516 | } |
||
517 | uhdr->id = tag; |
||
518 | uhdr->device_address = dev_addr; |
||
519 | uhdr->bus_id = handlep->bus_index; |
||
520 | uhdr->status = 0; |
||
521 | string += cnt; |
||
522 | |||
523 | /* don't use usbmon provided timestamp, since it have low precision*/ |
||
524 | if (gettimeofday(&pkth.ts, NULL) < 0) |
||
525 | { |
||
526 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
527 | "Can't get timestamp for message '%s' %d:%s", |
||
528 | string, errno, strerror(errno)); |
||
529 | return -1; |
||
530 | } |
||
531 | uhdr->ts_sec = pkth.ts.tv_sec; |
||
532 | uhdr->ts_usec = pkth.ts.tv_usec; |
||
533 | |||
534 | /* parse endpoint information */ |
||
535 | if (pipeid1 == 'C') |
||
536 | urb_transfer = URB_CONTROL; |
||
537 | else if (pipeid1 == 'Z') |
||
538 | urb_transfer = URB_ISOCHRONOUS; |
||
539 | else if (pipeid1 == 'I') |
||
540 | urb_transfer = URB_INTERRUPT; |
||
541 | else if (pipeid1 == 'B') |
||
542 | urb_transfer = URB_BULK; |
||
543 | if (pipeid2 == 'i') { |
||
544 | ep_num |= URB_TRANSFER_IN; |
||
545 | incoming = 1; |
||
546 | } |
||
547 | if (etype == 'C') |
||
548 | incoming = !incoming; |
||
549 | |||
550 | /* direction check*/ |
||
551 | if (incoming) |
||
552 | { |
||
553 | if (handle->direction == PCAP_D_OUT) |
||
554 | return 0; |
||
555 | } |
||
556 | else |
||
557 | if (handle->direction == PCAP_D_IN) |
||
558 | return 0; |
||
559 | uhdr->event_type = etype; |
||
560 | uhdr->transfer_type = urb_transfer; |
||
561 | uhdr->endpoint_number = ep_num; |
||
562 | pkth.caplen = sizeof(pcap_usb_header); |
||
563 | rawdata += sizeof(pcap_usb_header); |
||
564 | |||
565 | /* check if this is a setup packet */ |
||
566 | ret = sscanf(status, "%d", &dummy); |
||
567 | if (ret != 1) |
||
568 | { |
||
569 | /* this a setup packet, setup data can be filled with underscore if |
||
570 | * usbmon has not been able to read them, so we must parse this fields as |
||
571 | * strings */ |
||
572 | pcap_usb_setup* shdr; |
||
573 | char str1[3], str2[3], str3[5], str4[5], str5[5]; |
||
574 | ret = sscanf(string, "%s %s %s %s %s%n", str1, str2, str3, str4, |
||
575 | str5, &cnt); |
||
576 | if (ret < 5) |
||
577 | { |
||
578 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
579 | "Can't parse USB bus message '%s', too few tokens (expected 5 got %d)", |
||
580 | string, ret); |
||
581 | return -1; |
||
582 | } |
||
583 | string += cnt; |
||
584 | |||
585 | /* try to convert to corresponding integer */ |
||
586 | shdr = &uhdr->setup; |
||
587 | shdr->bmRequestType = strtoul(str1, 0, 16); |
||
588 | shdr->bRequest = strtoul(str2, 0, 16); |
||
589 | shdr->wValue = htols(strtoul(str3, 0, 16)); |
||
590 | shdr->wIndex = htols(strtoul(str4, 0, 16)); |
||
591 | shdr->wLength = htols(strtoul(str5, 0, 16)); |
||
592 | |||
593 | uhdr->setup_flag = 0; |
||
594 | } |
||
595 | else |
||
596 | uhdr->setup_flag = 1; |
||
597 | |||
598 | /* read urb data */ |
||
599 | ret = sscanf(string, " %d%n", &urb_len, &cnt); |
||
600 | if (ret < 1) |
||
601 | { |
||
602 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
603 | "Can't parse urb length from '%s'", string); |
||
604 | return -1; |
||
605 | } |
||
606 | string += cnt; |
||
607 | |||
608 | /* urb tag is not present if urb length is 0, so we can stop here |
||
609 | * text parsing */ |
||
610 | pkth.len = urb_len+pkth.caplen; |
||
611 | uhdr->urb_len = urb_len; |
||
612 | uhdr->data_flag = 1; |
||
613 | data_len = 0; |
||
614 | if (uhdr->urb_len == 0) |
||
615 | goto got; |
||
616 | |||
617 | /* check for data presence; data is present if and only if urb tag is '=' */ |
||
618 | if (sscanf(string, " %c", &urb_tag) != 1) |
||
619 | { |
||
620 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
621 | "Can't parse urb tag from '%s'", string); |
||
622 | return -1; |
||
623 | } |
||
624 | |||
625 | if (urb_tag != '=') |
||
626 | goto got; |
||
627 | |||
628 | /* skip urb tag and following space */ |
||
629 | string += 3; |
||
630 | |||
631 | /* if we reach this point we got some urb data*/ |
||
632 | uhdr->data_flag = 0; |
||
633 | |||
634 | /* read all urb data; if urb length is greater then the usbmon internal |
||
635 | * buffer length used by the kernel to spool the URB, we get only |
||
636 | * a partial information. |
||
637 | * At least until linux 2.6.17 there is no way to set usbmon intenal buffer |
||
638 | * length and default value is 130. */ |
||
639 | while ((string[0] != 0) && (string[1] != 0) && (pkth.caplen < handle->snapshot)) |
||
640 | { |
||
641 | rawdata[0] = ascii_to_int(string[0]) * 16 + ascii_to_int(string[1]); |
||
642 | rawdata++; |
||
643 | string+=2; |
||
644 | if (string[0] == ' ') |
||
645 | string++; |
||
646 | pkth.caplen++; |
||
647 | data_len++; |
||
648 | } |
||
649 | |||
650 | got: |
||
651 | uhdr->data_len = data_len; |
||
652 | if (pkth.caplen > handle->snapshot) |
||
653 | pkth.caplen = handle->snapshot; |
||
654 | |||
655 | if (handle->fcode.bf_insns == NULL || |
||
656 | bpf_filter(handle->fcode.bf_insns, handle->buffer, |
||
657 | pkth.len, pkth.caplen)) { |
||
658 | handlep->packets_read++; |
||
659 | callback(user, &pkth, handle->buffer); |
||
660 | return 1; |
||
661 | } |
||
662 | return 0; /* didn't pass filter */ |
||
663 | } |
||
664 | |||
665 | static int |
||
666 | usb_inject_linux(pcap_t *handle, const void *buf, size_t size) |
||
667 | { |
||
668 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on " |
||
669 | "USB devices"); |
||
670 | return (-1); |
||
671 | } |
||
672 | |||
673 | static int |
||
674 | usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) |
||
675 | { |
||
676 | struct pcap_usb_linux *handlep = handle->priv; |
||
677 | int dummy, ret, consumed, cnt; |
||
678 | char string[USB_LINE_LEN]; |
||
679 | char token[USB_LINE_LEN]; |
||
680 | char * ptr = string; |
||
681 | int fd; |
||
682 | |||
683 | snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handlep->bus_index); |
||
684 | fd = open(string, O_RDONLY, 0); |
||
685 | if (fd < 0) |
||
686 | { |
||
687 | if (errno == ENOENT) |
||
688 | { |
||
689 | /* |
||
690 | * Not found at the new location; try the old |
||
691 | * location. |
||
692 | */ |
||
693 | snprintf(string, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%ds", handlep->bus_index); |
||
694 | fd = open(string, O_RDONLY, 0); |
||
695 | } |
||
696 | if (fd < 0) { |
||
697 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
698 | "Can't open USB stats file %s: %s", |
||
699 | string, strerror(errno)); |
||
700 | return -1; |
||
701 | } |
||
702 | } |
||
703 | |||
704 | /* read stats line */ |
||
705 | do { |
||
706 | ret = read(fd, string, USB_LINE_LEN-1); |
||
707 | } while ((ret == -1) && (errno == EINTR)); |
||
708 | close(fd); |
||
709 | |||
710 | if (ret < 0) |
||
711 | { |
||
712 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
713 | "Can't read stats from fd %d ", fd); |
||
714 | return -1; |
||
715 | } |
||
716 | string[ret] = 0; |
||
717 | |||
718 | /* extract info on dropped urbs */ |
||
719 | for (consumed=0; consumed < ret; ) { |
||
720 | /* from the sscanf man page: |
||
721 | * The C standard says: "Execution of a %n directive does |
||
722 | * not increment the assignment count returned at the completion |
||
723 | * of execution" but the Corrigendum seems to contradict this. |
||
724 | * Do not make any assumptions on the effect of %n conversions |
||
725 | * on the return value and explicitly check for cnt assignmet*/ |
||
726 | int ntok; |
||
727 | |||
728 | cnt = -1; |
||
729 | ntok = sscanf(ptr, "%s%n", token, &cnt); |
||
730 | if ((ntok < 1) || (cnt < 0)) |
||
731 | break; |
||
732 | consumed += cnt; |
||
733 | ptr += cnt; |
||
734 | if (strcmp(token, "nreaders") == 0) |
||
735 | ret = sscanf(ptr, "%d", &stats->ps_drop); |
||
736 | else |
||
737 | ret = sscanf(ptr, "%d", &dummy); |
||
738 | if (ntok != 1) |
||
739 | break; |
||
740 | consumed += cnt; |
||
741 | ptr += cnt; |
||
742 | } |
||
743 | |||
744 | stats->ps_recv = handlep->packets_read; |
||
745 | stats->ps_ifdrop = 0; |
||
746 | return 0; |
||
747 | } |
||
748 | |||
749 | static int |
||
750 | usb_setdirection_linux(pcap_t *p, pcap_direction_t d) |
||
751 | { |
||
752 | p->direction = d; |
||
753 | return 0; |
||
754 | } |
||
755 | |||
756 | |||
757 | static int |
||
758 | usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats) |
||
759 | { |
||
760 | struct pcap_usb_linux *handlep = handle->priv; |
||
761 | int ret; |
||
762 | struct mon_bin_stats st; |
||
763 | ret = ioctl(handle->fd, MON_IOCG_STATS, &st); |
||
764 | if (ret < 0) |
||
765 | { |
||
766 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
767 | "Can't read stats from fd %d:%s ", handle->fd, strerror(errno)); |
||
768 | return -1; |
||
769 | } |
||
770 | |||
771 | stats->ps_recv = handlep->packets_read + st.queued; |
||
772 | stats->ps_drop = st.dropped; |
||
773 | stats->ps_ifdrop = 0; |
||
774 | return 0; |
||
775 | } |
||
776 | |||
777 | /* |
||
778 | * see <linux-kernel-source>/Documentation/usb/usbmon.txt and |
||
779 | * <linux-kernel-source>/drivers/usb/mon/mon_bin.c binary ABI |
||
780 | */ |
||
781 | static int |
||
782 | usb_read_linux_bin(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) |
||
783 | { |
||
784 | struct pcap_usb_linux *handlep = handle->priv; |
||
785 | struct mon_bin_get info; |
||
786 | int ret; |
||
787 | struct pcap_pkthdr pkth; |
||
788 | int clen = handle->snapshot - sizeof(pcap_usb_header); |
||
789 | |||
790 | /* the usb header is going to be part of 'packet' data*/ |
||
791 | info.hdr = (pcap_usb_header*) handle->buffer; |
||
792 | info.data = handle->buffer + sizeof(pcap_usb_header); |
||
793 | info.data_len = clen; |
||
794 | |||
795 | /* ignore interrupt system call errors */ |
||
796 | do { |
||
797 | ret = ioctl(handle->fd, MON_IOCX_GET, &info); |
||
798 | if (handle->break_loop) |
||
799 | { |
||
800 | handle->break_loop = 0; |
||
801 | return -2; |
||
802 | } |
||
803 | } while ((ret == -1) && (errno == EINTR)); |
||
804 | if (ret < 0) |
||
805 | { |
||
806 | if (errno == EAGAIN) |
||
807 | return 0; /* no data there */ |
||
808 | |||
809 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
810 | "Can't read from fd %d: %s", handle->fd, strerror(errno)); |
||
811 | return -1; |
||
812 | } |
||
813 | |||
814 | /* we can get less that than really captured from kernel, depending on |
||
815 | * snaplen, so adjust header accordingly */ |
||
816 | if (info.hdr->data_len < clen) |
||
817 | clen = info.hdr->data_len; |
||
818 | info.hdr->data_len = clen; |
||
819 | pkth.caplen = clen + sizeof(pcap_usb_header); |
||
820 | pkth.len = info.hdr->data_len + sizeof(pcap_usb_header); |
||
821 | pkth.ts.tv_sec = info.hdr->ts_sec; |
||
822 | pkth.ts.tv_usec = info.hdr->ts_usec; |
||
823 | |||
824 | if (handle->fcode.bf_insns == NULL || |
||
825 | bpf_filter(handle->fcode.bf_insns, handle->buffer, |
||
826 | pkth.len, pkth.caplen)) { |
||
827 | handlep->packets_read++; |
||
828 | callback(user, &pkth, handle->buffer); |
||
829 | return 1; |
||
830 | } |
||
831 | |||
832 | return 0; /* didn't pass filter */ |
||
833 | } |
||
834 | |||
835 | /* |
||
836 | * see <linux-kernel-source>/Documentation/usb/usbmon.txt and |
||
837 | * <linux-kernel-source>/drivers/usb/mon/mon_bin.c binary ABI |
||
838 | */ |
||
839 | #define VEC_SIZE 32 |
||
840 | static int |
||
841 | usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) |
||
842 | { |
||
843 | struct pcap_usb_linux *handlep = handle->priv; |
||
844 | struct mon_bin_mfetch fetch; |
||
845 | int32_t vec[VEC_SIZE]; |
||
846 | struct pcap_pkthdr pkth; |
||
847 | pcap_usb_header* hdr; |
||
848 | int nflush = 0; |
||
849 | int packets = 0; |
||
850 | int clen, max_clen; |
||
851 | |||
852 | max_clen = handle->snapshot - sizeof(pcap_usb_header); |
||
853 | |||
854 | for (;;) { |
||
855 | int i, ret; |
||
856 | int limit = max_packets - packets; |
||
857 | if (limit <= 0) |
||
858 | limit = VEC_SIZE; |
||
859 | if (limit > VEC_SIZE) |
||
860 | limit = VEC_SIZE; |
||
861 | |||
862 | /* try to fetch as many events as possible*/ |
||
863 | fetch.offvec = vec; |
||
864 | fetch.nfetch = limit; |
||
865 | fetch.nflush = nflush; |
||
866 | /* ignore interrupt system call errors */ |
||
867 | do { |
||
868 | ret = ioctl(handle->fd, MON_IOCX_MFETCH, &fetch); |
||
869 | if (handle->break_loop) |
||
870 | { |
||
871 | handle->break_loop = 0; |
||
872 | return -2; |
||
873 | } |
||
874 | } while ((ret == -1) && (errno == EINTR)); |
||
875 | if (ret < 0) |
||
876 | { |
||
877 | if (errno == EAGAIN) |
||
878 | return 0; /* no data there */ |
||
879 | |||
880 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
881 | "Can't mfetch fd %d: %s", handle->fd, strerror(errno)); |
||
882 | return -1; |
||
883 | } |
||
884 | |||
885 | /* keep track of processed events, we will flush them later */ |
||
886 | nflush = fetch.nfetch; |
||
887 | for (i=0; i<fetch.nfetch; ++i) { |
||
888 | /* discard filler */ |
||
889 | hdr = (pcap_usb_header*) &handlep->mmapbuf[vec[i]]; |
||
890 | if (hdr->event_type == '@') |
||
891 | continue; |
||
892 | |||
893 | /* we can get less that than really captured from kernel, depending on |
||
894 | * snaplen, so adjust header accordingly */ |
||
895 | clen = max_clen; |
||
896 | if (hdr->data_len < clen) |
||
897 | clen = hdr->data_len; |
||
898 | |||
899 | /* get packet info from header*/ |
||
900 | pkth.caplen = clen + sizeof(pcap_usb_header_mmapped); |
||
901 | pkth.len = hdr->data_len + sizeof(pcap_usb_header_mmapped); |
||
902 | pkth.ts.tv_sec = hdr->ts_sec; |
||
903 | pkth.ts.tv_usec = hdr->ts_usec; |
||
904 | |||
905 | if (handle->fcode.bf_insns == NULL || |
||
906 | bpf_filter(handle->fcode.bf_insns, (u_char*) hdr, |
||
907 | pkth.len, pkth.caplen)) { |
||
908 | handlep->packets_read++; |
||
909 | callback(user, &pkth, (u_char*) hdr); |
||
910 | packets++; |
||
911 | } |
||
912 | } |
||
913 | |||
914 | /* with max_packets specifying "unlimited" we stop afer the first chunk*/ |
||
915 | if (PACKET_COUNT_IS_UNLIMITED(max_packets) || (packets == max_packets)) |
||
916 | break; |
||
917 | } |
||
918 | |||
919 | /* flush pending events*/ |
||
920 | if (ioctl(handle->fd, MON_IOCH_MFLUSH, nflush) == -1) { |
||
921 | snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, |
||
922 | "Can't mflush fd %d: %s", handle->fd, strerror(errno)); |
||
923 | return -1; |
||
924 | } |
||
925 | return packets; |
||
926 | } |
||
927 | |||
928 | static void |
||
929 | usb_cleanup_linux_mmap(pcap_t* handle) |
||
930 | { |
||
931 | struct pcap_usb_linux *handlep = handle->priv; |
||
932 | |||
933 | /* if we have a memory-mapped buffer, unmap it */ |
||
934 | if (handlep->mmapbuf != NULL) { |
||
935 | munmap(handlep->mmapbuf, handlep->mmapbuflen); |
||
936 | handlep->mmapbuf = NULL; |
||
937 | } |
||
938 | pcap_cleanup_live_common(handle); |
||
939 | } |