usbdump.c revision 220314
1219820Sjeff/*-
2272407Shselasky * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org>
3219820Sjeff * All rights reserved.
4219820Sjeff *
5219820Sjeff * Redistribution and use in source and binary forms, with or without
6219820Sjeff * modification, are permitted provided that the following conditions
7219820Sjeff * are met:
8219820Sjeff * 1. Redistributions of source code must retain the above copyright
9219820Sjeff *    notice, this list of conditions and the following disclaimer,
10219820Sjeff *    without modification.
11219820Sjeff * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12219820Sjeff *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13219820Sjeff *    redistribution must be conditioned upon including a substantially
14219820Sjeff *    similar Disclaimer requirement for further binary redistribution.
15219820Sjeff *
16219820Sjeff * NO WARRANTY
17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18219820Sjeff * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19219820Sjeff * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20219820Sjeff * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21219820Sjeff * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22219820Sjeff * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23219820Sjeff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24219820Sjeff * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25219820Sjeff * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26219820Sjeff * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27219820Sjeff * THE POSSIBILITY OF SUCH DAMAGES.
28219820Sjeff *
29219820Sjeff * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 220314 2011-04-04 02:57:19Z thompsa $
30219820Sjeff */
31219820Sjeff
32219820Sjeff#include <sys/param.h>
33219820Sjeff#include <sys/endian.h>
34219820Sjeff#include <sys/ioctl.h>
35219820Sjeff#include <sys/socket.h>
36219820Sjeff#include <sys/stat.h>
37272407Shselasky#include <sys/utsname.h>
38219820Sjeff#include <net/if.h>
39219820Sjeff#include <net/bpf.h>
40219820Sjeff#include <dev/usb/usb.h>
41272407Shselasky#include <dev/usb/usb_pf.h>
42219820Sjeff#include <dev/usb/usbdi.h>
43272407Shselasky#include <assert.h>
44272407Shselasky#include <errno.h>
45272407Shselasky#include <fcntl.h>
46272407Shselasky#include <limits.h>
47272407Shselasky#include <stdio.h>
48219820Sjeff#include <stdlib.h>
49219820Sjeff#include <string.h>
50219820Sjeff#include <time.h>
51219820Sjeff#include <unistd.h>
52219820Sjeff
53219820Sjeffstruct usbcap {
54219820Sjeff	int		fd;		/* fd for /dev/usbpf */
55219820Sjeff	uint32_t	bufsize;
56219820Sjeff	uint8_t		*buffer;
57219820Sjeff
58219820Sjeff	/* for -w option */
59272407Shselasky	int		wfd;
60219820Sjeff	/* for -r option */
61219820Sjeff	int		rfd;
62219820Sjeff};
63219820Sjeff
64219820Sjeffstruct usbcap_filehdr {
65219820Sjeff	uint32_t	magic;
66219820Sjeff#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
67219820Sjeff	uint8_t   	major;
68219820Sjeff	uint8_t		minor;
69219820Sjeff	uint8_t		reserved[26];
70219820Sjeff} __packed;
71219820Sjeff
72298775Shselaskystatic int doexit = 0;
73272407Shselaskystatic int pkt_captured = 0;
74272407Shselaskystatic int verbose = 0;
75272407Shselaskystatic const char *i_arg = "usbus0";
76219820Sjeffstatic const char *r_arg = NULL;
77219820Sjeffstatic const char *w_arg = NULL;
78219820Sjeffstatic const char *errstr_table[USB_ERR_MAX] = {
79219820Sjeff	[USB_ERR_NORMAL_COMPLETION]	= "0",
80219820Sjeff	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
81219820Sjeff	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
82219820Sjeff	[USB_ERR_INVAL]			= "INVAL",
83272407Shselasky	[USB_ERR_NOMEM]			= "NOMEM",
84272407Shselasky	[USB_ERR_CANCELLED]		= "CANCELLED",
85219820Sjeff	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
86272407Shselasky	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
87272407Shselasky	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
88272407Shselasky	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
89272407Shselasky	[USB_ERR_IN_USE]		= "IN_USE",
90272407Shselasky	[USB_ERR_NO_ADDR]		= "NO_ADDR",
91272407Shselasky	[USB_ERR_NO_PIPE]		= "NO_PIPE",
92272407Shselasky	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
93272407Shselasky	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
94272407Shselasky	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
95272407Shselasky	[USB_ERR_NO_POWER]		= "NO_POWER",
96272407Shselasky	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
97219820Sjeff	[USB_ERR_IOERROR]		= "IOERROR",
98219820Sjeff	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
99219820Sjeff	[USB_ERR_TIMEOUT]		= "TIMEOUT",
100219820Sjeff	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
101219820Sjeff	[USB_ERR_STALLED]		= "STALLED",
102219820Sjeff	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
103272407Shselasky	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
104272407Shselasky	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
105219820Sjeff	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
106272407Shselasky	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
107272407Shselasky	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
108272407Shselasky};
109272407Shselasky
110272407Shselaskystatic const char *xfertype_table[4] = {
111219820Sjeff	[UE_CONTROL]			= "CTRL",
112272407Shselasky	[UE_ISOCHRONOUS]		= "ISOC",
113272407Shselasky	[UE_BULK]			= "BULK",
114272407Shselasky	[UE_INTERRUPT]			= "INTR"
115272407Shselasky};
116272407Shselasky
117272407Shselaskystatic const char *speed_table[USB_SPEED_MAX] = {
118272407Shselasky	[USB_SPEED_FULL] = "FULL",
119219820Sjeff	[USB_SPEED_HIGH] = "HIGH",
120219820Sjeff	[USB_SPEED_LOW] = "LOW",
121219820Sjeff	[USB_SPEED_VARIABLE] = "VARI",
122272407Shselasky	[USB_SPEED_SUPER] = "SUPER",
123272407Shselasky};
124219820Sjeff
125219820Sjeffstatic void
126219820Sjeffhandle_sigint(int sig)
127272407Shselasky{
128272407Shselasky
129272407Shselasky	(void)sig;
130272407Shselasky	doexit = 1;
131272407Shselasky}
132292107Shselasky
133272407Shselasky#define	FLAGS(x, name)	\
134272407Shselasky	(((x) & USBPF_FLAG_##name) ? #name "|" : "")
135219820Sjeff
136219820Sjeff#define	STATUS(x, name) \
137219820Sjeff	(((x) & USBPF_STATUS_##name) ? #name "|" : "")
138219820Sjeff
139219820Sjeffstatic const char *
140219820Sjeffusb_errstr(uint32_t error)
141272407Shselasky{
142272407Shselasky	if (error >= USB_ERR_MAX || errstr_table[error] == NULL)
143219820Sjeff		return ("UNKNOWN");
144219820Sjeff	else
145219820Sjeff		return (errstr_table[error]);
146219820Sjeff}
147219820Sjeff
148219820Sjeffstatic const char *
149219820Sjeffusb_speedstr(uint8_t speed)
150219820Sjeff{
151219820Sjeff	if (speed >= USB_SPEED_MAX  || speed_table[speed] == NULL)
152219820Sjeff		return ("UNKNOWN");
153219820Sjeff	else
154219820Sjeff		return (speed_table[speed]);
155219820Sjeff}
156219820Sjeff
157219820Sjeffstatic void
158272407Shselaskyprint_flags(uint32_t flags)
159219820Sjeff{
160219820Sjeff	printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n",
161219820Sjeff	    flags,
162219820Sjeff	    FLAGS(flags, FORCE_SHORT_XFER),
163272407Shselasky	    FLAGS(flags, SHORT_XFER_OK),
164272407Shselasky	    FLAGS(flags, SHORT_FRAMES_OK),
165219820Sjeff	    FLAGS(flags, PIPE_BOF),
166219820Sjeff	    FLAGS(flags, PROXY_BUFFER),
167219820Sjeff	    FLAGS(flags, EXT_BUFFER),
168219820Sjeff	    FLAGS(flags, MANUAL_STATUS),
169219820Sjeff	    FLAGS(flags, NO_PIPE_OK),
170219820Sjeff	    FLAGS(flags, STALL_PIPE));
171219820Sjeff}
172219820Sjeff
173219820Sjeffstatic void
174219820Sjeffprint_status(uint32_t status)
175219820Sjeff{
176219820Sjeff	printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n",
177219820Sjeff	    status,
178219820Sjeff	    STATUS(status, OPEN),
179219820Sjeff	    STATUS(status, TRANSFERRING),
180219820Sjeff	    STATUS(status, DID_DMA_DELAY),
181219820Sjeff	    STATUS(status, DID_CLOSE),
182219820Sjeff	    STATUS(status, DRAINING),
183219820Sjeff	    STATUS(status, STARTED),
184219820Sjeff	    STATUS(status, BW_RECLAIMED),
185219820Sjeff	    STATUS(status, CONTROL_XFR),
186219820Sjeff	    STATUS(status, CONTROL_HDR),
187219820Sjeff	    STATUS(status, CONTROL_ACT),
188219820Sjeff	    STATUS(status, CONTROL_STALL),
189219820Sjeff	    STATUS(status, SHORT_FRAMES_OK),
190219820Sjeff	    STATUS(status, SHORT_XFER_OK),
191219820Sjeff	    STATUS(status, BDMA_ENABLE),
192219820Sjeff	    STATUS(status, BDMA_NO_POST_SYNC),
193219820Sjeff	    STATUS(status, BDMA_SETUP),
194219820Sjeff	    STATUS(status, ISOCHRONOUS_XFR),
195219820Sjeff	    STATUS(status, CURR_DMA_SET),
196219820Sjeff	    STATUS(status, CAN_CANCEL_IMMED),
197219820Sjeff	    STATUS(status, DOING_CALLBACK));
198219820Sjeff}
199219820Sjeff
200219820Sjeff/*
201219820Sjeff * Dump a byte into hex format.
202219820Sjeff */
203219820Sjeffstatic void
204219820Sjeffhexbyte(char *buf, uint8_t temp)
205219820Sjeff{
206219820Sjeff	uint8_t lo;
207219820Sjeff	uint8_t hi;
208219820Sjeff
209219820Sjeff	lo = temp & 0xF;
210219820Sjeff	hi = temp >> 4;
211219820Sjeff
212219820Sjeff	if (hi < 10)
213292107Shselasky		buf[0] = '0' + hi;
214272407Shselasky	else
215272407Shselasky		buf[0] = 'A' + hi - 10;
216272407Shselasky
217219820Sjeff	if (lo < 10)
218219820Sjeff		buf[1] = '0' + lo;
219219820Sjeff	else
220219820Sjeff		buf[1] = 'A' + lo - 10;
221219820Sjeff}
222219820Sjeff
223219820Sjeff/*
224219820Sjeff * Display a region in traditional hexdump format.
225219820Sjeff */
226219820Sjeffstatic void
227219820Sjeffhexdump(const uint8_t *region, uint32_t len)
228219820Sjeff{
229219820Sjeff	const uint8_t *line;
230219820Sjeff	char linebuf[128];
231219820Sjeff	int i;
232219820Sjeff	int x;
233219820Sjeff	int c;
234219820Sjeff
235219820Sjeff	for (line = region; line < (region + len); line += 16) {
236219820Sjeff
237272407Shselasky		i = 0;
238272407Shselasky
239292107Shselasky		linebuf[i] = ' ';
240292107Shselasky		hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF);
241292107Shselasky		hexbyte(linebuf + i + 3, (line - region) & 0xFF);
242292107Shselasky		linebuf[i + 5] = ' ';
243292107Shselasky		linebuf[i + 6] = ' ';
244292107Shselasky		i += 7;
245292107Shselasky
246292107Shselasky		for (x = 0; x < 16; x++) {
247272407Shselasky		  if ((line + x) < (region + len)) {
248272407Shselasky			hexbyte(linebuf + i,
249272407Shselasky			    *(const u_int8_t *)(line + x));
250219820Sjeff		  } else {
251272407Shselasky			  linebuf[i] = '-';
252292107Shselasky			  linebuf[i + 1] = '-';
253219820Sjeff			}
254219820Sjeff			linebuf[i + 2] = ' ';
255219820Sjeff			if (x == 7) {
256219820Sjeff			  linebuf[i + 3] = ' ';
257219820Sjeff			  i += 4;
258219820Sjeff			} else {
259219820Sjeff			  i += 3;
260219820Sjeff			}
261219820Sjeff		}
262292107Shselasky		linebuf[i] = ' ';
263219820Sjeff		linebuf[i + 1] = '|';
264219820Sjeff		i += 2;
265219820Sjeff		for (x = 0; x < 16; x++) {
266272407Shselasky			if ((line + x) < (region + len)) {
267272407Shselasky				c = *(const u_int8_t *)(line + x);
268272407Shselasky				/* !isprint(c) */
269219820Sjeff				if ((c < ' ') || (c > '~'))
270219820Sjeff					c = '.';
271219820Sjeff				linebuf[i] = c;
272219820Sjeff			} else {
273219820Sjeff				linebuf[i] = ' ';
274219820Sjeff			}
275219820Sjeff			i++;
276219820Sjeff		}
277272407Shselasky		linebuf[i] = '|';
278272407Shselasky		linebuf[i + 1] = 0;
279292107Shselasky		i += 2;
280272407Shselasky		puts(linebuf);
281322531Shselasky	}
282322531Shselasky}
283219820Sjeff
284219820Sjeffstatic void
285272407Shselaskyprint_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len)
286272407Shselasky{
287272407Shselasky	struct tm *tm;
288219820Sjeff	struct usbpf_pkthdr up_temp;
289219820Sjeff	struct usbpf_pkthdr *up;
290219820Sjeff	struct timeval tv;
291219820Sjeff	size_t len;
292219820Sjeff	uint32_t x;
293219820Sjeff	char buf[64];
294219820Sjeff
295219820Sjeff	ptr += USBPF_HDR_LEN;
296292107Shselasky	ptr_len -= USBPF_HDR_LEN;
297292107Shselasky	if (ptr_len < 0)
298292107Shselasky		return;
299272407Shselasky
300272407Shselasky	/* make sure we don't change the source buffer */
301292107Shselasky	memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp));
302292107Shselasky	up = &up_temp;
303292107Shselasky
304292107Shselasky	/*
305292107Shselasky	 * A packet from the kernel is based on little endian byte
306292107Shselasky	 * order.
307219820Sjeff	 */
308219820Sjeff	up->up_totlen = le32toh(up->up_totlen);
309292107Shselasky	up->up_busunit = le32toh(up->up_busunit);
310292107Shselasky	up->up_address = le32toh(up->up_address);
311219820Sjeff	up->up_flags = le32toh(up->up_flags);
312219820Sjeff	up->up_status = le32toh(up->up_status);
313219820Sjeff	up->up_error = le32toh(up->up_error);
314219820Sjeff	up->up_interval = le32toh(up->up_interval);
315219820Sjeff	up->up_frames = le32toh(up->up_frames);
316219820Sjeff	up->up_packet_size = le32toh(up->up_packet_size);
317219820Sjeff	up->up_packet_count = le32toh(up->up_packet_count);
318219820Sjeff	up->up_endpoint = le32toh(up->up_endpoint);
319219820Sjeff
320272407Shselasky	tv.tv_sec = hdr->bh_tstamp.bt_sec;
321272407Shselasky	tv.tv_usec = hdr->bh_tstamp.bt_frac;
322272407Shselasky	tm = localtime(&tv.tv_sec);
323292107Shselasky
324292107Shselasky	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
325272407Shselasky
326219820Sjeff	printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n",
327219820Sjeff	    (int)len, buf, tv.tv_usec,
328272407Shselasky	    (int)up->up_busunit, (int)up->up_address,
329272407Shselasky	    (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE",
330272407Shselasky	    xfertype_table[up->up_xfertype],
331272407Shselasky	    (unsigned int)up->up_endpoint,
332272407Shselasky	    usb_speedstr(up->up_speed),
333272407Shselasky	    (int)up->up_frames,
334272407Shselasky	    (int)(up->up_totlen - USBPF_HDR_LEN -
335272407Shselasky	    (USBPF_FRAME_HDR_LEN * up->up_frames)),
336272407Shselasky	    (int)up->up_interval,
337219820Sjeff	    (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "",
338219820Sjeff	    (up->up_type == USBPF_XFERTAP_DONE) ?
339219820Sjeff	    usb_errstr(up->up_error) : "");
340219820Sjeff
341219820Sjeff	if (verbose >= 1) {
342279731Shselasky		for (x = 0; x != up->up_frames; x++) {
343272407Shselasky			const struct usbpf_framehdr *uf;
344272407Shselasky			uint32_t framelen;
345272407Shselasky			uint32_t flags;
346272407Shselasky
347272407Shselasky			uf = (const struct usbpf_framehdr *)ptr;
348272407Shselasky			ptr += USBPF_FRAME_HDR_LEN;
349272407Shselasky			ptr_len -= USBPF_FRAME_HDR_LEN;
350279731Shselasky			if (ptr_len < 0)
351272407Shselasky				return;
352272407Shselasky
353272407Shselasky			framelen = le32toh(uf->length);
354279731Shselasky			flags = le32toh(uf->flags);
355272407Shselasky
356272407Shselasky			printf(" frame[%u] %s %d bytes\n",
357272407Shselasky			    (unsigned int)x,
358272407Shselasky			    (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE",
359272407Shselasky			    (int)framelen);
360272407Shselasky
361272407Shselasky			if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) {
362219820Sjeff
363219820Sjeff				int tot_frame_len;
364219820Sjeff
365219820Sjeff				tot_frame_len = USBPF_FRAME_ALIGN(framelen);
366219820Sjeff
367219820Sjeff				ptr_len -= tot_frame_len;
368219820Sjeff
369219820Sjeff				if (tot_frame_len < 0 ||
370272407Shselasky				    (int)framelen < 0 || (int)ptr_len < 0)
371272407Shselasky					break;
372219820Sjeff
373219820Sjeff				hexdump(ptr, framelen);
374219820Sjeff
375219820Sjeff				ptr += tot_frame_len;
376219820Sjeff			}
377219820Sjeff		}
378219820Sjeff	}
379219820Sjeff	if (verbose >= 2)
380219820Sjeff		print_flags(up->up_flags);
381219820Sjeff	if (verbose >= 3)
382219820Sjeff		print_status(up->up_status);
383272407Shselasky}
384292107Shselasky
385272407Shselaskystatic void
386272407Shselaskyprint_packets(uint8_t *data, const int datalen)
387272407Shselasky{
388272407Shselasky	const struct bpf_xhdr *hdr;
389272407Shselasky	uint8_t *ptr;
390272407Shselasky	uint8_t *next;
391272407Shselasky
392272407Shselasky	for (ptr = data; ptr < (data + datalen); ptr = next) {
393272407Shselasky		hdr = (const struct bpf_xhdr *)ptr;
394272407Shselasky		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
395272407Shselasky
396272407Shselasky		if (w_arg == NULL) {
397272407Shselasky			print_apacket(hdr, ptr +
398219820Sjeff			    hdr->bh_hdrlen, hdr->bh_caplen);
399219820Sjeff		}
400219820Sjeff		pkt_captured++;
401219820Sjeff	}
402219820Sjeff}
403219820Sjeff
404219820Sjeffstatic void
405219820Sjeffwrite_packets(struct usbcap *p, const uint8_t *data, const int datalen)
406219820Sjeff{
407272407Shselasky	int len = htole32(datalen);
408219820Sjeff	int ret;
409272407Shselasky
410272407Shselasky	ret = write(p->wfd, &len, sizeof(int));
411219820Sjeff	assert(ret == sizeof(int));
412219820Sjeff	ret = write(p->wfd, data, datalen);
413219820Sjeff	assert(ret == datalen);
414219820Sjeff}
415219820Sjeff
416219820Sjeffstatic void
417219820Sjeffread_file(struct usbcap *p)
418219820Sjeff{
419219820Sjeff	int datalen;
420272407Shselasky	int ret;
421219820Sjeff	uint8_t *data;
422219820Sjeff
423219820Sjeff	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
424219820Sjeff		datalen = le32toh(datalen);
425272407Shselasky		data = malloc(datalen);
426219820Sjeff		assert(data != NULL);
427219820Sjeff		ret = read(p->rfd, data, datalen);
428272407Shselasky		assert(ret == datalen);
429272407Shselasky		print_packets(data, datalen);
430219820Sjeff		free(data);
431272407Shselasky	}
432219820Sjeff	if (ret == -1)
433219820Sjeff		fprintf(stderr, "read: %s\n", strerror(errno));
434272407Shselasky}
435272407Shselasky
436272407Shselaskystatic void
437219820Sjeffdo_loop(struct usbcap *p)
438272407Shselasky{
439272407Shselasky	int cc;
440219820Sjeff
441272407Shselasky	while (doexit == 0) {
442272407Shselasky		cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize);
443219820Sjeff		if (cc < 0) {
444219820Sjeff			switch (errno) {
445219820Sjeff			case EINTR:
446219820Sjeff				break;
447219820Sjeff			default:
448219820Sjeff				fprintf(stderr, "read: %s\n", strerror(errno));
449219820Sjeff				return;
450219820Sjeff			}
451219820Sjeff			continue;
452219820Sjeff		}
453219820Sjeff		if (cc == 0)
454219820Sjeff			continue;
455219820Sjeff		if (w_arg != NULL)
456219820Sjeff			write_packets(p, p->buffer, cc);
457219820Sjeff		print_packets(p->buffer, cc);
458272407Shselasky	}
459219820Sjeff}
460219820Sjeff
461272407Shselaskystatic void
462272407Shselaskyinit_rfile(struct usbcap *p)
463272407Shselasky{
464272407Shselasky	struct usbcap_filehdr uf;
465219820Sjeff	int ret;
466219820Sjeff
467272407Shselasky	p->rfd = open(r_arg, O_RDONLY);
468272407Shselasky	if (p->rfd < 0) {
469272407Shselasky		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
470272407Shselasky		exit(EXIT_FAILURE);
471272407Shselasky	}
472219820Sjeff	ret = read(p->rfd, &uf, sizeof(uf));
473219820Sjeff	assert(ret == sizeof(uf));
474272407Shselasky	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
475272407Shselasky	assert(uf.major == 0);
476272407Shselasky	assert(uf.minor == 2);
477272407Shselasky}
478272407Shselasky
479272407Shselaskystatic void
480272407Shselaskyinit_wfile(struct usbcap *p)
481272407Shselasky{
482272407Shselasky	struct usbcap_filehdr uf;
483272407Shselasky	int ret;
484272407Shselasky
485272407Shselasky	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
486272407Shselasky	if (p->wfd < 0) {
487272407Shselasky		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
488272407Shselasky		exit(EXIT_FAILURE);
489272407Shselasky	}
490272407Shselasky	bzero(&uf, sizeof(uf));
491272407Shselasky	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
492272407Shselasky	uf.major = 0;
493272407Shselasky	uf.minor = 2;
494272407Shselasky	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
495272407Shselasky	assert(ret == sizeof(uf));
496272407Shselasky}
497219820Sjeff
498219820Sjeffstatic void
499272407Shselaskyusage(void)
500272407Shselasky{
501272407Shselasky
502272407Shselasky#define FMT "    %-14s %s\n"
503272407Shselasky	fprintf(stderr, "usage: usbdump [options]\n");
504272407Shselasky	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
505272407Shselasky	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
506272407Shselasky	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
507219820Sjeff	fprintf(stderr, FMT, "-v", "Increases the verbose level");
508219820Sjeff	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
509219820Sjeff#undef FMT
510219820Sjeff	exit(1);
511219820Sjeff}
512219820Sjeff
513272407Shselaskyint
514219820Sjeffmain(int argc, char *argv[])
515219820Sjeff{
516272407Shselasky	struct timeval tv;
517272407Shselasky	struct bpf_insn total_insn;
518219820Sjeff	struct bpf_program total_prog;
519257867Salfred	struct bpf_stat us;
520219820Sjeff	struct bpf_version bv;
521257867Salfred	struct usbcap uc, *p = &uc;
522219820Sjeff	struct ifreq ifr;
523257867Salfred	long snapshot = 192;
524219820Sjeff	uint32_t v;
525219820Sjeff	int fd, o;
526219820Sjeff	const char *optstring;
527219820Sjeff
528219820Sjeff	bzero(&uc, sizeof(struct usbcap));
529273736Shselasky
530219820Sjeff	optstring = "i:r:s:vw:";
531273736Shselasky	while ((o = getopt(argc, argv, optstring)) != -1) {
532273736Shselasky		switch (o) {
533273736Shselasky		case 'i':
534219820Sjeff			i_arg = optarg;
535219820Sjeff			break;
536219820Sjeff		case 'r':
537219820Sjeff			r_arg = optarg;
538219820Sjeff			init_rfile(p);
539219820Sjeff			break;
540219820Sjeff		case 's':
541219820Sjeff			snapshot = strtol(optarg, NULL, 10);
542219820Sjeff			errno = 0;
543219820Sjeff			if (snapshot == 0 && errno == EINVAL)
544219820Sjeff				usage();
545272407Shselasky			/* snapeshot == 0 is special */
546272407Shselasky			if (snapshot == 0)
547272407Shselasky				snapshot = -1;
548219820Sjeff			break;
549219820Sjeff		case 'v':
550219820Sjeff			verbose++;
551272407Shselasky			break;
552219820Sjeff		case 'w':
553219820Sjeff			w_arg = optarg;
554219820Sjeff			init_wfile(p);
555272407Shselasky			break;
556219820Sjeff		default:
557219820Sjeff			usage();
558219820Sjeff			/* NOTREACHED */
559219820Sjeff		}
560272407Shselasky	}
561272407Shselasky
562272407Shselasky	if (r_arg != NULL) {
563272407Shselasky		read_file(p);
564272407Shselasky		exit(EXIT_SUCCESS);
565272407Shselasky	}
566219820Sjeff
567219820Sjeff	p->fd = fd = open("/dev/bpf", O_RDONLY);
568219820Sjeff	if (p->fd < 0) {
569272407Shselasky		fprintf(stderr, "(no devices found)\n");
570219820Sjeff		return (EXIT_FAILURE);
571219820Sjeff	}
572272407Shselasky
573219820Sjeff	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
574272407Shselasky		fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno));
575272407Shselasky		return (EXIT_FAILURE);
576272407Shselasky	}
577272407Shselasky	if (bv.bv_major != BPF_MAJOR_VERSION ||
578272407Shselasky	    bv.bv_minor < BPF_MINOR_VERSION) {
579219820Sjeff		fprintf(stderr, "kernel bpf filter out of date");
580272407Shselasky		return (EXIT_FAILURE);
581272407Shselasky	}
582272407Shselasky
583272407Shselasky	/* USB transfers can be greater than 64KByte */
584272407Shselasky	v = 1U << 16;
585219820Sjeff
586219820Sjeff	/* clear ifr structure */
587219820Sjeff	memset(&ifr, 0, sizeof(ifr));
588272407Shselasky
589219820Sjeff	for ( ; v >= USBPF_HDR_LEN; v >>= 1) {
590318540Shselasky		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
591318540Shselasky		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
592219820Sjeff		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
593219820Sjeff			break;
594272407Shselasky	}
595272407Shselasky	if (v == 0) {
596272407Shselasky		fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg);
597272407Shselasky		return (EXIT_FAILURE);
598272407Shselasky	}
599272407Shselasky
600272407Shselasky	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
601272407Shselasky		fprintf(stderr, "BIOCGBLEN: %s", strerror(errno));
602272407Shselasky		return (EXIT_FAILURE);
603272407Shselasky	}
604272407Shselasky
605272407Shselasky	p->bufsize = v;
606272407Shselasky	p->buffer = (uint8_t *)malloc(p->bufsize);
607272407Shselasky	if (p->buffer == NULL) {
608272407Shselasky		fprintf(stderr, "malloc: %s", strerror(errno));
609272407Shselasky		return (EXIT_FAILURE);
610272407Shselasky	}
611272407Shselasky
612219820Sjeff	/* XXX no read filter rules yet so at this moment accept everything */
613219820Sjeff	total_insn.code = (u_short)(BPF_RET | BPF_K);
614220016Sjeff	total_insn.jt = 0;
615220016Sjeff	total_insn.jf = 0;
616220016Sjeff	total_insn.k = snapshot;
617220016Sjeff
618219820Sjeff	total_prog.bf_len = 1;
619272407Shselasky	total_prog.bf_insns = &total_insn;
620272407Shselasky	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
621272407Shselasky		fprintf(stderr, "BIOCSETF: %s", strerror(errno));
622272407Shselasky		return (EXIT_FAILURE);
623272407Shselasky	}
624219820Sjeff
625272407Shselasky	/* 1 second read timeout */
626272407Shselasky	tv.tv_sec = 1;
627272407Shselasky	tv.tv_usec = 0;
628272407Shselasky	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
629272407Shselasky		fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno));
630272407Shselasky		return (EXIT_FAILURE);
631272407Shselasky	}
632272407Shselasky
633272407Shselasky	(void)signal(SIGINT, handle_sigint);
634272407Shselasky
635272407Shselasky	do_loop(p);
636272407Shselasky
637272407Shselasky	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) {
638272407Shselasky		fprintf(stderr, "BIOCGSTATS: %s", strerror(errno));
639272407Shselasky		return (EXIT_FAILURE);
640272407Shselasky	}
641272407Shselasky
642272407Shselasky	/* XXX what's difference between pkt_captured and us.us_recv? */
643272407Shselasky	printf("\n");
644272407Shselasky	printf("%d packets captured\n", pkt_captured);
645272407Shselasky	printf("%d packets received by filter\n", us.bs_recv);
646272407Shselasky	printf("%d packets dropped by kernel\n", us.bs_drop);
647272407Shselasky
648272407Shselasky	if (p->fd > 0)
649272407Shselasky		close(p->fd);
650272407Shselasky	if (p->rfd > 0)
651272407Shselasky		close(p->rfd);
652272407Shselasky	if (p->wfd > 0)
653272407Shselasky		close(p->wfd);
654272407Shselasky
655272407Shselasky	return (EXIT_SUCCESS);
656272407Shselasky}
657272407Shselasky