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