usbdump.c revision 231835
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 231835 2012-02-16 21:18:36Z 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> 38231835Shselasky#include <sys/queue.h> 39215803Sweongyo#include <net/if.h> 40215803Sweongyo#include <net/bpf.h> 41215651Sweongyo#include <dev/usb/usb.h> 42215651Sweongyo#include <dev/usb/usb_pf.h> 43215651Sweongyo#include <dev/usb/usbdi.h> 44215651Sweongyo#include <errno.h> 45215651Sweongyo#include <fcntl.h> 46215651Sweongyo#include <limits.h> 47215651Sweongyo#include <stdio.h> 48215651Sweongyo#include <stdlib.h> 49231835Shselasky#include <stdint.h> 50215651Sweongyo#include <string.h> 51215651Sweongyo#include <time.h> 52215651Sweongyo#include <unistd.h> 53221604Shselasky#include <sysexits.h> 54221604Shselasky#include <err.h> 55215651Sweongyo 56231835Shselasky#define BPF_STORE_JUMP(x,_c,_k,_jt,_jf) do { \ 57231835Shselasky (x).code = (_c); \ 58231835Shselasky (x).k = (_k); \ 59231835Shselasky (x).jt = (_jt); \ 60231835Shselasky (x).jf = (_jf); \ 61231835Shselasky} while (0) 62231835Shselasky 63231835Shselasky#define BPF_STORE_STMT(x,_c,_k) do { \ 64231835Shselasky (x).code = (_c); \ 65231835Shselasky (x).k = (_k); \ 66231835Shselasky (x).jt = 0; \ 67231835Shselasky (x).jf = 0; \ 68231835Shselasky} while (0) 69231835Shselasky 70231835Shselaskystruct usb_filt { 71231835Shselasky STAILQ_ENTRY(usb_filt) entry; 72231835Shselasky int unit; 73231835Shselasky int endpoint; 74231835Shselasky}; 75231835Shselasky 76215651Sweongyostruct usbcap { 77215651Sweongyo int fd; /* fd for /dev/usbpf */ 78220301Shselasky uint32_t bufsize; 79220301Shselasky uint8_t *buffer; 80215651Sweongyo 81215651Sweongyo /* for -w option */ 82215651Sweongyo int wfd; 83215651Sweongyo /* for -r option */ 84215651Sweongyo int rfd; 85215651Sweongyo}; 86215651Sweongyo 87215651Sweongyostruct usbcap_filehdr { 88220301Shselasky uint32_t magic; 89215651Sweongyo#define USBCAP_FILEHDR_MAGIC 0x9a90000e 90220301Shselasky uint8_t major; 91220301Shselasky uint8_t minor; 92220301Shselasky uint8_t reserved[26]; 93215651Sweongyo} __packed; 94215651Sweongyo 95215651Sweongyostatic int doexit = 0; 96215651Sweongyostatic int pkt_captured = 0; 97215651Sweongyostatic int verbose = 0; 98218010Shselaskystatic const char *i_arg = "usbus0"; 99215651Sweongyostatic const char *r_arg = NULL; 100215651Sweongyostatic const char *w_arg = NULL; 101215651Sweongyostatic const char *errstr_table[USB_ERR_MAX] = { 102220301Shselasky [USB_ERR_NORMAL_COMPLETION] = "0", 103215651Sweongyo [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", 104215651Sweongyo [USB_ERR_NOT_STARTED] = "NOT_STARTED", 105215651Sweongyo [USB_ERR_INVAL] = "INVAL", 106215651Sweongyo [USB_ERR_NOMEM] = "NOMEM", 107215651Sweongyo [USB_ERR_CANCELLED] = "CANCELLED", 108215651Sweongyo [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", 109215651Sweongyo [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", 110215651Sweongyo [USB_ERR_BAD_FLAG] = "BAD_FLAG", 111215651Sweongyo [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", 112215651Sweongyo [USB_ERR_IN_USE] = "IN_USE", 113215651Sweongyo [USB_ERR_NO_ADDR] = "NO_ADDR", 114215651Sweongyo [USB_ERR_NO_PIPE] = "NO_PIPE", 115215651Sweongyo [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", 116215651Sweongyo [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", 117215651Sweongyo [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", 118215651Sweongyo [USB_ERR_NO_POWER] = "NO_POWER", 119215651Sweongyo [USB_ERR_TOO_DEEP] = "TOO_DEEP", 120215651Sweongyo [USB_ERR_IOERROR] = "IOERROR", 121215651Sweongyo [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", 122215651Sweongyo [USB_ERR_TIMEOUT] = "TIMEOUT", 123215651Sweongyo [USB_ERR_SHORT_XFER] = "SHORT_XFER", 124215651Sweongyo [USB_ERR_STALLED] = "STALLED", 125215651Sweongyo [USB_ERR_INTERRUPTED] = "INTERRUPTED", 126215651Sweongyo [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", 127215651Sweongyo [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", 128215651Sweongyo [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", 129215651Sweongyo [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", 130215651Sweongyo [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", 131215651Sweongyo}; 132215651Sweongyo 133220301Shselaskystatic const char *xfertype_table[4] = { 134215651Sweongyo [UE_CONTROL] = "CTRL", 135215651Sweongyo [UE_ISOCHRONOUS] = "ISOC", 136215651Sweongyo [UE_BULK] = "BULK", 137215651Sweongyo [UE_INTERRUPT] = "INTR" 138215651Sweongyo}; 139215651Sweongyo 140220301Shselaskystatic const char *speed_table[USB_SPEED_MAX] = { 141220301Shselasky [USB_SPEED_FULL] = "FULL", 142220301Shselasky [USB_SPEED_HIGH] = "HIGH", 143220301Shselasky [USB_SPEED_LOW] = "LOW", 144220301Shselasky [USB_SPEED_VARIABLE] = "VARI", 145220301Shselasky [USB_SPEED_SUPER] = "SUPER", 146220301Shselasky}; 147220301Shselasky 148231835Shselaskystatic STAILQ_HEAD(,usb_filt) usb_filt_head = 149231835Shselasky STAILQ_HEAD_INITIALIZER(usb_filt_head); 150231835Shselasky 151215651Sweongyostatic void 152231835Shselaskyadd_filter(int usb_filt_unit, int usb_filt_ep) 153231835Shselasky{ 154231835Shselasky struct usb_filt *puf; 155231835Shselasky 156231835Shselasky puf = malloc(sizeof(struct usb_filt)); 157231835Shselasky if (puf == NULL) 158231835Shselasky errx(EX_SOFTWARE, "Out of memory."); 159231835Shselasky 160231835Shselasky puf->unit = usb_filt_unit; 161231835Shselasky puf->endpoint = usb_filt_ep; 162231835Shselasky 163231835Shselasky STAILQ_INSERT_TAIL(&usb_filt_head, puf, entry); 164231835Shselasky} 165231835Shselasky 166231835Shselaskystatic void 167231835Shselaskymake_filter(struct bpf_program *pprog, int snapshot) 168231835Shselasky{ 169231835Shselasky struct usb_filt *puf; 170231835Shselasky struct bpf_insn *dynamic_insn; 171231835Shselasky int len; 172231835Shselasky 173231835Shselasky len = 0; 174231835Shselasky 175231835Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) 176231835Shselasky len++; 177231835Shselasky 178231835Shselasky dynamic_insn = malloc(((len * 5) + 1) * sizeof(struct bpf_insn)); 179231835Shselasky 180231835Shselasky if (dynamic_insn == NULL) 181231835Shselasky errx(EX_SOFTWARE, "Out of memory."); 182231835Shselasky 183231835Shselasky len++; 184231835Shselasky 185231835Shselasky if (len == 1) { 186231835Shselasky /* accept all packets */ 187231835Shselasky 188231835Shselasky BPF_STORE_STMT(dynamic_insn[0], BPF_RET | BPF_K, snapshot); 189231835Shselasky 190231835Shselasky goto done; 191231835Shselasky } 192231835Shselasky 193231835Shselasky len = 0; 194231835Shselasky 195231835Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) { 196231835Shselasky const int addr_off = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_address; 197231835Shselasky const int addr_ep = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_endpoint; 198231835Shselasky 199231835Shselasky if (puf->unit != -1) { 200231835Shselasky if (puf->endpoint != -1) { 201231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 202231835Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 203231835Shselasky len++; 204231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 205231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 3); 206231835Shselasky len++; 207231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 208231835Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 209231835Shselasky len++; 210231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 211231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 212231835Shselasky len++; 213231835Shselasky } else { 214231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 215231835Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 216231835Shselasky len++; 217231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 218231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 1); 219231835Shselasky len++; 220231835Shselasky } 221231835Shselasky } else { 222231835Shselasky if (puf->endpoint != -1) { 223231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 224231835Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 225231835Shselasky len++; 226231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 227231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 228231835Shselasky len++; 229231835Shselasky } 230231835Shselasky } 231231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 232231835Shselasky BPF_RET | BPF_K, snapshot); 233231835Shselasky len++; 234231835Shselasky } 235231835Shselasky 236231835Shselasky BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, 0); 237231835Shselasky len++; 238231835Shselasky 239231835Shselaskydone: 240231835Shselasky pprog->bf_len = len; 241231835Shselasky pprog->bf_insns = dynamic_insn; 242231835Shselasky} 243231835Shselasky 244231835Shselaskystatic void 245231835Shselaskyfree_filter(struct bpf_program *pprog) 246231835Shselasky{ 247231835Shselasky struct usb_filt *puf; 248231835Shselasky 249231835Shselasky while ((puf = STAILQ_FIRST(&usb_filt_head)) != NULL) { 250231835Shselasky STAILQ_REMOVE_HEAD(&usb_filt_head, entry); 251231835Shselasky free(puf); 252231835Shselasky } 253231835Shselasky free(pprog->bf_insns); 254231835Shselasky} 255231835Shselasky 256231835Shselaskystatic void 257215651Sweongyohandle_sigint(int sig) 258215651Sweongyo{ 259215651Sweongyo 260215651Sweongyo (void)sig; 261215651Sweongyo doexit = 1; 262215651Sweongyo} 263215651Sweongyo 264220301Shselasky#define FLAGS(x, name) \ 265220301Shselasky (((x) & USBPF_FLAG_##name) ? #name "|" : "") 266220301Shselasky 267220301Shselasky#define STATUS(x, name) \ 268220301Shselasky (((x) & USBPF_STATUS_##name) ? #name "|" : "") 269220301Shselasky 270220301Shselaskystatic const char * 271220301Shselaskyusb_errstr(uint32_t error) 272220301Shselasky{ 273220301Shselasky if (error >= USB_ERR_MAX || errstr_table[error] == NULL) 274220301Shselasky return ("UNKNOWN"); 275220301Shselasky else 276220301Shselasky return (errstr_table[error]); 277220301Shselasky} 278220301Shselasky 279220301Shselaskystatic const char * 280220301Shselaskyusb_speedstr(uint8_t speed) 281220301Shselasky{ 282220301Shselasky if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) 283220301Shselasky return ("UNKNOWN"); 284220301Shselasky else 285220301Shselasky return (speed_table[speed]); 286220301Shselasky} 287220301Shselasky 288215651Sweongyostatic void 289220301Shselaskyprint_flags(uint32_t flags) 290215651Sweongyo{ 291220301Shselasky printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", 292220301Shselasky flags, 293220301Shselasky FLAGS(flags, FORCE_SHORT_XFER), 294220301Shselasky FLAGS(flags, SHORT_XFER_OK), 295220301Shselasky FLAGS(flags, SHORT_FRAMES_OK), 296220301Shselasky FLAGS(flags, PIPE_BOF), 297220301Shselasky FLAGS(flags, PROXY_BUFFER), 298220301Shselasky FLAGS(flags, EXT_BUFFER), 299220301Shselasky FLAGS(flags, MANUAL_STATUS), 300220301Shselasky FLAGS(flags, NO_PIPE_OK), 301220301Shselasky FLAGS(flags, STALL_PIPE)); 302215651Sweongyo} 303215651Sweongyo 304215651Sweongyostatic void 305220301Shselaskyprint_status(uint32_t status) 306215651Sweongyo{ 307220301Shselasky printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", 308220301Shselasky status, 309220301Shselasky STATUS(status, OPEN), 310220301Shselasky STATUS(status, TRANSFERRING), 311220301Shselasky STATUS(status, DID_DMA_DELAY), 312220301Shselasky STATUS(status, DID_CLOSE), 313220301Shselasky STATUS(status, DRAINING), 314220301Shselasky STATUS(status, STARTED), 315220301Shselasky STATUS(status, BW_RECLAIMED), 316220301Shselasky STATUS(status, CONTROL_XFR), 317220301Shselasky STATUS(status, CONTROL_HDR), 318220301Shselasky STATUS(status, CONTROL_ACT), 319220301Shselasky STATUS(status, CONTROL_STALL), 320220301Shselasky STATUS(status, SHORT_FRAMES_OK), 321220301Shselasky STATUS(status, SHORT_XFER_OK), 322220301Shselasky STATUS(status, BDMA_ENABLE), 323220301Shselasky STATUS(status, BDMA_NO_POST_SYNC), 324220301Shselasky STATUS(status, BDMA_SETUP), 325220301Shselasky STATUS(status, ISOCHRONOUS_XFR), 326220301Shselasky STATUS(status, CURR_DMA_SET), 327220301Shselasky STATUS(status, CAN_CANCEL_IMMED), 328220301Shselasky STATUS(status, DOING_CALLBACK)); 329220301Shselasky} 330215651Sweongyo 331220301Shselasky/* 332220301Shselasky * Dump a byte into hex format. 333220301Shselasky */ 334220301Shselaskystatic void 335220301Shselaskyhexbyte(char *buf, uint8_t temp) 336220301Shselasky{ 337220301Shselasky uint8_t lo; 338220301Shselasky uint8_t hi; 339220301Shselasky 340220301Shselasky lo = temp & 0xF; 341220301Shselasky hi = temp >> 4; 342220301Shselasky 343220301Shselasky if (hi < 10) 344220301Shselasky buf[0] = '0' + hi; 345220301Shselasky else 346220301Shselasky buf[0] = 'A' + hi - 10; 347220301Shselasky 348220301Shselasky if (lo < 10) 349220301Shselasky buf[1] = '0' + lo; 350220301Shselasky else 351220301Shselasky buf[1] = 'A' + lo - 10; 352215651Sweongyo} 353215651Sweongyo 354215651Sweongyo/* 355215651Sweongyo * Display a region in traditional hexdump format. 356215651Sweongyo */ 357215651Sweongyostatic void 358220301Shselaskyhexdump(const uint8_t *region, uint32_t len) 359215651Sweongyo{ 360220301Shselasky const uint8_t *line; 361220301Shselasky char linebuf[128]; 362220301Shselasky int i; 363218010Shselasky int x; 364218010Shselasky int c; 365215651Sweongyo 366215651Sweongyo for (line = region; line < (region + len); line += 16) { 367220301Shselasky 368220301Shselasky i = 0; 369220301Shselasky 370220301Shselasky linebuf[i] = ' '; 371220301Shselasky hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); 372220301Shselasky hexbyte(linebuf + i + 3, (line - region) & 0xFF); 373220301Shselasky linebuf[i + 5] = ' '; 374220301Shselasky linebuf[i + 6] = ' '; 375220301Shselasky i += 7; 376220301Shselasky 377215651Sweongyo for (x = 0; x < 16; x++) { 378220301Shselasky if ((line + x) < (region + len)) { 379220301Shselasky hexbyte(linebuf + i, 380220301Shselasky *(const u_int8_t *)(line + x)); 381220301Shselasky } else { 382220301Shselasky linebuf[i] = '-'; 383220301Shselasky linebuf[i + 1] = '-'; 384220301Shselasky } 385220301Shselasky linebuf[i + 2] = ' '; 386220301Shselasky if (x == 7) { 387220301Shselasky linebuf[i + 3] = ' '; 388220301Shselasky i += 4; 389220301Shselasky } else { 390220301Shselasky i += 3; 391220301Shselasky } 392215651Sweongyo } 393220301Shselasky linebuf[i] = ' '; 394220301Shselasky linebuf[i + 1] = '|'; 395220301Shselasky i += 2; 396215651Sweongyo for (x = 0; x < 16; x++) { 397215651Sweongyo if ((line + x) < (region + len)) { 398215651Sweongyo c = *(const u_int8_t *)(line + x); 399215651Sweongyo /* !isprint(c) */ 400215651Sweongyo if ((c < ' ') || (c > '~')) 401215651Sweongyo c = '.'; 402220301Shselasky linebuf[i] = c; 403220301Shselasky } else { 404220301Shselasky linebuf[i] = ' '; 405220301Shselasky } 406220301Shselasky i++; 407215651Sweongyo } 408220301Shselasky linebuf[i] = '|'; 409220301Shselasky linebuf[i + 1] = 0; 410220301Shselasky i += 2; 411220301Shselasky puts(linebuf); 412215651Sweongyo } 413215651Sweongyo} 414215651Sweongyo 415215651Sweongyostatic void 416226474Shselaskyprint_apacket(const struct bpf_hdr *hdr, const uint8_t *ptr, int ptr_len) 417215651Sweongyo{ 418215651Sweongyo struct tm *tm; 419220301Shselasky struct usbpf_pkthdr up_temp; 420220301Shselasky struct usbpf_pkthdr *up; 421215651Sweongyo struct timeval tv; 422215651Sweongyo size_t len; 423220301Shselasky uint32_t x; 424215651Sweongyo char buf[64]; 425215651Sweongyo 426220301Shselasky ptr += USBPF_HDR_LEN; 427220301Shselasky ptr_len -= USBPF_HDR_LEN; 428220301Shselasky if (ptr_len < 0) 429220301Shselasky return; 430220301Shselasky 431220301Shselasky /* make sure we don't change the source buffer */ 432220301Shselasky memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); 433220301Shselasky up = &up_temp; 434220301Shselasky 435220301Shselasky /* 436220301Shselasky * A packet from the kernel is based on little endian byte 437220301Shselasky * order. 438220301Shselasky */ 439220301Shselasky up->up_totlen = le32toh(up->up_totlen); 440215651Sweongyo up->up_busunit = le32toh(up->up_busunit); 441220301Shselasky up->up_address = le32toh(up->up_address); 442215651Sweongyo up->up_flags = le32toh(up->up_flags); 443215651Sweongyo up->up_status = le32toh(up->up_status); 444215651Sweongyo up->up_error = le32toh(up->up_error); 445215651Sweongyo up->up_interval = le32toh(up->up_interval); 446220301Shselasky up->up_frames = le32toh(up->up_frames); 447220301Shselasky up->up_packet_size = le32toh(up->up_packet_size); 448220301Shselasky up->up_packet_count = le32toh(up->up_packet_count); 449220301Shselasky up->up_endpoint = le32toh(up->up_endpoint); 450215651Sweongyo 451226474Shselasky tv.tv_sec = hdr->bh_tstamp.tv_sec; 452226474Shselasky tv.tv_usec = hdr->bh_tstamp.tv_usec; 453215651Sweongyo tm = localtime(&tv.tv_sec); 454215651Sweongyo 455215651Sweongyo len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 456220301Shselasky 457220314Sthompsa printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", 458220301Shselasky (int)len, buf, tv.tv_usec, 459220301Shselasky (int)up->up_busunit, (int)up->up_address, 460220301Shselasky (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", 461215651Sweongyo xfertype_table[up->up_xfertype], 462220301Shselasky (unsigned int)up->up_endpoint, 463220301Shselasky usb_speedstr(up->up_speed), 464220301Shselasky (int)up->up_frames, 465220301Shselasky (int)(up->up_totlen - USBPF_HDR_LEN - 466220301Shselasky (USBPF_FRAME_HDR_LEN * up->up_frames)), 467220301Shselasky (int)up->up_interval, 468220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", 469220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? 470220301Shselasky usb_errstr(up->up_error) : ""); 471215651Sweongyo 472215651Sweongyo if (verbose >= 1) { 473220301Shselasky for (x = 0; x != up->up_frames; x++) { 474220301Shselasky const struct usbpf_framehdr *uf; 475220301Shselasky uint32_t framelen; 476220301Shselasky uint32_t flags; 477220301Shselasky 478220301Shselasky uf = (const struct usbpf_framehdr *)ptr; 479220301Shselasky ptr += USBPF_FRAME_HDR_LEN; 480220301Shselasky ptr_len -= USBPF_FRAME_HDR_LEN; 481220301Shselasky if (ptr_len < 0) 482220301Shselasky return; 483220301Shselasky 484220301Shselasky framelen = le32toh(uf->length); 485220301Shselasky flags = le32toh(uf->flags); 486220301Shselasky 487220301Shselasky printf(" frame[%u] %s %d bytes\n", 488220301Shselasky (unsigned int)x, 489220301Shselasky (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", 490220301Shselasky (int)framelen); 491220301Shselasky 492220301Shselasky if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { 493220301Shselasky 494220301Shselasky int tot_frame_len; 495220301Shselasky 496220301Shselasky tot_frame_len = USBPF_FRAME_ALIGN(framelen); 497220301Shselasky 498220301Shselasky ptr_len -= tot_frame_len; 499220301Shselasky 500220301Shselasky if (tot_frame_len < 0 || 501220301Shselasky (int)framelen < 0 || (int)ptr_len < 0) 502220301Shselasky break; 503220301Shselasky 504220301Shselasky hexdump(ptr, framelen); 505220301Shselasky 506220301Shselasky ptr += tot_frame_len; 507220301Shselasky } 508215651Sweongyo } 509215651Sweongyo } 510220301Shselasky if (verbose >= 2) 511215651Sweongyo print_flags(up->up_flags); 512220301Shselasky if (verbose >= 3) 513215651Sweongyo print_status(up->up_status); 514215651Sweongyo} 515215651Sweongyo 516215651Sweongyostatic void 517220301Shselaskyprint_packets(uint8_t *data, const int datalen) 518215651Sweongyo{ 519226474Shselasky const struct bpf_hdr *hdr; 520220301Shselasky uint8_t *ptr; 521220301Shselasky uint8_t *next; 522215651Sweongyo 523215651Sweongyo for (ptr = data; ptr < (data + datalen); ptr = next) { 524226474Shselasky hdr = (const struct bpf_hdr *)ptr; 525215803Sweongyo next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 526215651Sweongyo 527220301Shselasky if (w_arg == NULL) { 528220301Shselasky print_apacket(hdr, ptr + 529220301Shselasky hdr->bh_hdrlen, hdr->bh_caplen); 530220301Shselasky } 531215651Sweongyo pkt_captured++; 532215651Sweongyo } 533215651Sweongyo} 534215651Sweongyo 535215651Sweongyostatic void 536220301Shselaskywrite_packets(struct usbcap *p, const uint8_t *data, const int datalen) 537215651Sweongyo{ 538220301Shselasky int len = htole32(datalen); 539220301Shselasky int ret; 540215651Sweongyo 541215651Sweongyo ret = write(p->wfd, &len, sizeof(int)); 542221604Shselasky if (ret != sizeof(int)) { 543221604Shselasky err(EXIT_FAILURE, "Could not write length " 544221604Shselasky "field of USB data payload"); 545221604Shselasky } 546215651Sweongyo ret = write(p->wfd, data, datalen); 547221604Shselasky if (ret != datalen) { 548221604Shselasky err(EXIT_FAILURE, "Could not write " 549221604Shselasky "complete USB data payload"); 550221604Shselasky } 551215651Sweongyo} 552215651Sweongyo 553215651Sweongyostatic void 554215651Sweongyoread_file(struct usbcap *p) 555215651Sweongyo{ 556220301Shselasky int datalen; 557220301Shselasky int ret; 558220301Shselasky uint8_t *data; 559215651Sweongyo 560215651Sweongyo while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 561215651Sweongyo datalen = le32toh(datalen); 562215651Sweongyo data = malloc(datalen); 563221604Shselasky if (data == NULL) 564221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 565215651Sweongyo ret = read(p->rfd, data, datalen); 566221604Shselasky if (ret != datalen) { 567221604Shselasky err(EXIT_FAILURE, "Could not read complete " 568221604Shselasky "USB data payload"); 569221604Shselasky } 570215651Sweongyo print_packets(data, datalen); 571215651Sweongyo free(data); 572215651Sweongyo } 573215651Sweongyo} 574215651Sweongyo 575215651Sweongyostatic void 576215651Sweongyodo_loop(struct usbcap *p) 577215651Sweongyo{ 578215651Sweongyo int cc; 579215651Sweongyo 580215651Sweongyo while (doexit == 0) { 581220301Shselasky cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); 582215651Sweongyo if (cc < 0) { 583215651Sweongyo switch (errno) { 584215651Sweongyo case EINTR: 585215651Sweongyo break; 586215651Sweongyo default: 587215651Sweongyo fprintf(stderr, "read: %s\n", strerror(errno)); 588215651Sweongyo return; 589215651Sweongyo } 590215651Sweongyo continue; 591215651Sweongyo } 592215651Sweongyo if (cc == 0) 593215651Sweongyo continue; 594215651Sweongyo if (w_arg != NULL) 595215651Sweongyo write_packets(p, p->buffer, cc); 596215651Sweongyo print_packets(p->buffer, cc); 597215651Sweongyo } 598215651Sweongyo} 599215651Sweongyo 600215651Sweongyostatic void 601215651Sweongyoinit_rfile(struct usbcap *p) 602215651Sweongyo{ 603215651Sweongyo struct usbcap_filehdr uf; 604215651Sweongyo int ret; 605215651Sweongyo 606215651Sweongyo p->rfd = open(r_arg, O_RDONLY); 607215651Sweongyo if (p->rfd < 0) { 608221604Shselasky err(EXIT_FAILURE, "Could not open " 609221604Shselasky "'%s' for read", r_arg); 610215651Sweongyo } 611215651Sweongyo ret = read(p->rfd, &uf, sizeof(uf)); 612221604Shselasky if (ret != sizeof(uf)) { 613221604Shselasky err(EXIT_FAILURE, "Could not read USB capture " 614221604Shselasky "file header"); 615221604Shselasky } 616221604Shselasky if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) { 617221604Shselasky errx(EX_SOFTWARE, "Invalid magic field(0x%08x) " 618221604Shselasky "in USB capture file header.", 619221604Shselasky (unsigned int)le32toh(uf.magic)); 620221604Shselasky } 621221604Shselasky if (uf.major != 0) { 622221604Shselasky errx(EX_SOFTWARE, "Invalid major version(%d) " 623221604Shselasky "field in USB capture file header.", (int)uf.major); 624221604Shselasky } 625221604Shselasky if (uf.minor != 2) { 626221604Shselasky errx(EX_SOFTWARE, "Invalid minor version(%d) " 627221604Shselasky "field in USB capture file header.", (int)uf.minor); 628221604Shselasky } 629215651Sweongyo} 630215651Sweongyo 631215651Sweongyostatic void 632215651Sweongyoinit_wfile(struct usbcap *p) 633215651Sweongyo{ 634215651Sweongyo struct usbcap_filehdr uf; 635215651Sweongyo int ret; 636215651Sweongyo 637215651Sweongyo p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 638215651Sweongyo if (p->wfd < 0) { 639221604Shselasky err(EXIT_FAILURE, "Could not open " 640221604Shselasky "'%s' for write", r_arg); 641215651Sweongyo } 642221604Shselasky memset(&uf, 0, sizeof(uf)); 643215651Sweongyo uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 644215651Sweongyo uf.major = 0; 645220301Shselasky uf.minor = 2; 646215651Sweongyo ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 647221604Shselasky if (ret != sizeof(uf)) { 648221604Shselasky err(EXIT_FAILURE, "Could not write " 649221604Shselasky "USB capture header"); 650221604Shselasky } 651215651Sweongyo} 652215651Sweongyo 653215651Sweongyostatic void 654215651Sweongyousage(void) 655215651Sweongyo{ 656215651Sweongyo 657215651Sweongyo#define FMT " %-14s %s\n" 658215651Sweongyo fprintf(stderr, "usage: usbdump [options]\n"); 659221604Shselasky fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface"); 660231835Shselasky fprintf(stderr, FMT, "-f <unit[.endpoint]>", "Specify a device and endpoint filter"); 661221604Shselasky fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file"); 662221604Shselasky fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet"); 663221604Shselasky fprintf(stderr, FMT, "-v", "Increase the verbose level"); 664221604Shselasky fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file"); 665215651Sweongyo#undef FMT 666221604Shselasky exit(EX_USAGE); 667215651Sweongyo} 668215651Sweongyo 669215651Sweongyoint 670215651Sweongyomain(int argc, char *argv[]) 671215651Sweongyo{ 672215651Sweongyo struct timeval tv; 673215803Sweongyo struct bpf_program total_prog; 674215803Sweongyo struct bpf_stat us; 675215803Sweongyo struct bpf_version bv; 676215651Sweongyo struct usbcap uc, *p = &uc; 677215803Sweongyo struct ifreq ifr; 678215651Sweongyo long snapshot = 192; 679220301Shselasky uint32_t v; 680231835Shselasky int fd; 681231835Shselasky int o; 682231835Shselasky int filt_unit; 683231835Shselasky int filt_ep; 684215651Sweongyo const char *optstring; 685231835Shselasky char *pp; 686215651Sweongyo 687221604Shselasky memset(&uc, 0, sizeof(struct usbcap)); 688215651Sweongyo 689231835Shselasky optstring = "i:r:s:vw:f:"; 690215651Sweongyo while ((o = getopt(argc, argv, optstring)) != -1) { 691215651Sweongyo switch (o) { 692215651Sweongyo case 'i': 693215651Sweongyo i_arg = optarg; 694215651Sweongyo break; 695215651Sweongyo case 'r': 696215651Sweongyo r_arg = optarg; 697215651Sweongyo init_rfile(p); 698215651Sweongyo break; 699215651Sweongyo case 's': 700231835Shselasky snapshot = strtol(optarg, &pp, 10); 701215651Sweongyo errno = 0; 702231835Shselasky if (pp != NULL && *pp != 0) 703231835Shselasky usage(); 704215651Sweongyo if (snapshot == 0 && errno == EINVAL) 705215651Sweongyo usage(); 706215651Sweongyo /* snapeshot == 0 is special */ 707215651Sweongyo if (snapshot == 0) 708215651Sweongyo snapshot = -1; 709215651Sweongyo break; 710215651Sweongyo case 'v': 711215651Sweongyo verbose++; 712215651Sweongyo break; 713215651Sweongyo case 'w': 714215651Sweongyo w_arg = optarg; 715215651Sweongyo init_wfile(p); 716215651Sweongyo break; 717231835Shselasky case 'f': 718231835Shselasky filt_unit = strtol(optarg, &pp, 10); 719231835Shselasky filt_ep = -1; 720231835Shselasky if (pp != NULL) { 721231835Shselasky if (*pp == '.') { 722231835Shselasky filt_ep = strtol(pp + 1, &pp, 10); 723231835Shselasky if (pp != NULL && *pp != 0) 724231835Shselasky usage(); 725231835Shselasky } else if (*pp != 0) { 726231835Shselasky usage(); 727231835Shselasky } 728231835Shselasky } 729231835Shselasky add_filter(filt_unit, filt_ep); 730231835Shselasky break; 731215651Sweongyo default: 732215651Sweongyo usage(); 733215651Sweongyo /* NOTREACHED */ 734215651Sweongyo } 735215651Sweongyo } 736215651Sweongyo 737215651Sweongyo if (r_arg != NULL) { 738215651Sweongyo read_file(p); 739215651Sweongyo exit(EXIT_SUCCESS); 740215651Sweongyo } 741215651Sweongyo 742215803Sweongyo p->fd = fd = open("/dev/bpf", O_RDONLY); 743221604Shselasky if (p->fd < 0) 744221604Shselasky err(EXIT_FAILURE, "Could not open BPF device"); 745215651Sweongyo 746221604Shselasky if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) 747221604Shselasky err(EXIT_FAILURE, "BIOCVERSION ioctl failed"); 748221604Shselasky 749215803Sweongyo if (bv.bv_major != BPF_MAJOR_VERSION || 750221604Shselasky bv.bv_minor < BPF_MINOR_VERSION) 751221604Shselasky errx(EXIT_FAILURE, "Kernel BPF filter out of date"); 752215651Sweongyo 753220301Shselasky /* USB transfers can be greater than 64KByte */ 754220301Shselasky v = 1U << 16; 755220301Shselasky 756220301Shselasky /* clear ifr structure */ 757220301Shselasky memset(&ifr, 0, sizeof(ifr)); 758220301Shselasky 759220301Shselasky for ( ; v >= USBPF_HDR_LEN; v >>= 1) { 760215803Sweongyo (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 761215803Sweongyo (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 762215803Sweongyo if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 763215651Sweongyo break; 764215651Sweongyo } 765221604Shselasky if (v == 0) 766221604Shselasky errx(EXIT_FAILURE, "No buffer size worked."); 767215651Sweongyo 768221604Shselasky if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) 769221604Shselasky err(EXIT_FAILURE, "BIOCGBLEN ioctl failed"); 770215651Sweongyo 771215651Sweongyo p->bufsize = v; 772220301Shselasky p->buffer = (uint8_t *)malloc(p->bufsize); 773221604Shselasky if (p->buffer == NULL) 774221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 775215651Sweongyo 776231835Shselasky make_filter(&total_prog, snapshot); 777215651Sweongyo 778221604Shselasky if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) 779221604Shselasky err(EXIT_FAILURE, "BIOCSETF ioctl failed"); 780215651Sweongyo 781231835Shselasky free_filter(&total_prog); 782231835Shselasky 783215651Sweongyo /* 1 second read timeout */ 784215651Sweongyo tv.tv_sec = 1; 785215651Sweongyo tv.tv_usec = 0; 786221604Shselasky if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) 787221604Shselasky err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed"); 788215651Sweongyo 789215651Sweongyo (void)signal(SIGINT, handle_sigint); 790215651Sweongyo 791215651Sweongyo do_loop(p); 792215651Sweongyo 793221604Shselasky if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) 794221604Shselasky err(EXIT_FAILURE, "BIOCGSTATS ioctl failed"); 795215651Sweongyo 796215651Sweongyo /* XXX what's difference between pkt_captured and us.us_recv? */ 797215651Sweongyo printf("\n"); 798215651Sweongyo printf("%d packets captured\n", pkt_captured); 799215803Sweongyo printf("%d packets received by filter\n", us.bs_recv); 800215803Sweongyo printf("%d packets dropped by kernel\n", us.bs_drop); 801215651Sweongyo 802215651Sweongyo if (p->fd > 0) 803215651Sweongyo close(p->fd); 804215651Sweongyo if (p->rfd > 0) 805215651Sweongyo close(p->rfd); 806215651Sweongyo if (p->wfd > 0) 807215651Sweongyo close(p->wfd); 808215651Sweongyo 809215651Sweongyo return (EXIT_SUCCESS); 810215651Sweongyo} 811