usbdump.c revision 220314
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 220314 2011-04-04 02:57:19Z thompsa $
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 <net/if.h>
39#include <net/bpf.h>
40#include <dev/usb/usb.h>
41#include <dev/usb/usb_pf.h>
42#include <dev/usb/usbdi.h>
43#include <assert.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <limits.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51#include <unistd.h>
52
53struct usbcap {
54	int		fd;		/* fd for /dev/usbpf */
55	uint32_t	bufsize;
56	uint8_t		*buffer;
57
58	/* for -w option */
59	int		wfd;
60	/* for -r option */
61	int		rfd;
62};
63
64struct usbcap_filehdr {
65	uint32_t	magic;
66#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
67	uint8_t   	major;
68	uint8_t		minor;
69	uint8_t		reserved[26];
70} __packed;
71
72static int doexit = 0;
73static int pkt_captured = 0;
74static int verbose = 0;
75static const char *i_arg = "usbus0";
76static const char *r_arg = NULL;
77static const char *w_arg = NULL;
78static const char *errstr_table[USB_ERR_MAX] = {
79	[USB_ERR_NORMAL_COMPLETION]	= "0",
80	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
81	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
82	[USB_ERR_INVAL]			= "INVAL",
83	[USB_ERR_NOMEM]			= "NOMEM",
84	[USB_ERR_CANCELLED]		= "CANCELLED",
85	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
86	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
87	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
88	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
89	[USB_ERR_IN_USE]		= "IN_USE",
90	[USB_ERR_NO_ADDR]		= "NO_ADDR",
91	[USB_ERR_NO_PIPE]		= "NO_PIPE",
92	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
93	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
94	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
95	[USB_ERR_NO_POWER]		= "NO_POWER",
96	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
97	[USB_ERR_IOERROR]		= "IOERROR",
98	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
99	[USB_ERR_TIMEOUT]		= "TIMEOUT",
100	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
101	[USB_ERR_STALLED]		= "STALLED",
102	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
103	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
104	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
105	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
106	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
107	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
108};
109
110static const char *xfertype_table[4] = {
111	[UE_CONTROL]			= "CTRL",
112	[UE_ISOCHRONOUS]		= "ISOC",
113	[UE_BULK]			= "BULK",
114	[UE_INTERRUPT]			= "INTR"
115};
116
117static const char *speed_table[USB_SPEED_MAX] = {
118	[USB_SPEED_FULL] = "FULL",
119	[USB_SPEED_HIGH] = "HIGH",
120	[USB_SPEED_LOW] = "LOW",
121	[USB_SPEED_VARIABLE] = "VARI",
122	[USB_SPEED_SUPER] = "SUPER",
123};
124
125static void
126handle_sigint(int sig)
127{
128
129	(void)sig;
130	doexit = 1;
131}
132
133#define	FLAGS(x, name)	\
134	(((x) & USBPF_FLAG_##name) ? #name "|" : "")
135
136#define	STATUS(x, name) \
137	(((x) & USBPF_STATUS_##name) ? #name "|" : "")
138
139static const char *
140usb_errstr(uint32_t error)
141{
142	if (error >= USB_ERR_MAX || errstr_table[error] == NULL)
143		return ("UNKNOWN");
144	else
145		return (errstr_table[error]);
146}
147
148static const char *
149usb_speedstr(uint8_t speed)
150{
151	if (speed >= USB_SPEED_MAX  || speed_table[speed] == NULL)
152		return ("UNKNOWN");
153	else
154		return (speed_table[speed]);
155}
156
157static void
158print_flags(uint32_t flags)
159{
160	printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n",
161	    flags,
162	    FLAGS(flags, FORCE_SHORT_XFER),
163	    FLAGS(flags, SHORT_XFER_OK),
164	    FLAGS(flags, SHORT_FRAMES_OK),
165	    FLAGS(flags, PIPE_BOF),
166	    FLAGS(flags, PROXY_BUFFER),
167	    FLAGS(flags, EXT_BUFFER),
168	    FLAGS(flags, MANUAL_STATUS),
169	    FLAGS(flags, NO_PIPE_OK),
170	    FLAGS(flags, STALL_PIPE));
171}
172
173static void
174print_status(uint32_t status)
175{
176	printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n",
177	    status,
178	    STATUS(status, OPEN),
179	    STATUS(status, TRANSFERRING),
180	    STATUS(status, DID_DMA_DELAY),
181	    STATUS(status, DID_CLOSE),
182	    STATUS(status, DRAINING),
183	    STATUS(status, STARTED),
184	    STATUS(status, BW_RECLAIMED),
185	    STATUS(status, CONTROL_XFR),
186	    STATUS(status, CONTROL_HDR),
187	    STATUS(status, CONTROL_ACT),
188	    STATUS(status, CONTROL_STALL),
189	    STATUS(status, SHORT_FRAMES_OK),
190	    STATUS(status, SHORT_XFER_OK),
191	    STATUS(status, BDMA_ENABLE),
192	    STATUS(status, BDMA_NO_POST_SYNC),
193	    STATUS(status, BDMA_SETUP),
194	    STATUS(status, ISOCHRONOUS_XFR),
195	    STATUS(status, CURR_DMA_SET),
196	    STATUS(status, CAN_CANCEL_IMMED),
197	    STATUS(status, DOING_CALLBACK));
198}
199
200/*
201 * Dump a byte into hex format.
202 */
203static void
204hexbyte(char *buf, uint8_t temp)
205{
206	uint8_t lo;
207	uint8_t hi;
208
209	lo = temp & 0xF;
210	hi = temp >> 4;
211
212	if (hi < 10)
213		buf[0] = '0' + hi;
214	else
215		buf[0] = 'A' + hi - 10;
216
217	if (lo < 10)
218		buf[1] = '0' + lo;
219	else
220		buf[1] = 'A' + lo - 10;
221}
222
223/*
224 * Display a region in traditional hexdump format.
225 */
226static void
227hexdump(const uint8_t *region, uint32_t len)
228{
229	const uint8_t *line;
230	char linebuf[128];
231	int i;
232	int x;
233	int c;
234
235	for (line = region; line < (region + len); line += 16) {
236
237		i = 0;
238
239		linebuf[i] = ' ';
240		hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF);
241		hexbyte(linebuf + i + 3, (line - region) & 0xFF);
242		linebuf[i + 5] = ' ';
243		linebuf[i + 6] = ' ';
244		i += 7;
245
246		for (x = 0; x < 16; x++) {
247		  if ((line + x) < (region + len)) {
248			hexbyte(linebuf + i,
249			    *(const u_int8_t *)(line + x));
250		  } else {
251			  linebuf[i] = '-';
252			  linebuf[i + 1] = '-';
253			}
254			linebuf[i + 2] = ' ';
255			if (x == 7) {
256			  linebuf[i + 3] = ' ';
257			  i += 4;
258			} else {
259			  i += 3;
260			}
261		}
262		linebuf[i] = ' ';
263		linebuf[i + 1] = '|';
264		i += 2;
265		for (x = 0; x < 16; x++) {
266			if ((line + x) < (region + len)) {
267				c = *(const u_int8_t *)(line + x);
268				/* !isprint(c) */
269				if ((c < ' ') || (c > '~'))
270					c = '.';
271				linebuf[i] = c;
272			} else {
273				linebuf[i] = ' ';
274			}
275			i++;
276		}
277		linebuf[i] = '|';
278		linebuf[i + 1] = 0;
279		i += 2;
280		puts(linebuf);
281	}
282}
283
284static void
285print_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len)
286{
287	struct tm *tm;
288	struct usbpf_pkthdr up_temp;
289	struct usbpf_pkthdr *up;
290	struct timeval tv;
291	size_t len;
292	uint32_t x;
293	char buf[64];
294
295	ptr += USBPF_HDR_LEN;
296	ptr_len -= USBPF_HDR_LEN;
297	if (ptr_len < 0)
298		return;
299
300	/* make sure we don't change the source buffer */
301	memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp));
302	up = &up_temp;
303
304	/*
305	 * A packet from the kernel is based on little endian byte
306	 * order.
307	 */
308	up->up_totlen = le32toh(up->up_totlen);
309	up->up_busunit = le32toh(up->up_busunit);
310	up->up_address = le32toh(up->up_address);
311	up->up_flags = le32toh(up->up_flags);
312	up->up_status = le32toh(up->up_status);
313	up->up_error = le32toh(up->up_error);
314	up->up_interval = le32toh(up->up_interval);
315	up->up_frames = le32toh(up->up_frames);
316	up->up_packet_size = le32toh(up->up_packet_size);
317	up->up_packet_count = le32toh(up->up_packet_count);
318	up->up_endpoint = le32toh(up->up_endpoint);
319
320	tv.tv_sec = hdr->bh_tstamp.bt_sec;
321	tv.tv_usec = hdr->bh_tstamp.bt_frac;
322	tm = localtime(&tv.tv_sec);
323
324	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
325
326	printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n",
327	    (int)len, buf, tv.tv_usec,
328	    (int)up->up_busunit, (int)up->up_address,
329	    (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE",
330	    xfertype_table[up->up_xfertype],
331	    (unsigned int)up->up_endpoint,
332	    usb_speedstr(up->up_speed),
333	    (int)up->up_frames,
334	    (int)(up->up_totlen - USBPF_HDR_LEN -
335	    (USBPF_FRAME_HDR_LEN * up->up_frames)),
336	    (int)up->up_interval,
337	    (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "",
338	    (up->up_type == USBPF_XFERTAP_DONE) ?
339	    usb_errstr(up->up_error) : "");
340
341	if (verbose >= 1) {
342		for (x = 0; x != up->up_frames; x++) {
343			const struct usbpf_framehdr *uf;
344			uint32_t framelen;
345			uint32_t flags;
346
347			uf = (const struct usbpf_framehdr *)ptr;
348			ptr += USBPF_FRAME_HDR_LEN;
349			ptr_len -= USBPF_FRAME_HDR_LEN;
350			if (ptr_len < 0)
351				return;
352
353			framelen = le32toh(uf->length);
354			flags = le32toh(uf->flags);
355
356			printf(" frame[%u] %s %d bytes\n",
357			    (unsigned int)x,
358			    (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE",
359			    (int)framelen);
360
361			if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) {
362
363				int tot_frame_len;
364
365				tot_frame_len = USBPF_FRAME_ALIGN(framelen);
366
367				ptr_len -= tot_frame_len;
368
369				if (tot_frame_len < 0 ||
370				    (int)framelen < 0 || (int)ptr_len < 0)
371					break;
372
373				hexdump(ptr, framelen);
374
375				ptr += tot_frame_len;
376			}
377		}
378	}
379	if (verbose >= 2)
380		print_flags(up->up_flags);
381	if (verbose >= 3)
382		print_status(up->up_status);
383}
384
385static void
386print_packets(uint8_t *data, const int datalen)
387{
388	const struct bpf_xhdr *hdr;
389	uint8_t *ptr;
390	uint8_t *next;
391
392	for (ptr = data; ptr < (data + datalen); ptr = next) {
393		hdr = (const struct bpf_xhdr *)ptr;
394		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
395
396		if (w_arg == NULL) {
397			print_apacket(hdr, ptr +
398			    hdr->bh_hdrlen, hdr->bh_caplen);
399		}
400		pkt_captured++;
401	}
402}
403
404static void
405write_packets(struct usbcap *p, const uint8_t *data, const int datalen)
406{
407	int len = htole32(datalen);
408	int ret;
409
410	ret = write(p->wfd, &len, sizeof(int));
411	assert(ret == sizeof(int));
412	ret = write(p->wfd, data, datalen);
413	assert(ret == datalen);
414}
415
416static void
417read_file(struct usbcap *p)
418{
419	int datalen;
420	int ret;
421	uint8_t *data;
422
423	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
424		datalen = le32toh(datalen);
425		data = malloc(datalen);
426		assert(data != NULL);
427		ret = read(p->rfd, data, datalen);
428		assert(ret == datalen);
429		print_packets(data, datalen);
430		free(data);
431	}
432	if (ret == -1)
433		fprintf(stderr, "read: %s\n", strerror(errno));
434}
435
436static void
437do_loop(struct usbcap *p)
438{
439	int cc;
440
441	while (doexit == 0) {
442		cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize);
443		if (cc < 0) {
444			switch (errno) {
445			case EINTR:
446				break;
447			default:
448				fprintf(stderr, "read: %s\n", strerror(errno));
449				return;
450			}
451			continue;
452		}
453		if (cc == 0)
454			continue;
455		if (w_arg != NULL)
456			write_packets(p, p->buffer, cc);
457		print_packets(p->buffer, cc);
458	}
459}
460
461static void
462init_rfile(struct usbcap *p)
463{
464	struct usbcap_filehdr uf;
465	int ret;
466
467	p->rfd = open(r_arg, O_RDONLY);
468	if (p->rfd < 0) {
469		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
470		exit(EXIT_FAILURE);
471	}
472	ret = read(p->rfd, &uf, sizeof(uf));
473	assert(ret == sizeof(uf));
474	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
475	assert(uf.major == 0);
476	assert(uf.minor == 2);
477}
478
479static void
480init_wfile(struct usbcap *p)
481{
482	struct usbcap_filehdr uf;
483	int ret;
484
485	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
486	if (p->wfd < 0) {
487		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
488		exit(EXIT_FAILURE);
489	}
490	bzero(&uf, sizeof(uf));
491	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
492	uf.major = 0;
493	uf.minor = 2;
494	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
495	assert(ret == sizeof(uf));
496}
497
498static void
499usage(void)
500{
501
502#define FMT "    %-14s %s\n"
503	fprintf(stderr, "usage: usbdump [options]\n");
504	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
505	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
506	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
507	fprintf(stderr, FMT, "-v", "Increases the verbose level");
508	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
509#undef FMT
510	exit(1);
511}
512
513int
514main(int argc, char *argv[])
515{
516	struct timeval tv;
517	struct bpf_insn total_insn;
518	struct bpf_program total_prog;
519	struct bpf_stat us;
520	struct bpf_version bv;
521	struct usbcap uc, *p = &uc;
522	struct ifreq ifr;
523	long snapshot = 192;
524	uint32_t v;
525	int fd, o;
526	const char *optstring;
527
528	bzero(&uc, sizeof(struct usbcap));
529
530	optstring = "i:r:s:vw:";
531	while ((o = getopt(argc, argv, optstring)) != -1) {
532		switch (o) {
533		case 'i':
534			i_arg = optarg;
535			break;
536		case 'r':
537			r_arg = optarg;
538			init_rfile(p);
539			break;
540		case 's':
541			snapshot = strtol(optarg, NULL, 10);
542			errno = 0;
543			if (snapshot == 0 && errno == EINVAL)
544				usage();
545			/* snapeshot == 0 is special */
546			if (snapshot == 0)
547				snapshot = -1;
548			break;
549		case 'v':
550			verbose++;
551			break;
552		case 'w':
553			w_arg = optarg;
554			init_wfile(p);
555			break;
556		default:
557			usage();
558			/* NOTREACHED */
559		}
560	}
561
562	if (r_arg != NULL) {
563		read_file(p);
564		exit(EXIT_SUCCESS);
565	}
566
567	p->fd = fd = open("/dev/bpf", O_RDONLY);
568	if (p->fd < 0) {
569		fprintf(stderr, "(no devices found)\n");
570		return (EXIT_FAILURE);
571	}
572
573	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
574		fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno));
575		return (EXIT_FAILURE);
576	}
577	if (bv.bv_major != BPF_MAJOR_VERSION ||
578	    bv.bv_minor < BPF_MINOR_VERSION) {
579		fprintf(stderr, "kernel bpf filter out of date");
580		return (EXIT_FAILURE);
581	}
582
583	/* USB transfers can be greater than 64KByte */
584	v = 1U << 16;
585
586	/* clear ifr structure */
587	memset(&ifr, 0, sizeof(ifr));
588
589	for ( ; v >= USBPF_HDR_LEN; v >>= 1) {
590		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
591		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
592		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
593			break;
594	}
595	if (v == 0) {
596		fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg);
597		return (EXIT_FAILURE);
598	}
599
600	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
601		fprintf(stderr, "BIOCGBLEN: %s", strerror(errno));
602		return (EXIT_FAILURE);
603	}
604
605	p->bufsize = v;
606	p->buffer = (uint8_t *)malloc(p->bufsize);
607	if (p->buffer == NULL) {
608		fprintf(stderr, "malloc: %s", strerror(errno));
609		return (EXIT_FAILURE);
610	}
611
612	/* XXX no read filter rules yet so at this moment accept everything */
613	total_insn.code = (u_short)(BPF_RET | BPF_K);
614	total_insn.jt = 0;
615	total_insn.jf = 0;
616	total_insn.k = snapshot;
617
618	total_prog.bf_len = 1;
619	total_prog.bf_insns = &total_insn;
620	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
621		fprintf(stderr, "BIOCSETF: %s", strerror(errno));
622		return (EXIT_FAILURE);
623	}
624
625	/* 1 second read timeout */
626	tv.tv_sec = 1;
627	tv.tv_usec = 0;
628	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
629		fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno));
630		return (EXIT_FAILURE);
631	}
632
633	(void)signal(SIGINT, handle_sigint);
634
635	do_loop(p);
636
637	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) {
638		fprintf(stderr, "BIOCGSTATS: %s", strerror(errno));
639		return (EXIT_FAILURE);
640	}
641
642	/* XXX what's difference between pkt_captured and us.us_recv? */
643	printf("\n");
644	printf("%d packets captured\n", pkt_captured);
645	printf("%d packets received by filter\n", us.bs_recv);
646	printf("%d packets dropped by kernel\n", us.bs_drop);
647
648	if (p->fd > 0)
649		close(p->fd);
650	if (p->rfd > 0)
651		close(p->rfd);
652	if (p->wfd > 0)
653		close(p->wfd);
654
655	return (EXIT_SUCCESS);
656}
657