usbdump.c revision 233037
1/*-
2 * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org>
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 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 233037 2012-03-16 16:29:21Z hselasky $
30 */
31
32#include <sys/param.h>
33#include <sys/endian.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/utsname.h>
38#include <sys/queue.h>
39#include <net/if.h>
40#include <net/bpf.h>
41#include <dev/usb/usb.h>
42#include <dev/usb/usb_pf.h>
43#include <dev/usb/usbdi.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <limits.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <stdint.h>
50#include <string.h>
51#include <time.h>
52#include <unistd.h>
53#include <sysexits.h>
54#include <err.h>
55
56#define	BPF_STORE_JUMP(x,_c,_k,_jt,_jf) do {	\
57  (x).code = (_c);				\
58  (x).k = (_k);					\
59  (x).jt = (_jt);				\
60  (x).jf = (_jf);				\
61} while (0)
62
63#define	BPF_STORE_STMT(x,_c,_k) do {		\
64  (x).code = (_c);				\
65  (x).k = (_k);					\
66  (x).jt = 0;					\
67  (x).jf = 0;					\
68} while (0)
69
70struct usb_filt {
71	STAILQ_ENTRY(usb_filt) entry;
72	int unit;
73	int endpoint;
74};
75
76struct usbcap {
77	int		fd;		/* fd for /dev/usbpf */
78	uint32_t	bufsize;
79	uint8_t		*buffer;
80
81	/* for -w option */
82	int		wfd;
83	/* for -r option */
84	int		rfd;
85};
86
87struct usbcap_filehdr {
88	uint32_t	magic;
89#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
90	uint8_t   	major;
91	uint8_t		minor;
92	uint8_t		reserved[26];
93} __packed;
94
95#define	HEADER_ALIGN(x,a) (((x) + (a) - 1) & ~((a) - 1))
96
97struct header_32 {
98	uint32_t ts_sec;
99	uint32_t ts_usec;
100	uint32_t caplen;
101	uint32_t datalen;
102	uint16_t hdrlen;
103	uint16_t dummy;
104} __packed;
105
106struct header_64 {
107	uint64_t ts_sec;
108	uint64_t ts_usec;
109	uint32_t caplen;
110	uint32_t datalen;
111	uint16_t hdrlen;
112	uint16_t dummy;
113} __packed;
114
115static int doexit = 0;
116static int pkt_captured = 0;
117static int verbose = 0;
118static const char *i_arg = "usbus0";
119static const char *r_arg = NULL;
120static const char *w_arg = NULL;
121static const char *errstr_table[USB_ERR_MAX] = {
122	[USB_ERR_NORMAL_COMPLETION]	= "0",
123	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
124	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
125	[USB_ERR_INVAL]			= "INVAL",
126	[USB_ERR_NOMEM]			= "NOMEM",
127	[USB_ERR_CANCELLED]		= "CANCELLED",
128	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
129	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
130	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
131	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
132	[USB_ERR_IN_USE]		= "IN_USE",
133	[USB_ERR_NO_ADDR]		= "NO_ADDR",
134	[USB_ERR_NO_PIPE]		= "NO_PIPE",
135	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
136	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
137	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
138	[USB_ERR_NO_POWER]		= "NO_POWER",
139	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
140	[USB_ERR_IOERROR]		= "IOERROR",
141	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
142	[USB_ERR_TIMEOUT]		= "TIMEOUT",
143	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
144	[USB_ERR_STALLED]		= "STALLED",
145	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
146	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
147	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
148	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
149	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
150	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
151};
152
153static const char *xfertype_table[4] = {
154	[UE_CONTROL]			= "CTRL",
155	[UE_ISOCHRONOUS]		= "ISOC",
156	[UE_BULK]			= "BULK",
157	[UE_INTERRUPT]			= "INTR"
158};
159
160static const char *speed_table[USB_SPEED_MAX] = {
161	[USB_SPEED_FULL] = "FULL",
162	[USB_SPEED_HIGH] = "HIGH",
163	[USB_SPEED_LOW] = "LOW",
164	[USB_SPEED_VARIABLE] = "VARI",
165	[USB_SPEED_SUPER] = "SUPER",
166};
167
168static STAILQ_HEAD(,usb_filt) usb_filt_head =
169    STAILQ_HEAD_INITIALIZER(usb_filt_head);
170
171static void
172add_filter(int usb_filt_unit, int usb_filt_ep)
173{
174	struct usb_filt *puf;
175
176	puf = malloc(sizeof(struct usb_filt));
177	if (puf == NULL)
178		errx(EX_SOFTWARE, "Out of memory.");
179
180	puf->unit = usb_filt_unit;
181	puf->endpoint = usb_filt_ep;
182
183	STAILQ_INSERT_TAIL(&usb_filt_head, puf, entry);
184}
185
186static void
187make_filter(struct bpf_program *pprog, int snapshot)
188{
189	struct usb_filt *puf;
190	struct bpf_insn *dynamic_insn;
191	int len;
192
193	len = 0;
194
195	STAILQ_FOREACH(puf, &usb_filt_head, entry)
196		len++;
197
198	dynamic_insn = malloc(((len * 5) + 1) * sizeof(struct bpf_insn));
199
200	if (dynamic_insn == NULL)
201		errx(EX_SOFTWARE, "Out of memory.");
202
203	len++;
204
205	if (len == 1) {
206		/* accept all packets */
207
208		BPF_STORE_STMT(dynamic_insn[0], BPF_RET | BPF_K, snapshot);
209
210		goto done;
211	}
212
213	len = 0;
214
215	STAILQ_FOREACH(puf, &usb_filt_head, entry) {
216		const int addr_off = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_address;
217		const int addr_ep = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_endpoint;
218
219		if (puf->unit != -1) {
220			if (puf->endpoint != -1) {
221				BPF_STORE_STMT(dynamic_insn[len],
222				    BPF_LD | BPF_B | BPF_ABS, addr_off);
223				len++;
224				BPF_STORE_JUMP(dynamic_insn[len],
225				    BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 3);
226				len++;
227				BPF_STORE_STMT(dynamic_insn[len],
228				    BPF_LD | BPF_W | BPF_ABS, addr_ep);
229				len++;
230				BPF_STORE_JUMP(dynamic_insn[len],
231				    BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1);
232				len++;
233			} else {
234				BPF_STORE_STMT(dynamic_insn[len],
235				    BPF_LD | BPF_B | BPF_ABS, addr_off);
236				len++;
237				BPF_STORE_JUMP(dynamic_insn[len],
238				    BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 1);
239				len++;
240			}
241		} else {
242			if (puf->endpoint != -1) {
243				BPF_STORE_STMT(dynamic_insn[len],
244				    BPF_LD | BPF_W | BPF_ABS, addr_ep);
245				len++;
246				BPF_STORE_JUMP(dynamic_insn[len],
247				    BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1);
248				len++;
249			}
250		}
251		BPF_STORE_STMT(dynamic_insn[len],
252		    BPF_RET | BPF_K, snapshot);
253		len++;
254	}
255
256	BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, 0);
257	len++;
258
259done:
260	pprog->bf_len = len;
261	pprog->bf_insns = dynamic_insn;
262}
263
264static void
265free_filter(struct bpf_program *pprog)
266{
267	struct usb_filt *puf;
268
269	while ((puf = STAILQ_FIRST(&usb_filt_head)) != NULL) {
270		STAILQ_REMOVE_HEAD(&usb_filt_head, entry);
271		free(puf);
272	}
273	free(pprog->bf_insns);
274}
275
276static void
277handle_sigint(int sig)
278{
279
280	(void)sig;
281	doexit = 1;
282}
283
284#define	FLAGS(x, name)	\
285	(((x) & USBPF_FLAG_##name) ? #name "|" : "")
286
287#define	STATUS(x, name) \
288	(((x) & USBPF_STATUS_##name) ? #name "|" : "")
289
290static const char *
291usb_errstr(uint32_t error)
292{
293	if (error >= USB_ERR_MAX || errstr_table[error] == NULL)
294		return ("UNKNOWN");
295	else
296		return (errstr_table[error]);
297}
298
299static const char *
300usb_speedstr(uint8_t speed)
301{
302	if (speed >= USB_SPEED_MAX  || speed_table[speed] == NULL)
303		return ("UNKNOWN");
304	else
305		return (speed_table[speed]);
306}
307
308static void
309print_flags(uint32_t flags)
310{
311	printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n",
312	    flags,
313	    FLAGS(flags, FORCE_SHORT_XFER),
314	    FLAGS(flags, SHORT_XFER_OK),
315	    FLAGS(flags, SHORT_FRAMES_OK),
316	    FLAGS(flags, PIPE_BOF),
317	    FLAGS(flags, PROXY_BUFFER),
318	    FLAGS(flags, EXT_BUFFER),
319	    FLAGS(flags, MANUAL_STATUS),
320	    FLAGS(flags, NO_PIPE_OK),
321	    FLAGS(flags, STALL_PIPE));
322}
323
324static void
325print_status(uint32_t status)
326{
327	printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n",
328	    status,
329	    STATUS(status, OPEN),
330	    STATUS(status, TRANSFERRING),
331	    STATUS(status, DID_DMA_DELAY),
332	    STATUS(status, DID_CLOSE),
333	    STATUS(status, DRAINING),
334	    STATUS(status, STARTED),
335	    STATUS(status, BW_RECLAIMED),
336	    STATUS(status, CONTROL_XFR),
337	    STATUS(status, CONTROL_HDR),
338	    STATUS(status, CONTROL_ACT),
339	    STATUS(status, CONTROL_STALL),
340	    STATUS(status, SHORT_FRAMES_OK),
341	    STATUS(status, SHORT_XFER_OK),
342	    STATUS(status, BDMA_ENABLE),
343	    STATUS(status, BDMA_NO_POST_SYNC),
344	    STATUS(status, BDMA_SETUP),
345	    STATUS(status, ISOCHRONOUS_XFR),
346	    STATUS(status, CURR_DMA_SET),
347	    STATUS(status, CAN_CANCEL_IMMED),
348	    STATUS(status, DOING_CALLBACK));
349}
350
351/*
352 * Dump a byte into hex format.
353 */
354static void
355hexbyte(char *buf, uint8_t temp)
356{
357	uint8_t lo;
358	uint8_t hi;
359
360	lo = temp & 0xF;
361	hi = temp >> 4;
362
363	if (hi < 10)
364		buf[0] = '0' + hi;
365	else
366		buf[0] = 'A' + hi - 10;
367
368	if (lo < 10)
369		buf[1] = '0' + lo;
370	else
371		buf[1] = 'A' + lo - 10;
372}
373
374/*
375 * Display a region in traditional hexdump format.
376 */
377static void
378hexdump(const uint8_t *region, uint32_t len)
379{
380	const uint8_t *line;
381	char linebuf[128];
382	int i;
383	int x;
384	int c;
385
386	for (line = region; line < (region + len); line += 16) {
387
388		i = 0;
389
390		linebuf[i] = ' ';
391		hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF);
392		hexbyte(linebuf + i + 3, (line - region) & 0xFF);
393		linebuf[i + 5] = ' ';
394		linebuf[i + 6] = ' ';
395		i += 7;
396
397		for (x = 0; x < 16; x++) {
398		  if ((line + x) < (region + len)) {
399			hexbyte(linebuf + i,
400			    *(const u_int8_t *)(line + x));
401		  } else {
402			  linebuf[i] = '-';
403			  linebuf[i + 1] = '-';
404			}
405			linebuf[i + 2] = ' ';
406			if (x == 7) {
407			  linebuf[i + 3] = ' ';
408			  i += 4;
409			} else {
410			  i += 3;
411			}
412		}
413		linebuf[i] = ' ';
414		linebuf[i + 1] = '|';
415		i += 2;
416		for (x = 0; x < 16; x++) {
417			if ((line + x) < (region + len)) {
418				c = *(const u_int8_t *)(line + x);
419				/* !isprint(c) */
420				if ((c < ' ') || (c > '~'))
421					c = '.';
422				linebuf[i] = c;
423			} else {
424				linebuf[i] = ' ';
425			}
426			i++;
427		}
428		linebuf[i] = '|';
429		linebuf[i + 1] = 0;
430		i += 2;
431		puts(linebuf);
432	}
433}
434
435static void
436print_apacket(const struct header_32 *hdr, const uint8_t *ptr, int ptr_len)
437{
438	struct tm *tm;
439	struct usbpf_pkthdr up_temp;
440	struct usbpf_pkthdr *up;
441	struct timeval tv;
442	size_t len;
443	uint32_t x;
444	char buf[64];
445
446	ptr += USBPF_HDR_LEN;
447	ptr_len -= USBPF_HDR_LEN;
448	if (ptr_len < 0)
449		return;
450
451	/* make sure we don't change the source buffer */
452	memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp));
453	up = &up_temp;
454
455	/*
456	 * A packet from the kernel is based on little endian byte
457	 * order.
458	 */
459	up->up_totlen = le32toh(up->up_totlen);
460	up->up_busunit = le32toh(up->up_busunit);
461	up->up_address = le32toh(up->up_address);
462	up->up_flags = le32toh(up->up_flags);
463	up->up_status = le32toh(up->up_status);
464	up->up_error = le32toh(up->up_error);
465	up->up_interval = le32toh(up->up_interval);
466	up->up_frames = le32toh(up->up_frames);
467	up->up_packet_size = le32toh(up->up_packet_size);
468	up->up_packet_count = le32toh(up->up_packet_count);
469	up->up_endpoint = le32toh(up->up_endpoint);
470
471	tv.tv_sec = hdr->ts_sec;
472	tv.tv_usec = hdr->ts_usec;
473	tm = localtime(&tv.tv_sec);
474
475	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
476
477	printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n",
478	    (int)len, buf, tv.tv_usec,
479	    (int)up->up_busunit, (int)up->up_address,
480	    (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE",
481	    xfertype_table[up->up_xfertype],
482	    (unsigned int)up->up_endpoint,
483	    usb_speedstr(up->up_speed),
484	    (int)up->up_frames,
485	    (int)(up->up_totlen - USBPF_HDR_LEN -
486	    (USBPF_FRAME_HDR_LEN * up->up_frames)),
487	    (int)up->up_interval,
488	    (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "",
489	    (up->up_type == USBPF_XFERTAP_DONE) ?
490	    usb_errstr(up->up_error) : "");
491
492	if (verbose >= 1) {
493		for (x = 0; x != up->up_frames; x++) {
494			const struct usbpf_framehdr *uf;
495			uint32_t framelen;
496			uint32_t flags;
497
498			uf = (const struct usbpf_framehdr *)ptr;
499			ptr += USBPF_FRAME_HDR_LEN;
500			ptr_len -= USBPF_FRAME_HDR_LEN;
501			if (ptr_len < 0)
502				return;
503
504			framelen = le32toh(uf->length);
505			flags = le32toh(uf->flags);
506
507			printf(" frame[%u] %s %d bytes\n",
508			    (unsigned int)x,
509			    (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE",
510			    (int)framelen);
511
512			if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) {
513
514				int tot_frame_len;
515
516				tot_frame_len = USBPF_FRAME_ALIGN(framelen);
517
518				ptr_len -= tot_frame_len;
519
520				if (tot_frame_len < 0 ||
521				    (int)framelen < 0 || (int)ptr_len < 0)
522					break;
523
524				hexdump(ptr, framelen);
525
526				ptr += tot_frame_len;
527			}
528		}
529	}
530	if (verbose >= 2)
531		print_flags(up->up_flags);
532	if (verbose >= 3)
533		print_status(up->up_status);
534}
535
536static void
537print_packets(uint8_t *data, const int datalen)
538{
539	struct header_32 temp;
540	uint8_t *ptr;
541	uint8_t *next;
542
543	for (ptr = data; ptr < (data + datalen); ptr = next) {
544
545		/* automatically figure out endian and size of header */
546
547		if (r_arg != NULL) {
548
549			const struct header_32 *hdr32;
550			const struct header_64 *hdr64;
551
552			hdr32 = (const struct header_32 *)ptr;
553			hdr64 = (const struct header_64 *)ptr;
554
555			temp.hdrlen = le16toh(hdr32->hdrlen);
556			temp.dummy = le16toh(hdr32->dummy);
557
558			if ((temp.hdrlen != 18 && temp.hdrlen != 20) || (temp.dummy != 0)) {
559				temp.hdrlen = be16toh(hdr32->hdrlen);
560				temp.dummy = be16toh(hdr32->dummy);
561
562				if ((temp.hdrlen != 18 && temp.hdrlen != 20) || (temp.dummy != 0)) {
563					temp.hdrlen = le16toh(hdr64->hdrlen);
564					temp.dummy = le16toh(hdr64->dummy);
565
566					if ((temp.hdrlen != 28 && temp.hdrlen != 32) || (temp.dummy != 0)) {
567						temp.hdrlen = be16toh(hdr64->hdrlen);
568						temp.dummy = be16toh(hdr64->dummy);
569
570						if ((temp.hdrlen != 28 && temp.hdrlen != 32) || (temp.dummy != 0)) {
571							err(EXIT_FAILURE, "Invalid header detected");
572							next = NULL;
573						} else {
574							temp.ts_sec = be64toh(hdr64->ts_sec);
575							temp.ts_usec = be64toh(hdr64->ts_usec);
576							temp.caplen = be32toh(hdr64->caplen);
577							temp.datalen = be32toh(hdr64->datalen);
578							next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 8);
579						}
580					} else {
581						temp.ts_sec = le64toh(hdr64->ts_sec);
582						temp.ts_usec = le64toh(hdr64->ts_usec);
583						temp.caplen = le32toh(hdr64->caplen);
584						temp.datalen = le32toh(hdr64->datalen);
585						next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 8);
586					}
587				} else {
588					temp.ts_sec = be32toh(hdr32->ts_sec);
589					temp.ts_usec = be32toh(hdr32->ts_usec);
590					temp.caplen = be32toh(hdr32->caplen);
591					temp.datalen = be32toh(hdr32->datalen);
592					next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 4);
593				}
594			} else {
595				temp.ts_sec = le32toh(hdr32->ts_sec);
596				temp.ts_usec = le32toh(hdr32->ts_usec);
597				temp.caplen = le32toh(hdr32->caplen);
598				temp.datalen = le32toh(hdr32->datalen);
599				next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 4);
600			}
601		} else {
602			const struct bpf_hdr *hdr;
603
604			hdr = (const struct bpf_hdr *)ptr;
605			temp.ts_sec = hdr->bh_tstamp.tv_sec;
606			temp.ts_usec = hdr->bh_tstamp.tv_usec;
607			temp.caplen = hdr->bh_caplen;
608			temp.datalen = hdr->bh_datalen;
609			temp.hdrlen = hdr->bh_hdrlen;
610			next = ptr + BPF_WORDALIGN(temp.hdrlen + temp.caplen);
611		}
612
613		if (next <= ptr)
614			err(EXIT_FAILURE, "Invalid header length");
615
616		if (w_arg == NULL) {
617			print_apacket(&temp, ptr +
618			    temp.hdrlen, temp.caplen);
619		}
620		pkt_captured++;
621	}
622}
623
624static void
625write_packets(struct usbcap *p, const uint8_t *data, const int datalen)
626{
627	int len = htole32(datalen);
628	int ret;
629
630	ret = write(p->wfd, &len, sizeof(int));
631	if (ret != sizeof(int)) {
632		err(EXIT_FAILURE, "Could not write length "
633		    "field of USB data payload");
634	}
635	ret = write(p->wfd, data, datalen);
636	if (ret != datalen) {
637		err(EXIT_FAILURE, "Could not write "
638		    "complete USB data payload");
639	}
640}
641
642static void
643read_file(struct usbcap *p)
644{
645	int datalen;
646	int ret;
647	uint8_t *data;
648
649	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
650		datalen = le32toh(datalen);
651		data = malloc(datalen);
652		if (data == NULL)
653			errx(EX_SOFTWARE, "Out of memory.");
654		ret = read(p->rfd, data, datalen);
655		if (ret != datalen) {
656			err(EXIT_FAILURE, "Could not read complete "
657			    "USB data payload");
658		}
659		print_packets(data, datalen);
660		free(data);
661	}
662}
663
664static void
665do_loop(struct usbcap *p)
666{
667	int cc;
668
669	while (doexit == 0) {
670		cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize);
671		if (cc < 0) {
672			switch (errno) {
673			case EINTR:
674				break;
675			default:
676				fprintf(stderr, "read: %s\n", strerror(errno));
677				return;
678			}
679			continue;
680		}
681		if (cc == 0)
682			continue;
683		if (w_arg != NULL)
684			write_packets(p, p->buffer, cc);
685		print_packets(p->buffer, cc);
686	}
687}
688
689static void
690init_rfile(struct usbcap *p)
691{
692	struct usbcap_filehdr uf;
693	int ret;
694
695	p->rfd = open(r_arg, O_RDONLY);
696	if (p->rfd < 0) {
697		err(EXIT_FAILURE, "Could not open "
698		    "'%s' for read", r_arg);
699	}
700	ret = read(p->rfd, &uf, sizeof(uf));
701	if (ret != sizeof(uf)) {
702		err(EXIT_FAILURE, "Could not read USB capture "
703		    "file header");
704	}
705	if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) {
706		errx(EX_SOFTWARE, "Invalid magic field(0x%08x) "
707		    "in USB capture file header.",
708		    (unsigned int)le32toh(uf.magic));
709	}
710	if (uf.major != 0) {
711		errx(EX_SOFTWARE, "Invalid major version(%d) "
712		    "field in USB capture file header.", (int)uf.major);
713	}
714	if (uf.minor != 2) {
715		errx(EX_SOFTWARE, "Invalid minor version(%d) "
716		    "field in USB capture file header.", (int)uf.minor);
717	}
718}
719
720static void
721init_wfile(struct usbcap *p)
722{
723	struct usbcap_filehdr uf;
724	int ret;
725
726	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
727	if (p->wfd < 0) {
728		err(EXIT_FAILURE, "Could not open "
729		    "'%s' for write", r_arg);
730	}
731	memset(&uf, 0, sizeof(uf));
732	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
733	uf.major = 0;
734	uf.minor = 2;
735	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
736	if (ret != sizeof(uf)) {
737		err(EXIT_FAILURE, "Could not write "
738		    "USB capture header");
739	}
740}
741
742static void
743usage(void)
744{
745
746#define FMT "    %-14s %s\n"
747	fprintf(stderr, "usage: usbdump [options]\n");
748	fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface");
749	fprintf(stderr, FMT, "-f <unit[.endpoint]>", "Specify a device and endpoint filter");
750	fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file");
751	fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet");
752	fprintf(stderr, FMT, "-v", "Increase the verbose level");
753	fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file");
754#undef FMT
755	exit(EX_USAGE);
756}
757
758int
759main(int argc, char *argv[])
760{
761	struct timeval tv;
762	struct bpf_program total_prog;
763	struct bpf_stat us;
764	struct bpf_version bv;
765	struct usbcap uc, *p = &uc;
766	struct ifreq ifr;
767	long snapshot = 192;
768	uint32_t v;
769	int fd;
770	int o;
771	int filt_unit;
772	int filt_ep;
773	const char *optstring;
774	char *pp;
775
776	memset(&uc, 0, sizeof(struct usbcap));
777
778	optstring = "i:r:s:vw:f:";
779	while ((o = getopt(argc, argv, optstring)) != -1) {
780		switch (o) {
781		case 'i':
782			i_arg = optarg;
783			break;
784		case 'r':
785			r_arg = optarg;
786			init_rfile(p);
787			break;
788		case 's':
789			snapshot = strtol(optarg, &pp, 10);
790			errno = 0;
791			if (pp != NULL && *pp != 0)
792				usage();
793			if (snapshot == 0 && errno == EINVAL)
794				usage();
795			/* snapeshot == 0 is special */
796			if (snapshot == 0)
797				snapshot = -1;
798			break;
799		case 'v':
800			verbose++;
801			break;
802		case 'w':
803			w_arg = optarg;
804			init_wfile(p);
805			break;
806		case 'f':
807			filt_unit = strtol(optarg, &pp, 10);
808			filt_ep = -1;
809			if (pp != NULL) {
810				if (*pp == '.') {
811					filt_ep = strtol(pp + 1, &pp, 10);
812					if (pp != NULL && *pp != 0)
813						usage();
814				} else if (*pp != 0) {
815					usage();
816				}
817			}
818			add_filter(filt_unit, filt_ep);
819			break;
820		default:
821			usage();
822			/* NOTREACHED */
823		}
824	}
825
826	if (r_arg != NULL) {
827		read_file(p);
828		exit(EXIT_SUCCESS);
829	}
830
831	p->fd = fd = open("/dev/bpf", O_RDONLY);
832	if (p->fd < 0)
833		err(EXIT_FAILURE, "Could not open BPF device");
834
835	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0)
836		err(EXIT_FAILURE, "BIOCVERSION ioctl failed");
837
838	if (bv.bv_major != BPF_MAJOR_VERSION ||
839	    bv.bv_minor < BPF_MINOR_VERSION)
840		errx(EXIT_FAILURE, "Kernel BPF filter out of date");
841
842	/* USB transfers can be greater than 64KByte */
843	v = 1U << 16;
844
845	/* clear ifr structure */
846	memset(&ifr, 0, sizeof(ifr));
847
848	for ( ; v >= USBPF_HDR_LEN; v >>= 1) {
849		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
850		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
851		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
852			break;
853	}
854	if (v == 0)
855		errx(EXIT_FAILURE, "No buffer size worked.");
856
857	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0)
858		err(EXIT_FAILURE, "BIOCGBLEN ioctl failed");
859
860	p->bufsize = v;
861	p->buffer = (uint8_t *)malloc(p->bufsize);
862	if (p->buffer == NULL)
863		errx(EX_SOFTWARE, "Out of memory.");
864
865	make_filter(&total_prog, snapshot);
866
867	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0)
868		err(EXIT_FAILURE, "BIOCSETF ioctl failed");
869
870	free_filter(&total_prog);
871
872	/* 1 second read timeout */
873	tv.tv_sec = 1;
874	tv.tv_usec = 0;
875	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0)
876		err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed");
877
878	(void)signal(SIGINT, handle_sigint);
879
880	do_loop(p);
881
882	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0)
883		err(EXIT_FAILURE, "BIOCGSTATS ioctl failed");
884
885	/* XXX what's difference between pkt_captured and us.us_recv? */
886	printf("\n");
887	printf("%d packets captured\n", pkt_captured);
888	printf("%d packets received by filter\n", us.bs_recv);
889	printf("%d packets dropped by kernel\n", us.bs_drop);
890
891	if (p->fd > 0)
892		close(p->fd);
893	if (p->rfd > 0)
894		close(p->rfd);
895	if (p->wfd > 0)
896		close(p->wfd);
897
898	return (EXIT_SUCCESS);
899}
900