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