usbdump.c revision 233037
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 233037 2012-03-16 16:29:21Z 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 95233037Shselasky#define HEADER_ALIGN(x,a) (((x) + (a) - 1) & ~((a) - 1)) 96233037Shselasky 97233037Shselaskystruct header_32 { 98233037Shselasky uint32_t ts_sec; 99233037Shselasky uint32_t ts_usec; 100233037Shselasky uint32_t caplen; 101233037Shselasky uint32_t datalen; 102233037Shselasky uint16_t hdrlen; 103233037Shselasky uint16_t dummy; 104233037Shselasky} __packed; 105233037Shselasky 106233037Shselaskystruct header_64 { 107233037Shselasky uint64_t ts_sec; 108233037Shselasky uint64_t ts_usec; 109233037Shselasky uint32_t caplen; 110233037Shselasky uint32_t datalen; 111233037Shselasky uint16_t hdrlen; 112233037Shselasky uint16_t dummy; 113233037Shselasky} __packed; 114233037Shselasky 115215651Sweongyostatic int doexit = 0; 116215651Sweongyostatic int pkt_captured = 0; 117215651Sweongyostatic int verbose = 0; 118218010Shselaskystatic const char *i_arg = "usbus0"; 119215651Sweongyostatic const char *r_arg = NULL; 120215651Sweongyostatic const char *w_arg = NULL; 121215651Sweongyostatic const char *errstr_table[USB_ERR_MAX] = { 122220301Shselasky [USB_ERR_NORMAL_COMPLETION] = "0", 123215651Sweongyo [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", 124215651Sweongyo [USB_ERR_NOT_STARTED] = "NOT_STARTED", 125215651Sweongyo [USB_ERR_INVAL] = "INVAL", 126215651Sweongyo [USB_ERR_NOMEM] = "NOMEM", 127215651Sweongyo [USB_ERR_CANCELLED] = "CANCELLED", 128215651Sweongyo [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", 129215651Sweongyo [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", 130215651Sweongyo [USB_ERR_BAD_FLAG] = "BAD_FLAG", 131215651Sweongyo [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", 132215651Sweongyo [USB_ERR_IN_USE] = "IN_USE", 133215651Sweongyo [USB_ERR_NO_ADDR] = "NO_ADDR", 134215651Sweongyo [USB_ERR_NO_PIPE] = "NO_PIPE", 135215651Sweongyo [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", 136215651Sweongyo [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", 137215651Sweongyo [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", 138215651Sweongyo [USB_ERR_NO_POWER] = "NO_POWER", 139215651Sweongyo [USB_ERR_TOO_DEEP] = "TOO_DEEP", 140215651Sweongyo [USB_ERR_IOERROR] = "IOERROR", 141215651Sweongyo [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", 142215651Sweongyo [USB_ERR_TIMEOUT] = "TIMEOUT", 143215651Sweongyo [USB_ERR_SHORT_XFER] = "SHORT_XFER", 144215651Sweongyo [USB_ERR_STALLED] = "STALLED", 145215651Sweongyo [USB_ERR_INTERRUPTED] = "INTERRUPTED", 146215651Sweongyo [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", 147215651Sweongyo [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", 148215651Sweongyo [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", 149215651Sweongyo [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", 150215651Sweongyo [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", 151215651Sweongyo}; 152215651Sweongyo 153220301Shselaskystatic const char *xfertype_table[4] = { 154215651Sweongyo [UE_CONTROL] = "CTRL", 155215651Sweongyo [UE_ISOCHRONOUS] = "ISOC", 156215651Sweongyo [UE_BULK] = "BULK", 157215651Sweongyo [UE_INTERRUPT] = "INTR" 158215651Sweongyo}; 159215651Sweongyo 160220301Shselaskystatic const char *speed_table[USB_SPEED_MAX] = { 161220301Shselasky [USB_SPEED_FULL] = "FULL", 162220301Shselasky [USB_SPEED_HIGH] = "HIGH", 163220301Shselasky [USB_SPEED_LOW] = "LOW", 164220301Shselasky [USB_SPEED_VARIABLE] = "VARI", 165220301Shselasky [USB_SPEED_SUPER] = "SUPER", 166220301Shselasky}; 167220301Shselasky 168231835Shselaskystatic STAILQ_HEAD(,usb_filt) usb_filt_head = 169231835Shselasky STAILQ_HEAD_INITIALIZER(usb_filt_head); 170231835Shselasky 171215651Sweongyostatic void 172231835Shselaskyadd_filter(int usb_filt_unit, int usb_filt_ep) 173231835Shselasky{ 174231835Shselasky struct usb_filt *puf; 175231835Shselasky 176231835Shselasky puf = malloc(sizeof(struct usb_filt)); 177231835Shselasky if (puf == NULL) 178231835Shselasky errx(EX_SOFTWARE, "Out of memory."); 179231835Shselasky 180231835Shselasky puf->unit = usb_filt_unit; 181231835Shselasky puf->endpoint = usb_filt_ep; 182231835Shselasky 183231835Shselasky STAILQ_INSERT_TAIL(&usb_filt_head, puf, entry); 184231835Shselasky} 185231835Shselasky 186231835Shselaskystatic void 187231835Shselaskymake_filter(struct bpf_program *pprog, int snapshot) 188231835Shselasky{ 189231835Shselasky struct usb_filt *puf; 190231835Shselasky struct bpf_insn *dynamic_insn; 191231835Shselasky int len; 192231835Shselasky 193231835Shselasky len = 0; 194231835Shselasky 195231835Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) 196231835Shselasky len++; 197231835Shselasky 198231835Shselasky dynamic_insn = malloc(((len * 5) + 1) * sizeof(struct bpf_insn)); 199231835Shselasky 200231835Shselasky if (dynamic_insn == NULL) 201231835Shselasky errx(EX_SOFTWARE, "Out of memory."); 202231835Shselasky 203231835Shselasky len++; 204231835Shselasky 205231835Shselasky if (len == 1) { 206231835Shselasky /* accept all packets */ 207231835Shselasky 208231835Shselasky BPF_STORE_STMT(dynamic_insn[0], BPF_RET | BPF_K, snapshot); 209231835Shselasky 210231835Shselasky goto done; 211231835Shselasky } 212231835Shselasky 213231835Shselasky len = 0; 214231835Shselasky 215231835Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) { 216231835Shselasky const int addr_off = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_address; 217231835Shselasky const int addr_ep = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_endpoint; 218231835Shselasky 219231835Shselasky if (puf->unit != -1) { 220231835Shselasky if (puf->endpoint != -1) { 221231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 222231835Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 223231835Shselasky len++; 224231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 225231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 3); 226231835Shselasky len++; 227231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 228231835Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 229231835Shselasky len++; 230231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 231231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 232231835Shselasky len++; 233231835Shselasky } else { 234231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 235231835Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 236231835Shselasky len++; 237231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 238231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 1); 239231835Shselasky len++; 240231835Shselasky } 241231835Shselasky } else { 242231835Shselasky if (puf->endpoint != -1) { 243231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 244231835Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 245231835Shselasky len++; 246231835Shselasky BPF_STORE_JUMP(dynamic_insn[len], 247231835Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 248231835Shselasky len++; 249231835Shselasky } 250231835Shselasky } 251231835Shselasky BPF_STORE_STMT(dynamic_insn[len], 252231835Shselasky BPF_RET | BPF_K, snapshot); 253231835Shselasky len++; 254231835Shselasky } 255231835Shselasky 256231835Shselasky BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, 0); 257231835Shselasky len++; 258231835Shselasky 259231835Shselaskydone: 260231835Shselasky pprog->bf_len = len; 261231835Shselasky pprog->bf_insns = dynamic_insn; 262231835Shselasky} 263231835Shselasky 264231835Shselaskystatic void 265231835Shselaskyfree_filter(struct bpf_program *pprog) 266231835Shselasky{ 267231835Shselasky struct usb_filt *puf; 268231835Shselasky 269231835Shselasky while ((puf = STAILQ_FIRST(&usb_filt_head)) != NULL) { 270231835Shselasky STAILQ_REMOVE_HEAD(&usb_filt_head, entry); 271231835Shselasky free(puf); 272231835Shselasky } 273231835Shselasky free(pprog->bf_insns); 274231835Shselasky} 275231835Shselasky 276231835Shselaskystatic void 277215651Sweongyohandle_sigint(int sig) 278215651Sweongyo{ 279215651Sweongyo 280215651Sweongyo (void)sig; 281215651Sweongyo doexit = 1; 282215651Sweongyo} 283215651Sweongyo 284220301Shselasky#define FLAGS(x, name) \ 285220301Shselasky (((x) & USBPF_FLAG_##name) ? #name "|" : "") 286220301Shselasky 287220301Shselasky#define STATUS(x, name) \ 288220301Shselasky (((x) & USBPF_STATUS_##name) ? #name "|" : "") 289220301Shselasky 290220301Shselaskystatic const char * 291220301Shselaskyusb_errstr(uint32_t error) 292220301Shselasky{ 293220301Shselasky if (error >= USB_ERR_MAX || errstr_table[error] == NULL) 294220301Shselasky return ("UNKNOWN"); 295220301Shselasky else 296220301Shselasky return (errstr_table[error]); 297220301Shselasky} 298220301Shselasky 299220301Shselaskystatic const char * 300220301Shselaskyusb_speedstr(uint8_t speed) 301220301Shselasky{ 302220301Shselasky if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) 303220301Shselasky return ("UNKNOWN"); 304220301Shselasky else 305220301Shselasky return (speed_table[speed]); 306220301Shselasky} 307220301Shselasky 308215651Sweongyostatic void 309220301Shselaskyprint_flags(uint32_t flags) 310215651Sweongyo{ 311220301Shselasky printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", 312220301Shselasky flags, 313220301Shselasky FLAGS(flags, FORCE_SHORT_XFER), 314220301Shselasky FLAGS(flags, SHORT_XFER_OK), 315220301Shselasky FLAGS(flags, SHORT_FRAMES_OK), 316220301Shselasky FLAGS(flags, PIPE_BOF), 317220301Shselasky FLAGS(flags, PROXY_BUFFER), 318220301Shselasky FLAGS(flags, EXT_BUFFER), 319220301Shselasky FLAGS(flags, MANUAL_STATUS), 320220301Shselasky FLAGS(flags, NO_PIPE_OK), 321220301Shselasky FLAGS(flags, STALL_PIPE)); 322215651Sweongyo} 323215651Sweongyo 324215651Sweongyostatic void 325220301Shselaskyprint_status(uint32_t status) 326215651Sweongyo{ 327220301Shselasky printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", 328220301Shselasky status, 329220301Shselasky STATUS(status, OPEN), 330220301Shselasky STATUS(status, TRANSFERRING), 331220301Shselasky STATUS(status, DID_DMA_DELAY), 332220301Shselasky STATUS(status, DID_CLOSE), 333220301Shselasky STATUS(status, DRAINING), 334220301Shselasky STATUS(status, STARTED), 335220301Shselasky STATUS(status, BW_RECLAIMED), 336220301Shselasky STATUS(status, CONTROL_XFR), 337220301Shselasky STATUS(status, CONTROL_HDR), 338220301Shselasky STATUS(status, CONTROL_ACT), 339220301Shselasky STATUS(status, CONTROL_STALL), 340220301Shselasky STATUS(status, SHORT_FRAMES_OK), 341220301Shselasky STATUS(status, SHORT_XFER_OK), 342220301Shselasky STATUS(status, BDMA_ENABLE), 343220301Shselasky STATUS(status, BDMA_NO_POST_SYNC), 344220301Shselasky STATUS(status, BDMA_SETUP), 345220301Shselasky STATUS(status, ISOCHRONOUS_XFR), 346220301Shselasky STATUS(status, CURR_DMA_SET), 347220301Shselasky STATUS(status, CAN_CANCEL_IMMED), 348220301Shselasky STATUS(status, DOING_CALLBACK)); 349220301Shselasky} 350215651Sweongyo 351220301Shselasky/* 352220301Shselasky * Dump a byte into hex format. 353220301Shselasky */ 354220301Shselaskystatic void 355220301Shselaskyhexbyte(char *buf, uint8_t temp) 356220301Shselasky{ 357220301Shselasky uint8_t lo; 358220301Shselasky uint8_t hi; 359220301Shselasky 360220301Shselasky lo = temp & 0xF; 361220301Shselasky hi = temp >> 4; 362220301Shselasky 363220301Shselasky if (hi < 10) 364220301Shselasky buf[0] = '0' + hi; 365220301Shselasky else 366220301Shselasky buf[0] = 'A' + hi - 10; 367220301Shselasky 368220301Shselasky if (lo < 10) 369220301Shselasky buf[1] = '0' + lo; 370220301Shselasky else 371220301Shselasky buf[1] = 'A' + lo - 10; 372215651Sweongyo} 373215651Sweongyo 374215651Sweongyo/* 375215651Sweongyo * Display a region in traditional hexdump format. 376215651Sweongyo */ 377215651Sweongyostatic void 378220301Shselaskyhexdump(const uint8_t *region, uint32_t len) 379215651Sweongyo{ 380220301Shselasky const uint8_t *line; 381220301Shselasky char linebuf[128]; 382220301Shselasky int i; 383218010Shselasky int x; 384218010Shselasky int c; 385215651Sweongyo 386215651Sweongyo for (line = region; line < (region + len); line += 16) { 387220301Shselasky 388220301Shselasky i = 0; 389220301Shselasky 390220301Shselasky linebuf[i] = ' '; 391220301Shselasky hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); 392220301Shselasky hexbyte(linebuf + i + 3, (line - region) & 0xFF); 393220301Shselasky linebuf[i + 5] = ' '; 394220301Shselasky linebuf[i + 6] = ' '; 395220301Shselasky i += 7; 396220301Shselasky 397215651Sweongyo for (x = 0; x < 16; x++) { 398220301Shselasky if ((line + x) < (region + len)) { 399220301Shselasky hexbyte(linebuf + i, 400220301Shselasky *(const u_int8_t *)(line + x)); 401220301Shselasky } else { 402220301Shselasky linebuf[i] = '-'; 403220301Shselasky linebuf[i + 1] = '-'; 404220301Shselasky } 405220301Shselasky linebuf[i + 2] = ' '; 406220301Shselasky if (x == 7) { 407220301Shselasky linebuf[i + 3] = ' '; 408220301Shselasky i += 4; 409220301Shselasky } else { 410220301Shselasky i += 3; 411220301Shselasky } 412215651Sweongyo } 413220301Shselasky linebuf[i] = ' '; 414220301Shselasky linebuf[i + 1] = '|'; 415220301Shselasky i += 2; 416215651Sweongyo for (x = 0; x < 16; x++) { 417215651Sweongyo if ((line + x) < (region + len)) { 418215651Sweongyo c = *(const u_int8_t *)(line + x); 419215651Sweongyo /* !isprint(c) */ 420215651Sweongyo if ((c < ' ') || (c > '~')) 421215651Sweongyo c = '.'; 422220301Shselasky linebuf[i] = c; 423220301Shselasky } else { 424220301Shselasky linebuf[i] = ' '; 425220301Shselasky } 426220301Shselasky i++; 427215651Sweongyo } 428220301Shselasky linebuf[i] = '|'; 429220301Shselasky linebuf[i + 1] = 0; 430220301Shselasky i += 2; 431220301Shselasky puts(linebuf); 432215651Sweongyo } 433215651Sweongyo} 434215651Sweongyo 435215651Sweongyostatic void 436233037Shselaskyprint_apacket(const struct header_32 *hdr, const uint8_t *ptr, int ptr_len) 437215651Sweongyo{ 438215651Sweongyo struct tm *tm; 439220301Shselasky struct usbpf_pkthdr up_temp; 440220301Shselasky struct usbpf_pkthdr *up; 441215651Sweongyo struct timeval tv; 442215651Sweongyo size_t len; 443220301Shselasky uint32_t x; 444215651Sweongyo char buf[64]; 445215651Sweongyo 446220301Shselasky ptr += USBPF_HDR_LEN; 447220301Shselasky ptr_len -= USBPF_HDR_LEN; 448220301Shselasky if (ptr_len < 0) 449220301Shselasky return; 450220301Shselasky 451220301Shselasky /* make sure we don't change the source buffer */ 452220301Shselasky memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); 453220301Shselasky up = &up_temp; 454220301Shselasky 455220301Shselasky /* 456220301Shselasky * A packet from the kernel is based on little endian byte 457220301Shselasky * order. 458220301Shselasky */ 459220301Shselasky up->up_totlen = le32toh(up->up_totlen); 460215651Sweongyo up->up_busunit = le32toh(up->up_busunit); 461220301Shselasky up->up_address = le32toh(up->up_address); 462215651Sweongyo up->up_flags = le32toh(up->up_flags); 463215651Sweongyo up->up_status = le32toh(up->up_status); 464215651Sweongyo up->up_error = le32toh(up->up_error); 465215651Sweongyo up->up_interval = le32toh(up->up_interval); 466220301Shselasky up->up_frames = le32toh(up->up_frames); 467220301Shselasky up->up_packet_size = le32toh(up->up_packet_size); 468220301Shselasky up->up_packet_count = le32toh(up->up_packet_count); 469220301Shselasky up->up_endpoint = le32toh(up->up_endpoint); 470215651Sweongyo 471233037Shselasky tv.tv_sec = hdr->ts_sec; 472233037Shselasky tv.tv_usec = hdr->ts_usec; 473215651Sweongyo tm = localtime(&tv.tv_sec); 474215651Sweongyo 475215651Sweongyo len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 476220301Shselasky 477220314Sthompsa printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", 478220301Shselasky (int)len, buf, tv.tv_usec, 479220301Shselasky (int)up->up_busunit, (int)up->up_address, 480220301Shselasky (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", 481215651Sweongyo xfertype_table[up->up_xfertype], 482220301Shselasky (unsigned int)up->up_endpoint, 483220301Shselasky usb_speedstr(up->up_speed), 484220301Shselasky (int)up->up_frames, 485220301Shselasky (int)(up->up_totlen - USBPF_HDR_LEN - 486220301Shselasky (USBPF_FRAME_HDR_LEN * up->up_frames)), 487220301Shselasky (int)up->up_interval, 488220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", 489220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? 490220301Shselasky usb_errstr(up->up_error) : ""); 491215651Sweongyo 492215651Sweongyo if (verbose >= 1) { 493220301Shselasky for (x = 0; x != up->up_frames; x++) { 494220301Shselasky const struct usbpf_framehdr *uf; 495220301Shselasky uint32_t framelen; 496220301Shselasky uint32_t flags; 497220301Shselasky 498220301Shselasky uf = (const struct usbpf_framehdr *)ptr; 499220301Shselasky ptr += USBPF_FRAME_HDR_LEN; 500220301Shselasky ptr_len -= USBPF_FRAME_HDR_LEN; 501220301Shselasky if (ptr_len < 0) 502220301Shselasky return; 503220301Shselasky 504220301Shselasky framelen = le32toh(uf->length); 505220301Shselasky flags = le32toh(uf->flags); 506220301Shselasky 507220301Shselasky printf(" frame[%u] %s %d bytes\n", 508220301Shselasky (unsigned int)x, 509220301Shselasky (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", 510220301Shselasky (int)framelen); 511220301Shselasky 512220301Shselasky if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { 513220301Shselasky 514220301Shselasky int tot_frame_len; 515220301Shselasky 516220301Shselasky tot_frame_len = USBPF_FRAME_ALIGN(framelen); 517220301Shselasky 518220301Shselasky ptr_len -= tot_frame_len; 519220301Shselasky 520220301Shselasky if (tot_frame_len < 0 || 521220301Shselasky (int)framelen < 0 || (int)ptr_len < 0) 522220301Shselasky break; 523220301Shselasky 524220301Shselasky hexdump(ptr, framelen); 525220301Shselasky 526220301Shselasky ptr += tot_frame_len; 527220301Shselasky } 528215651Sweongyo } 529215651Sweongyo } 530220301Shselasky if (verbose >= 2) 531215651Sweongyo print_flags(up->up_flags); 532220301Shselasky if (verbose >= 3) 533215651Sweongyo print_status(up->up_status); 534215651Sweongyo} 535215651Sweongyo 536215651Sweongyostatic void 537220301Shselaskyprint_packets(uint8_t *data, const int datalen) 538215651Sweongyo{ 539233037Shselasky struct header_32 temp; 540220301Shselasky uint8_t *ptr; 541220301Shselasky uint8_t *next; 542215651Sweongyo 543215651Sweongyo for (ptr = data; ptr < (data + datalen); ptr = next) { 544215651Sweongyo 545233037Shselasky /* automatically figure out endian and size of header */ 546233037Shselasky 547233037Shselasky if (r_arg != NULL) { 548233037Shselasky 549233037Shselasky const struct header_32 *hdr32; 550233037Shselasky const struct header_64 *hdr64; 551233037Shselasky 552233037Shselasky hdr32 = (const struct header_32 *)ptr; 553233037Shselasky hdr64 = (const struct header_64 *)ptr; 554233037Shselasky 555233037Shselasky temp.hdrlen = le16toh(hdr32->hdrlen); 556233037Shselasky temp.dummy = le16toh(hdr32->dummy); 557233037Shselasky 558233037Shselasky if ((temp.hdrlen != 18 && temp.hdrlen != 20) || (temp.dummy != 0)) { 559233037Shselasky temp.hdrlen = be16toh(hdr32->hdrlen); 560233037Shselasky temp.dummy = be16toh(hdr32->dummy); 561233037Shselasky 562233037Shselasky if ((temp.hdrlen != 18 && temp.hdrlen != 20) || (temp.dummy != 0)) { 563233037Shselasky temp.hdrlen = le16toh(hdr64->hdrlen); 564233037Shselasky temp.dummy = le16toh(hdr64->dummy); 565233037Shselasky 566233037Shselasky if ((temp.hdrlen != 28 && temp.hdrlen != 32) || (temp.dummy != 0)) { 567233037Shselasky temp.hdrlen = be16toh(hdr64->hdrlen); 568233037Shselasky temp.dummy = be16toh(hdr64->dummy); 569233037Shselasky 570233037Shselasky if ((temp.hdrlen != 28 && temp.hdrlen != 32) || (temp.dummy != 0)) { 571233037Shselasky err(EXIT_FAILURE, "Invalid header detected"); 572233037Shselasky next = NULL; 573233037Shselasky } else { 574233037Shselasky temp.ts_sec = be64toh(hdr64->ts_sec); 575233037Shselasky temp.ts_usec = be64toh(hdr64->ts_usec); 576233037Shselasky temp.caplen = be32toh(hdr64->caplen); 577233037Shselasky temp.datalen = be32toh(hdr64->datalen); 578233037Shselasky next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 8); 579233037Shselasky } 580233037Shselasky } else { 581233037Shselasky temp.ts_sec = le64toh(hdr64->ts_sec); 582233037Shselasky temp.ts_usec = le64toh(hdr64->ts_usec); 583233037Shselasky temp.caplen = le32toh(hdr64->caplen); 584233037Shselasky temp.datalen = le32toh(hdr64->datalen); 585233037Shselasky next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 8); 586233037Shselasky } 587233037Shselasky } else { 588233037Shselasky temp.ts_sec = be32toh(hdr32->ts_sec); 589233037Shselasky temp.ts_usec = be32toh(hdr32->ts_usec); 590233037Shselasky temp.caplen = be32toh(hdr32->caplen); 591233037Shselasky temp.datalen = be32toh(hdr32->datalen); 592233037Shselasky next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 4); 593233037Shselasky } 594233037Shselasky } else { 595233037Shselasky temp.ts_sec = le32toh(hdr32->ts_sec); 596233037Shselasky temp.ts_usec = le32toh(hdr32->ts_usec); 597233037Shselasky temp.caplen = le32toh(hdr32->caplen); 598233037Shselasky temp.datalen = le32toh(hdr32->datalen); 599233037Shselasky next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, 4); 600233037Shselasky } 601233037Shselasky } else { 602233037Shselasky const struct bpf_hdr *hdr; 603233037Shselasky 604233037Shselasky hdr = (const struct bpf_hdr *)ptr; 605233037Shselasky temp.ts_sec = hdr->bh_tstamp.tv_sec; 606233037Shselasky temp.ts_usec = hdr->bh_tstamp.tv_usec; 607233037Shselasky temp.caplen = hdr->bh_caplen; 608233037Shselasky temp.datalen = hdr->bh_datalen; 609233037Shselasky temp.hdrlen = hdr->bh_hdrlen; 610233037Shselasky next = ptr + BPF_WORDALIGN(temp.hdrlen + temp.caplen); 611233037Shselasky } 612233037Shselasky 613233037Shselasky if (next <= ptr) 614233037Shselasky err(EXIT_FAILURE, "Invalid header length"); 615233037Shselasky 616220301Shselasky if (w_arg == NULL) { 617233037Shselasky print_apacket(&temp, ptr + 618233037Shselasky temp.hdrlen, temp.caplen); 619220301Shselasky } 620215651Sweongyo pkt_captured++; 621215651Sweongyo } 622215651Sweongyo} 623215651Sweongyo 624215651Sweongyostatic void 625220301Shselaskywrite_packets(struct usbcap *p, const uint8_t *data, const int datalen) 626215651Sweongyo{ 627220301Shselasky int len = htole32(datalen); 628220301Shselasky int ret; 629215651Sweongyo 630215651Sweongyo ret = write(p->wfd, &len, sizeof(int)); 631221604Shselasky if (ret != sizeof(int)) { 632221604Shselasky err(EXIT_FAILURE, "Could not write length " 633221604Shselasky "field of USB data payload"); 634221604Shselasky } 635215651Sweongyo ret = write(p->wfd, data, datalen); 636221604Shselasky if (ret != datalen) { 637221604Shselasky err(EXIT_FAILURE, "Could not write " 638221604Shselasky "complete USB data payload"); 639221604Shselasky } 640215651Sweongyo} 641215651Sweongyo 642215651Sweongyostatic void 643215651Sweongyoread_file(struct usbcap *p) 644215651Sweongyo{ 645220301Shselasky int datalen; 646220301Shselasky int ret; 647220301Shselasky uint8_t *data; 648215651Sweongyo 649215651Sweongyo while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 650215651Sweongyo datalen = le32toh(datalen); 651215651Sweongyo data = malloc(datalen); 652221604Shselasky if (data == NULL) 653221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 654215651Sweongyo ret = read(p->rfd, data, datalen); 655221604Shselasky if (ret != datalen) { 656221604Shselasky err(EXIT_FAILURE, "Could not read complete " 657221604Shselasky "USB data payload"); 658221604Shselasky } 659215651Sweongyo print_packets(data, datalen); 660215651Sweongyo free(data); 661215651Sweongyo } 662215651Sweongyo} 663215651Sweongyo 664215651Sweongyostatic void 665215651Sweongyodo_loop(struct usbcap *p) 666215651Sweongyo{ 667215651Sweongyo int cc; 668215651Sweongyo 669215651Sweongyo while (doexit == 0) { 670220301Shselasky cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); 671215651Sweongyo if (cc < 0) { 672215651Sweongyo switch (errno) { 673215651Sweongyo case EINTR: 674215651Sweongyo break; 675215651Sweongyo default: 676215651Sweongyo fprintf(stderr, "read: %s\n", strerror(errno)); 677215651Sweongyo return; 678215651Sweongyo } 679215651Sweongyo continue; 680215651Sweongyo } 681215651Sweongyo if (cc == 0) 682215651Sweongyo continue; 683215651Sweongyo if (w_arg != NULL) 684215651Sweongyo write_packets(p, p->buffer, cc); 685215651Sweongyo print_packets(p->buffer, cc); 686215651Sweongyo } 687215651Sweongyo} 688215651Sweongyo 689215651Sweongyostatic void 690215651Sweongyoinit_rfile(struct usbcap *p) 691215651Sweongyo{ 692215651Sweongyo struct usbcap_filehdr uf; 693215651Sweongyo int ret; 694215651Sweongyo 695215651Sweongyo p->rfd = open(r_arg, O_RDONLY); 696215651Sweongyo if (p->rfd < 0) { 697221604Shselasky err(EXIT_FAILURE, "Could not open " 698221604Shselasky "'%s' for read", r_arg); 699215651Sweongyo } 700215651Sweongyo ret = read(p->rfd, &uf, sizeof(uf)); 701221604Shselasky if (ret != sizeof(uf)) { 702221604Shselasky err(EXIT_FAILURE, "Could not read USB capture " 703221604Shselasky "file header"); 704221604Shselasky } 705221604Shselasky if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) { 706221604Shselasky errx(EX_SOFTWARE, "Invalid magic field(0x%08x) " 707221604Shselasky "in USB capture file header.", 708221604Shselasky (unsigned int)le32toh(uf.magic)); 709221604Shselasky } 710221604Shselasky if (uf.major != 0) { 711221604Shselasky errx(EX_SOFTWARE, "Invalid major version(%d) " 712221604Shselasky "field in USB capture file header.", (int)uf.major); 713221604Shselasky } 714221604Shselasky if (uf.minor != 2) { 715221604Shselasky errx(EX_SOFTWARE, "Invalid minor version(%d) " 716221604Shselasky "field in USB capture file header.", (int)uf.minor); 717221604Shselasky } 718215651Sweongyo} 719215651Sweongyo 720215651Sweongyostatic void 721215651Sweongyoinit_wfile(struct usbcap *p) 722215651Sweongyo{ 723215651Sweongyo struct usbcap_filehdr uf; 724215651Sweongyo int ret; 725215651Sweongyo 726215651Sweongyo p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 727215651Sweongyo if (p->wfd < 0) { 728221604Shselasky err(EXIT_FAILURE, "Could not open " 729221604Shselasky "'%s' for write", r_arg); 730215651Sweongyo } 731221604Shselasky memset(&uf, 0, sizeof(uf)); 732215651Sweongyo uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 733215651Sweongyo uf.major = 0; 734220301Shselasky uf.minor = 2; 735215651Sweongyo ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 736221604Shselasky if (ret != sizeof(uf)) { 737221604Shselasky err(EXIT_FAILURE, "Could not write " 738221604Shselasky "USB capture header"); 739221604Shselasky } 740215651Sweongyo} 741215651Sweongyo 742215651Sweongyostatic void 743215651Sweongyousage(void) 744215651Sweongyo{ 745215651Sweongyo 746215651Sweongyo#define FMT " %-14s %s\n" 747215651Sweongyo fprintf(stderr, "usage: usbdump [options]\n"); 748221604Shselasky fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface"); 749231835Shselasky fprintf(stderr, FMT, "-f <unit[.endpoint]>", "Specify a device and endpoint filter"); 750221604Shselasky fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file"); 751221604Shselasky fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet"); 752221604Shselasky fprintf(stderr, FMT, "-v", "Increase the verbose level"); 753221604Shselasky fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file"); 754215651Sweongyo#undef FMT 755221604Shselasky exit(EX_USAGE); 756215651Sweongyo} 757215651Sweongyo 758215651Sweongyoint 759215651Sweongyomain(int argc, char *argv[]) 760215651Sweongyo{ 761215651Sweongyo struct timeval tv; 762215803Sweongyo struct bpf_program total_prog; 763215803Sweongyo struct bpf_stat us; 764215803Sweongyo struct bpf_version bv; 765215651Sweongyo struct usbcap uc, *p = &uc; 766215803Sweongyo struct ifreq ifr; 767215651Sweongyo long snapshot = 192; 768220301Shselasky uint32_t v; 769231835Shselasky int fd; 770231835Shselasky int o; 771231835Shselasky int filt_unit; 772231835Shselasky int filt_ep; 773215651Sweongyo const char *optstring; 774231835Shselasky char *pp; 775215651Sweongyo 776221604Shselasky memset(&uc, 0, sizeof(struct usbcap)); 777215651Sweongyo 778231835Shselasky optstring = "i:r:s:vw:f:"; 779215651Sweongyo while ((o = getopt(argc, argv, optstring)) != -1) { 780215651Sweongyo switch (o) { 781215651Sweongyo case 'i': 782215651Sweongyo i_arg = optarg; 783215651Sweongyo break; 784215651Sweongyo case 'r': 785215651Sweongyo r_arg = optarg; 786215651Sweongyo init_rfile(p); 787215651Sweongyo break; 788215651Sweongyo case 's': 789231835Shselasky snapshot = strtol(optarg, &pp, 10); 790215651Sweongyo errno = 0; 791231835Shselasky if (pp != NULL && *pp != 0) 792231835Shselasky usage(); 793215651Sweongyo if (snapshot == 0 && errno == EINVAL) 794215651Sweongyo usage(); 795215651Sweongyo /* snapeshot == 0 is special */ 796215651Sweongyo if (snapshot == 0) 797215651Sweongyo snapshot = -1; 798215651Sweongyo break; 799215651Sweongyo case 'v': 800215651Sweongyo verbose++; 801215651Sweongyo break; 802215651Sweongyo case 'w': 803215651Sweongyo w_arg = optarg; 804215651Sweongyo init_wfile(p); 805215651Sweongyo break; 806231835Shselasky case 'f': 807231835Shselasky filt_unit = strtol(optarg, &pp, 10); 808231835Shselasky filt_ep = -1; 809231835Shselasky if (pp != NULL) { 810231835Shselasky if (*pp == '.') { 811231835Shselasky filt_ep = strtol(pp + 1, &pp, 10); 812231835Shselasky if (pp != NULL && *pp != 0) 813231835Shselasky usage(); 814231835Shselasky } else if (*pp != 0) { 815231835Shselasky usage(); 816231835Shselasky } 817231835Shselasky } 818231835Shselasky add_filter(filt_unit, filt_ep); 819231835Shselasky break; 820215651Sweongyo default: 821215651Sweongyo usage(); 822215651Sweongyo /* NOTREACHED */ 823215651Sweongyo } 824215651Sweongyo } 825215651Sweongyo 826215651Sweongyo if (r_arg != NULL) { 827215651Sweongyo read_file(p); 828215651Sweongyo exit(EXIT_SUCCESS); 829215651Sweongyo } 830215651Sweongyo 831215803Sweongyo p->fd = fd = open("/dev/bpf", O_RDONLY); 832221604Shselasky if (p->fd < 0) 833221604Shselasky err(EXIT_FAILURE, "Could not open BPF device"); 834215651Sweongyo 835221604Shselasky if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) 836221604Shselasky err(EXIT_FAILURE, "BIOCVERSION ioctl failed"); 837221604Shselasky 838215803Sweongyo if (bv.bv_major != BPF_MAJOR_VERSION || 839221604Shselasky bv.bv_minor < BPF_MINOR_VERSION) 840221604Shselasky errx(EXIT_FAILURE, "Kernel BPF filter out of date"); 841215651Sweongyo 842220301Shselasky /* USB transfers can be greater than 64KByte */ 843220301Shselasky v = 1U << 16; 844220301Shselasky 845220301Shselasky /* clear ifr structure */ 846220301Shselasky memset(&ifr, 0, sizeof(ifr)); 847220301Shselasky 848220301Shselasky for ( ; v >= USBPF_HDR_LEN; v >>= 1) { 849215803Sweongyo (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 850215803Sweongyo (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 851215803Sweongyo if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 852215651Sweongyo break; 853215651Sweongyo } 854221604Shselasky if (v == 0) 855221604Shselasky errx(EXIT_FAILURE, "No buffer size worked."); 856215651Sweongyo 857221604Shselasky if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) 858221604Shselasky err(EXIT_FAILURE, "BIOCGBLEN ioctl failed"); 859215651Sweongyo 860215651Sweongyo p->bufsize = v; 861220301Shselasky p->buffer = (uint8_t *)malloc(p->bufsize); 862221604Shselasky if (p->buffer == NULL) 863221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 864215651Sweongyo 865231835Shselasky make_filter(&total_prog, snapshot); 866215651Sweongyo 867221604Shselasky if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) 868221604Shselasky err(EXIT_FAILURE, "BIOCSETF ioctl failed"); 869215651Sweongyo 870231835Shselasky free_filter(&total_prog); 871231835Shselasky 872215651Sweongyo /* 1 second read timeout */ 873215651Sweongyo tv.tv_sec = 1; 874215651Sweongyo tv.tv_usec = 0; 875221604Shselasky if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) 876221604Shselasky err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed"); 877215651Sweongyo 878215651Sweongyo (void)signal(SIGINT, handle_sigint); 879215651Sweongyo 880215651Sweongyo do_loop(p); 881215651Sweongyo 882221604Shselasky if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) 883221604Shselasky err(EXIT_FAILURE, "BIOCGSTATS ioctl failed"); 884215651Sweongyo 885215651Sweongyo /* XXX what's difference between pkt_captured and us.us_recv? */ 886215651Sweongyo printf("\n"); 887215651Sweongyo printf("%d packets captured\n", pkt_captured); 888215803Sweongyo printf("%d packets received by filter\n", us.bs_recv); 889215803Sweongyo printf("%d packets dropped by kernel\n", us.bs_drop); 890215651Sweongyo 891215651Sweongyo if (p->fd > 0) 892215651Sweongyo close(p->fd); 893215651Sweongyo if (p->rfd > 0) 894215651Sweongyo close(p->rfd); 895215651Sweongyo if (p->wfd > 0) 896215651Sweongyo close(p->wfd); 897215651Sweongyo 898215651Sweongyo return (EXIT_SUCCESS); 899215651Sweongyo} 900