usbdump.c revision 220301
1215651Sweongyo/*-
2215651Sweongyo * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org>
3215651Sweongyo * All rights reserved.
4215651Sweongyo *
5215651Sweongyo * Redistribution and use in source and binary forms, with or without
6215651Sweongyo * modification, are permitted provided that the following conditions
7215651Sweongyo * are met:
8215651Sweongyo * 1. Redistributions of source code must retain the above copyright
9215651Sweongyo *    notice, this list of conditions and the following disclaimer,
10215651Sweongyo *    without modification.
11215651Sweongyo * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12215651Sweongyo *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13215651Sweongyo *    redistribution must be conditioned upon including a substantially
14215651Sweongyo *    similar Disclaimer requirement for further binary redistribution.
15215651Sweongyo *
16215651Sweongyo * NO WARRANTY
17215651Sweongyo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18215651Sweongyo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19215651Sweongyo * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20215651Sweongyo * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21215651Sweongyo * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22215651Sweongyo * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23215651Sweongyo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24215651Sweongyo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25215651Sweongyo * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26215651Sweongyo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27215651Sweongyo * THE POSSIBILITY OF SUCH DAMAGES.
28215651Sweongyo *
29215651Sweongyo * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 220301 2011-04-03 20:03:45Z hselasky $
30215651Sweongyo */
31215651Sweongyo
32215651Sweongyo#include <sys/param.h>
33215651Sweongyo#include <sys/endian.h>
34215651Sweongyo#include <sys/ioctl.h>
35215803Sweongyo#include <sys/socket.h>
36215651Sweongyo#include <sys/stat.h>
37215651Sweongyo#include <sys/utsname.h>
38215803Sweongyo#include <net/if.h>
39215803Sweongyo#include <net/bpf.h>
40215651Sweongyo#include <dev/usb/usb.h>
41215651Sweongyo#include <dev/usb/usb_pf.h>
42215651Sweongyo#include <dev/usb/usbdi.h>
43215651Sweongyo#include <assert.h>
44215651Sweongyo#include <errno.h>
45215651Sweongyo#include <fcntl.h>
46215651Sweongyo#include <limits.h>
47215651Sweongyo#include <stdio.h>
48215651Sweongyo#include <stdlib.h>
49215651Sweongyo#include <string.h>
50215651Sweongyo#include <time.h>
51215651Sweongyo#include <unistd.h>
52215651Sweongyo
53215651Sweongyostruct usbcap {
54215651Sweongyo	int		fd;		/* fd for /dev/usbpf */
55220301Shselasky	uint32_t	bufsize;
56220301Shselasky	uint8_t		*buffer;
57215651Sweongyo
58215651Sweongyo	/* for -w option */
59215651Sweongyo	int		wfd;
60215651Sweongyo	/* for -r option */
61215651Sweongyo	int		rfd;
62215651Sweongyo};
63215651Sweongyo
64215651Sweongyostruct usbcap_filehdr {
65220301Shselasky	uint32_t	magic;
66215651Sweongyo#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
67220301Shselasky	uint8_t   	major;
68220301Shselasky	uint8_t		minor;
69220301Shselasky	uint8_t		reserved[26];
70215651Sweongyo} __packed;
71215651Sweongyo
72215651Sweongyostatic int doexit = 0;
73215651Sweongyostatic int pkt_captured = 0;
74215651Sweongyostatic int verbose = 0;
75218010Shselaskystatic const char *i_arg = "usbus0";
76215651Sweongyostatic const char *r_arg = NULL;
77215651Sweongyostatic const char *w_arg = NULL;
78215651Sweongyostatic const char *errstr_table[USB_ERR_MAX] = {
79220301Shselasky	[USB_ERR_NORMAL_COMPLETION]	= "0",
80215651Sweongyo	[USB_ERR_PENDING_REQUESTS]	= "PENDING_REQUESTS",
81215651Sweongyo	[USB_ERR_NOT_STARTED]		= "NOT_STARTED",
82215651Sweongyo	[USB_ERR_INVAL]			= "INVAL",
83215651Sweongyo	[USB_ERR_NOMEM]			= "NOMEM",
84215651Sweongyo	[USB_ERR_CANCELLED]		= "CANCELLED",
85215651Sweongyo	[USB_ERR_BAD_ADDRESS]		= "BAD_ADDRESS",
86215651Sweongyo	[USB_ERR_BAD_BUFSIZE]		= "BAD_BUFSIZE",
87215651Sweongyo	[USB_ERR_BAD_FLAG]		= "BAD_FLAG",
88215651Sweongyo	[USB_ERR_NO_CALLBACK]		= "NO_CALLBACK",
89215651Sweongyo	[USB_ERR_IN_USE]		= "IN_USE",
90215651Sweongyo	[USB_ERR_NO_ADDR]		= "NO_ADDR",
91215651Sweongyo	[USB_ERR_NO_PIPE]		= "NO_PIPE",
92215651Sweongyo	[USB_ERR_ZERO_NFRAMES]		= "ZERO_NFRAMES",
93215651Sweongyo	[USB_ERR_ZERO_MAXP]		= "ZERO_MAXP",
94215651Sweongyo	[USB_ERR_SET_ADDR_FAILED]	= "SET_ADDR_FAILED",
95215651Sweongyo	[USB_ERR_NO_POWER]		= "NO_POWER",
96215651Sweongyo	[USB_ERR_TOO_DEEP]		= "TOO_DEEP",
97215651Sweongyo	[USB_ERR_IOERROR]		= "IOERROR",
98215651Sweongyo	[USB_ERR_NOT_CONFIGURED]	= "NOT_CONFIGURED",
99215651Sweongyo	[USB_ERR_TIMEOUT]		= "TIMEOUT",
100215651Sweongyo	[USB_ERR_SHORT_XFER]		= "SHORT_XFER",
101215651Sweongyo	[USB_ERR_STALLED]		= "STALLED",
102215651Sweongyo	[USB_ERR_INTERRUPTED]		= "INTERRUPTED",
103215651Sweongyo	[USB_ERR_DMA_LOAD_FAILED]	= "DMA_LOAD_FAILED",
104215651Sweongyo	[USB_ERR_BAD_CONTEXT]		= "BAD_CONTEXT",
105215651Sweongyo	[USB_ERR_NO_ROOT_HUB]		= "NO_ROOT_HUB",
106215651Sweongyo	[USB_ERR_NO_INTR_THREAD]	= "NO_INTR_THREAD",
107215651Sweongyo	[USB_ERR_NOT_LOCKED]		= "NOT_LOCKED",
108215651Sweongyo};
109215651Sweongyo
110220301Shselaskystatic const char *xfertype_table[4] = {
111215651Sweongyo	[UE_CONTROL]			= "CTRL",
112215651Sweongyo	[UE_ISOCHRONOUS]		= "ISOC",
113215651Sweongyo	[UE_BULK]			= "BULK",
114215651Sweongyo	[UE_INTERRUPT]			= "INTR"
115215651Sweongyo};
116215651Sweongyo
117220301Shselaskystatic const char *speed_table[USB_SPEED_MAX] = {
118220301Shselasky	[USB_SPEED_FULL] = "FULL",
119220301Shselasky	[USB_SPEED_HIGH] = "HIGH",
120220301Shselasky	[USB_SPEED_LOW] = "LOW",
121220301Shselasky	[USB_SPEED_VARIABLE] = "VARI",
122220301Shselasky	[USB_SPEED_SUPER] = "SUPER",
123220301Shselasky};
124220301Shselasky
125215651Sweongyostatic void
126215651Sweongyohandle_sigint(int sig)
127215651Sweongyo{
128215651Sweongyo
129215651Sweongyo	(void)sig;
130215651Sweongyo	doexit = 1;
131215651Sweongyo}
132215651Sweongyo
133220301Shselasky#define	FLAGS(x, name)	\
134220301Shselasky	(((x) & USBPF_FLAG_##name) ? #name "|" : "")
135220301Shselasky
136220301Shselasky#define	STATUS(x, name) \
137220301Shselasky	(((x) & USBPF_STATUS_##name) ? #name "|" : "")
138220301Shselasky
139220301Shselaskystatic const char *
140220301Shselaskyusb_errstr(uint32_t error)
141220301Shselasky{
142220301Shselasky	if (error >= USB_ERR_MAX || errstr_table[error] == NULL)
143220301Shselasky		return ("UNKNOWN");
144220301Shselasky	else
145220301Shselasky		return (errstr_table[error]);
146220301Shselasky}
147220301Shselasky
148220301Shselaskystatic const char *
149220301Shselaskyusb_speedstr(uint8_t speed)
150220301Shselasky{
151220301Shselasky	if (speed >= USB_SPEED_MAX  || speed_table[speed] == NULL)
152220301Shselasky		return ("UNKNOWN");
153220301Shselasky	else
154220301Shselasky		return (speed_table[speed]);
155220301Shselasky}
156220301Shselasky
157215651Sweongyostatic void
158220301Shselaskyprint_flags(uint32_t flags)
159215651Sweongyo{
160220301Shselasky	printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n",
161220301Shselasky	    flags,
162220301Shselasky	    FLAGS(flags, FORCE_SHORT_XFER),
163220301Shselasky	    FLAGS(flags, SHORT_XFER_OK),
164220301Shselasky	    FLAGS(flags, SHORT_FRAMES_OK),
165220301Shselasky	    FLAGS(flags, PIPE_BOF),
166220301Shselasky	    FLAGS(flags, PROXY_BUFFER),
167220301Shselasky	    FLAGS(flags, EXT_BUFFER),
168220301Shselasky	    FLAGS(flags, MANUAL_STATUS),
169220301Shselasky	    FLAGS(flags, NO_PIPE_OK),
170220301Shselasky	    FLAGS(flags, STALL_PIPE));
171215651Sweongyo}
172215651Sweongyo
173215651Sweongyostatic void
174220301Shselaskyprint_status(uint32_t status)
175215651Sweongyo{
176220301Shselasky	printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n",
177220301Shselasky	    status,
178220301Shselasky	    STATUS(status, OPEN),
179220301Shselasky	    STATUS(status, TRANSFERRING),
180220301Shselasky	    STATUS(status, DID_DMA_DELAY),
181220301Shselasky	    STATUS(status, DID_CLOSE),
182220301Shselasky	    STATUS(status, DRAINING),
183220301Shselasky	    STATUS(status, STARTED),
184220301Shselasky	    STATUS(status, BW_RECLAIMED),
185220301Shselasky	    STATUS(status, CONTROL_XFR),
186220301Shselasky	    STATUS(status, CONTROL_HDR),
187220301Shselasky	    STATUS(status, CONTROL_ACT),
188220301Shselasky	    STATUS(status, CONTROL_STALL),
189220301Shselasky	    STATUS(status, SHORT_FRAMES_OK),
190220301Shselasky	    STATUS(status, SHORT_XFER_OK),
191220301Shselasky	    STATUS(status, BDMA_ENABLE),
192220301Shselasky	    STATUS(status, BDMA_NO_POST_SYNC),
193220301Shselasky	    STATUS(status, BDMA_SETUP),
194220301Shselasky	    STATUS(status, ISOCHRONOUS_XFR),
195220301Shselasky	    STATUS(status, CURR_DMA_SET),
196220301Shselasky	    STATUS(status, CAN_CANCEL_IMMED),
197220301Shselasky	    STATUS(status, DOING_CALLBACK));
198220301Shselasky}
199215651Sweongyo
200220301Shselasky/*
201220301Shselasky * Dump a byte into hex format.
202220301Shselasky */
203220301Shselaskystatic void
204220301Shselaskyhexbyte(char *buf, uint8_t temp)
205220301Shselasky{
206220301Shselasky	uint8_t lo;
207220301Shselasky	uint8_t hi;
208220301Shselasky
209220301Shselasky	lo = temp & 0xF;
210220301Shselasky	hi = temp >> 4;
211220301Shselasky
212220301Shselasky	if (hi < 10)
213220301Shselasky		buf[0] = '0' + hi;
214220301Shselasky	else
215220301Shselasky		buf[0] = 'A' + hi - 10;
216220301Shselasky
217220301Shselasky	if (lo < 10)
218220301Shselasky		buf[1] = '0' + lo;
219220301Shselasky	else
220220301Shselasky		buf[1] = 'A' + lo - 10;
221215651Sweongyo}
222215651Sweongyo
223215651Sweongyo/*
224215651Sweongyo * Display a region in traditional hexdump format.
225215651Sweongyo */
226215651Sweongyostatic void
227220301Shselaskyhexdump(const uint8_t *region, uint32_t len)
228215651Sweongyo{
229220301Shselasky	const uint8_t *line;
230220301Shselasky	char linebuf[128];
231220301Shselasky	int i;
232218010Shselasky	int x;
233218010Shselasky	int c;
234215651Sweongyo
235215651Sweongyo	for (line = region; line < (region + len); line += 16) {
236220301Shselasky
237220301Shselasky		i = 0;
238220301Shselasky
239220301Shselasky		linebuf[i] = ' ';
240220301Shselasky		hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF);
241220301Shselasky		hexbyte(linebuf + i + 3, (line - region) & 0xFF);
242220301Shselasky		linebuf[i + 5] = ' ';
243220301Shselasky		linebuf[i + 6] = ' ';
244220301Shselasky		i += 7;
245220301Shselasky
246215651Sweongyo		for (x = 0; x < 16; x++) {
247220301Shselasky		  if ((line + x) < (region + len)) {
248220301Shselasky			hexbyte(linebuf + i,
249220301Shselasky			    *(const u_int8_t *)(line + x));
250220301Shselasky		  } else {
251220301Shselasky			  linebuf[i] = '-';
252220301Shselasky			  linebuf[i + 1] = '-';
253220301Shselasky			}
254220301Shselasky			linebuf[i + 2] = ' ';
255220301Shselasky			if (x == 7) {
256220301Shselasky			  linebuf[i + 3] = ' ';
257220301Shselasky			  i += 4;
258220301Shselasky			} else {
259220301Shselasky			  i += 3;
260220301Shselasky			}
261215651Sweongyo		}
262220301Shselasky		linebuf[i] = ' ';
263220301Shselasky		linebuf[i + 1] = '|';
264220301Shselasky		i += 2;
265215651Sweongyo		for (x = 0; x < 16; x++) {
266215651Sweongyo			if ((line + x) < (region + len)) {
267215651Sweongyo				c = *(const u_int8_t *)(line + x);
268215651Sweongyo				/* !isprint(c) */
269215651Sweongyo				if ((c < ' ') || (c > '~'))
270215651Sweongyo					c = '.';
271220301Shselasky				linebuf[i] = c;
272220301Shselasky			} else {
273220301Shselasky				linebuf[i] = ' ';
274220301Shselasky			}
275220301Shselasky			i++;
276215651Sweongyo		}
277220301Shselasky		linebuf[i] = '|';
278220301Shselasky		linebuf[i + 1] = 0;
279220301Shselasky		i += 2;
280220301Shselasky		puts(linebuf);
281215651Sweongyo	}
282215651Sweongyo}
283215651Sweongyo
284215651Sweongyostatic void
285220301Shselaskyprint_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len)
286215651Sweongyo{
287215651Sweongyo	struct tm *tm;
288220301Shselasky	struct usbpf_pkthdr up_temp;
289220301Shselasky	struct usbpf_pkthdr *up;
290215651Sweongyo	struct timeval tv;
291215651Sweongyo	size_t len;
292220301Shselasky	uint32_t x;
293215651Sweongyo	char buf[64];
294215651Sweongyo
295220301Shselasky	ptr += USBPF_HDR_LEN;
296220301Shselasky	ptr_len -= USBPF_HDR_LEN;
297220301Shselasky	if (ptr_len < 0)
298220301Shselasky		return;
299220301Shselasky
300220301Shselasky	/* make sure we don't change the source buffer */
301220301Shselasky	memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp));
302220301Shselasky	up = &up_temp;
303220301Shselasky
304220301Shselasky	/*
305220301Shselasky	 * A packet from the kernel is based on little endian byte
306220301Shselasky	 * order.
307220301Shselasky	 */
308220301Shselasky	up->up_totlen = le32toh(up->up_totlen);
309215651Sweongyo	up->up_busunit = le32toh(up->up_busunit);
310220301Shselasky	up->up_address = le32toh(up->up_address);
311215651Sweongyo	up->up_flags = le32toh(up->up_flags);
312215651Sweongyo	up->up_status = le32toh(up->up_status);
313215651Sweongyo	up->up_error = le32toh(up->up_error);
314215651Sweongyo	up->up_interval = le32toh(up->up_interval);
315220301Shselasky	up->up_frames = le32toh(up->up_frames);
316220301Shselasky	up->up_packet_size = le32toh(up->up_packet_size);
317220301Shselasky	up->up_packet_count = le32toh(up->up_packet_count);
318220301Shselasky	up->up_endpoint = le32toh(up->up_endpoint);
319215651Sweongyo
320215803Sweongyo	tv.tv_sec = hdr->bh_tstamp.bt_sec;
321215803Sweongyo	tv.tv_usec = hdr->bh_tstamp.bt_frac;
322215651Sweongyo	tm = localtime(&tv.tv_sec);
323215651Sweongyo
324215651Sweongyo	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
325220301Shselasky
326220301Shselasky	printf("%.*s.%06ju usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n",
327220301Shselasky	    (int)len, buf, tv.tv_usec,
328220301Shselasky	    (int)up->up_busunit, (int)up->up_address,
329220301Shselasky	    (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE",
330215651Sweongyo	    xfertype_table[up->up_xfertype],
331220301Shselasky	    (unsigned int)up->up_endpoint,
332220301Shselasky	    usb_speedstr(up->up_speed),
333220301Shselasky	    (int)up->up_frames,
334220301Shselasky	    (int)(up->up_totlen - USBPF_HDR_LEN -
335220301Shselasky	    (USBPF_FRAME_HDR_LEN * up->up_frames)),
336220301Shselasky	    (int)up->up_interval,
337220301Shselasky	    (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "",
338220301Shselasky	    (up->up_type == USBPF_XFERTAP_DONE) ?
339220301Shselasky	    usb_errstr(up->up_error) : "");
340215651Sweongyo
341215651Sweongyo	if (verbose >= 1) {
342220301Shselasky		for (x = 0; x != up->up_frames; x++) {
343220301Shselasky			const struct usbpf_framehdr *uf;
344220301Shselasky			uint32_t framelen;
345220301Shselasky			uint32_t flags;
346220301Shselasky
347220301Shselasky			uf = (const struct usbpf_framehdr *)ptr;
348220301Shselasky			ptr += USBPF_FRAME_HDR_LEN;
349220301Shselasky			ptr_len -= USBPF_FRAME_HDR_LEN;
350220301Shselasky			if (ptr_len < 0)
351220301Shselasky				return;
352220301Shselasky
353220301Shselasky			framelen = le32toh(uf->length);
354220301Shselasky			flags = le32toh(uf->flags);
355220301Shselasky
356220301Shselasky			printf(" frame[%u] %s %d bytes\n",
357220301Shselasky			    (unsigned int)x,
358220301Shselasky			    (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE",
359220301Shselasky			    (int)framelen);
360220301Shselasky
361220301Shselasky			if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) {
362220301Shselasky
363220301Shselasky				int tot_frame_len;
364220301Shselasky
365220301Shselasky				tot_frame_len = USBPF_FRAME_ALIGN(framelen);
366220301Shselasky
367220301Shselasky				ptr_len -= tot_frame_len;
368220301Shselasky
369220301Shselasky				if (tot_frame_len < 0 ||
370220301Shselasky				    (int)framelen < 0 || (int)ptr_len < 0)
371220301Shselasky					break;
372220301Shselasky
373220301Shselasky				hexdump(ptr, framelen);
374220301Shselasky
375220301Shselasky				ptr += tot_frame_len;
376220301Shselasky			}
377215651Sweongyo		}
378215651Sweongyo	}
379220301Shselasky	if (verbose >= 2)
380215651Sweongyo		print_flags(up->up_flags);
381220301Shselasky	if (verbose >= 3)
382215651Sweongyo		print_status(up->up_status);
383215651Sweongyo}
384215651Sweongyo
385215651Sweongyostatic void
386220301Shselaskyprint_packets(uint8_t *data, const int datalen)
387215651Sweongyo{
388215803Sweongyo	const struct bpf_xhdr *hdr;
389220301Shselasky	uint8_t *ptr;
390220301Shselasky	uint8_t *next;
391215651Sweongyo
392215651Sweongyo	for (ptr = data; ptr < (data + datalen); ptr = next) {
393215803Sweongyo		hdr = (const struct bpf_xhdr *)ptr;
394215803Sweongyo		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
395215651Sweongyo
396220301Shselasky		if (w_arg == NULL) {
397220301Shselasky			print_apacket(hdr, ptr +
398220301Shselasky			    hdr->bh_hdrlen, hdr->bh_caplen);
399220301Shselasky		}
400215651Sweongyo		pkt_captured++;
401215651Sweongyo	}
402215651Sweongyo}
403215651Sweongyo
404215651Sweongyostatic void
405220301Shselaskywrite_packets(struct usbcap *p, const uint8_t *data, const int datalen)
406215651Sweongyo{
407220301Shselasky	int len = htole32(datalen);
408220301Shselasky	int ret;
409215651Sweongyo
410215651Sweongyo	ret = write(p->wfd, &len, sizeof(int));
411215651Sweongyo	assert(ret == sizeof(int));
412215651Sweongyo	ret = write(p->wfd, data, datalen);
413215651Sweongyo	assert(ret == datalen);
414215651Sweongyo}
415215651Sweongyo
416215651Sweongyostatic void
417215651Sweongyoread_file(struct usbcap *p)
418215651Sweongyo{
419220301Shselasky	int datalen;
420220301Shselasky	int ret;
421220301Shselasky	uint8_t *data;
422215651Sweongyo
423215651Sweongyo	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
424215651Sweongyo		datalen = le32toh(datalen);
425215651Sweongyo		data = malloc(datalen);
426215651Sweongyo		assert(data != NULL);
427215651Sweongyo		ret = read(p->rfd, data, datalen);
428215651Sweongyo		assert(ret == datalen);
429215651Sweongyo		print_packets(data, datalen);
430215651Sweongyo		free(data);
431215651Sweongyo	}
432215651Sweongyo	if (ret == -1)
433215651Sweongyo		fprintf(stderr, "read: %s\n", strerror(errno));
434215651Sweongyo}
435215651Sweongyo
436215651Sweongyostatic void
437215651Sweongyodo_loop(struct usbcap *p)
438215651Sweongyo{
439215651Sweongyo	int cc;
440215651Sweongyo
441215651Sweongyo	while (doexit == 0) {
442220301Shselasky		cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize);
443215651Sweongyo		if (cc < 0) {
444215651Sweongyo			switch (errno) {
445215651Sweongyo			case EINTR:
446215651Sweongyo				break;
447215651Sweongyo			default:
448215651Sweongyo				fprintf(stderr, "read: %s\n", strerror(errno));
449215651Sweongyo				return;
450215651Sweongyo			}
451215651Sweongyo			continue;
452215651Sweongyo		}
453215651Sweongyo		if (cc == 0)
454215651Sweongyo			continue;
455215651Sweongyo		if (w_arg != NULL)
456215651Sweongyo			write_packets(p, p->buffer, cc);
457215651Sweongyo		print_packets(p->buffer, cc);
458215651Sweongyo	}
459215651Sweongyo}
460215651Sweongyo
461215651Sweongyostatic void
462215651Sweongyoinit_rfile(struct usbcap *p)
463215651Sweongyo{
464215651Sweongyo	struct usbcap_filehdr uf;
465215651Sweongyo	int ret;
466215651Sweongyo
467215651Sweongyo	p->rfd = open(r_arg, O_RDONLY);
468215651Sweongyo	if (p->rfd < 0) {
469215651Sweongyo		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
470215651Sweongyo		exit(EXIT_FAILURE);
471215651Sweongyo	}
472215651Sweongyo	ret = read(p->rfd, &uf, sizeof(uf));
473215651Sweongyo	assert(ret == sizeof(uf));
474215651Sweongyo	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
475215651Sweongyo	assert(uf.major == 0);
476220301Shselasky	assert(uf.minor == 2);
477215651Sweongyo}
478215651Sweongyo
479215651Sweongyostatic void
480215651Sweongyoinit_wfile(struct usbcap *p)
481215651Sweongyo{
482215651Sweongyo	struct usbcap_filehdr uf;
483215651Sweongyo	int ret;
484215651Sweongyo
485215651Sweongyo	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
486215651Sweongyo	if (p->wfd < 0) {
487215651Sweongyo		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
488215651Sweongyo		exit(EXIT_FAILURE);
489215651Sweongyo	}
490215651Sweongyo	bzero(&uf, sizeof(uf));
491215651Sweongyo	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
492215651Sweongyo	uf.major = 0;
493220301Shselasky	uf.minor = 2;
494215651Sweongyo	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
495215651Sweongyo	assert(ret == sizeof(uf));
496215651Sweongyo}
497215651Sweongyo
498215651Sweongyostatic void
499215651Sweongyousage(void)
500215651Sweongyo{
501215651Sweongyo
502215651Sweongyo#define FMT "    %-14s %s\n"
503215651Sweongyo	fprintf(stderr, "usage: usbdump [options]\n");
504215651Sweongyo	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
505215651Sweongyo	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
506215651Sweongyo	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
507215651Sweongyo	fprintf(stderr, FMT, "-v", "Increases the verbose level");
508215651Sweongyo	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
509215651Sweongyo#undef FMT
510215651Sweongyo	exit(1);
511215651Sweongyo}
512215651Sweongyo
513215651Sweongyoint
514215651Sweongyomain(int argc, char *argv[])
515215651Sweongyo{
516215651Sweongyo	struct timeval tv;
517215803Sweongyo	struct bpf_insn total_insn;
518215803Sweongyo	struct bpf_program total_prog;
519215803Sweongyo	struct bpf_stat us;
520215803Sweongyo	struct bpf_version bv;
521215651Sweongyo	struct usbcap uc, *p = &uc;
522215803Sweongyo	struct ifreq ifr;
523215651Sweongyo	long snapshot = 192;
524220301Shselasky	uint32_t v;
525215651Sweongyo	int fd, o;
526215651Sweongyo	const char *optstring;
527215651Sweongyo
528215651Sweongyo	bzero(&uc, sizeof(struct usbcap));
529215651Sweongyo
530215651Sweongyo	optstring = "i:r:s:vw:";
531215651Sweongyo	while ((o = getopt(argc, argv, optstring)) != -1) {
532215651Sweongyo		switch (o) {
533215651Sweongyo		case 'i':
534215651Sweongyo			i_arg = optarg;
535215651Sweongyo			break;
536215651Sweongyo		case 'r':
537215651Sweongyo			r_arg = optarg;
538215651Sweongyo			init_rfile(p);
539215651Sweongyo			break;
540215651Sweongyo		case 's':
541215651Sweongyo			snapshot = strtol(optarg, NULL, 10);
542215651Sweongyo			errno = 0;
543215651Sweongyo			if (snapshot == 0 && errno == EINVAL)
544215651Sweongyo				usage();
545215651Sweongyo			/* snapeshot == 0 is special */
546215651Sweongyo			if (snapshot == 0)
547215651Sweongyo				snapshot = -1;
548215651Sweongyo			break;
549215651Sweongyo		case 'v':
550215651Sweongyo			verbose++;
551215651Sweongyo			break;
552215651Sweongyo		case 'w':
553215651Sweongyo			w_arg = optarg;
554215651Sweongyo			init_wfile(p);
555215651Sweongyo			break;
556215651Sweongyo		default:
557215651Sweongyo			usage();
558215651Sweongyo			/* NOTREACHED */
559215651Sweongyo		}
560215651Sweongyo	}
561215651Sweongyo
562215651Sweongyo	if (r_arg != NULL) {
563215651Sweongyo		read_file(p);
564215651Sweongyo		exit(EXIT_SUCCESS);
565215651Sweongyo	}
566215651Sweongyo
567215803Sweongyo	p->fd = fd = open("/dev/bpf", O_RDONLY);
568215651Sweongyo	if (p->fd < 0) {
569215651Sweongyo		fprintf(stderr, "(no devices found)\n");
570215651Sweongyo		return (EXIT_FAILURE);
571215651Sweongyo	}
572215651Sweongyo
573215803Sweongyo	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
574215803Sweongyo		fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno));
575215651Sweongyo		return (EXIT_FAILURE);
576215651Sweongyo	}
577215803Sweongyo	if (bv.bv_major != BPF_MAJOR_VERSION ||
578215803Sweongyo	    bv.bv_minor < BPF_MINOR_VERSION) {
579215651Sweongyo		fprintf(stderr, "kernel bpf filter out of date");
580215651Sweongyo		return (EXIT_FAILURE);
581215651Sweongyo	}
582215651Sweongyo
583220301Shselasky	/* USB transfers can be greater than 64KByte */
584220301Shselasky	v = 1U << 16;
585220301Shselasky
586220301Shselasky	/* clear ifr structure */
587220301Shselasky	memset(&ifr, 0, sizeof(ifr));
588220301Shselasky
589220301Shselasky	for ( ; v >= USBPF_HDR_LEN; v >>= 1) {
590215803Sweongyo		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
591215803Sweongyo		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
592215803Sweongyo		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
593215651Sweongyo			break;
594215651Sweongyo	}
595215651Sweongyo	if (v == 0) {
596215803Sweongyo		fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg);
597215651Sweongyo		return (EXIT_FAILURE);
598215651Sweongyo	}
599215651Sweongyo
600215803Sweongyo	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
601215803Sweongyo		fprintf(stderr, "BIOCGBLEN: %s", strerror(errno));
602215651Sweongyo		return (EXIT_FAILURE);
603215651Sweongyo	}
604215651Sweongyo
605215651Sweongyo	p->bufsize = v;
606220301Shselasky	p->buffer = (uint8_t *)malloc(p->bufsize);
607215651Sweongyo	if (p->buffer == NULL) {
608215651Sweongyo		fprintf(stderr, "malloc: %s", strerror(errno));
609215651Sweongyo		return (EXIT_FAILURE);
610215651Sweongyo	}
611215651Sweongyo
612215651Sweongyo	/* XXX no read filter rules yet so at this moment accept everything */
613215803Sweongyo	total_insn.code = (u_short)(BPF_RET | BPF_K);
614215651Sweongyo	total_insn.jt = 0;
615215651Sweongyo	total_insn.jf = 0;
616215651Sweongyo	total_insn.k = snapshot;
617215651Sweongyo
618215803Sweongyo	total_prog.bf_len = 1;
619215803Sweongyo	total_prog.bf_insns = &total_insn;
620215803Sweongyo	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
621215803Sweongyo		fprintf(stderr, "BIOCSETF: %s", strerror(errno));
622215651Sweongyo		return (EXIT_FAILURE);
623215651Sweongyo	}
624215651Sweongyo
625215651Sweongyo	/* 1 second read timeout */
626215651Sweongyo	tv.tv_sec = 1;
627215651Sweongyo	tv.tv_usec = 0;
628215803Sweongyo	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
629215803Sweongyo		fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno));
630215651Sweongyo		return (EXIT_FAILURE);
631215651Sweongyo	}
632215651Sweongyo
633215651Sweongyo	(void)signal(SIGINT, handle_sigint);
634215651Sweongyo
635215651Sweongyo	do_loop(p);
636215651Sweongyo
637215803Sweongyo	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) {
638215803Sweongyo		fprintf(stderr, "BIOCGSTATS: %s", strerror(errno));
639215651Sweongyo		return (EXIT_FAILURE);
640215651Sweongyo	}
641215651Sweongyo
642215651Sweongyo	/* XXX what's difference between pkt_captured and us.us_recv? */
643215651Sweongyo	printf("\n");
644215651Sweongyo	printf("%d packets captured\n", pkt_captured);
645215803Sweongyo	printf("%d packets received by filter\n", us.bs_recv);
646215803Sweongyo	printf("%d packets dropped by kernel\n", us.bs_drop);
647215651Sweongyo
648215651Sweongyo	if (p->fd > 0)
649215651Sweongyo		close(p->fd);
650215651Sweongyo	if (p->rfd > 0)
651215651Sweongyo		close(p->rfd);
652215651Sweongyo	if (p->wfd > 0)
653215651Sweongyo		close(p->wfd);
654215651Sweongyo
655215651Sweongyo	return (EXIT_SUCCESS);
656215651Sweongyo}
657