usbdump.c revision 215651
1127474Stjr/*-
2127474Stjr * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org>
3127474Stjr * All rights reserved.
4127474Stjr *
5127474Stjr * Redistribution and use in source and binary forms, with or without
6127474Stjr * modification, are permitted provided that the following conditions
7127474Stjr * are met:
8127474Stjr * 1. Redistributions of source code must retain the above copyright
9127474Stjr *    notice, this list of conditions and the following disclaimer,
10127474Stjr *    without modification.
11127474Stjr * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12127474Stjr *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13127474Stjr *    redistribution must be conditioned upon including a substantially
14127474Stjr *    similar Disclaimer requirement for further binary redistribution.
15127474Stjr *
16127474Stjr * NO WARRANTY
17127474Stjr * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18127474Stjr * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19127474Stjr * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20127474Stjr * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21127474Stjr * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22127474Stjr * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23127474Stjr * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24127474Stjr * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25127474Stjr * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26127474Stjr * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27127474Stjr * THE POSSIBILITY OF SUCH DAMAGES.
28127474Stjr *
29127474Stjr * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 215651 2010-11-22 01:28:29Z weongyo $
30127474Stjr */
31127474Stjr
32127474Stjr#include <sys/param.h>
33127474Stjr#include <sys/endian.h>
34127474Stjr#include <sys/ioctl.h>
35127474Stjr#include <sys/stat.h>
36127474Stjr#include <sys/utsname.h>
37#include <dev/usb/usb.h>
38#include <dev/usb/usb_pf.h>
39#include <dev/usb/usbdi.h>
40#include <assert.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <limits.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <time.h>
48#include <unistd.h>
49
50struct usbcap {
51	int		fd;		/* fd for /dev/usbpf */
52	u_int		bufsize;
53	char		*buffer;
54
55	/* for -w option */
56	int		wfd;
57	/* for -r option */
58	int		rfd;
59};
60
61struct usbcap_filehdr {
62	u_int		magic;
63#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
64	u_char		major;
65	u_char		minor;
66	u_char		reserved[26];
67} __packed;
68
69static int doexit = 0;
70static int pkt_captured = 0;
71static int verbose = 0;
72static const char *i_arg = "usbus0";;
73static const char *r_arg = NULL;
74static const char *w_arg = NULL;
75static const char *errstr_table[USB_ERR_MAX] = {
76	[USB_ERR_NORMAL_COMPLETION]	= "NORMAL_COMPLETION",
77	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
78	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
79	[USB_ERR_INVAL]			= "INVAL",
80	[USB_ERR_NOMEM]			= "NOMEM",
81	[USB_ERR_CANCELLED]		= "CANCELLED",
82	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
83	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
84	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
85	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
86	[USB_ERR_IN_USE]		= "IN_USE",
87	[USB_ERR_NO_ADDR]		= "NO_ADDR",
88	[USB_ERR_NO_PIPE]		= "NO_PIPE",
89	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
90	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
91	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
92	[USB_ERR_NO_POWER]		= "NO_POWER",
93	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
94	[USB_ERR_IOERROR]		= "IOERROR",
95	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
96	[USB_ERR_TIMEOUT]		= "TIMEOUT",
97	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
98	[USB_ERR_STALLED]		= "STALLED",
99	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
100	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
101	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
102	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
103	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
104	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
105};
106
107static const char *xfertype_table[] = {
108	[UE_CONTROL]			= "CTRL",
109	[UE_ISOCHRONOUS]		= "ISOC",
110	[UE_BULK]			= "BULK",
111	[UE_INTERRUPT]			= "INTR"
112};
113
114static void
115handle_sigint(int sig)
116{
117
118	(void)sig;
119	doexit = 1;
120}
121
122static void
123print_flags(u_int32_t flags)
124{
125#define	PRINTFLAGS(name)			\
126	if ((flags & USBPF_FLAG_##name) != 0)	\
127		printf("%s ", #name);
128	printf(" flags %#x", flags);
129	printf(" < ");
130	PRINTFLAGS(FORCE_SHORT_XFER);
131	PRINTFLAGS(SHORT_XFER_OK);
132	PRINTFLAGS(SHORT_FRAMES_OK);
133	PRINTFLAGS(PIPE_BOF);
134	PRINTFLAGS(PROXY_BUFFER);
135	PRINTFLAGS(EXT_BUFFER);
136	PRINTFLAGS(MANUAL_STATUS);
137	PRINTFLAGS(NO_PIPE_OK);
138	PRINTFLAGS(STALL_PIPE);
139	printf(">\n");
140#undef PRINTFLAGS
141}
142
143static void
144print_status(u_int32_t status)
145{
146#define	PRINTSTATUS(name)				\
147	if ((status & USBPF_STATUS_##name) != 0)	\
148		printf("%s ", #name);
149
150	printf(" status %#x", status);
151	printf(" < ");
152	PRINTSTATUS(OPEN);
153	PRINTSTATUS(TRANSFERRING);
154	PRINTSTATUS(DID_DMA_DELAY);
155	PRINTSTATUS(DID_CLOSE);
156	PRINTSTATUS(DRAINING);
157	PRINTSTATUS(STARTED);
158	PRINTSTATUS(BW_RECLAIMED);
159	PRINTSTATUS(CONTROL_XFR);
160	PRINTSTATUS(CONTROL_HDR);
161	PRINTSTATUS(CONTROL_ACT);
162	PRINTSTATUS(CONTROL_STALL);
163	PRINTSTATUS(SHORT_FRAMES_OK);
164	PRINTSTATUS(SHORT_XFER_OK);
165#if USB_HAVE_BUSDMA
166	PRINTSTATUS(BDMA_ENABLE);
167	PRINTSTATUS(BDMA_NO_POST_SYNC);
168	PRINTSTATUS(BDMA_SETUP);
169#endif
170	PRINTSTATUS(ISOCHRONOUS_XFR);
171	PRINTSTATUS(CURR_DMA_SET);
172	PRINTSTATUS(CAN_CANCEL_IMMED);
173	PRINTSTATUS(DOING_CALLBACK);
174	printf(">\n");
175#undef PRINTSTATUS
176}
177
178/*
179 * Display a region in traditional hexdump format.
180 */
181static void
182hexdump(const char *region, size_t len)
183{
184	const char *line;
185	int x, c;
186	char lbuf[80];
187#define EMIT(fmt, args...)	do {		\
188	sprintf(lbuf, fmt , ## args);		\
189	printf("%s", lbuf);			\
190} while (0)
191
192	for (line = region; line < (region + len); line += 16) {
193		EMIT(" %04lx  ", (long) (line - region));
194		for (x = 0; x < 16; x++) {
195			if ((line + x) < (region + len))
196				EMIT("%02x ", *(const u_int8_t *)(line + x));
197			else
198				EMIT("-- ");
199			if (x == 7)
200				EMIT(" ");
201		}
202		EMIT(" |");
203		for (x = 0; x < 16; x++) {
204			if ((line + x) < (region + len)) {
205				c = *(const u_int8_t *)(line + x);
206				/* !isprint(c) */
207				if ((c < ' ') || (c > '~'))
208					c = '.';
209				EMIT("%c", c);
210			} else
211				EMIT(" ");
212		}
213		EMIT("|\n");
214	}
215#undef EMIT
216}
217
218static void
219print_apacket(const struct usbpf_xhdr *hdr, struct usbpf_pkthdr *up,
220    const char *payload)
221{
222	struct tm *tm;
223	struct timeval tv;
224	size_t len;
225	u_int32_t framelen, x;
226	const char *ptr = payload;
227	char buf[64];
228
229	/* A packet from the kernel is based on little endian byte order. */
230	up->up_busunit = le32toh(up->up_busunit);
231	up->up_flags = le32toh(up->up_flags);
232	up->up_status = le32toh(up->up_status);
233	up->up_length = le32toh(up->up_length);
234	up->up_frames = le32toh(up->up_frames);
235	up->up_error = le32toh(up->up_error);
236	up->up_interval = le32toh(up->up_interval);
237
238	tv.tv_sec = hdr->uh_tstamp.ut_sec;
239	tv.tv_usec = hdr->uh_tstamp.ut_frac;
240	tm = localtime(&tv.tv_sec);
241
242	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
243	printf("%.*s.%06ju", (int)len, buf, tv.tv_usec);
244	printf(" usbus%d.%d 0x%02x %s %s", up->up_busunit, up->up_address,
245	    up->up_endpoint,
246	    xfertype_table[up->up_xfertype],
247	    up->up_type == USBPF_XFERTAP_SUBMIT ? ">" : "<");
248	printf(" (%d/%d)", up->up_frames, up->up_length);
249	if (up->up_type == USBPF_XFERTAP_DONE)
250		printf(" %s", errstr_table[up->up_error]);
251	if (up->up_xfertype == UE_BULK || up->up_xfertype == UE_ISOCHRONOUS)
252		printf(" %d", up->up_interval);
253	printf("\n");
254
255	if (verbose >= 1) {
256		for (x = 0; x < up->up_frames; x++) {
257			framelen = le32toh(*((const u_int32_t *)ptr));
258			ptr += sizeof(u_int32_t);
259			printf(" frame[%u] len %d\n", x, framelen);
260			assert(framelen < (1024 * 4));
261			hexdump(ptr, framelen);
262			ptr += framelen;
263		}
264	}
265	if (verbose >= 2) {
266		print_flags(up->up_flags);
267		print_status(up->up_status);
268	}
269}
270
271
272static void
273print_packets(char *data, const int datalen)
274{
275	struct usbpf_pkthdr *up;
276	const struct usbpf_xhdr *hdr;
277	u_int32_t framelen, x;
278	char *ptr, *next;
279
280	for (ptr = data; ptr < (data + datalen); ptr = next) {
281		hdr = (const struct usbpf_xhdr *)ptr;
282		up = (struct usbpf_pkthdr *)(ptr + hdr->uh_hdrlen);
283		next = ptr + USBPF_WORDALIGN(hdr->uh_hdrlen + hdr->uh_caplen);
284
285		ptr = ((char *)up) + sizeof(struct usbpf_pkthdr);
286		if (w_arg == NULL)
287			print_apacket(hdr, up, ptr);
288		pkt_captured++;
289		for (x = 0; x < up->up_frames; x++) {
290			framelen = le32toh(*((const u_int32_t *)ptr));
291			ptr += sizeof(u_int32_t) + framelen;
292		}
293	}
294}
295
296static void
297write_packets(struct usbcap *p, const char *data, const int datalen)
298{
299	int len = htole32(datalen), ret;
300
301	ret = write(p->wfd, &len, sizeof(int));
302	assert(ret == sizeof(int));
303	ret = write(p->wfd, data, datalen);
304	assert(ret == datalen);
305}
306
307static void
308read_file(struct usbcap *p)
309{
310	int datalen, ret;
311	char *data;
312
313	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
314		datalen = le32toh(datalen);
315		data = malloc(datalen);
316		assert(data != NULL);
317		ret = read(p->rfd, data, datalen);
318		assert(ret == datalen);
319		print_packets(data, datalen);
320		free(data);
321	}
322	if (ret == -1)
323		fprintf(stderr, "read: %s\n", strerror(errno));
324}
325
326static void
327do_loop(struct usbcap *p)
328{
329	int cc;
330
331	while (doexit == 0) {
332		cc = read(p->fd, (char *)p->buffer, p->bufsize);
333		if (cc < 0) {
334			switch (errno) {
335			case EINTR:
336				break;
337			default:
338				fprintf(stderr, "read: %s\n", strerror(errno));
339				return;
340			}
341			continue;
342		}
343		if (cc == 0)
344			continue;
345		if (w_arg != NULL)
346			write_packets(p, p->buffer, cc);
347		print_packets(p->buffer, cc);
348	}
349}
350
351static void
352init_rfile(struct usbcap *p)
353{
354	struct usbcap_filehdr uf;
355	int ret;
356
357	p->rfd = open(r_arg, O_RDONLY);
358	if (p->rfd < 0) {
359		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
360		exit(EXIT_FAILURE);
361	}
362	ret = read(p->rfd, &uf, sizeof(uf));
363	assert(ret == sizeof(uf));
364	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
365	assert(uf.major == 0);
366	assert(uf.minor == 1);
367}
368
369static void
370init_wfile(struct usbcap *p)
371{
372	struct usbcap_filehdr uf;
373	int ret;
374
375	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
376	if (p->wfd < 0) {
377		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
378		exit(EXIT_FAILURE);
379	}
380	bzero(&uf, sizeof(uf));
381	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
382	uf.major = 0;
383	uf.minor = 1;
384	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
385	assert(ret == sizeof(uf));
386}
387
388static void
389usage(void)
390{
391
392#define FMT "    %-14s %s\n"
393	fprintf(stderr, "usage: usbdump [options]\n");
394	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
395	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
396	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
397	fprintf(stderr, FMT, "-v", "Increases the verbose level");
398	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
399#undef FMT
400	exit(1);
401}
402
403int
404main(int argc, char *argv[])
405{
406	struct timeval tv;
407	struct usbpf_insn total_insn;
408	struct usbpf_program total_prog;
409	struct usbpf_stat us;
410	struct usbpf_version uv;
411	struct usbcap uc, *p = &uc;
412	struct usbpf_ifreq ufr;
413	long snapshot = 192;
414	u_int v;
415	int fd, o;
416	const char *optstring;
417
418	bzero(&uc, sizeof(struct usbcap));
419
420	optstring = "i:r:s:vw:";
421	while ((o = getopt(argc, argv, optstring)) != -1) {
422		switch (o) {
423		case 'i':
424			i_arg = optarg;
425			break;
426		case 'r':
427			r_arg = optarg;
428			init_rfile(p);
429			break;
430		case 's':
431			snapshot = strtol(optarg, NULL, 10);
432			errno = 0;
433			if (snapshot == 0 && errno == EINVAL)
434				usage();
435			/* snapeshot == 0 is special */
436			if (snapshot == 0)
437				snapshot = -1;
438			break;
439		case 'v':
440			verbose++;
441			break;
442		case 'w':
443			w_arg = optarg;
444			init_wfile(p);
445			break;
446		default:
447			usage();
448			/* NOTREACHED */
449		}
450	}
451
452	if (r_arg != NULL) {
453		read_file(p);
454		exit(EXIT_SUCCESS);
455	}
456
457	p->fd = fd = open("/dev/usbpf", O_RDONLY);
458	if (p->fd < 0) {
459		fprintf(stderr, "(no devices found)\n");
460		return (EXIT_FAILURE);
461	}
462
463	if (ioctl(fd, UIOCVERSION, (caddr_t)&uv) < 0) {
464		fprintf(stderr, "UIOCVERSION: %s\n", strerror(errno));
465		return (EXIT_FAILURE);
466	}
467	if (uv.uv_major != USBPF_MAJOR_VERSION ||
468	    uv.uv_minor < USBPF_MINOR_VERSION) {
469		fprintf(stderr, "kernel bpf filter out of date");
470		return (EXIT_FAILURE);
471	}
472
473	if ((ioctl(fd, UIOCGBLEN, (caddr_t)&v) < 0) || v < 65536)
474		v = 65536;
475	for ( ; v != 0; v >>= 1) {
476		(void)ioctl(fd, UIOCSBLEN, (caddr_t)&v);
477		(void)strncpy(ufr.ufr_name, i_arg, sizeof(ufr.ufr_name));
478		if (ioctl(fd, UIOCSETIF, (caddr_t)&ufr) >= 0)
479			break;
480	}
481	if (v == 0) {
482		fprintf(stderr, "UIOCSBLEN: %s: No buffer size worked", i_arg);
483		return (EXIT_FAILURE);
484	}
485
486	if (ioctl(fd, UIOCGBLEN, (caddr_t)&v) < 0) {
487		fprintf(stderr, "UIOCGBLEN: %s", strerror(errno));
488		return (EXIT_FAILURE);
489	}
490
491	p->bufsize = v;
492	p->buffer = (u_char *)malloc(p->bufsize);
493	if (p->buffer == NULL) {
494		fprintf(stderr, "malloc: %s", strerror(errno));
495		return (EXIT_FAILURE);
496	}
497
498	/* XXX no read filter rules yet so at this moment accept everything */
499	total_insn.code = (u_short)(USBPF_RET | USBPF_K);
500	total_insn.jt = 0;
501	total_insn.jf = 0;
502	total_insn.k = snapshot;
503
504	total_prog.uf_len = 1;
505	total_prog.uf_insns = &total_insn;
506	if (ioctl(p->fd, UIOCSETF, (caddr_t)&total_prog) < 0) {
507		fprintf(stderr, "UIOCSETF: %s", strerror(errno));
508		return (EXIT_FAILURE);
509	}
510
511	/* 1 second read timeout */
512	tv.tv_sec = 1;
513	tv.tv_usec = 0;
514	if (ioctl(p->fd, UIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
515		fprintf(stderr, "UIOCSRTIMEOUT: %s", strerror(errno));
516		return (EXIT_FAILURE);
517	}
518
519	(void)signal(SIGINT, handle_sigint);
520
521	do_loop(p);
522
523	if (ioctl(fd, UIOCGSTATS, (caddr_t)&us) < 0) {
524		fprintf(stderr, "UIOCGSTATS: %s", strerror(errno));
525		return (EXIT_FAILURE);
526	}
527
528	/* XXX what's difference between pkt_captured and us.us_recv? */
529	printf("\n");
530	printf("%d packets captured\n", pkt_captured);
531	printf("%d packets received by filter\n", us.us_recv);
532	printf("%d packets dropped by kernel\n", us.us_drop);
533
534	if (p->fd > 0)
535		close(p->fd);
536	if (p->rfd > 0)
537		close(p->rfd);
538	if (p->wfd > 0)
539		close(p->wfd);
540
541	return (EXIT_SUCCESS);
542}
543