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