usbdump.c revision 215803
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 215803 2010-11-24 19:15:26Z weongyo $ 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 u_int bufsize; 56 char *buffer; 57 58 /* for -w option */ 59 int wfd; 60 /* for -r option */ 61 int rfd; 62}; 63 64struct usbcap_filehdr { 65 u_int magic; 66#define USBCAP_FILEHDR_MAGIC 0x9a90000e 67 u_char major; 68 u_char minor; 69 u_char 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] = "NORMAL_COMPLETION", 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[] = { 111 [UE_CONTROL] = "CTRL", 112 [UE_ISOCHRONOUS] = "ISOC", 113 [UE_BULK] = "BULK", 114 [UE_INTERRUPT] = "INTR" 115}; 116 117static void 118handle_sigint(int sig) 119{ 120 121 (void)sig; 122 doexit = 1; 123} 124 125static void 126print_flags(u_int32_t flags) 127{ 128#define PRINTFLAGS(name) \ 129 if ((flags & USBPF_FLAG_##name) != 0) \ 130 printf("%s ", #name); 131 printf(" flags %#x", flags); 132 printf(" < "); 133 PRINTFLAGS(FORCE_SHORT_XFER); 134 PRINTFLAGS(SHORT_XFER_OK); 135 PRINTFLAGS(SHORT_FRAMES_OK); 136 PRINTFLAGS(PIPE_BOF); 137 PRINTFLAGS(PROXY_BUFFER); 138 PRINTFLAGS(EXT_BUFFER); 139 PRINTFLAGS(MANUAL_STATUS); 140 PRINTFLAGS(NO_PIPE_OK); 141 PRINTFLAGS(STALL_PIPE); 142 printf(">\n"); 143#undef PRINTFLAGS 144} 145 146static void 147print_status(u_int32_t status) 148{ 149#define PRINTSTATUS(name) \ 150 if ((status & USBPF_STATUS_##name) != 0) \ 151 printf("%s ", #name); 152 153 printf(" status %#x", status); 154 printf(" < "); 155 PRINTSTATUS(OPEN); 156 PRINTSTATUS(TRANSFERRING); 157 PRINTSTATUS(DID_DMA_DELAY); 158 PRINTSTATUS(DID_CLOSE); 159 PRINTSTATUS(DRAINING); 160 PRINTSTATUS(STARTED); 161 PRINTSTATUS(BW_RECLAIMED); 162 PRINTSTATUS(CONTROL_XFR); 163 PRINTSTATUS(CONTROL_HDR); 164 PRINTSTATUS(CONTROL_ACT); 165 PRINTSTATUS(CONTROL_STALL); 166 PRINTSTATUS(SHORT_FRAMES_OK); 167 PRINTSTATUS(SHORT_XFER_OK); 168#if USB_HAVE_BUSDMA 169 PRINTSTATUS(BDMA_ENABLE); 170 PRINTSTATUS(BDMA_NO_POST_SYNC); 171 PRINTSTATUS(BDMA_SETUP); 172#endif 173 PRINTSTATUS(ISOCHRONOUS_XFR); 174 PRINTSTATUS(CURR_DMA_SET); 175 PRINTSTATUS(CAN_CANCEL_IMMED); 176 PRINTSTATUS(DOING_CALLBACK); 177 printf(">\n"); 178#undef PRINTSTATUS 179} 180 181/* 182 * Display a region in traditional hexdump format. 183 */ 184static void 185hexdump(const char *region, size_t len) 186{ 187 const char *line; 188 int x, c; 189 char lbuf[80]; 190#define EMIT(fmt, args...) do { \ 191 sprintf(lbuf, fmt , ## args); \ 192 printf("%s", lbuf); \ 193} while (0) 194 195 for (line = region; line < (region + len); line += 16) { 196 EMIT(" %04lx ", (long) (line - region)); 197 for (x = 0; x < 16; x++) { 198 if ((line + x) < (region + len)) 199 EMIT("%02x ", *(const u_int8_t *)(line + x)); 200 else 201 EMIT("-- "); 202 if (x == 7) 203 EMIT(" "); 204 } 205 EMIT(" |"); 206 for (x = 0; x < 16; x++) { 207 if ((line + x) < (region + len)) { 208 c = *(const u_int8_t *)(line + x); 209 /* !isprint(c) */ 210 if ((c < ' ') || (c > '~')) 211 c = '.'; 212 EMIT("%c", c); 213 } else 214 EMIT(" "); 215 } 216 EMIT("|\n"); 217 } 218#undef EMIT 219} 220 221static void 222print_apacket(const struct bpf_xhdr *hdr, struct usbpf_pkthdr *up, 223 const char *payload) 224{ 225 struct tm *tm; 226 struct timeval tv; 227 size_t len; 228 u_int32_t framelen, x; 229 const char *ptr = payload; 230 char buf[64]; 231 232 /* A packet from the kernel is based on little endian byte order. */ 233 up->up_busunit = le32toh(up->up_busunit); 234 up->up_flags = le32toh(up->up_flags); 235 up->up_status = le32toh(up->up_status); 236 up->up_length = le32toh(up->up_length); 237 up->up_frames = le32toh(up->up_frames); 238 up->up_error = le32toh(up->up_error); 239 up->up_interval = le32toh(up->up_interval); 240 241 tv.tv_sec = hdr->bh_tstamp.bt_sec; 242 tv.tv_usec = hdr->bh_tstamp.bt_frac; 243 tm = localtime(&tv.tv_sec); 244 245 len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 246 printf("%.*s.%06ju", (int)len, buf, tv.tv_usec); 247 printf(" usbus%d.%d 0x%02x %s %s", up->up_busunit, up->up_address, 248 up->up_endpoint, 249 xfertype_table[up->up_xfertype], 250 up->up_type == USBPF_XFERTAP_SUBMIT ? ">" : "<"); 251 printf(" (%d/%d)", up->up_frames, up->up_length); 252 if (up->up_type == USBPF_XFERTAP_DONE) 253 printf(" %s", errstr_table[up->up_error]); 254 if (up->up_xfertype == UE_BULK || up->up_xfertype == UE_ISOCHRONOUS) 255 printf(" %d", up->up_interval); 256 printf("\n"); 257 258 if (verbose >= 1) { 259 for (x = 0; x < up->up_frames; x++) { 260 framelen = le32toh(*((const u_int32_t *)ptr)); 261 ptr += sizeof(u_int32_t); 262 printf(" frame[%u] len %d\n", x, framelen); 263 assert(framelen < (1024 * 4)); 264 hexdump(ptr, framelen); 265 ptr += framelen; 266 } 267 } 268 if (verbose >= 2) { 269 print_flags(up->up_flags); 270 print_status(up->up_status); 271 } 272} 273 274static void 275print_packets(char *data, const int datalen) 276{ 277 struct usbpf_pkthdr *up; 278 const struct bpf_xhdr *hdr; 279 u_int32_t framelen, x; 280 char *ptr, *next; 281 282 for (ptr = data; ptr < (data + datalen); ptr = next) { 283 hdr = (const struct bpf_xhdr *)ptr; 284 up = (struct usbpf_pkthdr *)(ptr + hdr->bh_hdrlen); 285 next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 286 287 ptr = ((char *)up) + sizeof(struct usbpf_pkthdr); 288 if (w_arg == NULL) 289 print_apacket(hdr, up, ptr); 290 pkt_captured++; 291 for (x = 0; x < up->up_frames; x++) { 292 framelen = le32toh(*((const u_int32_t *)ptr)); 293 ptr += sizeof(u_int32_t) + framelen; 294 } 295 } 296} 297 298static void 299write_packets(struct usbcap *p, const char *data, const int datalen) 300{ 301 int len = htole32(datalen), ret; 302 303 ret = write(p->wfd, &len, sizeof(int)); 304 assert(ret == sizeof(int)); 305 ret = write(p->wfd, data, datalen); 306 assert(ret == datalen); 307} 308 309static void 310read_file(struct usbcap *p) 311{ 312 int datalen, ret; 313 char *data; 314 315 while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 316 datalen = le32toh(datalen); 317 data = malloc(datalen); 318 assert(data != NULL); 319 ret = read(p->rfd, data, datalen); 320 assert(ret == datalen); 321 print_packets(data, datalen); 322 free(data); 323 } 324 if (ret == -1) 325 fprintf(stderr, "read: %s\n", strerror(errno)); 326} 327 328static void 329do_loop(struct usbcap *p) 330{ 331 int cc; 332 333 while (doexit == 0) { 334 cc = read(p->fd, (char *)p->buffer, p->bufsize); 335 if (cc < 0) { 336 switch (errno) { 337 case EINTR: 338 break; 339 default: 340 fprintf(stderr, "read: %s\n", strerror(errno)); 341 return; 342 } 343 continue; 344 } 345 if (cc == 0) 346 continue; 347 if (w_arg != NULL) 348 write_packets(p, p->buffer, cc); 349 print_packets(p->buffer, cc); 350 } 351} 352 353static void 354init_rfile(struct usbcap *p) 355{ 356 struct usbcap_filehdr uf; 357 int ret; 358 359 p->rfd = open(r_arg, O_RDONLY); 360 if (p->rfd < 0) { 361 fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno)); 362 exit(EXIT_FAILURE); 363 } 364 ret = read(p->rfd, &uf, sizeof(uf)); 365 assert(ret == sizeof(uf)); 366 assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC); 367 assert(uf.major == 0); 368 assert(uf.minor == 1); 369} 370 371static void 372init_wfile(struct usbcap *p) 373{ 374 struct usbcap_filehdr uf; 375 int ret; 376 377 p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 378 if (p->wfd < 0) { 379 fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno)); 380 exit(EXIT_FAILURE); 381 } 382 bzero(&uf, sizeof(uf)); 383 uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 384 uf.major = 0; 385 uf.minor = 1; 386 ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 387 assert(ret == sizeof(uf)); 388} 389 390static void 391usage(void) 392{ 393 394#define FMT " %-14s %s\n" 395 fprintf(stderr, "usage: usbdump [options]\n"); 396 fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface"); 397 fprintf(stderr, FMT, "-r file", "Read the raw packets from file"); 398 fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet"); 399 fprintf(stderr, FMT, "-v", "Increases the verbose level"); 400 fprintf(stderr, FMT, "-w file", "Write the raw packets to file"); 401#undef FMT 402 exit(1); 403} 404 405int 406main(int argc, char *argv[]) 407{ 408 struct timeval tv; 409 struct bpf_insn total_insn; 410 struct bpf_program total_prog; 411 struct bpf_stat us; 412 struct bpf_version bv; 413 struct usbcap uc, *p = &uc; 414 struct ifreq ifr; 415 long snapshot = 192; 416 u_int v; 417 int fd, o; 418 const char *optstring; 419 420 bzero(&uc, sizeof(struct usbcap)); 421 422 optstring = "i:r:s:vw:"; 423 while ((o = getopt(argc, argv, optstring)) != -1) { 424 switch (o) { 425 case 'i': 426 i_arg = optarg; 427 break; 428 case 'r': 429 r_arg = optarg; 430 init_rfile(p); 431 break; 432 case 's': 433 snapshot = strtol(optarg, NULL, 10); 434 errno = 0; 435 if (snapshot == 0 && errno == EINVAL) 436 usage(); 437 /* snapeshot == 0 is special */ 438 if (snapshot == 0) 439 snapshot = -1; 440 break; 441 case 'v': 442 verbose++; 443 break; 444 case 'w': 445 w_arg = optarg; 446 init_wfile(p); 447 break; 448 default: 449 usage(); 450 /* NOTREACHED */ 451 } 452 } 453 454 if (r_arg != NULL) { 455 read_file(p); 456 exit(EXIT_SUCCESS); 457 } 458 459 p->fd = fd = open("/dev/bpf", O_RDONLY); 460 if (p->fd < 0) { 461 fprintf(stderr, "(no devices found)\n"); 462 return (EXIT_FAILURE); 463 } 464 465 if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { 466 fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno)); 467 return (EXIT_FAILURE); 468 } 469 if (bv.bv_major != BPF_MAJOR_VERSION || 470 bv.bv_minor < BPF_MINOR_VERSION) { 471 fprintf(stderr, "kernel bpf filter out of date"); 472 return (EXIT_FAILURE); 473 } 474 475 if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 4096) 476 v = 4096; 477 for ( ; v != 0; v >>= 1) { 478 (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 479 (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 480 if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 481 break; 482 } 483 if (v == 0) { 484 fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg); 485 return (EXIT_FAILURE); 486 } 487 488 if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { 489 fprintf(stderr, "BIOCGBLEN: %s", strerror(errno)); 490 return (EXIT_FAILURE); 491 } 492 493 p->bufsize = v; 494 p->buffer = (u_char *)malloc(p->bufsize); 495 if (p->buffer == NULL) { 496 fprintf(stderr, "malloc: %s", strerror(errno)); 497 return (EXIT_FAILURE); 498 } 499 500 /* XXX no read filter rules yet so at this moment accept everything */ 501 total_insn.code = (u_short)(BPF_RET | BPF_K); 502 total_insn.jt = 0; 503 total_insn.jf = 0; 504 total_insn.k = snapshot; 505 506 total_prog.bf_len = 1; 507 total_prog.bf_insns = &total_insn; 508 if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) { 509 fprintf(stderr, "BIOCSETF: %s", strerror(errno)); 510 return (EXIT_FAILURE); 511 } 512 513 /* 1 second read timeout */ 514 tv.tv_sec = 1; 515 tv.tv_usec = 0; 516 if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) { 517 fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno)); 518 return (EXIT_FAILURE); 519 } 520 521 (void)signal(SIGINT, handle_sigint); 522 523 do_loop(p); 524 525 if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) { 526 fprintf(stderr, "BIOCGSTATS: %s", strerror(errno)); 527 return (EXIT_FAILURE); 528 } 529 530 /* XXX what's difference between pkt_captured and us.us_recv? */ 531 printf("\n"); 532 printf("%d packets captured\n", pkt_captured); 533 printf("%d packets received by filter\n", us.bs_recv); 534 printf("%d packets dropped by kernel\n", us.bs_drop); 535 536 if (p->fd > 0) 537 close(p->fd); 538 if (p->rfd > 0) 539 close(p->rfd); 540 if (p->wfd > 0) 541 close(p->wfd); 542 543 return (EXIT_SUCCESS); 544} 545