usbdump.c revision 218010
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 218010 2011-01-28 08:00:57Z 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 */
55215651Sweongyo	u_int		bufsize;
56215651Sweongyo	char		*buffer;
57215651Sweongyo
58215651Sweongyo	/* for -w option */
59215651Sweongyo	int		wfd;
60215651Sweongyo	/* for -r option */
61215651Sweongyo	int		rfd;
62215651Sweongyo};
63215651Sweongyo
64215651Sweongyostruct usbcap_filehdr {
65215651Sweongyo	u_int		magic;
66215651Sweongyo#define	USBCAP_FILEHDR_MAGIC	0x9a90000e
67215651Sweongyo	u_char		major;
68215651Sweongyo	u_char		minor;
69215651Sweongyo	u_char		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] = {
79215651Sweongyo	[USB_ERR_NORMAL_COMPLETION]	= "NORMAL_COMPLETION",
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
110215651Sweongyostatic const char *xfertype_table[] = {
111215651Sweongyo	[UE_CONTROL]			= "CTRL",
112215651Sweongyo	[UE_ISOCHRONOUS]		= "ISOC",
113215651Sweongyo	[UE_BULK]			= "BULK",
114215651Sweongyo	[UE_INTERRUPT]			= "INTR"
115215651Sweongyo};
116215651Sweongyo
117215651Sweongyostatic void
118215651Sweongyohandle_sigint(int sig)
119215651Sweongyo{
120215651Sweongyo
121215651Sweongyo	(void)sig;
122215651Sweongyo	doexit = 1;
123215651Sweongyo}
124215651Sweongyo
125215651Sweongyostatic void
126215651Sweongyoprint_flags(u_int32_t flags)
127215651Sweongyo{
128215651Sweongyo#define	PRINTFLAGS(name)			\
129215651Sweongyo	if ((flags & USBPF_FLAG_##name) != 0)	\
130215651Sweongyo		printf("%s ", #name);
131215651Sweongyo	printf(" flags %#x", flags);
132215651Sweongyo	printf(" < ");
133215651Sweongyo	PRINTFLAGS(FORCE_SHORT_XFER);
134215651Sweongyo	PRINTFLAGS(SHORT_XFER_OK);
135215651Sweongyo	PRINTFLAGS(SHORT_FRAMES_OK);
136215651Sweongyo	PRINTFLAGS(PIPE_BOF);
137215651Sweongyo	PRINTFLAGS(PROXY_BUFFER);
138215651Sweongyo	PRINTFLAGS(EXT_BUFFER);
139215651Sweongyo	PRINTFLAGS(MANUAL_STATUS);
140215651Sweongyo	PRINTFLAGS(NO_PIPE_OK);
141215651Sweongyo	PRINTFLAGS(STALL_PIPE);
142215651Sweongyo	printf(">\n");
143215651Sweongyo#undef PRINTFLAGS
144215651Sweongyo}
145215651Sweongyo
146215651Sweongyostatic void
147215651Sweongyoprint_status(u_int32_t status)
148215651Sweongyo{
149215651Sweongyo#define	PRINTSTATUS(name)				\
150215651Sweongyo	if ((status & USBPF_STATUS_##name) != 0)	\
151215651Sweongyo		printf("%s ", #name);
152215651Sweongyo
153215651Sweongyo	printf(" status %#x", status);
154215651Sweongyo	printf(" < ");
155215651Sweongyo	PRINTSTATUS(OPEN);
156215651Sweongyo	PRINTSTATUS(TRANSFERRING);
157215651Sweongyo	PRINTSTATUS(DID_DMA_DELAY);
158215651Sweongyo	PRINTSTATUS(DID_CLOSE);
159215651Sweongyo	PRINTSTATUS(DRAINING);
160215651Sweongyo	PRINTSTATUS(STARTED);
161215651Sweongyo	PRINTSTATUS(BW_RECLAIMED);
162215651Sweongyo	PRINTSTATUS(CONTROL_XFR);
163215651Sweongyo	PRINTSTATUS(CONTROL_HDR);
164215651Sweongyo	PRINTSTATUS(CONTROL_ACT);
165215651Sweongyo	PRINTSTATUS(CONTROL_STALL);
166215651Sweongyo	PRINTSTATUS(SHORT_FRAMES_OK);
167215651Sweongyo	PRINTSTATUS(SHORT_XFER_OK);
168215651Sweongyo#if USB_HAVE_BUSDMA
169215651Sweongyo	PRINTSTATUS(BDMA_ENABLE);
170215651Sweongyo	PRINTSTATUS(BDMA_NO_POST_SYNC);
171215651Sweongyo	PRINTSTATUS(BDMA_SETUP);
172215651Sweongyo#endif
173215651Sweongyo	PRINTSTATUS(ISOCHRONOUS_XFR);
174215651Sweongyo	PRINTSTATUS(CURR_DMA_SET);
175215651Sweongyo	PRINTSTATUS(CAN_CANCEL_IMMED);
176215651Sweongyo	PRINTSTATUS(DOING_CALLBACK);
177215651Sweongyo	printf(">\n");
178215651Sweongyo#undef PRINTSTATUS
179215651Sweongyo}
180215651Sweongyo
181215651Sweongyo/*
182215651Sweongyo * Display a region in traditional hexdump format.
183215651Sweongyo */
184215651Sweongyostatic void
185215651Sweongyohexdump(const char *region, size_t len)
186215651Sweongyo{
187215651Sweongyo	const char *line;
188218010Shselasky	int x;
189218010Shselasky	int c;
190218010Shselasky#define EMIT(fmt, ...)	do {		\
191218010Shselasky	printf(fmt,## __VA_ARGS__);	\
192215651Sweongyo} while (0)
193215651Sweongyo
194215651Sweongyo	for (line = region; line < (region + len); line += 16) {
195215651Sweongyo		EMIT(" %04lx  ", (long) (line - region));
196215651Sweongyo		for (x = 0; x < 16; x++) {
197215651Sweongyo			if ((line + x) < (region + len))
198215651Sweongyo				EMIT("%02x ", *(const u_int8_t *)(line + x));
199215651Sweongyo			else
200215651Sweongyo				EMIT("-- ");
201215651Sweongyo			if (x == 7)
202215651Sweongyo				EMIT(" ");
203215651Sweongyo		}
204215651Sweongyo		EMIT(" |");
205215651Sweongyo		for (x = 0; x < 16; x++) {
206215651Sweongyo			if ((line + x) < (region + len)) {
207215651Sweongyo				c = *(const u_int8_t *)(line + x);
208215651Sweongyo				/* !isprint(c) */
209215651Sweongyo				if ((c < ' ') || (c > '~'))
210215651Sweongyo					c = '.';
211215651Sweongyo				EMIT("%c", c);
212215651Sweongyo			} else
213215651Sweongyo				EMIT(" ");
214215651Sweongyo		}
215215651Sweongyo		EMIT("|\n");
216215651Sweongyo	}
217215651Sweongyo#undef EMIT
218215651Sweongyo}
219215651Sweongyo
220215651Sweongyostatic void
221215803Sweongyoprint_apacket(const struct bpf_xhdr *hdr, struct usbpf_pkthdr *up,
222215651Sweongyo    const char *payload)
223215651Sweongyo{
224215651Sweongyo	struct tm *tm;
225215651Sweongyo	struct timeval tv;
226215651Sweongyo	size_t len;
227215651Sweongyo	u_int32_t framelen, x;
228215651Sweongyo	const char *ptr = payload;
229215651Sweongyo	char buf[64];
230215651Sweongyo
231215651Sweongyo	/* A packet from the kernel is based on little endian byte order. */
232215651Sweongyo	up->up_busunit = le32toh(up->up_busunit);
233215651Sweongyo	up->up_flags = le32toh(up->up_flags);
234215651Sweongyo	up->up_status = le32toh(up->up_status);
235215651Sweongyo	up->up_length = le32toh(up->up_length);
236215651Sweongyo	up->up_frames = le32toh(up->up_frames);
237215651Sweongyo	up->up_error = le32toh(up->up_error);
238215651Sweongyo	up->up_interval = le32toh(up->up_interval);
239215651Sweongyo
240215803Sweongyo	tv.tv_sec = hdr->bh_tstamp.bt_sec;
241215803Sweongyo	tv.tv_usec = hdr->bh_tstamp.bt_frac;
242215651Sweongyo	tm = localtime(&tv.tv_sec);
243215651Sweongyo
244215651Sweongyo	len = strftime(buf, sizeof(buf), "%H:%M:%S", tm);
245215651Sweongyo	printf("%.*s.%06ju", (int)len, buf, tv.tv_usec);
246215651Sweongyo	printf(" usbus%d.%d 0x%02x %s %s", up->up_busunit, up->up_address,
247215651Sweongyo	    up->up_endpoint,
248215651Sweongyo	    xfertype_table[up->up_xfertype],
249216138Sweongyo	    up->up_type == USBPF_XFERTAP_SUBMIT ? "S" : "D");
250215651Sweongyo	printf(" (%d/%d)", up->up_frames, up->up_length);
251215651Sweongyo	if (up->up_type == USBPF_XFERTAP_DONE)
252215651Sweongyo		printf(" %s", errstr_table[up->up_error]);
253215651Sweongyo	if (up->up_xfertype == UE_BULK || up->up_xfertype == UE_ISOCHRONOUS)
254215651Sweongyo		printf(" %d", up->up_interval);
255215651Sweongyo	printf("\n");
256215651Sweongyo
257215651Sweongyo	if (verbose >= 1) {
258215651Sweongyo		for (x = 0; x < up->up_frames; x++) {
259215651Sweongyo			framelen = le32toh(*((const u_int32_t *)ptr));
260215651Sweongyo			ptr += sizeof(u_int32_t);
261215651Sweongyo			printf(" frame[%u] len %d\n", x, framelen);
262215651Sweongyo			assert(framelen < (1024 * 4));
263215651Sweongyo			hexdump(ptr, framelen);
264215651Sweongyo			ptr += framelen;
265215651Sweongyo		}
266215651Sweongyo	}
267215651Sweongyo	if (verbose >= 2) {
268215651Sweongyo		print_flags(up->up_flags);
269215651Sweongyo		print_status(up->up_status);
270215651Sweongyo	}
271215651Sweongyo}
272215651Sweongyo
273215651Sweongyostatic void
274215651Sweongyoprint_packets(char *data, const int datalen)
275215651Sweongyo{
276215651Sweongyo	struct usbpf_pkthdr *up;
277215803Sweongyo	const struct bpf_xhdr *hdr;
278215651Sweongyo	u_int32_t framelen, x;
279215651Sweongyo	char *ptr, *next;
280215651Sweongyo
281215651Sweongyo	for (ptr = data; ptr < (data + datalen); ptr = next) {
282215803Sweongyo		hdr = (const struct bpf_xhdr *)ptr;
283215803Sweongyo		up = (struct usbpf_pkthdr *)(ptr + hdr->bh_hdrlen);
284215803Sweongyo		next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
285215651Sweongyo
286215651Sweongyo		ptr = ((char *)up) + sizeof(struct usbpf_pkthdr);
287215651Sweongyo		if (w_arg == NULL)
288215651Sweongyo			print_apacket(hdr, up, ptr);
289215651Sweongyo		pkt_captured++;
290215651Sweongyo		for (x = 0; x < up->up_frames; x++) {
291215651Sweongyo			framelen = le32toh(*((const u_int32_t *)ptr));
292215651Sweongyo			ptr += sizeof(u_int32_t) + framelen;
293215651Sweongyo		}
294215651Sweongyo	}
295215651Sweongyo}
296215651Sweongyo
297215651Sweongyostatic void
298215651Sweongyowrite_packets(struct usbcap *p, const char *data, const int datalen)
299215651Sweongyo{
300215651Sweongyo	int len = htole32(datalen), ret;
301215651Sweongyo
302215651Sweongyo	ret = write(p->wfd, &len, sizeof(int));
303215651Sweongyo	assert(ret == sizeof(int));
304215651Sweongyo	ret = write(p->wfd, data, datalen);
305215651Sweongyo	assert(ret == datalen);
306215651Sweongyo}
307215651Sweongyo
308215651Sweongyostatic void
309215651Sweongyoread_file(struct usbcap *p)
310215651Sweongyo{
311215651Sweongyo	int datalen, ret;
312215651Sweongyo	char *data;
313215651Sweongyo
314215651Sweongyo	while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) {
315215651Sweongyo		datalen = le32toh(datalen);
316215651Sweongyo		data = malloc(datalen);
317215651Sweongyo		assert(data != NULL);
318215651Sweongyo		ret = read(p->rfd, data, datalen);
319215651Sweongyo		assert(ret == datalen);
320215651Sweongyo		print_packets(data, datalen);
321215651Sweongyo		free(data);
322215651Sweongyo	}
323215651Sweongyo	if (ret == -1)
324215651Sweongyo		fprintf(stderr, "read: %s\n", strerror(errno));
325215651Sweongyo}
326215651Sweongyo
327215651Sweongyostatic void
328215651Sweongyodo_loop(struct usbcap *p)
329215651Sweongyo{
330215651Sweongyo	int cc;
331215651Sweongyo
332215651Sweongyo	while (doexit == 0) {
333215651Sweongyo		cc = read(p->fd, (char *)p->buffer, p->bufsize);
334215651Sweongyo		if (cc < 0) {
335215651Sweongyo			switch (errno) {
336215651Sweongyo			case EINTR:
337215651Sweongyo				break;
338215651Sweongyo			default:
339215651Sweongyo				fprintf(stderr, "read: %s\n", strerror(errno));
340215651Sweongyo				return;
341215651Sweongyo			}
342215651Sweongyo			continue;
343215651Sweongyo		}
344215651Sweongyo		if (cc == 0)
345215651Sweongyo			continue;
346215651Sweongyo		if (w_arg != NULL)
347215651Sweongyo			write_packets(p, p->buffer, cc);
348215651Sweongyo		print_packets(p->buffer, cc);
349215651Sweongyo	}
350215651Sweongyo}
351215651Sweongyo
352215651Sweongyostatic void
353215651Sweongyoinit_rfile(struct usbcap *p)
354215651Sweongyo{
355215651Sweongyo	struct usbcap_filehdr uf;
356215651Sweongyo	int ret;
357215651Sweongyo
358215651Sweongyo	p->rfd = open(r_arg, O_RDONLY);
359215651Sweongyo	if (p->rfd < 0) {
360215651Sweongyo		fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno));
361215651Sweongyo		exit(EXIT_FAILURE);
362215651Sweongyo	}
363215651Sweongyo	ret = read(p->rfd, &uf, sizeof(uf));
364215651Sweongyo	assert(ret == sizeof(uf));
365215651Sweongyo	assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC);
366215651Sweongyo	assert(uf.major == 0);
367215651Sweongyo	assert(uf.minor == 1);
368215651Sweongyo}
369215651Sweongyo
370215651Sweongyostatic void
371215651Sweongyoinit_wfile(struct usbcap *p)
372215651Sweongyo{
373215651Sweongyo	struct usbcap_filehdr uf;
374215651Sweongyo	int ret;
375215651Sweongyo
376215651Sweongyo	p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
377215651Sweongyo	if (p->wfd < 0) {
378215651Sweongyo		fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno));
379215651Sweongyo		exit(EXIT_FAILURE);
380215651Sweongyo	}
381215651Sweongyo	bzero(&uf, sizeof(uf));
382215651Sweongyo	uf.magic = htole32(USBCAP_FILEHDR_MAGIC);
383215651Sweongyo	uf.major = 0;
384215651Sweongyo	uf.minor = 1;
385215651Sweongyo	ret = write(p->wfd, (const void *)&uf, sizeof(uf));
386215651Sweongyo	assert(ret == sizeof(uf));
387215651Sweongyo}
388215651Sweongyo
389215651Sweongyostatic void
390215651Sweongyousage(void)
391215651Sweongyo{
392215651Sweongyo
393215651Sweongyo#define FMT "    %-14s %s\n"
394215651Sweongyo	fprintf(stderr, "usage: usbdump [options]\n");
395215651Sweongyo	fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface");
396215651Sweongyo	fprintf(stderr, FMT, "-r file", "Read the raw packets from file");
397215651Sweongyo	fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet");
398215651Sweongyo	fprintf(stderr, FMT, "-v", "Increases the verbose level");
399215651Sweongyo	fprintf(stderr, FMT, "-w file", "Write the raw packets to file");
400215651Sweongyo#undef FMT
401215651Sweongyo	exit(1);
402215651Sweongyo}
403215651Sweongyo
404215651Sweongyoint
405215651Sweongyomain(int argc, char *argv[])
406215651Sweongyo{
407215651Sweongyo	struct timeval tv;
408215803Sweongyo	struct bpf_insn total_insn;
409215803Sweongyo	struct bpf_program total_prog;
410215803Sweongyo	struct bpf_stat us;
411215803Sweongyo	struct bpf_version bv;
412215651Sweongyo	struct usbcap uc, *p = &uc;
413215803Sweongyo	struct ifreq ifr;
414215651Sweongyo	long snapshot = 192;
415215651Sweongyo	u_int v;
416215651Sweongyo	int fd, o;
417215651Sweongyo	const char *optstring;
418215651Sweongyo
419215651Sweongyo	bzero(&uc, sizeof(struct usbcap));
420215651Sweongyo
421215651Sweongyo	optstring = "i:r:s:vw:";
422215651Sweongyo	while ((o = getopt(argc, argv, optstring)) != -1) {
423215651Sweongyo		switch (o) {
424215651Sweongyo		case 'i':
425215651Sweongyo			i_arg = optarg;
426215651Sweongyo			break;
427215651Sweongyo		case 'r':
428215651Sweongyo			r_arg = optarg;
429215651Sweongyo			init_rfile(p);
430215651Sweongyo			break;
431215651Sweongyo		case 's':
432215651Sweongyo			snapshot = strtol(optarg, NULL, 10);
433215651Sweongyo			errno = 0;
434215651Sweongyo			if (snapshot == 0 && errno == EINVAL)
435215651Sweongyo				usage();
436215651Sweongyo			/* snapeshot == 0 is special */
437215651Sweongyo			if (snapshot == 0)
438215651Sweongyo				snapshot = -1;
439215651Sweongyo			break;
440215651Sweongyo		case 'v':
441215651Sweongyo			verbose++;
442215651Sweongyo			break;
443215651Sweongyo		case 'w':
444215651Sweongyo			w_arg = optarg;
445215651Sweongyo			init_wfile(p);
446215651Sweongyo			break;
447215651Sweongyo		default:
448215651Sweongyo			usage();
449215651Sweongyo			/* NOTREACHED */
450215651Sweongyo		}
451215651Sweongyo	}
452215651Sweongyo
453215651Sweongyo	if (r_arg != NULL) {
454215651Sweongyo		read_file(p);
455215651Sweongyo		exit(EXIT_SUCCESS);
456215651Sweongyo	}
457215651Sweongyo
458215803Sweongyo	p->fd = fd = open("/dev/bpf", O_RDONLY);
459215651Sweongyo	if (p->fd < 0) {
460215651Sweongyo		fprintf(stderr, "(no devices found)\n");
461215651Sweongyo		return (EXIT_FAILURE);
462215651Sweongyo	}
463215651Sweongyo
464215803Sweongyo	if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
465215803Sweongyo		fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno));
466215651Sweongyo		return (EXIT_FAILURE);
467215651Sweongyo	}
468215803Sweongyo	if (bv.bv_major != BPF_MAJOR_VERSION ||
469215803Sweongyo	    bv.bv_minor < BPF_MINOR_VERSION) {
470215651Sweongyo		fprintf(stderr, "kernel bpf filter out of date");
471215651Sweongyo		return (EXIT_FAILURE);
472215651Sweongyo	}
473215651Sweongyo
474215803Sweongyo	if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 4096)
475215803Sweongyo		v = 4096;
476215651Sweongyo	for ( ; v != 0; v >>= 1) {
477215803Sweongyo		(void)ioctl(fd, BIOCSBLEN, (caddr_t)&v);
478215803Sweongyo		(void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name));
479215803Sweongyo		if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
480215651Sweongyo			break;
481215651Sweongyo	}
482215651Sweongyo	if (v == 0) {
483215803Sweongyo		fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg);
484215651Sweongyo		return (EXIT_FAILURE);
485215651Sweongyo	}
486215651Sweongyo
487215803Sweongyo	if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
488215803Sweongyo		fprintf(stderr, "BIOCGBLEN: %s", strerror(errno));
489215651Sweongyo		return (EXIT_FAILURE);
490215651Sweongyo	}
491215651Sweongyo
492215651Sweongyo	p->bufsize = v;
493215651Sweongyo	p->buffer = (u_char *)malloc(p->bufsize);
494215651Sweongyo	if (p->buffer == NULL) {
495215651Sweongyo		fprintf(stderr, "malloc: %s", strerror(errno));
496215651Sweongyo		return (EXIT_FAILURE);
497215651Sweongyo	}
498215651Sweongyo
499215651Sweongyo	/* XXX no read filter rules yet so at this moment accept everything */
500215803Sweongyo	total_insn.code = (u_short)(BPF_RET | BPF_K);
501215651Sweongyo	total_insn.jt = 0;
502215651Sweongyo	total_insn.jf = 0;
503215651Sweongyo	total_insn.k = snapshot;
504215651Sweongyo
505215803Sweongyo	total_prog.bf_len = 1;
506215803Sweongyo	total_prog.bf_insns = &total_insn;
507215803Sweongyo	if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
508215803Sweongyo		fprintf(stderr, "BIOCSETF: %s", strerror(errno));
509215651Sweongyo		return (EXIT_FAILURE);
510215651Sweongyo	}
511215651Sweongyo
512215651Sweongyo	/* 1 second read timeout */
513215651Sweongyo	tv.tv_sec = 1;
514215651Sweongyo	tv.tv_usec = 0;
515215803Sweongyo	if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) {
516215803Sweongyo		fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno));
517215651Sweongyo		return (EXIT_FAILURE);
518215651Sweongyo	}
519215651Sweongyo
520215651Sweongyo	(void)signal(SIGINT, handle_sigint);
521215651Sweongyo
522215651Sweongyo	do_loop(p);
523215651Sweongyo
524215803Sweongyo	if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) {
525215803Sweongyo		fprintf(stderr, "BIOCGSTATS: %s", strerror(errno));
526215651Sweongyo		return (EXIT_FAILURE);
527215651Sweongyo	}
528215651Sweongyo
529215651Sweongyo	/* XXX what's difference between pkt_captured and us.us_recv? */
530215651Sweongyo	printf("\n");
531215651Sweongyo	printf("%d packets captured\n", pkt_captured);
532215803Sweongyo	printf("%d packets received by filter\n", us.bs_recv);
533215803Sweongyo	printf("%d packets dropped by kernel\n", us.bs_drop);
534215651Sweongyo
535215651Sweongyo	if (p->fd > 0)
536215651Sweongyo		close(p->fd);
537215651Sweongyo	if (p->rfd > 0)
538215651Sweongyo		close(p->rfd);
539215651Sweongyo	if (p->wfd > 0)
540215651Sweongyo		close(p->wfd);
541215651Sweongyo
542215651Sweongyo	return (EXIT_SUCCESS);
543215651Sweongyo}
544