From 7944ce5745aaff515c0962c40357c087bfc00a3e Mon Sep 17 00:00:00 2001 From: hselasky Date: Thu, 28 Apr 2011 16:18:30 +0000 Subject: [PATCH] MFC r215649,r215764,r215802,r215804,r215810,r215812,r216091,r216267,r218165,r220301,r215651,r215803,r216138,r218010,r217558,r220312,r220314,r215846 and r216268. Backport USB PF and usbdump from head to 8-stable. Approved by: thompsa git-svn-id: svn://svn.freebsd.org/base/stable/8@221174 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sbin/ifconfig/ifconfig.c | 2 + sys/conf/files | 1 + sys/dev/usb/controller/usb_controller.c | 8 + sys/dev/usb/usb_bus.h | 2 + sys/dev/usb/usb_freebsd.h | 1 + sys/dev/usb/usb_pf.c | 392 ++++++++++++++ sys/dev/usb/usb_pf.h | 120 +++++ sys/dev/usb/usb_transfer.c | 40 +- sys/dev/usb/usbdi.h | 2 + sys/modules/usb/usb/Makefile | 4 +- sys/net/if.h | 4 +- usr.sbin/Makefile | 2 + usr.sbin/usbdump/Makefile | 8 + usr.sbin/usbdump/usbdump.8 | 77 +++ usr.sbin/usbdump/usbdump.c | 662 ++++++++++++++++++++++++ 15 files changed, 1318 insertions(+), 7 deletions(-) create mode 100644 sys/dev/usb/usb_pf.c create mode 100644 sys/dev/usb/usb_pf.h create mode 100644 usr.sbin/usbdump/Makefile create mode 100644 usr.sbin/usbdump/usbdump.8 create mode 100644 usr.sbin/usbdump/usbdump.c diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index fd20d17ea..d82560418 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -305,6 +305,8 @@ main(int argc, char *argv[]) } cp = ifa->ifa_name; + if ((ifa->ifa_flags & IFF_CANTCONFIG) != 0) + continue; if (downonly && (ifa->ifa_flags & IFF_UP) != 0) continue; if (uponly && (ifa->ifa_flags & IFF_UP) == 0) diff --git a/sys/conf/files b/sys/conf/files index 5d28a68bc..747f88b0c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1749,6 +1749,7 @@ dev/usb/usb_lookup.c optional usb dev/usb/usb_mbuf.c optional usb dev/usb/usb_msctest.c optional usb dev/usb/usb_parse.c optional usb +dev/usb/usb_pf.c optional usb dev/usb/usb_process.c optional usb dev/usb/usb_request.c optional usb dev/usb/usb_transfer.c optional usb diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c index f33f2b13b..dade0ad32 100644 --- a/sys/dev/usb/controller/usb_controller.c +++ b/sys/dev/usb/controller/usb_controller.c @@ -60,6 +60,7 @@ #include #include +#include /* function prototypes */ @@ -205,6 +206,9 @@ usb_detach(device_t dev) usb_proc_free(&bus->control_xfer_proc); +#if USB_HAVE_PF + usbpf_detach(bus); +#endif return (0); } @@ -433,6 +437,9 @@ usb_attach_sub(device_t dev, struct usb_bus *bus) usb_devclass_ptr = devclass_find("usbus"); mtx_unlock(&Giant); +#if USB_HAVE_PF + usbpf_attach(bus); +#endif /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; bus->explore_msg[0].bus = bus; @@ -595,3 +602,4 @@ usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) mtx_destroy(&bus->bus_mtx); } + diff --git a/sys/dev/usb/usb_bus.h b/sys/dev/usb/usb_bus.h index 99e977706..1dd9d6a1f 100644 --- a/sys/dev/usb/usb_bus.h +++ b/sys/dev/usb/usb_bus.h @@ -86,6 +86,8 @@ struct usb_bus { struct usb_bus_methods *methods; /* filled by HC driver */ struct usb_device **devices; + struct ifnet *ifp; /* only for USB Packet Filter */ + usb_power_mask_t hw_power_state; /* see USB_HW_POWER_XXX */ usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; diff --git a/sys/dev/usb/usb_freebsd.h b/sys/dev/usb/usb_freebsd.h index 8a008cd47..a646971af 100644 --- a/sys/dev/usb/usb_freebsd.h +++ b/sys/dev/usb/usb_freebsd.h @@ -40,6 +40,7 @@ #define USB_HAVE_TT_SUPPORT 1 #define USB_HAVE_POWERD 1 #define USB_HAVE_MSCTEST 1 +#define USB_HAVE_PF 1 #define USB_TD_GET_PROC(td) (td)->td_proc #define USB_PROC_GET_GID(td) (td)->p_pgid diff --git a/sys/dev/usb/usb_pf.c b/sys/dev/usb/usb_pf.c new file mode 100644 index 000000000..6d6543936 --- /dev/null +++ b/sys/dev/usb/usb_pf.c @@ -0,0 +1,392 @@ +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int usb_no_pf; + +SYSCTL_INT(_hw_usb, OID_AUTO, no_pf, CTLFLAG_RW, + &usb_no_pf, 0, "Set to disable USB packet filtering"); + +TUNABLE_INT("hw.usb.no_pf", &usb_no_pf); + +void +usbpf_attach(struct usb_bus *ubus) +{ + struct ifnet *ifp; + + if (usb_no_pf != 0) { + ubus->ifp = NULL; + return; + } + + ifp = ubus->ifp = if_alloc(IFT_USB); + if (ifp == NULL) { + device_printf(ubus->parent, "usbpf: Could not allocate " + "instance\n"); + return; + } + + if_initname(ifp, "usbus", device_get_unit(ubus->bdev)); + ifp->if_flags = IFF_CANTCONFIG; + if_attach(ifp); + if_up(ifp); + + /* + * XXX According to the specification of DLT_USB, it indicates + * packets beginning with USB setup header. But not sure all + * packets would be. + */ + bpfattach(ifp, DLT_USB, USBPF_HDR_LEN); + + if (bootverbose) + device_printf(ubus->parent, "usbpf: Attached\n"); +} + +void +usbpf_detach(struct usb_bus *ubus) +{ + struct ifnet *ifp = ubus->ifp; + + if (ifp != NULL) { + bpfdetach(ifp); + if_down(ifp); + if_detach(ifp); + if_free(ifp); + } + ubus->ifp = NULL; +} + +static uint32_t +usbpf_aggregate_xferflags(struct usb_xfer_flags *flags) +{ + uint32_t val = 0; + + if (flags->force_short_xfer == 1) + val |= USBPF_FLAG_FORCE_SHORT_XFER; + if (flags->short_xfer_ok == 1) + val |= USBPF_FLAG_SHORT_XFER_OK; + if (flags->short_frames_ok == 1) + val |= USBPF_FLAG_SHORT_FRAMES_OK; + if (flags->pipe_bof == 1) + val |= USBPF_FLAG_PIPE_BOF; + if (flags->proxy_buffer == 1) + val |= USBPF_FLAG_PROXY_BUFFER; + if (flags->ext_buffer == 1) + val |= USBPF_FLAG_EXT_BUFFER; + if (flags->manual_status == 1) + val |= USBPF_FLAG_MANUAL_STATUS; + if (flags->no_pipe_ok == 1) + val |= USBPF_FLAG_NO_PIPE_OK; + if (flags->stall_pipe == 1) + val |= USBPF_FLAG_STALL_PIPE; + return (val); +} + +static uint32_t +usbpf_aggregate_status(struct usb_xfer_flags_int *flags) +{ + uint32_t val = 0; + + if (flags->open == 1) + val |= USBPF_STATUS_OPEN; + if (flags->transferring == 1) + val |= USBPF_STATUS_TRANSFERRING; + if (flags->did_dma_delay == 1) + val |= USBPF_STATUS_DID_DMA_DELAY; + if (flags->did_close == 1) + val |= USBPF_STATUS_DID_CLOSE; + if (flags->draining == 1) + val |= USBPF_STATUS_DRAINING; + if (flags->started == 1) + val |= USBPF_STATUS_STARTED; + if (flags->bandwidth_reclaimed == 1) + val |= USBPF_STATUS_BW_RECLAIMED; + if (flags->control_xfr == 1) + val |= USBPF_STATUS_CONTROL_XFR; + if (flags->control_hdr == 1) + val |= USBPF_STATUS_CONTROL_HDR; + if (flags->control_act == 1) + val |= USBPF_STATUS_CONTROL_ACT; + if (flags->control_stall == 1) + val |= USBPF_STATUS_CONTROL_STALL; + if (flags->short_frames_ok == 1) + val |= USBPF_STATUS_SHORT_FRAMES_OK; + if (flags->short_xfer_ok == 1) + val |= USBPF_STATUS_SHORT_XFER_OK; +#if USB_HAVE_BUSDMA + if (flags->bdma_enable == 1) + val |= USBPF_STATUS_BDMA_ENABLE; + if (flags->bdma_no_post_sync == 1) + val |= USBPF_STATUS_BDMA_NO_POST_SYNC; + if (flags->bdma_setup == 1) + val |= USBPF_STATUS_BDMA_SETUP; +#endif + if (flags->isochronous_xfr == 1) + val |= USBPF_STATUS_ISOCHRONOUS_XFR; + if (flags->curr_dma_set == 1) + val |= USBPF_STATUS_CURR_DMA_SET; + if (flags->can_cancel_immed == 1) + val |= USBPF_STATUS_CAN_CANCEL_IMMED; + if (flags->doing_callback == 1) + val |= USBPF_STATUS_DOING_CALLBACK; + + return (val); +} + +static int +usbpf_xfer_frame_is_read(struct usb_xfer *xfer, uint32_t frame) +{ + int isread; + + if ((frame == 0) && (xfer->flags_int.control_xfr != 0) && + (xfer->flags_int.control_hdr != 0)) { + /* special case */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + /* The device controller writes to memory */ + isread = 1; + } else { + /* The host controller reads from memory */ + isread = 0; + } + } else { + isread = USB_GET_DATA_ISREAD(xfer); + } + return (isread); +} + +static uint32_t +usbpf_xfer_precompute_size(struct usb_xfer *xfer, int type) +{ + uint32_t totlen; + uint32_t x; + uint32_t nframes; + + if (type == USBPF_XFERTAP_SUBMIT) + nframes = xfer->nframes; + else + nframes = xfer->aframes; + + totlen = USBPF_HDR_LEN + (USBPF_FRAME_HDR_LEN * nframes); + + /* precompute all trace lengths */ + for (x = 0; x != nframes; x++) { + if (usbpf_xfer_frame_is_read(xfer, x)) { + if (type != USBPF_XFERTAP_SUBMIT) { + totlen += USBPF_FRAME_ALIGN( + xfer->frlengths[x]); + } + } else { + if (type == USBPF_XFERTAP_SUBMIT) { + totlen += USBPF_FRAME_ALIGN( + xfer->frlengths[x]); + } + } + } + return (totlen); +} + +void +usbpf_xfertap(struct usb_xfer *xfer, int type) +{ + struct usb_bus *bus; + struct usbpf_pkthdr *up; + struct usbpf_framehdr *uf; + usb_frlength_t offset; + uint32_t totlen; + uint32_t frame; + uint32_t temp; + uint32_t nframes; + uint32_t x; + uint8_t *buf; + uint8_t *ptr; + + bus = xfer->xroot->bus; + + /* sanity checks */ + if (usb_no_pf != 0) + return; + if (bus->ifp == NULL) + return; + if (!bpf_peers_present(bus->ifp->if_bpf)) + return; + + totlen = usbpf_xfer_precompute_size(xfer, type); + + if (type == USBPF_XFERTAP_SUBMIT) + nframes = xfer->nframes; + else + nframes = xfer->aframes; + + /* + * XXX TODO XXX + * + * When BPF supports it we could pass a fragmented array of + * buffers avoiding the data copy operation here. + */ + buf = ptr = malloc(totlen, M_TEMP, M_NOWAIT); + if (buf == NULL) { + device_printf(bus->parent, "usbpf: Out of memory\n"); + return; + } + + up = (struct usbpf_pkthdr *)ptr; + ptr += USBPF_HDR_LEN; + + /* fill out header */ + temp = device_get_unit(bus->bdev); + up->up_totlen = htole32(totlen); + up->up_busunit = htole32(temp); + up->up_address = xfer->xroot->udev->device_index; + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) + up->up_mode = USBPF_MODE_DEVICE; + else + up->up_mode = USBPF_MODE_HOST; + up->up_type = type; + up->up_xfertype = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE; + temp = usbpf_aggregate_xferflags(&xfer->flags); + up->up_flags = htole32(temp); + temp = usbpf_aggregate_status(&xfer->flags_int); + up->up_status = htole32(temp); + temp = xfer->error; + up->up_error = htole32(temp); + temp = xfer->interval; + up->up_interval = htole32(temp); + up->up_frames = htole32(nframes); + temp = xfer->max_packet_size; + up->up_packet_size = htole32(temp); + temp = xfer->max_packet_count; + up->up_packet_count = htole32(temp); + temp = xfer->endpointno; + up->up_endpoint = htole32(temp); + up->up_speed = xfer->xroot->udev->speed; + + /* clear reserved area */ + memset(up->up_reserved, 0, sizeof(up->up_reserved)); + + /* init offset and frame */ + offset = 0; + frame = 0; + + /* iterate all the USB frames and copy data, if any */ + for (x = 0; x != nframes; x++) { + uint32_t length; + int isread; + + /* get length */ + length = xfer->frlengths[x]; + + /* get frame header pointer */ + uf = (struct usbpf_framehdr *)ptr; + ptr += USBPF_FRAME_HDR_LEN; + + /* fill out packet header */ + uf->length = htole32(length); + uf->flags = 0; + + /* get information about data read/write */ + isread = usbpf_xfer_frame_is_read(xfer, x); + + /* check if we need to copy any data */ + if (isread) { + if (type == USBPF_XFERTAP_SUBMIT) + length = 0; + else { + uf->flags |= htole32( + USBPF_FRAMEFLAG_DATA_FOLLOWS); + } + } else { + if (type != USBPF_XFERTAP_SUBMIT) + length = 0; + else { + uf->flags |= htole32( + USBPF_FRAMEFLAG_DATA_FOLLOWS); + } + } + + /* check if data is read direction */ + if (isread) + uf->flags |= htole32(USBPF_FRAMEFLAG_READ); + + /* copy USB data, if any */ + if (length != 0) { + /* copy data */ + usbd_copy_out(&xfer->frbuffers[frame], + offset, ptr, length); + + /* align length */ + temp = USBPF_FRAME_ALIGN(length); + + /* zero pad */ + if (temp != length) + memset(ptr + length, 0, temp - length); + + ptr += temp; + } + + if (xfer->flags_int.isochronous_xfr) { + offset += usbd_xfer_old_frame_length(xfer, x); + } else { + frame ++; + } + } + + bpf_tap(bus->ifp->if_bpf, buf, totlen); + + free(buf, M_TEMP); +} diff --git a/sys/dev/usb/usb_pf.h b/sys/dev/usb/usb_pf.h new file mode 100644 index 000000000..9d51e98c0 --- /dev/null +++ b/sys/dev/usb/usb_pf.h @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_USB_PF_H +#define _DEV_USB_PF_H + +struct usbpf_pkthdr { + uint32_t up_totlen; /* Total length including all headers */ + uint32_t up_busunit; /* Host controller unit number */ + uint8_t up_address; /* USB device index */ + uint8_t up_mode; /* Mode of transfer */ +#define USBPF_MODE_HOST 0 +#define USBPF_MODE_DEVICE 1 + uint8_t up_type; /* points SUBMIT / DONE */ + uint8_t up_xfertype; /* Transfer type, see USB2.0 spec. */ + uint32_t up_flags; /* Transfer flags */ +#define USBPF_FLAG_FORCE_SHORT_XFER (1 << 0) +#define USBPF_FLAG_SHORT_XFER_OK (1 << 1) +#define USBPF_FLAG_SHORT_FRAMES_OK (1 << 2) +#define USBPF_FLAG_PIPE_BOF (1 << 3) +#define USBPF_FLAG_PROXY_BUFFER (1 << 4) +#define USBPF_FLAG_EXT_BUFFER (1 << 5) +#define USBPF_FLAG_MANUAL_STATUS (1 << 6) +#define USBPF_FLAG_NO_PIPE_OK (1 << 7) +#define USBPF_FLAG_STALL_PIPE (1 << 8) + uint32_t up_status; /* Transfer status */ +#define USBPF_STATUS_OPEN (1 << 0) +#define USBPF_STATUS_TRANSFERRING (1 << 1) +#define USBPF_STATUS_DID_DMA_DELAY (1 << 2) +#define USBPF_STATUS_DID_CLOSE (1 << 3) +#define USBPF_STATUS_DRAINING (1 << 4) +#define USBPF_STATUS_STARTED (1 << 5) +#define USBPF_STATUS_BW_RECLAIMED (1 << 6) +#define USBPF_STATUS_CONTROL_XFR (1 << 7) +#define USBPF_STATUS_CONTROL_HDR (1 << 8) +#define USBPF_STATUS_CONTROL_ACT (1 << 9) +#define USBPF_STATUS_CONTROL_STALL (1 << 10) +#define USBPF_STATUS_SHORT_FRAMES_OK (1 << 11) +#define USBPF_STATUS_SHORT_XFER_OK (1 << 12) +#define USBPF_STATUS_BDMA_ENABLE (1 << 13) +#define USBPF_STATUS_BDMA_NO_POST_SYNC (1 << 14) +#define USBPF_STATUS_BDMA_SETUP (1 << 15) +#define USBPF_STATUS_ISOCHRONOUS_XFR (1 << 16) +#define USBPF_STATUS_CURR_DMA_SET (1 << 17) +#define USBPF_STATUS_CAN_CANCEL_IMMED (1 << 18) +#define USBPF_STATUS_DOING_CALLBACK (1 << 19) + uint32_t up_error; /* USB error, see USB_ERR_XXX */ + uint32_t up_interval; /* For interrupt and isoc (ms) */ + uint32_t up_frames; /* Number of following frames */ + uint32_t up_packet_size; /* Packet size used */ + uint32_t up_packet_count; /* Packet count used */ + uint32_t up_endpoint; /* USB endpoint / stream ID */ + uint8_t up_speed; /* USB speed, see USB_SPEED_XXX */ + /* sizeof(struct usbpf_pkthdr) == 128 bytes */ + uint8_t up_reserved[83]; +}; + +struct usbpf_framehdr { + /* + * The frame length field excludes length of frame header and + * any alignment. + */ + uint32_t length; +#define USBPF_FRAME_ALIGN(x) (((x) + 3) & ~3) + uint32_t flags; +#define USBPF_FRAMEFLAG_READ (1 << 0) +#define USBPF_FRAMEFLAG_DATA_FOLLOWS (1 << 1) +}; + +#define USBPF_HDR_LEN 128 /* bytes */ +#define USBPF_FRAME_HDR_LEN 8 /* bytes */ + +extern uint8_t usbpf_pkthdr_size_ok[ + (sizeof(struct usbpf_pkthdr) == USBPF_HDR_LEN) ? 1 : -1]; +extern uint8_t usbpf_framehdr_size_ok[ + (sizeof(struct usbpf_framehdr) == USBPF_FRAME_HDR_LEN) ? 1 : -1]; + +#define USBPF_XFERTAP_SUBMIT 0 +#define USBPF_XFERTAP_DONE 1 + +#ifdef _KERNEL +void usbpf_attach(struct usb_bus *); +void usbpf_detach(struct usb_bus *); +void usbpf_xfertap(struct usb_xfer *, int); +#endif + +#endif diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index d2102b425..5fd4f5a19 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -59,6 +59,7 @@ #include #include +#include struct usb_std_packet_size { struct { @@ -663,9 +664,13 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) } xfer->max_data_length -= REQ_SIZE; } - /* setup "frlengths" */ + /* + * Setup "frlengths" and shadow "frlengths" for keeping the + * initial frame lengths when a USB transfer is complete. This + * information is useful when computing isochronous offsets. + */ xfer->frlengths = parm->xfer_length_ptr; - parm->xfer_length_ptr += n_frlengths; + parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; @@ -1578,9 +1583,12 @@ usbd_transfer_submit(struct usb_xfer *xfer) USB_BUS_UNLOCK(bus); return; } - /* compute total transfer length */ + /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { + /* make a copy of the frlenghts[] */ + xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; + /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ @@ -1969,6 +1977,22 @@ usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, *len = xfer->frlengths[frindex]; } +/*------------------------------------------------------------------------* + * usbd_xfer_old_frame_length + * + * This function returns the framelength of the given frame at the + * time the transfer was submitted. This function can be used to + * compute the starting data pointer of the next isochronous frame + * when an isochronous transfer has completed. + *------------------------------------------------------------------------*/ +usb_frlength_t +usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) +{ + KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); + + return (xfer->frlengths[frindex + xfer->max_frame_count]); +} + void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) @@ -2199,6 +2223,10 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq) } } +#if USB_HAVE_PF + if (xfer->usb_state != USB_ST_SETUP) + usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); +#endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); @@ -2386,6 +2414,9 @@ usbd_transfer_start_cb(void *arg) DPRINTF("start\n"); +#if USB_HAVE_PF + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); +#endif /* start the transfer */ (ep->methods->start) (xfer); @@ -2563,6 +2594,9 @@ usbd_pipe_start(struct usb_xfer_queue *pq) } DPRINTF("start\n"); +#if USB_HAVE_PF + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); +#endif /* start USB transfer */ (ep->methods->start) (xfer); diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 8ab155b1c..0db6cc035 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -493,6 +493,8 @@ void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); +usb_frlength_t + usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *xfer, diff --git a/sys/modules/usb/usb/Makefile b/sys/modules/usb/usb/Makefile index 32a210750..5bae119aa 100644 --- a/sys/modules/usb/usb/Makefile +++ b/sys/modules/usb/usb/Makefile @@ -32,11 +32,11 @@ S= ${.CURDIR}/../../.. KMOD= usb SRCS= bus_if.h device_if.h usb_if.h usb_if.c vnode_if.h \ opt_usb.h opt_bus.h opt_ddb.h \ - usbdevs.h usbdevs_data.h \ + usb_pf.h usbdevs.h usbdevs_data.h \ usb_busdma.c usb_controller.c usb_compat_linux.c usb_core.c usb_debug.c \ usb_dev.c usb_device.c usb_dynamic.c usb_error.c usb_generic.c \ usb_handle_request.c usb_hid.c usb_hub.c usb_lookup.c usb_mbuf.c \ - usb_msctest.c usb_parse.c usb_process.c usb_request.c \ + usb_msctest.c usb_parse.c usb_pf.c usb_process.c usb_request.c \ usb_transfer.c usb_util.c .include diff --git a/sys/net/if.h b/sys/net/if.h index a99b4a7c8..d291da85f 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -145,7 +145,7 @@ struct if_data { #define IFF_LINK2 0x4000 /* per link layer defined bit */ #define IFF_ALTPHYS IFF_LINK2 /* use alternate physical connection */ #define IFF_MULTICAST 0x8000 /* (i) supports multicast */ -/* 0x10000 */ +#define IFF_CANTCONFIG 0x10000 /* (i) unconfigurable using ioctl(2) */ #define IFF_PPROMISC 0x20000 /* (n) user-requested promisc mode */ #define IFF_MONITOR 0x40000 /* (n) user-requested monitor mode */ #define IFF_STATICARP 0x80000 /* (n) static ARP */ @@ -165,7 +165,7 @@ struct if_data { #define IFF_CANTCHANGE \ (IFF_BROADCAST|IFF_POINTOPOINT|IFF_DRV_RUNNING|IFF_DRV_OACTIVE|\ IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_SMART|IFF_PROMISC|\ - IFF_DYING) + IFF_DYING|IFF_CANTCONFIG) /* * Values for if_link_state. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 44f20a49c..82bc9c0e9 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -192,6 +192,7 @@ SUBDIR= ${_ac} \ ugidfw \ ${_uhsoctl} \ ${_usbdevs} \ + ${_usbdump} \ ${_usbconfig} \ ${_vidcontrol} \ vipw \ @@ -418,6 +419,7 @@ _uathload= uathload .endif _uhsoctl= uhsoctl #_usbdevs= usbdevs +_usbdump= usbdump _usbconfig= usbconfig .endif diff --git a/usr.sbin/usbdump/Makefile b/usr.sbin/usbdump/Makefile new file mode 100644 index 000000000..51889010b --- /dev/null +++ b/usr.sbin/usbdump/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= usbdump +SRCS= usbdump.c +MAN= usbdump.8 +WARNS?= 4 + +.include diff --git a/usr.sbin/usbdump/usbdump.8 b/usr.sbin/usbdump/usbdump.8 new file mode 100644 index 000000000..edd883ac7 --- /dev/null +++ b/usr.sbin/usbdump/usbdump.8 @@ -0,0 +1,77 @@ +.\" +.\" Copyright (c) 2010 Weongyo Jeong. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 14, 2010 +.Dt usbdump 8 +.Os +.Sh NAME +.Nm usbdump +.Nd "dump traffic on USB host controller" +.Sh SYNOPSIS +.Nm +.Op Fl i Ar ifname +.Op Fl r Ar file +.Op Fl s Ar snaplen +.Op Fl v +.Op Fl w Ar file +.Sh DESCRIPTION +The +.Nm +utility provides a way to dump USB packets on each host controller. +.Pp +The following options are accepted. +.Bl -tag -width ".Fl f Ar file" +.It Fl i Ar ifname +Listen on USB bus interface. +.It Fl r Ar file +Read the raw packets from file. +.It Fl s Ar snaplen +Snapshot bytes from each packet. +.It Fl v +Enable debugging messages. +When it defined multiple times the verbose level increases. +.It Fl w Ar file +Write the raw packets to file. +.El +.Sh EXAMPLES +Captures the USB raw packets alive on usbus2: +.Pp +.Dl "usbdump -i usbus2 -s 256 -v" +.Pp +Dumps the USB raw packets of usbus2 into the file without packet +size limit: +.Pp +.Dl "usbdump -i usbus2 -s 0 -w /tmp/dump_pkts" +.Pp +Read the USB raw packets from the file: +.Pp +.Dl "usbdump -r /tmp/dump_pkts -v" +.Sh SEE ALSO +.Xr usbconfig 8 +.Sh AUTHORS +.An Weongyo Jeong +.Aq weongyo@FreeBSD.org . diff --git a/usr.sbin/usbdump/usbdump.c b/usr.sbin/usbdump/usbdump.c new file mode 100644 index 000000000..617e5c131 --- /dev/null +++ b/usr.sbin/usbdump/usbdump.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 2010 Weongyo Jeong + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usbcap { + int fd; /* fd for /dev/usbpf */ + uint32_t bufsize; + uint8_t *buffer; + + /* for -w option */ + int wfd; + /* for -r option */ + int rfd; +}; + +struct usbcap_filehdr { + uint32_t magic; +#define USBCAP_FILEHDR_MAGIC 0x9a90000e + uint8_t major; + uint8_t minor; + uint8_t reserved[26]; +} __packed; + +#if __FreeBSD_version < 900000 +#define bpf_xhdr bpf_hdr +#define bt_sec tv_sec +#define bt_frac tv_usec +#endif + +static int doexit = 0; +static int pkt_captured = 0; +static int verbose = 0; +static const char *i_arg = "usbus0"; +static const char *r_arg = NULL; +static const char *w_arg = NULL; +static const char *errstr_table[USB_ERR_MAX] = { + [USB_ERR_NORMAL_COMPLETION] = "0", + [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", + [USB_ERR_NOT_STARTED] = "NOT_STARTED", + [USB_ERR_INVAL] = "INVAL", + [USB_ERR_NOMEM] = "NOMEM", + [USB_ERR_CANCELLED] = "CANCELLED", + [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", + [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", + [USB_ERR_BAD_FLAG] = "BAD_FLAG", + [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", + [USB_ERR_IN_USE] = "IN_USE", + [USB_ERR_NO_ADDR] = "NO_ADDR", + [USB_ERR_NO_PIPE] = "NO_PIPE", + [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", + [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", + [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", + [USB_ERR_NO_POWER] = "NO_POWER", + [USB_ERR_TOO_DEEP] = "TOO_DEEP", + [USB_ERR_IOERROR] = "IOERROR", + [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", + [USB_ERR_TIMEOUT] = "TIMEOUT", + [USB_ERR_SHORT_XFER] = "SHORT_XFER", + [USB_ERR_STALLED] = "STALLED", + [USB_ERR_INTERRUPTED] = "INTERRUPTED", + [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", + [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", + [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", + [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", + [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", +}; + +static const char *xfertype_table[4] = { + [UE_CONTROL] = "CTRL", + [UE_ISOCHRONOUS] = "ISOC", + [UE_BULK] = "BULK", + [UE_INTERRUPT] = "INTR" +}; + +static const char *speed_table[USB_SPEED_MAX] = { + [USB_SPEED_FULL] = "FULL", + [USB_SPEED_HIGH] = "HIGH", + [USB_SPEED_LOW] = "LOW", + [USB_SPEED_VARIABLE] = "VARI", + [USB_SPEED_SUPER] = "SUPER", +}; + +static void +handle_sigint(int sig) +{ + + (void)sig; + doexit = 1; +} + +#define FLAGS(x, name) \ + (((x) & USBPF_FLAG_##name) ? #name "|" : "") + +#define STATUS(x, name) \ + (((x) & USBPF_STATUS_##name) ? #name "|" : "") + +static const char * +usb_errstr(uint32_t error) +{ + if (error >= USB_ERR_MAX || errstr_table[error] == NULL) + return ("UNKNOWN"); + else + return (errstr_table[error]); +} + +static const char * +usb_speedstr(uint8_t speed) +{ + if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) + return ("UNKNOWN"); + else + return (speed_table[speed]); +} + +static void +print_flags(uint32_t flags) +{ + printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", + flags, + FLAGS(flags, FORCE_SHORT_XFER), + FLAGS(flags, SHORT_XFER_OK), + FLAGS(flags, SHORT_FRAMES_OK), + FLAGS(flags, PIPE_BOF), + FLAGS(flags, PROXY_BUFFER), + FLAGS(flags, EXT_BUFFER), + FLAGS(flags, MANUAL_STATUS), + FLAGS(flags, NO_PIPE_OK), + FLAGS(flags, STALL_PIPE)); +} + +static void +print_status(uint32_t status) +{ + printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", + status, + STATUS(status, OPEN), + STATUS(status, TRANSFERRING), + STATUS(status, DID_DMA_DELAY), + STATUS(status, DID_CLOSE), + STATUS(status, DRAINING), + STATUS(status, STARTED), + STATUS(status, BW_RECLAIMED), + STATUS(status, CONTROL_XFR), + STATUS(status, CONTROL_HDR), + STATUS(status, CONTROL_ACT), + STATUS(status, CONTROL_STALL), + STATUS(status, SHORT_FRAMES_OK), + STATUS(status, SHORT_XFER_OK), + STATUS(status, BDMA_ENABLE), + STATUS(status, BDMA_NO_POST_SYNC), + STATUS(status, BDMA_SETUP), + STATUS(status, ISOCHRONOUS_XFR), + STATUS(status, CURR_DMA_SET), + STATUS(status, CAN_CANCEL_IMMED), + STATUS(status, DOING_CALLBACK)); +} + +/* + * Dump a byte into hex format. + */ +static void +hexbyte(char *buf, uint8_t temp) +{ + uint8_t lo; + uint8_t hi; + + lo = temp & 0xF; + hi = temp >> 4; + + if (hi < 10) + buf[0] = '0' + hi; + else + buf[0] = 'A' + hi - 10; + + if (lo < 10) + buf[1] = '0' + lo; + else + buf[1] = 'A' + lo - 10; +} + +/* + * Display a region in traditional hexdump format. + */ +static void +hexdump(const uint8_t *region, uint32_t len) +{ + const uint8_t *line; + char linebuf[128]; + int i; + int x; + int c; + + for (line = region; line < (region + len); line += 16) { + + i = 0; + + linebuf[i] = ' '; + hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); + hexbyte(linebuf + i + 3, (line - region) & 0xFF); + linebuf[i + 5] = ' '; + linebuf[i + 6] = ' '; + i += 7; + + for (x = 0; x < 16; x++) { + if ((line + x) < (region + len)) { + hexbyte(linebuf + i, + *(const u_int8_t *)(line + x)); + } else { + linebuf[i] = '-'; + linebuf[i + 1] = '-'; + } + linebuf[i + 2] = ' '; + if (x == 7) { + linebuf[i + 3] = ' '; + i += 4; + } else { + i += 3; + } + } + linebuf[i] = ' '; + linebuf[i + 1] = '|'; + i += 2; + for (x = 0; x < 16; x++) { + if ((line + x) < (region + len)) { + c = *(const u_int8_t *)(line + x); + /* !isprint(c) */ + if ((c < ' ') || (c > '~')) + c = '.'; + linebuf[i] = c; + } else { + linebuf[i] = ' '; + } + i++; + } + linebuf[i] = '|'; + linebuf[i + 1] = 0; + i += 2; + puts(linebuf); + } +} + +static void +print_apacket(const struct bpf_xhdr *hdr, const uint8_t *ptr, int ptr_len) +{ + struct tm *tm; + struct usbpf_pkthdr up_temp; + struct usbpf_pkthdr *up; + struct timeval tv; + size_t len; + uint32_t x; + char buf[64]; + + ptr += USBPF_HDR_LEN; + ptr_len -= USBPF_HDR_LEN; + if (ptr_len < 0) + return; + + /* make sure we don't change the source buffer */ + memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); + up = &up_temp; + + /* + * A packet from the kernel is based on little endian byte + * order. + */ + up->up_totlen = le32toh(up->up_totlen); + up->up_busunit = le32toh(up->up_busunit); + up->up_address = le32toh(up->up_address); + up->up_flags = le32toh(up->up_flags); + up->up_status = le32toh(up->up_status); + up->up_error = le32toh(up->up_error); + up->up_interval = le32toh(up->up_interval); + up->up_frames = le32toh(up->up_frames); + up->up_packet_size = le32toh(up->up_packet_size); + up->up_packet_count = le32toh(up->up_packet_count); + up->up_endpoint = le32toh(up->up_endpoint); + + tv.tv_sec = hdr->bh_tstamp.bt_sec; + tv.tv_usec = hdr->bh_tstamp.bt_frac; + tm = localtime(&tv.tv_sec); + + len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); + + printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", + (int)len, buf, tv.tv_usec, + (int)up->up_busunit, (int)up->up_address, + (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", + xfertype_table[up->up_xfertype], + (unsigned int)up->up_endpoint, + usb_speedstr(up->up_speed), + (int)up->up_frames, + (int)(up->up_totlen - USBPF_HDR_LEN - + (USBPF_FRAME_HDR_LEN * up->up_frames)), + (int)up->up_interval, + (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", + (up->up_type == USBPF_XFERTAP_DONE) ? + usb_errstr(up->up_error) : ""); + + if (verbose >= 1) { + for (x = 0; x != up->up_frames; x++) { + const struct usbpf_framehdr *uf; + uint32_t framelen; + uint32_t flags; + + uf = (const struct usbpf_framehdr *)ptr; + ptr += USBPF_FRAME_HDR_LEN; + ptr_len -= USBPF_FRAME_HDR_LEN; + if (ptr_len < 0) + return; + + framelen = le32toh(uf->length); + flags = le32toh(uf->flags); + + printf(" frame[%u] %s %d bytes\n", + (unsigned int)x, + (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", + (int)framelen); + + if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { + + int tot_frame_len; + + tot_frame_len = USBPF_FRAME_ALIGN(framelen); + + ptr_len -= tot_frame_len; + + if (tot_frame_len < 0 || + (int)framelen < 0 || (int)ptr_len < 0) + break; + + hexdump(ptr, framelen); + + ptr += tot_frame_len; + } + } + } + if (verbose >= 2) + print_flags(up->up_flags); + if (verbose >= 3) + print_status(up->up_status); +} + +static void +print_packets(uint8_t *data, const int datalen) +{ + const struct bpf_xhdr *hdr; + uint8_t *ptr; + uint8_t *next; + + for (ptr = data; ptr < (data + datalen); ptr = next) { + hdr = (const struct bpf_xhdr *)ptr; + next = ptr + BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); + + if (w_arg == NULL) { + print_apacket(hdr, ptr + + hdr->bh_hdrlen, hdr->bh_caplen); + } + pkt_captured++; + } +} + +static void +write_packets(struct usbcap *p, const uint8_t *data, const int datalen) +{ + int len = htole32(datalen); + int ret; + + ret = write(p->wfd, &len, sizeof(int)); + assert(ret == sizeof(int)); + ret = write(p->wfd, data, datalen); + assert(ret == datalen); +} + +static void +read_file(struct usbcap *p) +{ + int datalen; + int ret; + uint8_t *data; + + while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { + datalen = le32toh(datalen); + data = malloc(datalen); + assert(data != NULL); + ret = read(p->rfd, data, datalen); + assert(ret == datalen); + print_packets(data, datalen); + free(data); + } + if (ret == -1) + fprintf(stderr, "read: %s\n", strerror(errno)); +} + +static void +do_loop(struct usbcap *p) +{ + int cc; + + while (doexit == 0) { + cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); + if (cc < 0) { + switch (errno) { + case EINTR: + break; + default: + fprintf(stderr, "read: %s\n", strerror(errno)); + return; + } + continue; + } + if (cc == 0) + continue; + if (w_arg != NULL) + write_packets(p, p->buffer, cc); + print_packets(p->buffer, cc); + } +} + +static void +init_rfile(struct usbcap *p) +{ + struct usbcap_filehdr uf; + int ret; + + p->rfd = open(r_arg, O_RDONLY); + if (p->rfd < 0) { + fprintf(stderr, "open: %s (%s)\n", r_arg, strerror(errno)); + exit(EXIT_FAILURE); + } + ret = read(p->rfd, &uf, sizeof(uf)); + assert(ret == sizeof(uf)); + assert(le32toh(uf.magic) == USBCAP_FILEHDR_MAGIC); + assert(uf.major == 0); + assert(uf.minor == 2); +} + +static void +init_wfile(struct usbcap *p) +{ + struct usbcap_filehdr uf; + int ret; + + p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); + if (p->wfd < 0) { + fprintf(stderr, "open: %s (%s)\n", w_arg, strerror(errno)); + exit(EXIT_FAILURE); + } + bzero(&uf, sizeof(uf)); + uf.magic = htole32(USBCAP_FILEHDR_MAGIC); + uf.major = 0; + uf.minor = 2; + ret = write(p->wfd, (const void *)&uf, sizeof(uf)); + assert(ret == sizeof(uf)); +} + +static void +usage(void) +{ + +#define FMT " %-14s %s\n" + fprintf(stderr, "usage: usbdump [options]\n"); + fprintf(stderr, FMT, "-i ifname", "Listen on USB bus interface"); + fprintf(stderr, FMT, "-r file", "Read the raw packets from file"); + fprintf(stderr, FMT, "-s snaplen", "Snapshot bytes from each packet"); + fprintf(stderr, FMT, "-v", "Increases the verbose level"); + fprintf(stderr, FMT, "-w file", "Write the raw packets to file"); +#undef FMT + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct timeval tv; + struct bpf_insn total_insn; + struct bpf_program total_prog; + struct bpf_stat us; + struct bpf_version bv; + struct usbcap uc, *p = &uc; + struct ifreq ifr; + long snapshot = 192; + uint32_t v; + int fd, o; + const char *optstring; + + bzero(&uc, sizeof(struct usbcap)); + + optstring = "i:r:s:vw:"; + while ((o = getopt(argc, argv, optstring)) != -1) { + switch (o) { + case 'i': + i_arg = optarg; + break; + case 'r': + r_arg = optarg; + init_rfile(p); + break; + case 's': + snapshot = strtol(optarg, NULL, 10); + errno = 0; + if (snapshot == 0 && errno == EINVAL) + usage(); + /* snapeshot == 0 is special */ + if (snapshot == 0) + snapshot = -1; + break; + case 'v': + verbose++; + break; + case 'w': + w_arg = optarg; + init_wfile(p); + break; + default: + usage(); + /* NOTREACHED */ + } + } + + if (r_arg != NULL) { + read_file(p); + exit(EXIT_SUCCESS); + } + + p->fd = fd = open("/dev/bpf", O_RDONLY); + if (p->fd < 0) { + fprintf(stderr, "(no devices found)\n"); + return (EXIT_FAILURE); + } + + if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { + fprintf(stderr, "BIOCVERSION: %s\n", strerror(errno)); + return (EXIT_FAILURE); + } + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + fprintf(stderr, "kernel bpf filter out of date"); + return (EXIT_FAILURE); + } + + /* USB transfers can be greater than 64KByte */ + v = 1U << 16; + + /* clear ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + + for ( ; v >= USBPF_HDR_LEN; v >>= 1) { + (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); + (void)strncpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) + break; + } + if (v == 0) { + fprintf(stderr, "BIOCSBLEN: %s: No buffer size worked", i_arg); + return (EXIT_FAILURE); + } + + if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { + fprintf(stderr, "BIOCGBLEN: %s", strerror(errno)); + return (EXIT_FAILURE); + } + + p->bufsize = v; + p->buffer = (uint8_t *)malloc(p->bufsize); + if (p->buffer == NULL) { + fprintf(stderr, "malloc: %s", strerror(errno)); + return (EXIT_FAILURE); + } + + /* XXX no read filter rules yet so at this moment accept everything */ + total_insn.code = (u_short)(BPF_RET | BPF_K); + total_insn.jt = 0; + total_insn.jf = 0; + total_insn.k = snapshot; + + total_prog.bf_len = 1; + total_prog.bf_insns = &total_insn; + if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) { + fprintf(stderr, "BIOCSETF: %s", strerror(errno)); + return (EXIT_FAILURE); + } + + /* 1 second read timeout */ + tv.tv_sec = 1; + tv.tv_usec = 0; + if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) { + fprintf(stderr, "BIOCSRTIMEOUT: %s", strerror(errno)); + return (EXIT_FAILURE); + } + + (void)signal(SIGINT, handle_sigint); + + do_loop(p); + + if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) { + fprintf(stderr, "BIOCGSTATS: %s", strerror(errno)); + return (EXIT_FAILURE); + } + + /* XXX what's difference between pkt_captured and us.us_recv? */ + printf("\n"); + printf("%d packets captured\n", pkt_captured); + printf("%d packets received by filter\n", us.bs_recv); + printf("%d packets dropped by kernel\n", us.bs_drop); + + if (p->fd > 0) + close(p->fd); + if (p->rfd > 0) + close(p->rfd); + if (p->wfd > 0) + close(p->wfd); + + return (EXIT_SUCCESS); +} -- 2.45.0