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$ 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> 37261222Shselasky#include <sys/sysctl.h> 38215651Sweongyo#include <sys/utsname.h> 39232035Shselasky#include <sys/queue.h> 40215803Sweongyo#include <net/if.h> 41215803Sweongyo#include <net/bpf.h> 42215651Sweongyo#include <dev/usb/usb.h> 43215651Sweongyo#include <dev/usb/usb_pf.h> 44215651Sweongyo#include <dev/usb/usbdi.h> 45215651Sweongyo#include <errno.h> 46215651Sweongyo#include <fcntl.h> 47215651Sweongyo#include <limits.h> 48215651Sweongyo#include <stdio.h> 49215651Sweongyo#include <stdlib.h> 50232035Shselasky#include <stdint.h> 51215651Sweongyo#include <string.h> 52215651Sweongyo#include <time.h> 53215651Sweongyo#include <unistd.h> 54221604Shselasky#include <sysexits.h> 55221604Shselasky#include <err.h> 56215651Sweongyo 57232035Shselasky#define BPF_STORE_JUMP(x,_c,_k,_jt,_jf) do { \ 58232035Shselasky (x).code = (_c); \ 59232035Shselasky (x).k = (_k); \ 60232035Shselasky (x).jt = (_jt); \ 61232035Shselasky (x).jf = (_jf); \ 62232035Shselasky} while (0) 63232035Shselasky 64232035Shselasky#define BPF_STORE_STMT(x,_c,_k) do { \ 65232035Shselasky (x).code = (_c); \ 66232035Shselasky (x).k = (_k); \ 67232035Shselasky (x).jt = 0; \ 68232035Shselasky (x).jf = 0; \ 69232035Shselasky} while (0) 70232035Shselasky 71232035Shselaskystruct usb_filt { 72232035Shselasky STAILQ_ENTRY(usb_filt) entry; 73232035Shselasky int unit; 74232035Shselasky int endpoint; 75232035Shselasky}; 76232035Shselasky 77215651Sweongyostruct usbcap { 78215651Sweongyo int fd; /* fd for /dev/usbpf */ 79220301Shselasky uint32_t bufsize; 80220301Shselasky uint8_t *buffer; 81215651Sweongyo 82215651Sweongyo /* for -w option */ 83215651Sweongyo int wfd; 84215651Sweongyo /* for -r option */ 85215651Sweongyo int rfd; 86235003Shselasky /* for -b option */ 87235003Shselasky int bfd; 88215651Sweongyo}; 89215651Sweongyo 90215651Sweongyostruct usbcap_filehdr { 91220301Shselasky uint32_t magic; 92215651Sweongyo#define USBCAP_FILEHDR_MAGIC 0x9a90000e 93220301Shselasky uint8_t major; 94220301Shselasky uint8_t minor; 95220301Shselasky uint8_t reserved[26]; 96215651Sweongyo} __packed; 97215651Sweongyo 98233315Shselasky#define HEADER_ALIGN(x,a) (((x) + (a) - 1) & ~((a) - 1)) 99233315Shselasky 100233315Shselaskystruct header_32 { 101233315Shselasky /* capture timestamp */ 102233315Shselasky uint32_t ts_sec; 103233315Shselasky uint32_t ts_usec; 104233315Shselasky /* data length and alignment information */ 105233315Shselasky uint32_t caplen; 106233315Shselasky uint32_t datalen; 107233315Shselasky uint8_t hdrlen; 108233315Shselasky uint8_t align; 109233315Shselasky} __packed; 110233315Shselasky 111215651Sweongyostatic int doexit = 0; 112215651Sweongyostatic int pkt_captured = 0; 113215651Sweongyostatic int verbose = 0; 114233315Shselaskystatic int uf_minor; 115218010Shselaskystatic const char *i_arg = "usbus0"; 116215651Sweongyostatic const char *r_arg = NULL; 117215651Sweongyostatic const char *w_arg = NULL; 118235003Shselaskystatic const char *b_arg = NULL; 119235003Shselaskystatic struct usbcap uc; 120215651Sweongyostatic const char *errstr_table[USB_ERR_MAX] = { 121220301Shselasky [USB_ERR_NORMAL_COMPLETION] = "0", 122215651Sweongyo [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", 123215651Sweongyo [USB_ERR_NOT_STARTED] = "NOT_STARTED", 124215651Sweongyo [USB_ERR_INVAL] = "INVAL", 125215651Sweongyo [USB_ERR_NOMEM] = "NOMEM", 126215651Sweongyo [USB_ERR_CANCELLED] = "CANCELLED", 127215651Sweongyo [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", 128215651Sweongyo [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", 129215651Sweongyo [USB_ERR_BAD_FLAG] = "BAD_FLAG", 130215651Sweongyo [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", 131215651Sweongyo [USB_ERR_IN_USE] = "IN_USE", 132215651Sweongyo [USB_ERR_NO_ADDR] = "NO_ADDR", 133215651Sweongyo [USB_ERR_NO_PIPE] = "NO_PIPE", 134215651Sweongyo [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", 135215651Sweongyo [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", 136215651Sweongyo [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", 137215651Sweongyo [USB_ERR_NO_POWER] = "NO_POWER", 138215651Sweongyo [USB_ERR_TOO_DEEP] = "TOO_DEEP", 139215651Sweongyo [USB_ERR_IOERROR] = "IOERROR", 140215651Sweongyo [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", 141215651Sweongyo [USB_ERR_TIMEOUT] = "TIMEOUT", 142215651Sweongyo [USB_ERR_SHORT_XFER] = "SHORT_XFER", 143215651Sweongyo [USB_ERR_STALLED] = "STALLED", 144215651Sweongyo [USB_ERR_INTERRUPTED] = "INTERRUPTED", 145215651Sweongyo [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", 146215651Sweongyo [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", 147215651Sweongyo [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", 148215651Sweongyo [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", 149215651Sweongyo [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", 150215651Sweongyo}; 151215651Sweongyo 152220301Shselaskystatic const char *xfertype_table[4] = { 153215651Sweongyo [UE_CONTROL] = "CTRL", 154215651Sweongyo [UE_ISOCHRONOUS] = "ISOC", 155215651Sweongyo [UE_BULK] = "BULK", 156215651Sweongyo [UE_INTERRUPT] = "INTR" 157215651Sweongyo}; 158215651Sweongyo 159220301Shselaskystatic const char *speed_table[USB_SPEED_MAX] = { 160220301Shselasky [USB_SPEED_FULL] = "FULL", 161220301Shselasky [USB_SPEED_HIGH] = "HIGH", 162220301Shselasky [USB_SPEED_LOW] = "LOW", 163220301Shselasky [USB_SPEED_VARIABLE] = "VARI", 164220301Shselasky [USB_SPEED_SUPER] = "SUPER", 165220301Shselasky}; 166220301Shselasky 167232035Shselaskystatic STAILQ_HEAD(,usb_filt) usb_filt_head = 168232035Shselasky STAILQ_HEAD_INITIALIZER(usb_filt_head); 169232035Shselasky 170215651Sweongyostatic void 171232035Shselaskyadd_filter(int usb_filt_unit, int usb_filt_ep) 172232035Shselasky{ 173232035Shselasky struct usb_filt *puf; 174232035Shselasky 175232035Shselasky puf = malloc(sizeof(struct usb_filt)); 176232035Shselasky if (puf == NULL) 177232035Shselasky errx(EX_SOFTWARE, "Out of memory."); 178232035Shselasky 179232035Shselasky puf->unit = usb_filt_unit; 180232035Shselasky puf->endpoint = usb_filt_ep; 181232035Shselasky 182232035Shselasky STAILQ_INSERT_TAIL(&usb_filt_head, puf, entry); 183232035Shselasky} 184232035Shselasky 185232035Shselaskystatic void 186232035Shselaskymake_filter(struct bpf_program *pprog, int snapshot) 187232035Shselasky{ 188232035Shselasky struct usb_filt *puf; 189232035Shselasky struct bpf_insn *dynamic_insn; 190232035Shselasky int len; 191232035Shselasky 192232035Shselasky len = 0; 193232035Shselasky 194232035Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) 195232035Shselasky len++; 196232035Shselasky 197232035Shselasky dynamic_insn = malloc(((len * 5) + 1) * sizeof(struct bpf_insn)); 198232035Shselasky 199232035Shselasky if (dynamic_insn == NULL) 200232035Shselasky errx(EX_SOFTWARE, "Out of memory."); 201232035Shselasky 202232035Shselasky len++; 203232035Shselasky 204232035Shselasky if (len == 1) { 205232035Shselasky /* accept all packets */ 206232035Shselasky 207232035Shselasky BPF_STORE_STMT(dynamic_insn[0], BPF_RET | BPF_K, snapshot); 208232035Shselasky 209232035Shselasky goto done; 210232035Shselasky } 211232035Shselasky 212232035Shselasky len = 0; 213232035Shselasky 214232035Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) { 215232035Shselasky const int addr_off = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_address; 216232035Shselasky const int addr_ep = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_endpoint; 217232035Shselasky 218232035Shselasky if (puf->unit != -1) { 219232035Shselasky if (puf->endpoint != -1) { 220232035Shselasky BPF_STORE_STMT(dynamic_insn[len], 221232035Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 222232035Shselasky len++; 223232035Shselasky BPF_STORE_JUMP(dynamic_insn[len], 224232035Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 3); 225232035Shselasky len++; 226232035Shselasky BPF_STORE_STMT(dynamic_insn[len], 227232035Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 228232035Shselasky len++; 229232035Shselasky BPF_STORE_JUMP(dynamic_insn[len], 230232035Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 231232035Shselasky len++; 232232035Shselasky } else { 233232035Shselasky BPF_STORE_STMT(dynamic_insn[len], 234232035Shselasky BPF_LD | BPF_B | BPF_ABS, addr_off); 235232035Shselasky len++; 236232035Shselasky BPF_STORE_JUMP(dynamic_insn[len], 237232035Shselasky BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 1); 238232035Shselasky len++; 239232035Shselasky } 240232035Shselasky } else { 241232035Shselasky if (puf->endpoint != -1) { 242232035Shselasky BPF_STORE_STMT(dynamic_insn[len], 243232035Shselasky BPF_LD | BPF_W | BPF_ABS, addr_ep); 244232035Shselasky len++; 245232035Shselasky BPF_STORE_JUMP(dynamic_insn[len], 246232035Shselasky BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); 247232035Shselasky len++; 248232035Shselasky } 249232035Shselasky } 250232035Shselasky BPF_STORE_STMT(dynamic_insn[len], 251232035Shselasky BPF_RET | BPF_K, snapshot); 252232035Shselasky len++; 253232035Shselasky } 254232035Shselasky 255232035Shselasky BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, 0); 256232035Shselasky len++; 257232035Shselasky 258232035Shselaskydone: 259232035Shselasky pprog->bf_len = len; 260232035Shselasky pprog->bf_insns = dynamic_insn; 261232035Shselasky} 262232035Shselasky 263235003Shselaskystatic int 264235003Shselaskymatch_filter(int unit, int endpoint) 265235003Shselasky{ 266235003Shselasky struct usb_filt *puf; 267235003Shselasky 268235003Shselasky if (STAILQ_FIRST(&usb_filt_head) == NULL) 269235003Shselasky return (1); 270235003Shselasky 271235003Shselasky STAILQ_FOREACH(puf, &usb_filt_head, entry) { 272235003Shselasky if ((puf->unit == -1 || puf->unit == unit) && 273235003Shselasky (puf->endpoint == -1 || puf->endpoint == endpoint)) 274235003Shselasky return (1); 275235003Shselasky } 276235003Shselasky return (0); 277235003Shselasky} 278235003Shselasky 279232035Shselaskystatic void 280232035Shselaskyfree_filter(struct bpf_program *pprog) 281232035Shselasky{ 282232035Shselasky struct usb_filt *puf; 283232035Shselasky 284232035Shselasky while ((puf = STAILQ_FIRST(&usb_filt_head)) != NULL) { 285232035Shselasky STAILQ_REMOVE_HEAD(&usb_filt_head, entry); 286232035Shselasky free(puf); 287232035Shselasky } 288232035Shselasky free(pprog->bf_insns); 289232035Shselasky} 290232035Shselasky 291232035Shselaskystatic void 292215651Sweongyohandle_sigint(int sig) 293215651Sweongyo{ 294215651Sweongyo 295215651Sweongyo (void)sig; 296215651Sweongyo doexit = 1; 297215651Sweongyo} 298215651Sweongyo 299220301Shselasky#define FLAGS(x, name) \ 300220301Shselasky (((x) & USBPF_FLAG_##name) ? #name "|" : "") 301220301Shselasky 302220301Shselasky#define STATUS(x, name) \ 303220301Shselasky (((x) & USBPF_STATUS_##name) ? #name "|" : "") 304220301Shselasky 305220301Shselaskystatic const char * 306220301Shselaskyusb_errstr(uint32_t error) 307220301Shselasky{ 308220301Shselasky if (error >= USB_ERR_MAX || errstr_table[error] == NULL) 309220301Shselasky return ("UNKNOWN"); 310220301Shselasky else 311220301Shselasky return (errstr_table[error]); 312220301Shselasky} 313220301Shselasky 314220301Shselaskystatic const char * 315220301Shselaskyusb_speedstr(uint8_t speed) 316220301Shselasky{ 317220301Shselasky if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) 318220301Shselasky return ("UNKNOWN"); 319220301Shselasky else 320220301Shselasky return (speed_table[speed]); 321220301Shselasky} 322220301Shselasky 323215651Sweongyostatic void 324220301Shselaskyprint_flags(uint32_t flags) 325215651Sweongyo{ 326220301Shselasky printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", 327220301Shselasky flags, 328220301Shselasky FLAGS(flags, FORCE_SHORT_XFER), 329220301Shselasky FLAGS(flags, SHORT_XFER_OK), 330220301Shselasky FLAGS(flags, SHORT_FRAMES_OK), 331220301Shselasky FLAGS(flags, PIPE_BOF), 332220301Shselasky FLAGS(flags, PROXY_BUFFER), 333220301Shselasky FLAGS(flags, EXT_BUFFER), 334220301Shselasky FLAGS(flags, MANUAL_STATUS), 335220301Shselasky FLAGS(flags, NO_PIPE_OK), 336220301Shselasky FLAGS(flags, STALL_PIPE)); 337215651Sweongyo} 338215651Sweongyo 339215651Sweongyostatic void 340220301Shselaskyprint_status(uint32_t status) 341215651Sweongyo{ 342220301Shselasky printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", 343220301Shselasky status, 344220301Shselasky STATUS(status, OPEN), 345220301Shselasky STATUS(status, TRANSFERRING), 346220301Shselasky STATUS(status, DID_DMA_DELAY), 347220301Shselasky STATUS(status, DID_CLOSE), 348220301Shselasky STATUS(status, DRAINING), 349220301Shselasky STATUS(status, STARTED), 350220301Shselasky STATUS(status, BW_RECLAIMED), 351220301Shselasky STATUS(status, CONTROL_XFR), 352220301Shselasky STATUS(status, CONTROL_HDR), 353220301Shselasky STATUS(status, CONTROL_ACT), 354220301Shselasky STATUS(status, CONTROL_STALL), 355220301Shselasky STATUS(status, SHORT_FRAMES_OK), 356220301Shselasky STATUS(status, SHORT_XFER_OK), 357220301Shselasky STATUS(status, BDMA_ENABLE), 358220301Shselasky STATUS(status, BDMA_NO_POST_SYNC), 359220301Shselasky STATUS(status, BDMA_SETUP), 360220301Shselasky STATUS(status, ISOCHRONOUS_XFR), 361220301Shselasky STATUS(status, CURR_DMA_SET), 362220301Shselasky STATUS(status, CAN_CANCEL_IMMED), 363220301Shselasky STATUS(status, DOING_CALLBACK)); 364220301Shselasky} 365215651Sweongyo 366220301Shselasky/* 367220301Shselasky * Dump a byte into hex format. 368220301Shselasky */ 369220301Shselaskystatic void 370220301Shselaskyhexbyte(char *buf, uint8_t temp) 371220301Shselasky{ 372220301Shselasky uint8_t lo; 373220301Shselasky uint8_t hi; 374220301Shselasky 375220301Shselasky lo = temp & 0xF; 376220301Shselasky hi = temp >> 4; 377220301Shselasky 378220301Shselasky if (hi < 10) 379220301Shselasky buf[0] = '0' + hi; 380220301Shselasky else 381220301Shselasky buf[0] = 'A' + hi - 10; 382220301Shselasky 383220301Shselasky if (lo < 10) 384220301Shselasky buf[1] = '0' + lo; 385220301Shselasky else 386220301Shselasky buf[1] = 'A' + lo - 10; 387215651Sweongyo} 388215651Sweongyo 389215651Sweongyo/* 390215651Sweongyo * Display a region in traditional hexdump format. 391215651Sweongyo */ 392215651Sweongyostatic void 393220301Shselaskyhexdump(const uint8_t *region, uint32_t len) 394215651Sweongyo{ 395220301Shselasky const uint8_t *line; 396220301Shselasky char linebuf[128]; 397220301Shselasky int i; 398218010Shselasky int x; 399218010Shselasky int c; 400215651Sweongyo 401215651Sweongyo for (line = region; line < (region + len); line += 16) { 402220301Shselasky 403220301Shselasky i = 0; 404220301Shselasky 405220301Shselasky linebuf[i] = ' '; 406220301Shselasky hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); 407220301Shselasky hexbyte(linebuf + i + 3, (line - region) & 0xFF); 408220301Shselasky linebuf[i + 5] = ' '; 409220301Shselasky linebuf[i + 6] = ' '; 410220301Shselasky i += 7; 411220301Shselasky 412215651Sweongyo for (x = 0; x < 16; x++) { 413220301Shselasky if ((line + x) < (region + len)) { 414220301Shselasky hexbyte(linebuf + i, 415220301Shselasky *(const u_int8_t *)(line + x)); 416220301Shselasky } else { 417220301Shselasky linebuf[i] = '-'; 418220301Shselasky linebuf[i + 1] = '-'; 419220301Shselasky } 420220301Shselasky linebuf[i + 2] = ' '; 421220301Shselasky if (x == 7) { 422220301Shselasky linebuf[i + 3] = ' '; 423220301Shselasky i += 4; 424220301Shselasky } else { 425220301Shselasky i += 3; 426220301Shselasky } 427215651Sweongyo } 428220301Shselasky linebuf[i] = ' '; 429220301Shselasky linebuf[i + 1] = '|'; 430220301Shselasky i += 2; 431215651Sweongyo for (x = 0; x < 16; x++) { 432215651Sweongyo if ((line + x) < (region + len)) { 433215651Sweongyo c = *(const u_int8_t *)(line + x); 434215651Sweongyo /* !isprint(c) */ 435215651Sweongyo if ((c < ' ') || (c > '~')) 436215651Sweongyo c = '.'; 437220301Shselasky linebuf[i] = c; 438220301Shselasky } else { 439220301Shselasky linebuf[i] = ' '; 440220301Shselasky } 441220301Shselasky i++; 442215651Sweongyo } 443220301Shselasky linebuf[i] = '|'; 444220301Shselasky linebuf[i + 1] = 0; 445220301Shselasky i += 2; 446220301Shselasky puts(linebuf); 447215651Sweongyo } 448215651Sweongyo} 449215651Sweongyo 450215651Sweongyostatic void 451233315Shselaskyprint_apacket(const struct header_32 *hdr, const uint8_t *ptr, int ptr_len) 452215651Sweongyo{ 453215651Sweongyo struct tm *tm; 454220301Shselasky struct usbpf_pkthdr up_temp; 455220301Shselasky struct usbpf_pkthdr *up; 456215651Sweongyo struct timeval tv; 457215651Sweongyo size_t len; 458220301Shselasky uint32_t x; 459215651Sweongyo char buf[64]; 460215651Sweongyo 461220301Shselasky ptr += USBPF_HDR_LEN; 462220301Shselasky ptr_len -= USBPF_HDR_LEN; 463220301Shselasky if (ptr_len < 0) 464220301Shselasky return; 465220301Shselasky 466220301Shselasky /* make sure we don't change the source buffer */ 467220301Shselasky memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); 468220301Shselasky up = &up_temp; 469220301Shselasky 470220301Shselasky /* 471220301Shselasky * A packet from the kernel is based on little endian byte 472220301Shselasky * order. 473220301Shselasky */ 474220301Shselasky up->up_totlen = le32toh(up->up_totlen); 475215651Sweongyo up->up_busunit = le32toh(up->up_busunit); 476215651Sweongyo up->up_flags = le32toh(up->up_flags); 477215651Sweongyo up->up_status = le32toh(up->up_status); 478215651Sweongyo up->up_error = le32toh(up->up_error); 479215651Sweongyo up->up_interval = le32toh(up->up_interval); 480220301Shselasky up->up_frames = le32toh(up->up_frames); 481220301Shselasky up->up_packet_size = le32toh(up->up_packet_size); 482220301Shselasky up->up_packet_count = le32toh(up->up_packet_count); 483220301Shselasky up->up_endpoint = le32toh(up->up_endpoint); 484215651Sweongyo 485235003Shselasky if (!match_filter(up->up_address, up->up_endpoint)) 486235003Shselasky return; 487235003Shselasky 488233315Shselasky tv.tv_sec = hdr->ts_sec; 489233315Shselasky tv.tv_usec = hdr->ts_usec; 490215651Sweongyo tm = localtime(&tv.tv_sec); 491215651Sweongyo 492215651Sweongyo len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); 493220301Shselasky 494235003Shselasky if (verbose >= 0) { 495235003Shselasky printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", 496235003Shselasky (int)len, buf, tv.tv_usec, 497235003Shselasky (int)up->up_busunit, (int)up->up_address, 498235003Shselasky (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", 499235003Shselasky xfertype_table[up->up_xfertype], 500235003Shselasky (unsigned int)up->up_endpoint, 501235003Shselasky usb_speedstr(up->up_speed), 502235003Shselasky (int)up->up_frames, 503235003Shselasky (int)(up->up_totlen - USBPF_HDR_LEN - 504235003Shselasky (USBPF_FRAME_HDR_LEN * up->up_frames)), 505235003Shselasky (int)up->up_interval, 506235003Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", 507235003Shselasky (up->up_type == USBPF_XFERTAP_DONE) ? 508235003Shselasky usb_errstr(up->up_error) : ""); 509235003Shselasky } 510215651Sweongyo 511235003Shselasky if (verbose >= 1 || b_arg != NULL) { 512220301Shselasky for (x = 0; x != up->up_frames; x++) { 513220301Shselasky const struct usbpf_framehdr *uf; 514220301Shselasky uint32_t framelen; 515220301Shselasky uint32_t flags; 516220301Shselasky 517220301Shselasky uf = (const struct usbpf_framehdr *)ptr; 518220301Shselasky ptr += USBPF_FRAME_HDR_LEN; 519220301Shselasky ptr_len -= USBPF_FRAME_HDR_LEN; 520220301Shselasky if (ptr_len < 0) 521220301Shselasky return; 522220301Shselasky 523220301Shselasky framelen = le32toh(uf->length); 524220301Shselasky flags = le32toh(uf->flags); 525220301Shselasky 526235003Shselasky if (verbose >= 1) { 527235003Shselasky printf(" frame[%u] %s %d bytes\n", 528235003Shselasky (unsigned int)x, 529235003Shselasky (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", 530235003Shselasky (int)framelen); 531235003Shselasky } 532220301Shselasky 533220301Shselasky if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { 534220301Shselasky 535220301Shselasky int tot_frame_len; 536220301Shselasky 537220301Shselasky tot_frame_len = USBPF_FRAME_ALIGN(framelen); 538220301Shselasky 539220301Shselasky ptr_len -= tot_frame_len; 540220301Shselasky 541220301Shselasky if (tot_frame_len < 0 || 542220301Shselasky (int)framelen < 0 || (int)ptr_len < 0) 543220301Shselasky break; 544220301Shselasky 545235003Shselasky if (b_arg != NULL) { 546235003Shselasky struct usbcap *p = &uc; 547235003Shselasky int ret; 548235003Shselasky ret = write(p->bfd, ptr, framelen); 549235003Shselasky if (ret != (int)framelen) 550235003Shselasky err(EXIT_FAILURE, "Could not write binary data"); 551235003Shselasky } 552235003Shselasky if (verbose >= 1) 553235003Shselasky hexdump(ptr, framelen); 554220301Shselasky 555220301Shselasky ptr += tot_frame_len; 556220301Shselasky } 557215651Sweongyo } 558215651Sweongyo } 559220301Shselasky if (verbose >= 2) 560215651Sweongyo print_flags(up->up_flags); 561220301Shselasky if (verbose >= 3) 562215651Sweongyo print_status(up->up_status); 563215651Sweongyo} 564215651Sweongyo 565215651Sweongyostatic void 566233315Shselaskyfix_packets(uint8_t *data, const int datalen) 567215651Sweongyo{ 568233315Shselasky struct header_32 temp; 569220301Shselasky uint8_t *ptr; 570220301Shselasky uint8_t *next; 571233315Shselasky uint32_t hdrlen; 572233315Shselasky uint32_t caplen; 573215651Sweongyo 574215651Sweongyo for (ptr = data; ptr < (data + datalen); ptr = next) { 575233315Shselasky 576233315Shselasky const struct bpf_hdr *hdr; 577233315Shselasky 578226565Shselasky hdr = (const struct bpf_hdr *)ptr; 579215651Sweongyo 580233315Shselasky temp.ts_sec = htole32(hdr->bh_tstamp.tv_sec); 581233315Shselasky temp.ts_usec = htole32(hdr->bh_tstamp.tv_usec); 582233315Shselasky temp.caplen = htole32(hdr->bh_caplen); 583233315Shselasky temp.datalen = htole32(hdr->bh_datalen); 584233315Shselasky temp.hdrlen = hdr->bh_hdrlen; 585233315Shselasky temp.align = BPF_WORDALIGN(1); 586233315Shselasky 587233315Shselasky hdrlen = hdr->bh_hdrlen; 588233315Shselasky caplen = hdr->bh_caplen; 589233315Shselasky 590233315Shselasky if ((hdrlen >= sizeof(temp)) && (hdrlen <= 255) && 591233315Shselasky ((ptr + hdrlen) <= (data + datalen))) { 592233315Shselasky memcpy(ptr, &temp, sizeof(temp)); 593233315Shselasky memset(ptr + sizeof(temp), 0, hdrlen - sizeof(temp)); 594233315Shselasky } else { 595233315Shselasky err(EXIT_FAILURE, "Invalid header length %d", hdrlen); 596220301Shselasky } 597233315Shselasky 598233315Shselasky next = ptr + BPF_WORDALIGN(hdrlen + caplen); 599233315Shselasky 600233315Shselasky if (next <= ptr) 601233315Shselasky err(EXIT_FAILURE, "Invalid length"); 602233315Shselasky } 603233315Shselasky} 604233315Shselasky 605233315Shselaskystatic void 606233315Shselaskyprint_packets(uint8_t *data, const int datalen) 607233315Shselasky{ 608233315Shselasky struct header_32 temp; 609233315Shselasky uint8_t *ptr; 610233315Shselasky uint8_t *next; 611233315Shselasky 612233315Shselasky for (ptr = data; ptr < (data + datalen); ptr = next) { 613233315Shselasky 614233315Shselasky const struct header_32 *hdr32; 615233315Shselasky 616233315Shselasky hdr32 = (const struct header_32 *)ptr; 617233315Shselasky 618233315Shselasky temp.ts_sec = le32toh(hdr32->ts_sec); 619233315Shselasky temp.ts_usec = le32toh(hdr32->ts_usec); 620233315Shselasky temp.caplen = le32toh(hdr32->caplen); 621233315Shselasky temp.datalen = le32toh(hdr32->datalen); 622233315Shselasky temp.hdrlen = hdr32->hdrlen; 623233315Shselasky temp.align = hdr32->align; 624233315Shselasky 625233315Shselasky next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, temp.align); 626233315Shselasky 627233315Shselasky if (next <= ptr) 628233315Shselasky err(EXIT_FAILURE, "Invalid length"); 629233315Shselasky 630235003Shselasky if (verbose >= 0 || r_arg != NULL || b_arg != NULL) { 631233315Shselasky print_apacket(&temp, ptr + 632233315Shselasky temp.hdrlen, temp.caplen); 633233315Shselasky } 634215651Sweongyo pkt_captured++; 635215651Sweongyo } 636215651Sweongyo} 637215651Sweongyo 638215651Sweongyostatic void 639220301Shselaskywrite_packets(struct usbcap *p, const uint8_t *data, const int datalen) 640215651Sweongyo{ 641220301Shselasky int len = htole32(datalen); 642220301Shselasky int ret; 643215651Sweongyo 644215651Sweongyo ret = write(p->wfd, &len, sizeof(int)); 645221604Shselasky if (ret != sizeof(int)) { 646221604Shselasky err(EXIT_FAILURE, "Could not write length " 647221604Shselasky "field of USB data payload"); 648221604Shselasky } 649215651Sweongyo ret = write(p->wfd, data, datalen); 650221604Shselasky if (ret != datalen) { 651221604Shselasky err(EXIT_FAILURE, "Could not write " 652221604Shselasky "complete USB data payload"); 653221604Shselasky } 654215651Sweongyo} 655215651Sweongyo 656215651Sweongyostatic void 657215651Sweongyoread_file(struct usbcap *p) 658215651Sweongyo{ 659220301Shselasky int datalen; 660220301Shselasky int ret; 661220301Shselasky uint8_t *data; 662215651Sweongyo 663215651Sweongyo while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { 664215651Sweongyo datalen = le32toh(datalen); 665215651Sweongyo data = malloc(datalen); 666221604Shselasky if (data == NULL) 667221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 668215651Sweongyo ret = read(p->rfd, data, datalen); 669221604Shselasky if (ret != datalen) { 670221604Shselasky err(EXIT_FAILURE, "Could not read complete " 671221604Shselasky "USB data payload"); 672221604Shselasky } 673233315Shselasky if (uf_minor == 2) 674233315Shselasky fix_packets(data, datalen); 675233315Shselasky 676215651Sweongyo print_packets(data, datalen); 677215651Sweongyo free(data); 678215651Sweongyo } 679215651Sweongyo} 680215651Sweongyo 681215651Sweongyostatic void 682215651Sweongyodo_loop(struct usbcap *p) 683215651Sweongyo{ 684215651Sweongyo int cc; 685215651Sweongyo 686215651Sweongyo while (doexit == 0) { 687220301Shselasky cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); 688215651Sweongyo if (cc < 0) { 689215651Sweongyo switch (errno) { 690215651Sweongyo case EINTR: 691215651Sweongyo break; 692215651Sweongyo default: 693215651Sweongyo fprintf(stderr, "read: %s\n", strerror(errno)); 694215651Sweongyo return; 695215651Sweongyo } 696215651Sweongyo continue; 697215651Sweongyo } 698215651Sweongyo if (cc == 0) 699215651Sweongyo continue; 700233315Shselasky 701233315Shselasky fix_packets(p->buffer, cc); 702233315Shselasky 703215651Sweongyo if (w_arg != NULL) 704215651Sweongyo write_packets(p, p->buffer, cc); 705215651Sweongyo print_packets(p->buffer, cc); 706215651Sweongyo } 707215651Sweongyo} 708215651Sweongyo 709215651Sweongyostatic void 710215651Sweongyoinit_rfile(struct usbcap *p) 711215651Sweongyo{ 712215651Sweongyo struct usbcap_filehdr uf; 713215651Sweongyo int ret; 714215651Sweongyo 715215651Sweongyo p->rfd = open(r_arg, O_RDONLY); 716215651Sweongyo if (p->rfd < 0) { 717221604Shselasky err(EXIT_FAILURE, "Could not open " 718221604Shselasky "'%s' for read", r_arg); 719215651Sweongyo } 720215651Sweongyo ret = read(p->rfd, &uf, sizeof(uf)); 721221604Shselasky if (ret != sizeof(uf)) { 722221604Shselasky err(EXIT_FAILURE, "Could not read USB capture " 723221604Shselasky "file header"); 724221604Shselasky } 725221604Shselasky if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) { 726221604Shselasky errx(EX_SOFTWARE, "Invalid magic field(0x%08x) " 727221604Shselasky "in USB capture file header.", 728221604Shselasky (unsigned int)le32toh(uf.magic)); 729221604Shselasky } 730221604Shselasky if (uf.major != 0) { 731221604Shselasky errx(EX_SOFTWARE, "Invalid major version(%d) " 732221604Shselasky "field in USB capture file header.", (int)uf.major); 733221604Shselasky } 734233315Shselasky 735233315Shselasky uf_minor = uf.minor; 736233315Shselasky 737233315Shselasky if (uf.minor != 3 && uf.minor != 2) { 738221604Shselasky errx(EX_SOFTWARE, "Invalid minor version(%d) " 739221604Shselasky "field in USB capture file header.", (int)uf.minor); 740221604Shselasky } 741215651Sweongyo} 742215651Sweongyo 743215651Sweongyostatic void 744215651Sweongyoinit_wfile(struct usbcap *p) 745215651Sweongyo{ 746215651Sweongyo struct usbcap_filehdr uf; 747215651Sweongyo int ret; 748215651Sweongyo 749215651Sweongyo p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); 750215651Sweongyo if (p->wfd < 0) { 751221604Shselasky err(EXIT_FAILURE, "Could not open " 752233315Shselasky "'%s' for write", w_arg); 753215651Sweongyo } 754221604Shselasky memset(&uf, 0, sizeof(uf)); 755215651Sweongyo uf.magic = htole32(USBCAP_FILEHDR_MAGIC); 756215651Sweongyo uf.major = 0; 757233315Shselasky uf.minor = 3; 758215651Sweongyo ret = write(p->wfd, (const void *)&uf, sizeof(uf)); 759221604Shselasky if (ret != sizeof(uf)) { 760221604Shselasky err(EXIT_FAILURE, "Could not write " 761221604Shselasky "USB capture header"); 762221604Shselasky } 763215651Sweongyo} 764215651Sweongyo 765215651Sweongyostatic void 766215651Sweongyousage(void) 767215651Sweongyo{ 768215651Sweongyo 769215651Sweongyo#define FMT " %-14s %s\n" 770215651Sweongyo fprintf(stderr, "usage: usbdump [options]\n"); 771221604Shselasky fprintf(stderr, FMT, "-i <usbusX>", "Listen on USB bus interface"); 772232035Shselasky fprintf(stderr, FMT, "-f <unit[.endpoint]>", "Specify a device and endpoint filter"); 773221604Shselasky fprintf(stderr, FMT, "-r <file>", "Read the raw packets from file"); 774221604Shselasky fprintf(stderr, FMT, "-s <snaplen>", "Snapshot bytes from each packet"); 775221604Shselasky fprintf(stderr, FMT, "-v", "Increase the verbose level"); 776235003Shselasky fprintf(stderr, FMT, "-b <file>", "Save raw version of all recorded data to file"); 777221604Shselasky fprintf(stderr, FMT, "-w <file>", "Write the raw packets to file"); 778235003Shselasky fprintf(stderr, FMT, "-h", "Display summary of command line options"); 779215651Sweongyo#undef FMT 780221604Shselasky exit(EX_USAGE); 781215651Sweongyo} 782215651Sweongyo 783261222Shselaskystatic void 784261222Shselaskycheck_usb_pf_sysctl(void) 785261222Shselasky{ 786261222Shselasky int error; 787261222Shselasky int no_pf_val = 0; 788261222Shselasky size_t no_pf_len = sizeof(int); 789261222Shselasky 790261222Shselasky /* check "hw.usb.no_pf" sysctl for 8- and 9- stable */ 791261222Shselasky 792261222Shselasky error = sysctlbyname("hw.usb.no_pf", &no_pf_val, 793261222Shselasky &no_pf_len, NULL, 0); 794261222Shselasky if (error == 0 && no_pf_val != 0) { 795261222Shselasky warnx("The USB packet filter might be disabled."); 796261222Shselasky warnx("See the \"hw.usb.no_pf\" sysctl for more information."); 797261222Shselasky } 798261222Shselasky} 799261222Shselasky 800215651Sweongyoint 801215651Sweongyomain(int argc, char *argv[]) 802215651Sweongyo{ 803215651Sweongyo struct timeval tv; 804215803Sweongyo struct bpf_program total_prog; 805215803Sweongyo struct bpf_stat us; 806215803Sweongyo struct bpf_version bv; 807235003Shselasky struct usbcap *p = &uc; 808215803Sweongyo struct ifreq ifr; 809215651Sweongyo long snapshot = 192; 810220301Shselasky uint32_t v; 811232035Shselasky int fd; 812232035Shselasky int o; 813232035Shselasky int filt_unit; 814232035Shselasky int filt_ep; 815215651Sweongyo const char *optstring; 816232035Shselasky char *pp; 817215651Sweongyo 818235003Shselasky optstring = "b:hi:r:s:vw:f:"; 819215651Sweongyo while ((o = getopt(argc, argv, optstring)) != -1) { 820215651Sweongyo switch (o) { 821215651Sweongyo case 'i': 822215651Sweongyo i_arg = optarg; 823215651Sweongyo break; 824215651Sweongyo case 'r': 825215651Sweongyo r_arg = optarg; 826215651Sweongyo init_rfile(p); 827215651Sweongyo break; 828215651Sweongyo case 's': 829232035Shselasky snapshot = strtol(optarg, &pp, 10); 830215651Sweongyo errno = 0; 831232035Shselasky if (pp != NULL && *pp != 0) 832232035Shselasky usage(); 833215651Sweongyo if (snapshot == 0 && errno == EINVAL) 834215651Sweongyo usage(); 835215651Sweongyo /* snapeshot == 0 is special */ 836215651Sweongyo if (snapshot == 0) 837215651Sweongyo snapshot = -1; 838215651Sweongyo break; 839235003Shselasky case 'b': 840235003Shselasky b_arg = optarg; 841235003Shselasky break; 842215651Sweongyo case 'v': 843215651Sweongyo verbose++; 844215651Sweongyo break; 845215651Sweongyo case 'w': 846215651Sweongyo w_arg = optarg; 847215651Sweongyo init_wfile(p); 848215651Sweongyo break; 849232035Shselasky case 'f': 850232035Shselasky filt_unit = strtol(optarg, &pp, 10); 851232035Shselasky filt_ep = -1; 852232035Shselasky if (pp != NULL) { 853232035Shselasky if (*pp == '.') { 854232035Shselasky filt_ep = strtol(pp + 1, &pp, 10); 855232035Shselasky if (pp != NULL && *pp != 0) 856232035Shselasky usage(); 857232035Shselasky } else if (*pp != 0) { 858232035Shselasky usage(); 859232035Shselasky } 860232035Shselasky } 861232035Shselasky add_filter(filt_unit, filt_ep); 862232035Shselasky break; 863215651Sweongyo default: 864215651Sweongyo usage(); 865215651Sweongyo /* NOTREACHED */ 866215651Sweongyo } 867215651Sweongyo } 868215651Sweongyo 869235003Shselasky if (b_arg != NULL) { 870235003Shselasky p->bfd = open(b_arg, O_CREAT | O_TRUNC | 871235003Shselasky O_WRONLY, S_IRUSR | S_IWUSR); 872235003Shselasky if (p->bfd < 0) { 873235003Shselasky err(EXIT_FAILURE, "Could not open " 874235003Shselasky "'%s' for write", b_arg); 875235003Shselasky } 876235003Shselasky } 877235003Shselasky 878235003Shselasky /* 879235003Shselasky * Require more verbosity to print anything when -w or -b is 880235003Shselasky * specified on the command line: 881235003Shselasky */ 882235003Shselasky if (w_arg != NULL || b_arg != NULL) 883235003Shselasky verbose--; 884235003Shselasky 885215651Sweongyo if (r_arg != NULL) { 886215651Sweongyo read_file(p); 887215651Sweongyo exit(EXIT_SUCCESS); 888215651Sweongyo } 889215651Sweongyo 890261222Shselasky check_usb_pf_sysctl(); 891261222Shselasky 892215803Sweongyo p->fd = fd = open("/dev/bpf", O_RDONLY); 893221604Shselasky if (p->fd < 0) 894221604Shselasky err(EXIT_FAILURE, "Could not open BPF device"); 895215651Sweongyo 896221604Shselasky if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) 897221604Shselasky err(EXIT_FAILURE, "BIOCVERSION ioctl failed"); 898221604Shselasky 899215803Sweongyo if (bv.bv_major != BPF_MAJOR_VERSION || 900221604Shselasky bv.bv_minor < BPF_MINOR_VERSION) 901221604Shselasky errx(EXIT_FAILURE, "Kernel BPF filter out of date"); 902215651Sweongyo 903220301Shselasky /* USB transfers can be greater than 64KByte */ 904220301Shselasky v = 1U << 16; 905220301Shselasky 906220301Shselasky /* clear ifr structure */ 907220301Shselasky memset(&ifr, 0, sizeof(ifr)); 908220301Shselasky 909220301Shselasky for ( ; v >= USBPF_HDR_LEN; v >>= 1) { 910215803Sweongyo (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); 911215803Sweongyo (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); 912215803Sweongyo if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 913215651Sweongyo break; 914215651Sweongyo } 915221604Shselasky if (v == 0) 916221604Shselasky errx(EXIT_FAILURE, "No buffer size worked."); 917215651Sweongyo 918221604Shselasky if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) 919221604Shselasky err(EXIT_FAILURE, "BIOCGBLEN ioctl failed"); 920215651Sweongyo 921215651Sweongyo p->bufsize = v; 922220301Shselasky p->buffer = (uint8_t *)malloc(p->bufsize); 923221604Shselasky if (p->buffer == NULL) 924221604Shselasky errx(EX_SOFTWARE, "Out of memory."); 925215651Sweongyo 926232035Shselasky make_filter(&total_prog, snapshot); 927215651Sweongyo 928221604Shselasky if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) 929221604Shselasky err(EXIT_FAILURE, "BIOCSETF ioctl failed"); 930215651Sweongyo 931232035Shselasky free_filter(&total_prog); 932232035Shselasky 933215651Sweongyo /* 1 second read timeout */ 934215651Sweongyo tv.tv_sec = 1; 935215651Sweongyo tv.tv_usec = 0; 936221604Shselasky if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) 937221604Shselasky err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed"); 938215651Sweongyo 939215651Sweongyo (void)signal(SIGINT, handle_sigint); 940215651Sweongyo 941215651Sweongyo do_loop(p); 942215651Sweongyo 943221604Shselasky if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) 944221604Shselasky err(EXIT_FAILURE, "BIOCGSTATS ioctl failed"); 945215651Sweongyo 946215651Sweongyo /* XXX what's difference between pkt_captured and us.us_recv? */ 947215651Sweongyo printf("\n"); 948215651Sweongyo printf("%d packets captured\n", pkt_captured); 949215803Sweongyo printf("%d packets received by filter\n", us.bs_recv); 950215803Sweongyo printf("%d packets dropped by kernel\n", us.bs_drop); 951215651Sweongyo 952215651Sweongyo if (p->fd > 0) 953215651Sweongyo close(p->fd); 954215651Sweongyo if (p->rfd > 0) 955215651Sweongyo close(p->rfd); 956215651Sweongyo if (p->wfd > 0) 957215651Sweongyo close(p->wfd); 958235003Shselasky if (p->bfd > 0) 959235003Shselasky close(p->bfd); 960215651Sweongyo 961215651Sweongyo return (EXIT_SUCCESS); 962215651Sweongyo} 963