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