usbdump.c revision 220314
1/*- 2 * Copyright (c) 2010 Weongyo Jeong <weongyo@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 * 29 * $FreeBSD: head/usr.sbin/usbdump/usbdump.c 220314 2011-04-04 02:57:19Z thompsa $ 30 */ 31 32#include <sys/param.h> 33#include <sys/endian.h> 34#include <sys/ioctl.h> 35#include <sys/socket.h> 36#include <sys/stat.h> 37#include <sys/utsname.h> 38#include <net/if.h> 39#include <net/bpf.h> 40#include <dev/usb/usb.h> 41#include <dev/usb/usb_pf.h> 42#include <dev/usb/usbdi.h> 43#include <assert.h> 44#include <errno.h> 45#include <fcntl.h> 46#include <limits.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <time.h> 51#include <unistd.h> 52 53struct usbcap { 54 int fd; /* fd for /dev/usbpf */ 55 uint32_t bufsize; 56 uint8_t *buffer; 57 58 /* for -w option */ 59 int wfd; 60 /* for -r option */ 61 int rfd; 62}; 63 64struct usbcap_filehdr { 65 uint32_t magic; 66#define USBCAP_FILEHDR_MAGIC 0x9a90000e 67 uint8_t major; 68 uint8_t minor; 69 uint8_t reserved[26]; 70} __packed; 71 72static int doexit = 0; 73static int pkt_captured = 0; 74static int verbose = 0; 75static const char *i_arg = "usbus0"; 76static const char *r_arg = NULL; 77static const char *w_arg = NULL; 78static const char *errstr_table[USB_ERR_MAX] = { 79 [USB_ERR_NORMAL_COMPLETION] = "0", 80 [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", 81 [USB_ERR_NOT_STARTED] = "NOT_STARTED", 82 [USB_ERR_INVAL] = "INVAL", 83 [USB_ERR_NOMEM] = "NOMEM", 84 [USB_ERR_CANCELLED] = "CANCELLED", 85 [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", 86 [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", 87 [USB_ERR_BAD_FLAG] = "BAD_FLAG", 88 [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", 89 [USB_ERR_IN_USE] = "IN_USE", 90 [USB_ERR_NO_ADDR] = "NO_ADDR", 91 [USB_ERR_NO_PIPE] = "NO_PIPE", 92 [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", 93 [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", 94 [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", 95 [USB_ERR_NO_POWER] = "NO_POWER", 96 [USB_ERR_TOO_DEEP] = "TOO_DEEP", 97 [USB_ERR_IOERROR] = "IOERROR", 98 [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", 99 [USB_ERR_TIMEOUT] = "TIMEOUT", 100 [USB_ERR_SHORT_XFER] = "SHORT_XFER", 101 [USB_ERR_STALLED] = "STALLED", 102 [USB_ERR_INTERRUPTED] = "INTERRUPTED", 103 [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", 104 [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", 105 [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", 106 [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", 107 [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", 108}; 109 110static const char *xfertype_table[4] = { 111 [UE_CONTROL] = "CTRL", 112 [UE_ISOCHRONOUS] = "ISOC", 113 [UE_BULK] = "BULK", 114 [UE_INTERRUPT] = "INTR" 115}; 116 117static const char *speed_table[USB_SPEED_MAX] = { 118 [USB_SPEED_FULL] = "FULL", 119 [USB_SPEED_HIGH] = "HIGH", 120 [USB_SPEED_LOW] = "LOW", 121 [USB_SPEED_VARIABLE] = "VARI", 122 [USB_SPEED_SUPER] = "SUPER", 123}; 124 125static void 126handle_sigint(int sig) 127{ 128 129 (void)sig; 130 doexit = 1; 131} 132 133#define FLAGS(x, name) \ 134 (((x) & USBPF_FLAG_##name) ? #name "|" : "") 135 136#define STATUS(x, name) \ 137 (((x) & USBPF_STATUS_##name) ? #name "|" : "") 138 139static const char * 140usb_errstr(uint32_t error) 141{ 142 if (error >= USB_ERR_MAX || errstr_table[error] == NULL) 143 return ("UNKNOWN"); 144 else 145 return (errstr_table[error]); 146} 147 148static const char * 149usb_speedstr(uint8_t speed) 150{ 151 if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) 152 return ("UNKNOWN"); 153 else 154 return (speed_table[speed]); 155} 156 157static void 158print_flags(uint32_t flags) 159{ 160 printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", 161 flags, 162 FLAGS(flags, FORCE_SHORT_XFER), 163 FLAGS(flags, SHORT_XFER_OK), 164 FLAGS(flags, SHORT_FRAMES_OK), 165 FLAGS(flags, PIPE_BOF), 166 FLAGS(flags, PROXY_BUFFER), 167 FLAGS(flags, EXT_BUFFER), 168 FLAGS(flags, MANUAL_STATUS), 169 FLAGS(flags, NO_PIPE_OK), 170 FLAGS(flags, STALL_PIPE)); 171} 172 173static void 174print_status(uint32_t status) 175{ 176 printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", 177 status, 178 STATUS(status, OPEN), 179 STATUS(status, TRANSFERRING), 180 STATUS(status, DID_DMA_DELAY), 181 STATUS(status, DID_CLOSE), 182 STATUS(status, DRAINING), 183 STATUS(status, STARTED), 184 STATUS(status, BW_RECLAIMED), 185 STATUS(status, CONTROL_XFR), 186 STATUS(status, CONTROL_HDR), 187 STATUS(status, CONTROL_ACT), 188 STATUS(status, CONTROL_STALL), 189 STATUS(status, SHORT_FRAMES_OK), 190 STATUS(status, SHORT_XFER_OK), 191 STATUS(status, BDMA_ENABLE), 192 STATUS(status, BDMA_NO_POST_SYNC), 193 STATUS(status, BDMA_SETUP), 194 STATUS(status, ISOCHRONOUS_XFR), 195 STATUS(status, CURR_DMA_SET), 196 STATUS(status, CAN_CANCEL_IMMED), 197 STATUS(status, DOING_CALLBACK)); 198} 199 200/* 201 * Dump a byte into hex format. 202 */ 203static void 204hexbyte(char *buf, uint8_t temp) 205{ 206 uint8_t lo; 207 uint8_t hi; 208 209 lo = temp & 0xF; 210 hi = temp >> 4; 211 212 if (hi < 10) 213 buf[0] = '0' + hi; 214 else 215 buf[0] = 'A' + hi - 10; 216 217 if (lo < 10) 218 buf[1] = '0' + lo; 219 else 220 buf[1] = 'A' + lo - 10; 221} 222 223/* 224 * Display a region in traditional hexdump format. 225 */ 226static void 227hexdump(const uint8_t *region, uint32_t len) 228{ 229 const uint8_t *line; 230 char linebuf[128]; 231 int i; 232 int x; 233 int c; 234 235 for (line = region; line < (region + len); line += 16) { 236 237 i = 0; 238 239 linebuf[i] = ' '; 240 hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); 241 hexbyte(linebuf + i + 3, (line - region) & 0xFF); 242 linebuf[i + 5] = ' '; 243 linebuf[i + 6] = ' '; 244 i += 7; 245 246 for (x = 0; x < 16; x++) { 247 if ((line + x) < (region + len)) { 248 hexbyte(linebuf + i, 249 *(const u_int8_t *)(line + x)); 250 } else { 251 linebuf[i] = '-'; 252 linebuf[i + 1] = '-'; 253 } 254 linebuf[i + 2] = ' '; 255 if (x == 7) { 256 linebuf[i + 3] = ' '; 257 i += 4; 258 } else { 259 i += 3; 260 } 261 } 262 linebuf[i] = ' '; 263 linebuf[i + 1] = '|'; 264 i += 2; 265 for (x = 0; x < 16; x++) { 266 if ((line + x) < (region + len)) { 267 c = *(const u_int8_t *)(line + x); 268 /* !isprint(c) */ 269 if ((c < ' ') || (c > '~')) 270 c = '.'; 271 linebuf[i] = c; 272 } else { 273 linebuf[i] = ' '; 274 } 275 i++; 276 } 277 linebuf[i] = '|'; 278 linebuf[i + 1] = 0; 279 i += 2; 280 puts(linebuf); 281 } 282} 283 284static void 285print_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len) 286{ 287 struct tm *tm; 288 struct usbpf_pkthdr up_temp; 289 struct usbpf_pkthdr *up; 290 struct timeval tv; 291 size_t len; 292 uint32_t x; 293 char buf[64]; 294 295 ptr += USBPF_HDR_LEN; 296 ptr_len -= USBPF_HDR_LEN; 297 if (ptr_len < 0) 298 return; 299 300 /* make sure we don't change the source buffer */ 301 memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); 302 up = &up_temp; 303 304 /* 305 * A packet from the kernel is based on little endian byte 306 * order. 307 */ 308 up->up_totlen = le32toh(up->up_totlen); 309 up->up_busunit = le32toh(up->up_busunit); 310 up->up_address = le32toh(up->up_address); 311 up->up_flags = le32toh(up->up_flags); 312 up->up_status = le32toh(up->up_status); 313 up->up_error = le32toh(up->up_error); 314 up->up_interval = le32toh(up->up_interval); 315 up->up_frames = le32toh(up->up_frames); 316 up->up_packet_size = le32toh(up->up_packet_size); 317 up->up_packet_count = le32toh(up->up_packet_count); 318 up->up_endpoint = le32toh(up->up_endpoint); 319 320 tv.tv_sec = hdr->bh_tstamp.bt_sec; 321 tv.tv_usec = hdr->bh_tstamp.bt_frac; 322 tm = localtime(&tv.tv_sec); 323 324 len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 325 326 printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", 327 (int)len, buf, tv.tv_usec, 328 (int)up->up_busunit, (int)up->up_address, 329 (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", 330 xfertype_table[up->up_xfertype], 331 (unsigned int)up->up_endpoint, 332 usb_speedstr(up->up_speed), 333 (int)up->up_frames, 334 (int)(up->up_totlen - USBPF_HDR_LEN - 335 (USBPF_FRAME_HDR_LEN * up->up_frames)), 336 (int)up->up_interval, 337 (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", 338 (up->up_type == USBPF_XFERTAP_DONE) ? 339 usb_errstr(up->up_error) : ""); 340 341 if (verbose >= 1) { 342 for (x = 0; x != up->up_frames; x++) { 343 const struct usbpf_framehdr *uf; 344 uint32_t framelen; 345 uint32_t flags; 346 347 uf = (const struct usbpf_framehdr *)ptr; 348 ptr += USBPF_FRAME_HDR_LEN; 349 ptr_len -= USBPF_FRAME_HDR_LEN; 350 if (ptr_len < 0) 351 return; 352 353 framelen = le32toh(uf->length); 354 flags = le32toh(uf->flags); 355 356 printf(" frame[%u] %s %d bytes\n", 357 (unsigned int)x, 358 (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", 359 (int)framelen); 360 361 if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { 362 363 int tot_frame_len; 364 365 tot_frame_len = USBPF_FRAME_ALIGN(framelen); 366 367 ptr_len -= tot_frame_len; 368 369 if (tot_frame_len < 0 || 370 (int)framelen < 0 || (int)ptr_len < 0) 371 break; 372 373 hexdump(ptr, framelen); 374 375 ptr += tot_frame_len; 376 } 377 } 378 } 379 if (verbose >= 2) 380 print_flags(up->up_flags); 381 if (verbose >= 3) 382 print_status(up->up_status); 383} 384 385static void 386print_packets(uint8_t *data, const int datalen) 387{ 388 const struct bpf_xhdr *hdr; 389 uint8_t *ptr; 390 uint8_t *next; 391 392 for (ptr = data; ptr < (data + datalen); ptr = next) { 393 hdr = (const struct bpf_xhdr *)ptr; 394 next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 395 396 if (w_arg == NULL) { 397 print_apacket(hdr, ptr + 398 hdr->bh_hdrlen, hdr->bh_caplen); 399 } 400 pkt_captured++; 401 } 402} 403 404static void 405write_packets(struct usbcap *p, const uint8_t *data, const int datalen) 406{ 407 int len = htole32(datalen); 408 int ret; 409 410 ret = write(p->wfd, &len, sizeof(int)); 411 assert(ret == sizeof(int)); 412 ret = write(p->wfd, data, datalen); 413 assert(ret == datalen); 414} 415 416static void 417read_file(struct usbcap *p) 418{ 419 int datalen; 420 int ret; 421 uint8_t *data; 422 423 while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 424 datalen = le32toh(datalen); 425 data = malloc(datalen); 426 assert(data != NULL); 427 ret = read(p->rfd, data, datalen); 428 assert(ret == datalen); 429 print_packets(data, datalen); 430 free(data); 431 } 432 if (ret == -1) 433 fprintf(stderr, "read: %s\n", strerror(errno)); 434} 435 436static void 437do_loop(struct usbcap *p) 438{ 439 int cc; 440 441 while (doexit == 0) { 442 cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); 443 if (cc < 0) { 444 switch (errno) { 445 case EINTR: 446 break; 447 default: 448 fprintf(stderr, "read: %s\n", strerror(errno)); 449 return; 450 } 451 continue; 452 } 453 if (cc == 0) 454 continue; 455 if (w_arg != NULL) 456 write_packets(p, p->buffer, cc); 457 print_packets(p->buffer, cc); 458 } 459} 460 461static void 462init_rfile(struct usbcap *p) 463{ 464 struct usbcap_filehdr uf; 465 int ret; 466 467 p->rfd = open(r_arg, O_RDONLY); 468 if (p->rfd < 0) { 469 fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno)); 470 exit(EXIT_FAILURE); 471 } 472 ret = read(p->rfd, &uf, sizeof(uf)); 473 assert(ret == sizeof(uf)); 474 assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC); 475 assert(uf.major == 0); 476 assert(uf.minor == 2); 477} 478 479static void 480init_wfile(struct usbcap *p) 481{ 482 struct usbcap_filehdr uf; 483 int ret; 484 485 p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 486 if (p->wfd < 0) { 487 fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno)); 488 exit(EXIT_FAILURE); 489 } 490 bzero(&uf, sizeof(uf)); 491 uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 492 uf.major = 0; 493 uf.minor = 2; 494 ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 495 assert(ret == sizeof(uf)); 496} 497 498static void 499usage(void) 500{ 501 502#define FMT " %-14s %s\n" 503 fprintf(stderr, "usage: usbdump [options]\n"); 504 fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface"); 505 fprintf(stderr, FMT, "-r file", "Read the raw packets from file"); 506 fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet"); 507 fprintf(stderr, FMT, "-v", "Increases the verbose level"); 508 fprintf(stderr, FMT, "-w file", "Write the raw packets to file"); 509#undef FMT 510 exit(1); 511} 512 513int 514main(int argc, char *argv[]) 515{ 516 struct timeval tv; 517 struct bpf_insn total_insn; 518 struct bpf_program total_prog; 519 struct bpf_stat us; 520 struct bpf_version bv; 521 struct usbcap uc, *p = &uc; 522 struct ifreq ifr; 523 long snapshot = 192; 524 uint32_t v; 525 int fd, o; 526 const char *optstring; 527 528 bzero(&uc, sizeof(struct usbcap)); 529 530 optstring = "i:r:s:vw:"; 531 while ((o = getopt(argc, argv, optstring)) != -1) { 532 switch (o) { 533 case 'i': 534 i_arg = optarg; 535 break; 536 case 'r': 537 r_arg = optarg; 538 init_rfile(p); 539 break; 540 case 's': 541 snapshot = strtol(optarg, NULL, 10); 542 errno = 0; 543 if (snapshot == 0 && errno == EINVAL) 544 usage(); 545 /* snapeshot == 0 is special */ 546 if (snapshot == 0) 547 snapshot = -1; 548 break; 549 case 'v': 550 verbose++; 551 break; 552 case 'w': 553 w_arg = optarg; 554 init_wfile(p); 555 break; 556 default: 557 usage(); 558 /* NOTREACHED */ 559 } 560 } 561 562 if (r_arg != NULL) { 563 read_file(p); 564 exit(EXIT_SUCCESS); 565 } 566 567 p->fd = fd = open("/dev/bpf", O_RDONLY); 568 if (p->fd < 0) { 569 fprintf(stderr, "(no devices found)\n"); 570 return (EXIT_FAILURE); 571 } 572 573 if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { 574 fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno)); 575 return (EXIT_FAILURE); 576 } 577 if (bv.bv_major != BPF_MAJOR_VERSION || 578 bv.bv_minor < BPF_MINOR_VERSION) { 579 fprintf(stderr, "kernel bpf filter out of date"); 580 return (EXIT_FAILURE); 581 } 582 583 /* USB transfers can be greater than 64KByte */ 584 v = 1U << 16; 585 586 /* clear ifr structure */ 587 memset(&ifr, 0, sizeof(ifr)); 588 589 for ( ; v >= USBPF_HDR_LEN; v >>= 1) { 590 (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 591 (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 592 if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 593 break; 594 } 595 if (v == 0) { 596 fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg); 597 return (EXIT_FAILURE); 598 } 599 600 if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { 601 fprintf(stderr, "BIOCGBLEN: %s", strerror(errno)); 602 return (EXIT_FAILURE); 603 } 604 605 p->bufsize = v; 606 p->buffer = (uint8_t *)malloc(p->bufsize); 607 if (p->buffer == NULL) { 608 fprintf(stderr, "malloc: %s", strerror(errno)); 609 return (EXIT_FAILURE); 610 } 611 612 /* XXX no read filter rules yet so at this moment accept everything */ 613 total_insn.code = (u_short)(BPF_RET | BPF_K); 614 total_insn.jt = 0; 615 total_insn.jf = 0; 616 total_insn.k = snapshot; 617 618 total_prog.bf_len = 1; 619 total_prog.bf_insns = &total_insn; 620 if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) { 621 fprintf(stderr, "BIOCSETF: %s", strerror(errno)); 622 return (EXIT_FAILURE); 623 } 624 625 /* 1 second read timeout */ 626 tv.tv_sec = 1; 627 tv.tv_usec = 0; 628 if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) { 629 fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno)); 630 return (EXIT_FAILURE); 631 } 632 633 (void)signal(SIGINT, handle_sigint); 634 635 do_loop(p); 636 637 if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) { 638 fprintf(stderr, "BIOCGSTATS: %s", strerror(errno)); 639 return (EXIT_FAILURE); 640 } 641 642 /* XXX what's difference between pkt_captured and us.us_recv? */ 643 printf("\n"); 644 printf("%d packets captured\n", pkt_captured); 645 printf("%d packets received by filter\n", us.bs_recv); 646 printf("%d packets dropped by kernel\n", us.bs_drop); 647 648 if (p->fd > 0) 649 close(p->fd); 650 if (p->rfd > 0) 651 close(p->rfd); 652 if (p->wfd > 0) 653 close(p->wfd); 654 655 return (EXIT_SUCCESS); 656} 657