usbdump.c revision 218010
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 218010 2011-01-28 08:00:57Z 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 <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	u_int		bufsize;
56	char		*buffer;
57
58	/* for -w option */
59	int		wfd;
60	/* for -r option */
61	int		rfd;
62};
63
64struct usbcap_filehdr {
65	u_int		magic;
66#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
67	u_char		major;
68	u_char		minor;
69	u_char		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]	= "NORMAL_COMPLETION",
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[] = {
111	[UE_CONTROL]			= "CTRL",
112	[UE_ISOCHRONOUS]		= "ISOC",
113	[UE_BULK]			= "BULK",
114	[UE_INTERRUPT]			= "INTR"
115};
116
117static void
118handle_sigint(int sig)
119{
120
121	(void)sig;
122	doexit = 1;
123}
124
125static void
126print_flags(u_int32_t flags)
127{
128#define	PRINTFLAGS(name)			\
129	if ((flags & USBPF_FLAG_##name) != 0)	\
130		printf("%s ", #name);
131	printf(" flags %#x", flags);
132	printf(" < ");
133	PRINTFLAGS(FORCE_SHORT_XFER);
134	PRINTFLAGS(SHORT_XFER_OK);
135	PRINTFLAGS(SHORT_FRAMES_OK);
136	PRINTFLAGS(PIPE_BOF);
137	PRINTFLAGS(PROXY_BUFFER);
138	PRINTFLAGS(EXT_BUFFER);
139	PRINTFLAGS(MANUAL_STATUS);
140	PRINTFLAGS(NO_PIPE_OK);
141	PRINTFLAGS(STALL_PIPE);
142	printf(">\n");
143#undef PRINTFLAGS
144}
145
146static void
147print_status(u_int32_t status)
148{
149#define	PRINTSTATUS(name)				\
150	if ((status & USBPF_STATUS_##name) != 0)	\
151		printf("%s ", #name);
152
153	printf(" status %#x", status);
154	printf(" < ");
155	PRINTSTATUS(OPEN);
156	PRINTSTATUS(TRANSFERRING);
157	PRINTSTATUS(DID_DMA_DELAY);
158	PRINTSTATUS(DID_CLOSE);
159	PRINTSTATUS(DRAINING);
160	PRINTSTATUS(STARTED);
161	PRINTSTATUS(BW_RECLAIMED);
162	PRINTSTATUS(CONTROL_XFR);
163	PRINTSTATUS(CONTROL_HDR);
164	PRINTSTATUS(CONTROL_ACT);
165	PRINTSTATUS(CONTROL_STALL);
166	PRINTSTATUS(SHORT_FRAMES_OK);
167	PRINTSTATUS(SHORT_XFER_OK);
168#if USB_HAVE_BUSDMA
169	PRINTSTATUS(BDMA_ENABLE);
170	PRINTSTATUS(BDMA_NO_POST_SYNC);
171	PRINTSTATUS(BDMA_SETUP);
172#endif
173	PRINTSTATUS(ISOCHRONOUS_XFR);
174	PRINTSTATUS(CURR_DMA_SET);
175	PRINTSTATUS(CAN_CANCEL_IMMED);
176	PRINTSTATUS(DOING_CALLBACK);
177	printf(">\n");
178#undef PRINTSTATUS
179}
180
181/*
182 * Display a region in traditional hexdump format.
183 */
184static void
185hexdump(const char *region, size_t len)
186{
187	const char *line;
188	int x;
189	int c;
190#define EMIT(fmt, ...)	do {		\
191	printf(fmt,## __VA_ARGS__);	\
192} while (0)
193
194	for (line = region; line < (region + len); line += 16) {
195		EMIT(" %04lx  ", (long) (line - region));
196		for (x = 0; x < 16; x++) {
197			if ((line + x) < (region + len))
198				EMIT("%02x ", *(const u_int8_t *)(line + x));
199			else
200				EMIT("-- ");
201			if (x == 7)
202				EMIT(" ");
203		}
204		EMIT(" |");
205		for (x = 0; x < 16; x++) {
206			if ((line + x) < (region + len)) {
207				c = *(const u_int8_t *)(line + x);
208				/* !isprint(c) */
209				if ((c < ' ') || (c > '~'))
210					c = '.';
211				EMIT("%c", c);
212			} else
213				EMIT(" ");
214		}
215		EMIT("|\n");
216	}
217#undef EMIT
218}
219
220static void
221print_apacket(const struct bpf_xhdr *hdr, struct usbpf_pkthdr *up,
222    const char *payload)
223{
224	struct tm *tm;
225	struct timeval tv;
226	size_t len;
227	u_int32_t framelen, x;
228	const char *ptr = payload;
229	char buf[64];
230
231	/* A packet from the kernel is based on little endian byte order. */
232	up->up_busunit = le32toh(up->up_busunit);
233	up->up_flags = le32toh(up->up_flags);
234	up->up_status = le32toh(up->up_status);
235	up->up_length = le32toh(up->up_length);
236	up->up_frames = le32toh(up->up_frames);
237	up->up_error = le32toh(up->up_error);
238	up->up_interval = le32toh(up->up_interval);
239
240	tv.tv_sec = hdr->bh_tstamp.bt_sec;
241	tv.tv_usec = hdr->bh_tstamp.bt_frac;
242	tm = localtime(&tv.tv_sec);
243
244	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
245	printf("%.*s.%06ju", (int)len, buf, tv.tv_usec);
246	printf(" usbus%d.%d 0x%02x %s %s", up->up_busunit, up->up_address,
247	    up->up_endpoint,
248	    xfertype_table[up->up_xfertype],
249	    up->up_type == USBPF_XFERTAP_SUBMIT ? "S" : "D");
250	printf(" (%d/%d)", up->up_frames, up->up_length);
251	if (up->up_type == USBPF_XFERTAP_DONE)
252		printf(" %s", errstr_table[up->up_error]);
253	if (up->up_xfertype == UE_BULK || up->up_xfertype == UE_ISOCHRONOUS)
254		printf(" %d", up->up_interval);
255	printf("\n");
256
257	if (verbose >= 1) {
258		for (x = 0; x < up->up_frames; x++) {
259			framelen = le32toh(*((const u_int32_t *)ptr));
260			ptr += sizeof(u_int32_t);
261			printf(" frame[%u] len %d\n", x, framelen);
262			assert(framelen < (1024 * 4));
263			hexdump(ptr, framelen);
264			ptr += framelen;
265		}
266	}
267	if (verbose >= 2) {
268		print_flags(up->up_flags);
269		print_status(up->up_status);
270	}
271}
272
273static void
274print_packets(char *data, const int datalen)
275{
276	struct usbpf_pkthdr *up;
277	const struct bpf_xhdr *hdr;
278	u_int32_t framelen, x;
279	char *ptr, *next;
280
281	for (ptr = data; ptr < (data + datalen); ptr = next) {
282		hdr = (const struct bpf_xhdr *)ptr;
283		up = (struct usbpf_pkthdr *)(ptr + hdr->bh_hdrlen);
284		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
285
286		ptr = ((char *)up) + sizeof(struct usbpf_pkthdr);
287		if (w_arg == NULL)
288			print_apacket(hdr, up, ptr);
289		pkt_captured++;
290		for (x = 0; x < up->up_frames; x++) {
291			framelen = le32toh(*((const u_int32_t *)ptr));
292			ptr += sizeof(u_int32_t) + framelen;
293		}
294	}
295}
296
297static void
298write_packets(struct usbcap *p, const char *data, const int datalen)
299{
300	int len = htole32(datalen), ret;
301
302	ret = write(p->wfd, &len, sizeof(int));
303	assert(ret == sizeof(int));
304	ret = write(p->wfd, data, datalen);
305	assert(ret == datalen);
306}
307
308static void
309read_file(struct usbcap *p)
310{
311	int datalen, ret;
312	char *data;
313
314	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
315		datalen = le32toh(datalen);
316		data = malloc(datalen);
317		assert(data != NULL);
318		ret = read(p->rfd, data, datalen);
319		assert(ret == datalen);
320		print_packets(data, datalen);
321		free(data);
322	}
323	if (ret == -1)
324		fprintf(stderr, "read: %s\n", strerror(errno));
325}
326
327static void
328do_loop(struct usbcap *p)
329{
330	int cc;
331
332	while (doexit == 0) {
333		cc = read(p->fd, (char *)p->buffer, p->bufsize);
334		if (cc < 0) {
335			switch (errno) {
336			case EINTR:
337				break;
338			default:
339				fprintf(stderr, "read: %s\n", strerror(errno));
340				return;
341			}
342			continue;
343		}
344		if (cc == 0)
345			continue;
346		if (w_arg != NULL)
347			write_packets(p, p->buffer, cc);
348		print_packets(p->buffer, cc);
349	}
350}
351
352static void
353init_rfile(struct usbcap *p)
354{
355	struct usbcap_filehdr uf;
356	int ret;
357
358	p->rfd = open(r_arg, O_RDONLY);
359	if (p->rfd < 0) {
360		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
361		exit(EXIT_FAILURE);
362	}
363	ret = read(p->rfd, &uf, sizeof(uf));
364	assert(ret == sizeof(uf));
365	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
366	assert(uf.major == 0);
367	assert(uf.minor == 1);
368}
369
370static void
371init_wfile(struct usbcap *p)
372{
373	struct usbcap_filehdr uf;
374	int ret;
375
376	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
377	if (p->wfd < 0) {
378		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
379		exit(EXIT_FAILURE);
380	}
381	bzero(&uf, sizeof(uf));
382	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
383	uf.major = 0;
384	uf.minor = 1;
385	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
386	assert(ret == sizeof(uf));
387}
388
389static void
390usage(void)
391{
392
393#define FMT "    %-14s %s\n"
394	fprintf(stderr, "usage: usbdump [options]\n");
395	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
396	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
397	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
398	fprintf(stderr, FMT, "-v", "Increases the verbose level");
399	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
400#undef FMT
401	exit(1);
402}
403
404int
405main(int argc, char *argv[])
406{
407	struct timeval tv;
408	struct bpf_insn total_insn;
409	struct bpf_program total_prog;
410	struct bpf_stat us;
411	struct bpf_version bv;
412	struct usbcap uc, *p = &uc;
413	struct ifreq ifr;
414	long snapshot = 192;
415	u_int v;
416	int fd, o;
417	const char *optstring;
418
419	bzero(&uc, sizeof(struct usbcap));
420
421	optstring = "i:r:s:vw:";
422	while ((o = getopt(argc, argv, optstring)) != -1) {
423		switch (o) {
424		case 'i':
425			i_arg = optarg;
426			break;
427		case 'r':
428			r_arg = optarg;
429			init_rfile(p);
430			break;
431		case 's':
432			snapshot = strtol(optarg, NULL, 10);
433			errno = 0;
434			if (snapshot == 0 && errno == EINVAL)
435				usage();
436			/* snapeshot == 0 is special */
437			if (snapshot == 0)
438				snapshot = -1;
439			break;
440		case 'v':
441			verbose++;
442			break;
443		case 'w':
444			w_arg = optarg;
445			init_wfile(p);
446			break;
447		default:
448			usage();
449			/* NOTREACHED */
450		}
451	}
452
453	if (r_arg != NULL) {
454		read_file(p);
455		exit(EXIT_SUCCESS);
456	}
457
458	p->fd = fd = open("/dev/bpf", O_RDONLY);
459	if (p->fd < 0) {
460		fprintf(stderr, "(no devices found)\n");
461		return (EXIT_FAILURE);
462	}
463
464	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
465		fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno));
466		return (EXIT_FAILURE);
467	}
468	if (bv.bv_major != BPF_MAJOR_VERSION ||
469	    bv.bv_minor < BPF_MINOR_VERSION) {
470		fprintf(stderr, "kernel bpf filter out of date");
471		return (EXIT_FAILURE);
472	}
473
474	if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 4096)
475		v = 4096;
476	for ( ; v != 0; v >>= 1) {
477		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
478		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
479		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
480			break;
481	}
482	if (v == 0) {
483		fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg);
484		return (EXIT_FAILURE);
485	}
486
487	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
488		fprintf(stderr, "BIOCGBLEN: %s", strerror(errno));
489		return (EXIT_FAILURE);
490	}
491
492	p->bufsize = v;
493	p->buffer = (u_char *)malloc(p->bufsize);
494	if (p->buffer == NULL) {
495		fprintf(stderr, "malloc: %s", strerror(errno));
496		return (EXIT_FAILURE);
497	}
498
499	/* XXX no read filter rules yet so at this moment accept everything */
500	total_insn.code = (u_short)(BPF_RET | BPF_K);
501	total_insn.jt = 0;
502	total_insn.jf = 0;
503	total_insn.k = snapshot;
504
505	total_prog.bf_len = 1;
506	total_prog.bf_insns = &total_insn;
507	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
508		fprintf(stderr, "BIOCSETF: %s", strerror(errno));
509		return (EXIT_FAILURE);
510	}
511
512	/* 1 second read timeout */
513	tv.tv_sec = 1;
514	tv.tv_usec = 0;
515	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
516		fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno));
517		return (EXIT_FAILURE);
518	}
519
520	(void)signal(SIGINT, handle_sigint);
521
522	do_loop(p);
523
524	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) {
525		fprintf(stderr, "BIOCGSTATS: %s", strerror(errno));
526		return (EXIT_FAILURE);
527	}
528
529	/* XXX what's difference between pkt_captured and us.us_recv? */
530	printf("\n");
531	printf("%d packets captured\n", pkt_captured);
532	printf("%d packets received by filter\n", us.bs_recv);
533	printf("%d packets dropped by kernel\n", us.bs_drop);
534
535	if (p->fd > 0)
536		close(p->fd);
537	if (p->rfd > 0)
538		close(p->rfd);
539	if (p->wfd > 0)
540		close(p->wfd);
541
542	return (EXIT_SUCCESS);
543}
544