usbdump.c revision 221604
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 221604 2011-05-07 16:28:23Z 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> 38215803Sweongyo#include <net/if.h> 39215803Sweongyo#include <net/bpf.h> 40215651Sweongyo#include <dev/usb/usb.h> 41215651Sweongyo#include <dev/usb/usb_pf.h> 42215651Sweongyo#include <dev/usb/usbdi.h> 43215651Sweongyo#include <errno.h> 44215651Sweongyo#include <fcntl.h> 45215651Sweongyo#include <limits.h> 46215651Sweongyo#include <stdio.h> 47215651Sweongyo#include <stdlib.h> 48215651Sweongyo#include <string.h> 49215651Sweongyo#include <time.h> 50215651Sweongyo#include <unistd.h> 51221604Shselasky#include <sysexits.h> 52221604Shselasky#include <err.h> 53215651Sweongyo 54215651Sweongyostruct usbcap { 55215651Sweongyo int fd; /* fd for /dev/usbpf */ 56220301Shselasky uint32_t bufsize; 57220301Shselasky uint8_t *buffer; 58215651Sweongyo 59215651Sweongyo /* for -w option */ 60215651Sweongyo int wfd; 61215651Sweongyo /* for -r option */ 62215651Sweongyo int rfd; 63215651Sweongyo}; 64215651Sweongyo 65215651Sweongyostruct usbcap_filehdr { 66220301Shselasky uint32_t magic; 67215651Sweongyo#define USBCAP_FILEHDR_MAGIC 0x9a90000e 68220301Shselasky uint8_t major; 69220301Shselasky uint8_t minor; 70220301Shselasky uint8_t reserved[26]; 71215651Sweongyo} __packed; 72215651Sweongyo 73215651Sweongyostatic int doexit = 0; 74215651Sweongyostatic int pkt_captured = 0; 75215651Sweongyostatic int verbose = 0; 76218010Shselaskystatic const char *i_arg = "usbus0"; 77215651Sweongyostatic const char *r_arg = NULL; 78215651Sweongyostatic const char *w_arg = NULL; 79215651Sweongyostatic const char *errstr_table[USB_ERR_MAX] = { 80220301Shselasky [USB_ERR_NORMAL_COMPLETION] = "0", 81215651Sweongyo [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", 82215651Sweongyo [USB_ERR_NOT_STARTED] = "NOT_STARTED", 83215651Sweongyo [USB_ERR_INVAL] = "INVAL", 84215651Sweongyo [USB_ERR_NOMEM] = "NOMEM", 85215651Sweongyo [USB_ERR_CANCELLED] = "CANCELLED", 86215651Sweongyo [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", 87215651Sweongyo [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", 88215651Sweongyo [USB_ERR_BAD_FLAG] = "BAD_FLAG", 89215651Sweongyo [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", 90215651Sweongyo [USB_ERR_IN_USE] = "IN_USE", 91215651Sweongyo [USB_ERR_NO_ADDR] = "NO_ADDR", 92215651Sweongyo [USB_ERR_NO_PIPE] = "NO_PIPE", 93215651Sweongyo [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", 94215651Sweongyo [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", 95215651Sweongyo [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", 96215651Sweongyo [USB_ERR_NO_POWER] = "NO_POWER", 97215651Sweongyo [USB_ERR_TOO_DEEP] = "TOO_DEEP", 98215651Sweongyo [USB_ERR_IOERROR] = "IOERROR", 99215651Sweongyo [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", 100215651Sweongyo [USB_ERR_TIMEOUT] = "TIMEOUT", 101215651Sweongyo [USB_ERR_SHORT_XFER] = "SHORT_XFER", 102215651Sweongyo [USB_ERR_STALLED] = "STALLED", 103215651Sweongyo [USB_ERR_INTERRUPTED] = "INTERRUPTED", 104215651Sweongyo [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", 105215651Sweongyo [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", 106215651Sweongyo [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", 107215651Sweongyo [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", 108215651Sweongyo [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", 109215651Sweongyo}; 110215651Sweongyo 111220301Shselaskystatic const char *xfertype_table[4] = { 112215651Sweongyo [UE_CONTROL] = "CTRL", 113215651Sweongyo [UE_ISOCHRONOUS] = "ISOC", 114215651Sweongyo [UE_BULK] = "BULK", 115215651Sweongyo [UE_INTERRUPT] = "INTR" 116215651Sweongyo}; 117215651Sweongyo 118220301Shselaskystatic const char *speed_table[USB_SPEED_MAX] = { 119220301Shselasky [USB_SPEED_FULL] = "FULL", 120220301Shselasky [USB_SPEED_HIGH] = "HIGH", 121220301Shselasky [USB_SPEED_LOW] = "LOW", 122220301Shselasky [USB_SPEED_VARIABLE] = "VARI", 123220301Shselasky [USB_SPEED_SUPER] = "SUPER", 124220301Shselasky}; 125220301Shselasky 126215651Sweongyostatic void 127215651Sweongyohandle_sigint(int sig) 128215651Sweongyo{ 129215651Sweongyo 130215651Sweongyo (void)sig; 131215651Sweongyo doexit = 1; 132215651Sweongyo} 133215651Sweongyo 134220301Shselasky#define FLAGS(x, name) \ 135220301Shselasky (((x) & USBPF_FLAG_##name) ? #name "|" : "") 136220301Shselasky 137220301Shselasky#define STATUS(x, name) \ 138220301Shselasky (((x) & USBPF_STATUS_##name) ? #name "|" : "") 139220301Shselasky 140220301Shselaskystatic const char * 141220301Shselaskyusb_errstr(uint32_t error) 142220301Shselasky{ 143220301Shselasky if (error >= USB_ERR_MAX || errstr_table[error] == NULL) 144220301Shselasky return ("UNKNOWN"); 145220301Shselasky else 146220301Shselasky return (errstr_table[error]); 147220301Shselasky} 148220301Shselasky 149220301Shselaskystatic const char * 150220301Shselaskyusb_speedstr(uint8_t speed) 151220301Shselasky{ 152220301Shselasky if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) 153220301Shselasky return ("UNKNOWN"); 154220301Shselasky else 155220301Shselasky return (speed_table[speed]); 156220301Shselasky} 157220301Shselasky 158215651Sweongyostatic void 159220301Shselaskyprint_flags(uint32_t flags) 160215651Sweongyo{ 161220301Shselasky printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", 162220301Shselasky flags, 163220301Shselasky FLAGS(flags, FORCE_SHORT_XFER), 164220301Shselasky FLAGS(flags, SHORT_XFER_OK), 165220301Shselasky FLAGS(flags, SHORT_FRAMES_OK), 166220301Shselasky FLAGS(flags, PIPE_BOF), 167220301Shselasky FLAGS(flags, PROXY_BUFFER), 168220301Shselasky FLAGS(flags, EXT_BUFFER), 169220301Shselasky FLAGS(flags, MANUAL_STATUS), 170220301Shselasky FLAGS(flags, NO_PIPE_OK), 171220301Shselasky FLAGS(flags, STALL_PIPE)); 172215651Sweongyo} 173215651Sweongyo 174215651Sweongyostatic void 175220301Shselaskyprint_status(uint32_t status) 176215651Sweongyo{ 177220301Shselasky printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", 178220301Shselasky status, 179220301Shselasky STATUS(status, OPEN), 180220301Shselasky STATUS(status, TRANSFERRING), 181220301Shselasky STATUS(status, DID_DMA_DELAY), 182220301Shselasky STATUS(status, DID_CLOSE), 183220301Shselasky STATUS(status, DRAINING), 184220301Shselasky STATUS(status, STARTED), 185220301Shselasky STATUS(status, BW_RECLAIMED), 186220301Shselasky STATUS(status, CONTROL_XFR), 187220301Shselasky STATUS(status, CONTROL_HDR), 188220301Shselasky STATUS(status, CONTROL_ACT), 189220301Shselasky STATUS(status, CONTROL_STALL), 190220301Shselasky STATUS(status, SHORT_FRAMES_OK), 191220301Shselasky STATUS(status, SHORT_XFER_OK), 192220301Shselasky STATUS(status, BDMA_ENABLE), 193220301Shselasky STATUS(status, BDMA_NO_POST_SYNC), 194220301Shselasky STATUS(status, BDMA_SETUP), 195220301Shselasky STATUS(status, ISOCHRONOUS_XFR), 196220301Shselasky STATUS(status, CURR_DMA_SET), 197220301Shselasky STATUS(status, CAN_CANCEL_IMMED), 198220301Shselasky STATUS(status, DOING_CALLBACK)); 199220301Shselasky} 200215651Sweongyo 201220301Shselasky/* 202220301Shselasky * Dump a byte into hex format. 203220301Shselasky */ 204220301Shselaskystatic void 205220301Shselaskyhexbyte(char *buf, uint8_t temp) 206220301Shselasky{ 207220301Shselasky uint8_t lo; 208220301Shselasky uint8_t hi; 209220301Shselasky 210220301Shselasky lo = temp & 0xF; 211220301Shselasky hi = temp >> 4; 212220301Shselasky 213220301Shselasky if (hi < 10) 214220301Shselasky buf[0] = '0' + hi; 215220301Shselasky else 216220301Shselasky buf[0] = 'A' + hi - 10; 217220301Shselasky 218220301Shselasky if (lo < 10) 219220301Shselasky buf[1] = '0' + lo; 220220301Shselasky else 221220301Shselasky buf[1] = 'A' + lo - 10; 222215651Sweongyo} 223215651Sweongyo 224215651Sweongyo/* 225215651Sweongyo * Display a region in traditional hexdump format. 226215651Sweongyo */ 227215651Sweongyostatic void 228220301Shselaskyhexdump(const uint8_t *region, uint32_t len) 229215651Sweongyo{ 230220301Shselasky const uint8_t *line; 231220301Shselasky char linebuf[128]; 232220301Shselasky int i; 233218010Shselasky int x; 234218010Shselasky int c; 235215651Sweongyo 236215651Sweongyo for (line = region; line < (region + len); line += 16) { 237220301Shselasky 238220301Shselasky i = 0; 239220301Shselasky 240220301Shselasky linebuf[i] = ' '; 241220301Shselasky hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); 242220301Shselasky hexbyte(linebuf + i + 3, (line - region) & 0xFF); 243220301Shselasky linebuf[i + 5] = ' '; 244220301Shselasky linebuf[i + 6] = ' '; 245220301Shselasky i += 7; 246220301Shselasky 247215651Sweongyo for (x = 0; x < 16; x++) { 248220301Shselasky if ((line + x) < (region + len)) { 249220301Shselasky hexbyte(linebuf + i, 250220301Shselasky *(const u_int8_t *)(line + x)); 251220301Shselasky } else { 252220301Shselasky linebuf[i] = '-'; 253220301Shselasky linebuf[i + 1] = '-'; 254220301Shselasky } 255220301Shselasky linebuf[i + 2] = ' '; 256220301Shselasky if (x == 7) { 257220301Shselasky linebuf[i + 3] = ' '; 258220301Shselasky i += 4; 259220301Shselasky } else { 260220301Shselasky i += 3; 261220301Shselasky } 262215651Sweongyo } 263220301Shselasky linebuf[i] = ' '; 264220301Shselasky linebuf[i + 1] = '|'; 265220301Shselasky i += 2; 266215651Sweongyo for (x = 0; x < 16; x++) { 267215651Sweongyo if ((line + x) < (region + len)) { 268215651Sweongyo c = *(const u_int8_t *)(line + x); 269215651Sweongyo /* !isprint(c) */ 270215651Sweongyo if ((c < ' ') || (c > '~')) 271215651Sweongyo c = '.'; 272220301Shselasky linebuf[i] = c; 273220301Shselasky } else { 274220301Shselasky linebuf[i] = ' '; 275220301Shselasky } 276220301Shselasky i++; 277215651Sweongyo } 278220301Shselasky linebuf[i] = '|'; 279220301Shselasky linebuf[i + 1] = 0; 280220301Shselasky i += 2; 281220301Shselasky puts(linebuf); 282215651Sweongyo } 283215651Sweongyo} 284215651Sweongyo 285215651Sweongyostatic void 286220301Shselaskyprint_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len) 287215651Sweongyo{ 288215651Sweongyo struct tm *tm; 289220301Shselasky struct usbpf_pkthdr up_temp; 290220301Shselasky struct usbpf_pkthdr *up; 291215651Sweongyo struct timeval tv; 292215651Sweongyo size_t len; 293220301Shselasky uint32_t x; 294215651Sweongyo char buf[64]; 295215651Sweongyo 296220301Shselasky ptr += USBPF_HDR_LEN; 297220301Shselasky ptr_len -= USBPF_HDR_LEN; 298220301Shselasky if (ptr_len < 0) 299220301Shselasky return; 300220301Shselasky 301220301Shselasky /* make sure we don't change the source buffer */ 302220301Shselasky memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); 303220301Shselasky up = &up_temp; 304220301Shselasky 305220301Shselasky /* 306220301Shselasky * A packet from the kernel is based on little endian byte 307220301Shselasky * order. 308220301Shselasky */ 309220301Shselasky up->up_totlen = le32toh(up->up_totlen); 310215651Sweongyo up->up_busunit = le32toh(up->up_busunit); 311220301Shselasky up->up_address = le32toh(up->up_address); 312215651Sweongyo up->up_flags = le32toh(up->up_flags); 313215651Sweongyo up->up_status = le32toh(up->up_status); 314215651Sweongyo up->up_error = le32toh(up->up_error); 315215651Sweongyo up->up_interval = le32toh(up->up_interval); 316220301Shselasky up->up_frames = le32toh(up->up_frames); 317220301Shselasky up->up_packet_size = le32toh(up->up_packet_size); 318220301Shselasky up->up_packet_count = le32toh(up->up_packet_count); 319220301Shselasky up->up_endpoint = le32toh(up->up_endpoint); 320215651Sweongyo 321215803Sweongyo tv.tv_sec = hdr->bh_tstamp.bt_sec; 322215803Sweongyo tv.tv_usec = hdr->bh_tstamp.bt_frac; 323215651Sweongyo tm = localtime(&tv.tv_sec); 324215651Sweongyo 325215651Sweongyo len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 326220301Shselasky 327220314Sthompsa printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", 328220301Shselasky (int)len, buf, tv.tv_usec, 329220301Shselasky (int)up->up_busunit, (int)up->up_address, 330220301Shselasky (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", 331215651Sweongyo xfertype_table[up->up_xfertype], 332220301Shselasky (unsigned int)up->up_endpoint, 333220301Shselasky usb_speedstr(up->up_speed), 334220301Shselasky (int)up->up_frames, 335220301Shselasky (int)(up->up_totlen - USBPF_HDR_LEN - 336220301Shselasky (USBPF_FRAME_HDR_LEN * up->up_frames)), 337220301Shselasky (int)up->up_interval, 338220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", 339220301Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? 340220301Shselasky usb_errstr(up->up_error) : ""); 341215651Sweongyo 342215651Sweongyo if (verbose >= 1) { 343220301Shselasky for (x = 0; x != up->up_frames; x++) { 344220301Shselasky const struct usbpf_framehdr *uf; 345220301Shselasky uint32_t framelen; 346220301Shselasky uint32_t flags; 347220301Shselasky 348220301Shselasky uf = (const struct usbpf_framehdr *)ptr; 349220301Shselasky ptr += USBPF_FRAME_HDR_LEN; 350220301Shselasky ptr_len -= USBPF_FRAME_HDR_LEN; 351220301Shselasky if (ptr_len < 0) 352220301Shselasky return; 353220301Shselasky 354220301Shselasky framelen = le32toh(uf->length); 355220301Shselasky flags = le32toh(uf->flags); 356220301Shselasky 357220301Shselasky printf(" frame[%u] %s %d bytes\n", 358220301Shselasky (unsigned int)x, 359220301Shselasky (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", 360220301Shselasky (int)framelen); 361220301Shselasky 362220301Shselasky if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { 363220301Shselasky 364220301Shselasky int tot_frame_len; 365220301Shselasky 366220301Shselasky tot_frame_len = USBPF_FRAME_ALIGN(framelen); 367220301Shselasky 368220301Shselasky ptr_len -= tot_frame_len; 369220301Shselasky 370220301Shselasky if (tot_frame_len < 0 || 371220301Shselasky (int)framelen < 0 || (int)ptr_len < 0) 372220301Shselasky break; 373220301Shselasky 374220301Shselasky hexdump(ptr, framelen); 375220301Shselasky 376220301Shselasky ptr += tot_frame_len; 377220301Shselasky } 378215651Sweongyo } 379215651Sweongyo } 380220301Shselasky if (verbose >= 2) 381215651Sweongyo print_flags(up->up_flags); 382220301Shselasky if (verbose >= 3) 383215651Sweongyo print_status(up->up_status); 384215651Sweongyo} 385215651Sweongyo 386215651Sweongyostatic void 387220301Shselaskyprint_packets(uint8_t *data, const int datalen) 388215651Sweongyo{ 389215803Sweongyo const struct bpf_xhdr *hdr; 390220301Shselasky uint8_t *ptr; 391220301Shselasky uint8_t *next; 392215651Sweongyo 393215651Sweongyo for (ptr = data; ptr < (data + datalen); ptr = next) { 394215803Sweongyo hdr = (const struct bpf_xhdr *)ptr; 395215803Sweongyo next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); 396215651Sweongyo 397220301Shselasky if (w_arg == NULL) { 398220301Shselasky print_apacket(hdr, ptr + 399220301Shselasky hdr->bh_hdrlen, hdr->bh_caplen); 400220301Shselasky } 401215651Sweongyo pkt_captured++; 402215651Sweongyo } 403215651Sweongyo} 404215651Sweongyo 405215651Sweongyostatic void 406220301Shselaskywrite_packets(struct usbcap *p, const uint8_t *data, const int datalen) 407215651Sweongyo{ 408220301Shselasky int len = htole32(datalen); 409220301Shselasky int ret; 410215651Sweongyo 411215651Sweongyo ret = write(p->wfd, &len, sizeof(int)); 412221604Shselasky if (ret != sizeof(int)) { 413221604Shselasky err(EXIT_FAILURE, "Could not write length " 414221604Shselasky "field of USB data payload"); 415221604Shselasky } 416215651Sweongyo ret = write(p->wfd, data, datalen); 417221604Shselasky if (ret != datalen) { 418221604Shselasky err(EXIT_FAILURE, "Could not write " 419221604Shselasky "complete USB data payload"); 420221604Shselasky } 421215651Sweongyo} 422215651Sweongyo 423215651Sweongyostatic void 424215651Sweongyoread_file(struct usbcap *p) 425215651Sweongyo{ 426220301Shselasky int datalen; 427220301Shselasky int ret; 428220301Shselasky uint8_t *data; 429215651Sweongyo 430215651Sweongyo while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 431215651Sweongyo datalen = le32toh(datalen); 432215651Sweongyo data = malloc(datalen); 433221604Shselasky if (data == NULL) 434221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 435215651Sweongyo ret = read(p->rfd, data, datalen); 436221604Shselasky if (ret != datalen) { 437221604Shselasky err(EXIT_FAILURE, "Could not read complete " 438221604Shselasky "USB data payload"); 439221604Shselasky } 440215651Sweongyo print_packets(data, datalen); 441215651Sweongyo free(data); 442215651Sweongyo } 443215651Sweongyo} 444215651Sweongyo 445215651Sweongyostatic void 446215651Sweongyodo_loop(struct usbcap *p) 447215651Sweongyo{ 448215651Sweongyo int cc; 449215651Sweongyo 450215651Sweongyo while (doexit == 0) { 451220301Shselasky cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); 452215651Sweongyo if (cc < 0) { 453215651Sweongyo switch (errno) { 454215651Sweongyo case EINTR: 455215651Sweongyo break; 456215651Sweongyo default: 457215651Sweongyo fprintf(stderr, "read: %s\n", strerror(errno)); 458215651Sweongyo return; 459215651Sweongyo } 460215651Sweongyo continue; 461215651Sweongyo } 462215651Sweongyo if (cc == 0) 463215651Sweongyo continue; 464215651Sweongyo if (w_arg != NULL) 465215651Sweongyo write_packets(p, p->buffer, cc); 466215651Sweongyo print_packets(p->buffer, cc); 467215651Sweongyo } 468215651Sweongyo} 469215651Sweongyo 470215651Sweongyostatic void 471215651Sweongyoinit_rfile(struct usbcap *p) 472215651Sweongyo{ 473215651Sweongyo struct usbcap_filehdr uf; 474215651Sweongyo int ret; 475215651Sweongyo 476215651Sweongyo p->rfd = open(r_arg, O_RDONLY); 477215651Sweongyo if (p->rfd < 0) { 478221604Shselasky err(EXIT_FAILURE, "Could not open " 479221604Shselasky "'%s' for read", r_arg); 480215651Sweongyo } 481215651Sweongyo ret = read(p->rfd, &uf, sizeof(uf)); 482221604Shselasky if (ret != sizeof(uf)) { 483221604Shselasky err(EXIT_FAILURE, "Could not read USB capture " 484221604Shselasky "file header"); 485221604Shselasky } 486221604Shselasky if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) { 487221604Shselasky errx(EX_SOFTWARE, "Invalid magic field(0x%08x) " 488221604Shselasky "in USB capture file header.", 489221604Shselasky (unsigned int)le32toh(uf.magic)); 490221604Shselasky } 491221604Shselasky if (uf.major != 0) { 492221604Shselasky errx(EX_SOFTWARE, "Invalid major version(%d) " 493221604Shselasky "field in USB capture file header.", (int)uf.major); 494221604Shselasky } 495221604Shselasky if (uf.minor != 2) { 496221604Shselasky errx(EX_SOFTWARE, "Invalid minor version(%d) " 497221604Shselasky "field in USB capture file header.", (int)uf.minor); 498221604Shselasky } 499215651Sweongyo} 500215651Sweongyo 501215651Sweongyostatic void 502215651Sweongyoinit_wfile(struct usbcap *p) 503215651Sweongyo{ 504215651Sweongyo struct usbcap_filehdr uf; 505215651Sweongyo int ret; 506215651Sweongyo 507215651Sweongyo p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 508215651Sweongyo if (p->wfd < 0) { 509221604Shselasky err(EXIT_FAILURE, "Could not open " 510221604Shselasky "'%s' for write", r_arg); 511215651Sweongyo } 512221604Shselasky memset(&uf, 0, sizeof(uf)); 513215651Sweongyo uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 514215651Sweongyo uf.major = 0; 515220301Shselasky uf.minor = 2; 516215651Sweongyo ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 517221604Shselasky if (ret != sizeof(uf)) { 518221604Shselasky err(EXIT_FAILURE, "Could not write " 519221604Shselasky "USB capture header"); 520221604Shselasky } 521215651Sweongyo} 522215651Sweongyo 523215651Sweongyostatic void 524215651Sweongyousage(void) 525215651Sweongyo{ 526215651Sweongyo 527215651Sweongyo#define FMT " %-14s %s\n" 528215651Sweongyo fprintf(stderr, "usage: usbdump [options]\n"); 529221604Shselasky fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface"); 530221604Shselasky fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file"); 531221604Shselasky fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet"); 532221604Shselasky fprintf(stderr, FMT, "-v", "Increase the verbose level"); 533221604Shselasky fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file"); 534215651Sweongyo#undef FMT 535221604Shselasky exit(EX_USAGE); 536215651Sweongyo} 537215651Sweongyo 538215651Sweongyoint 539215651Sweongyomain(int argc, char *argv[]) 540215651Sweongyo{ 541215651Sweongyo struct timeval tv; 542215803Sweongyo struct bpf_insn total_insn; 543215803Sweongyo struct bpf_program total_prog; 544215803Sweongyo struct bpf_stat us; 545215803Sweongyo struct bpf_version bv; 546215651Sweongyo struct usbcap uc, *p = &uc; 547215803Sweongyo struct ifreq ifr; 548215651Sweongyo long snapshot = 192; 549220301Shselasky uint32_t v; 550215651Sweongyo int fd, o; 551215651Sweongyo const char *optstring; 552215651Sweongyo 553221604Shselasky memset(&uc, 0, sizeof(struct usbcap)); 554215651Sweongyo 555215651Sweongyo optstring = "i:r:s:vw:"; 556215651Sweongyo while ((o = getopt(argc, argv, optstring)) != -1) { 557215651Sweongyo switch (o) { 558215651Sweongyo case 'i': 559215651Sweongyo i_arg = optarg; 560215651Sweongyo break; 561215651Sweongyo case 'r': 562215651Sweongyo r_arg = optarg; 563215651Sweongyo init_rfile(p); 564215651Sweongyo break; 565215651Sweongyo case 's': 566215651Sweongyo snapshot = strtol(optarg, NULL, 10); 567215651Sweongyo errno = 0; 568215651Sweongyo if (snapshot == 0 && errno == EINVAL) 569215651Sweongyo usage(); 570215651Sweongyo /* snapeshot == 0 is special */ 571215651Sweongyo if (snapshot == 0) 572215651Sweongyo snapshot = -1; 573215651Sweongyo break; 574215651Sweongyo case 'v': 575215651Sweongyo verbose++; 576215651Sweongyo break; 577215651Sweongyo case 'w': 578215651Sweongyo w_arg = optarg; 579215651Sweongyo init_wfile(p); 580215651Sweongyo break; 581215651Sweongyo default: 582215651Sweongyo usage(); 583215651Sweongyo /* NOTREACHED */ 584215651Sweongyo } 585215651Sweongyo } 586215651Sweongyo 587215651Sweongyo if (r_arg != NULL) { 588215651Sweongyo read_file(p); 589215651Sweongyo exit(EXIT_SUCCESS); 590215651Sweongyo } 591215651Sweongyo 592215803Sweongyo p->fd = fd = open("/dev/bpf", O_RDONLY); 593221604Shselasky if (p->fd < 0) 594221604Shselasky err(EXIT_FAILURE, "Could not open BPF device"); 595215651Sweongyo 596221604Shselasky if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) 597221604Shselasky err(EXIT_FAILURE, "BIOCVERSION ioctl failed"); 598221604Shselasky 599215803Sweongyo if (bv.bv_major != BPF_MAJOR_VERSION || 600221604Shselasky bv.bv_minor < BPF_MINOR_VERSION) 601221604Shselasky errx(EXIT_FAILURE, "Kernel BPF filter out of date"); 602215651Sweongyo 603220301Shselasky /* USB transfers can be greater than 64KByte */ 604220301Shselasky v = 1U << 16; 605220301Shselasky 606220301Shselasky /* clear ifr structure */ 607220301Shselasky memset(&ifr, 0, sizeof(ifr)); 608220301Shselasky 609220301Shselasky for ( ; v >= USBPF_HDR_LEN; v >>= 1) { 610215803Sweongyo (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 611215803Sweongyo (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 612215803Sweongyo if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 613215651Sweongyo break; 614215651Sweongyo } 615221604Shselasky if (v == 0) 616221604Shselasky errx(EXIT_FAILURE, "No buffer size worked."); 617215651Sweongyo 618221604Shselasky if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) 619221604Shselasky err(EXIT_FAILURE, "BIOCGBLEN ioctl failed"); 620215651Sweongyo 621215651Sweongyo p->bufsize = v; 622220301Shselasky p->buffer = (uint8_t *)malloc(p->bufsize); 623221604Shselasky if (p->buffer == NULL) 624221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 625215651Sweongyo 626215651Sweongyo /* XXX no read filter rules yet so at this moment accept everything */ 627215803Sweongyo total_insn.code = (u_short)(BPF_RET | BPF_K); 628215651Sweongyo total_insn.jt = 0; 629215651Sweongyo total_insn.jf = 0; 630215651Sweongyo total_insn.k = snapshot; 631215651Sweongyo 632215803Sweongyo total_prog.bf_len = 1; 633215803Sweongyo total_prog.bf_insns = &total_insn; 634221604Shselasky if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) 635221604Shselasky err(EXIT_FAILURE, "BIOCSETF ioctl failed"); 636215651Sweongyo 637215651Sweongyo /* 1 second read timeout */ 638215651Sweongyo tv.tv_sec = 1; 639215651Sweongyo tv.tv_usec = 0; 640221604Shselasky if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) 641221604Shselasky err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed"); 642215651Sweongyo 643215651Sweongyo (void)signal(SIGINT, handle_sigint); 644215651Sweongyo 645215651Sweongyo do_loop(p); 646215651Sweongyo 647221604Shselasky if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) 648221604Shselasky err(EXIT_FAILURE, "BIOCGSTATS ioctl failed"); 649215651Sweongyo 650215651Sweongyo /* XXX what's difference between pkt_captured and us.us_recv? */ 651215651Sweongyo printf("\n"); 652215651Sweongyo printf("%d packets captured\n", pkt_captured); 653215803Sweongyo printf("%d packets received by filter\n", us.bs_recv); 654215803Sweongyo printf("%d packets dropped by kernel\n", us.bs_drop); 655215651Sweongyo 656215651Sweongyo if (p->fd > 0) 657215651Sweongyo close(p->fd); 658215651Sweongyo if (p->rfd > 0) 659215651Sweongyo close(p->rfd); 660215651Sweongyo if (p->wfd > 0) 661215651Sweongyo close(p->wfd); 662215651Sweongyo 663215651Sweongyo return (EXIT_SUCCESS); 664215651Sweongyo} 665