From 4b5720c2d35696756422b95d8399f38f3102b203 Mon Sep 17 00:00:00 2001 From: mpi Date: Sat, 3 Feb 2018 13:37:37 +0000 Subject: [PATCH] Add support for dumping USB transfers via bpf(4) using USBPcap headers. ok deraadt@, dlg@ --- sys/dev/usb/usb.c | 102 ++++++++++++++++++++++++++++++++++++++++- sys/dev/usb/usbdi.c | 13 ++++-- sys/dev/usb/usbdivar.h | 15 +++++- sys/dev/usb/usbpcap.h | 56 ++++++++++++++++++++++ sys/net/bpf.h | 3 +- 5 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 sys/dev/usb/usbpcap.h diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c index 8139db6a4ae..a9b530ee65d 100644 --- a/sys/dev/usb/usb.c +++ b/sys/dev/usb/usb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usb.c,v 1.114 2017/07/29 18:26:14 ians Exp $ */ +/* $OpenBSD: usb.c,v 1.115 2018/02/03 13:37:37 mpi Exp $ */ /* $NetBSD: usb.c,v 1.77 2003/01/01 00:10:26 thorpej Exp $ */ /* @@ -63,6 +63,8 @@ #include #include +#include +#include #ifdef USB_DEBUG #define DPRINTF(x) do { if (usbdebug) printf x; } while (0) @@ -183,6 +185,11 @@ usb_attach(struct device *parent, struct device *self, void *aux) } printf("\n"); +#if NBPFILTER > 0 + sc->sc_bus->bpfif = bpfsattach(&sc->sc_bus->bpf, sc->sc_dev.dv_xname, + DLT_USBPCAP, sizeof(struct usbpcap_pkt_hdr)); +#endif + /* Make sure not to use tsleep() if we are cold booting. */ if (cold) sc->sc_bus->use_polling++; @@ -973,5 +980,98 @@ usb_detach(struct device *self, int flags) sc->sc_bus->soft = NULL; } +#if NBPFILTER > 0 + bpfsdetach(sc->sc_bus->bpfif); +#endif return (0); } + +void +usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) +{ +#if NBPFILTER > 0 + struct usb_softc *sc = (struct usb_softc *)bus->usbctl; + usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc; + struct usbpcap_ctl_hdr uch; + struct usbpcap_pkt_hdr *uph = &uch.uch_hdr; + unsigned int bpfdir; + void *data = NULL; + caddr_t bpf; + + bpf = bus->bpf; + if (bpf == NULL) + return; + + switch (UE_GET_XFERTYPE(ed->bmAttributes)) { + case UE_CONTROL: + /* Control transfer headers include an extra byte. */ + uph->uph_hlen = htole16(sizeof(uch)); + uph->uph_xfertype = USBPCAP_TRANSFER_CONTROL; + break; + case UE_ISOCHRONOUS: + uph->uph_hlen = htole16(sizeof(*uph)); + uph->uph_xfertype = USBPCAP_TRANSFER_ISOCHRONOUS; + break; + case UE_BULK: + uph->uph_hlen = htole16(sizeof(*uph)); + uph->uph_xfertype = USBPCAP_TRANSFER_BULK; + break; + case UE_INTERRUPT: + uph->uph_hlen = htole16(sizeof(*uph)); + uph->uph_xfertype = USBPCAP_TRANSFER_INTERRUPT; + break; + } + + uph->uph_id = 0; /* not yet used */ + uph->uph_status = htole32(xfer->status); + uph->uph_function = 0; /* not yet used */ + uph->uph_bus = htole32(sc->sc_dev.dv_unit); + uph->uph_devaddr = htole16(xfer->device->address); + uph->uph_epaddr = ed->bEndpointAddress; + uph->uph_info = 0; + + /* Outgoing control requests start with a STAGE dump. */ + if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_OUT)) { + uch.uch_stage = USBPCAP_CONTROL_STAGE_SETUP; + uph->uph_dlen = sizeof(usb_device_request_t); + bpf_tap_hdr(bpf, uph, uph->uph_hlen, &xfer->request, + uph->uph_dlen, BPF_DIRECTION_OUT); + } + + if (dir == USBTAP_DIR_OUT) { + bpfdir = BPF_DIRECTION_OUT; + if (!usbd_xfer_isread(xfer)) { + data = KERNADDR(&xfer->dmabuf, 0); + uph->uph_dlen = xfer->length; + uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; + } else { + data = NULL; + uph->uph_dlen = 0; + uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; + } + } else { /* USBTAP_DIR_IN */ + bpfdir = BPF_DIRECTION_IN; + uph->uph_info = USBPCAP_INFO_DIRECTION_IN; + if (usbd_xfer_isread(xfer)) { + data = KERNADDR(&xfer->dmabuf, 0); + uph->uph_dlen = xfer->actlen; + uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; + } else { + data = NULL; + uph->uph_dlen = 0; + uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; + } + } + + /* Dump bulk/intr/iso data, ctrl DATA or STATUS stage. */ + bpf_tap_hdr(bpf, uph, uph->uph_hlen, data, uph->uph_dlen, bpfdir); + + /* Incoming control requests with DATA need a STATUS stage. */ + if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_IN) && + (uch.uch_stage == USBPCAP_CONTROL_STAGE_DATA)) { + uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; + uph->uph_dlen = 0; + bpf_tap_hdr(bpf, uph, uph->uph_hlen, NULL, 0, BPF_DIRECTION_IN); + } +#endif +} diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c index b0d3f38f36c..29ff9e41ad9 100644 --- a/sys/dev/usb/usbdi.c +++ b/sys/dev/usb/usbdi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdi.c,v 1.96 2017/09/21 07:44:06 mpi Exp $ */ +/* $OpenBSD: usbdi.c,v 1.97 2018/02/03 13:37:37 mpi Exp $ */ /* $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.28 1999/11/17 22:33:49 n_hibma Exp $ */ @@ -320,6 +320,8 @@ usbd_transfer(struct usbd_xfer *xfer) usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_PREREAD); + usb_tap(bus, xfer, USBTAP_DIR_OUT); + err = pipe->methods->transfer(xfer); if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION) { @@ -715,7 +717,8 @@ void usb_transfer_complete(struct usbd_xfer *xfer) { struct usbd_pipe *pipe = xfer->pipe; - int polling = pipe->device->bus->use_polling; + struct usbd_bus *bus = pipe->device->bus; + int polling = bus->use_polling; int status, flags; #if 0 @@ -759,7 +762,7 @@ usb_transfer_complete(struct usbd_xfer *xfer) /* if we allocated the buffer in usbd_transfer() we free it here. */ if (xfer->rqflags & URQ_AUTO_DMABUF) { if (!pipe->repeat) { - usb_freemem(pipe->device->bus, &xfer->dmabuf); + usb_freemem(bus, &xfer->dmabuf); xfer->rqflags &= ~URQ_AUTO_DMABUF; } } @@ -778,7 +781,7 @@ usb_transfer_complete(struct usbd_xfer *xfer) pipe->repeat, SIMPLEQ_FIRST(&pipe->queue))); /* Count completed transfers. */ - ++pipe->device->bus->stats.uds_requests + ++bus->stats.uds_requests [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE]; xfer->done = 1; @@ -789,6 +792,8 @@ usb_transfer_complete(struct usbd_xfer *xfer) xfer->status = USBD_SHORT_XFER; } + usb_tap(bus, xfer, USBTAP_DIR_IN); + /* * We cannot dereference ``xfer'' after calling the callback as * it might free it. diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index 08462e687c3..cc653bc5c28 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdivar.h,v 1.72 2017/04/08 02:57:25 deraadt Exp $ */ +/* $OpenBSD: usbdivar.h,v 1.73 2018/02/03 13:37:37 mpi Exp $ */ /* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */ @@ -35,6 +35,11 @@ #ifndef _USBDIVAR_H_ #define _USBDIVAR_H_ +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#endif + #include /* From usb_mem.h */ @@ -100,6 +105,10 @@ struct usbd_bus { /* Filled by HC driver */ struct device bdev; /* base device, host adapter */ struct usbd_bus_methods *methods; +#if NBPFILTER > 0 + void *bpfif; + caddr_t bpf; +#endif u_int32_t pipe_size; /* size of a pipe struct */ /* Filled by usb driver */ struct usbd_device *root_hub; @@ -253,6 +262,10 @@ int usbd_detach(struct usbd_device *, struct device *); void usb_needs_explore(struct usbd_device *, int); void usb_needs_reattach(struct usbd_device *); void usb_schedsoftintr(struct usbd_bus *); +void usb_tap(struct usbd_bus *, struct usbd_xfer *, uint8_t); + +#define USBTAP_DIR_OUT 0 +#define USBTAP_DIR_IN 1 #define UHUB_UNK_CONFIGURATION -1 #define UHUB_UNK_INTERFACE -1 diff --git a/sys/dev/usb/usbpcap.h b/sys/dev/usb/usbpcap.h new file mode 100644 index 00000000000..890c830707e --- /dev/null +++ b/sys/dev/usb/usbpcap.h @@ -0,0 +1,56 @@ +/* $OpenBSD: usbpcap.h,v 1.1 2018/02/03 13:37:37 mpi Exp $ */ + +/* + * Copyright (c) 2018 Martin Pieuchot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _USBCAP_H_ +#define _USBCAP_H_ + +/* + * Common DLT_USBPCAP header. + */ +struct usbpcap_pkt_hdr { + uint16_t uph_hlen; /* header length */ + uint64_t uph_id; /* request ID */ + uint32_t uph_status; /* USB status code */ + uint16_t uph_function; /* stack function ID */ + uint8_t uph_info; /* info flags */ +#define USBPCAP_INFO_DIRECTION_IN (1 << 0)/* from Device to Host */ + + uint16_t uph_bus; /* bus number */ + uint16_t uph_devaddr; /* device address */ + uint8_t uph_epaddr; /* copy of bEndpointAddress */ + uint8_t uph_xfertype; /* transfer type */ +#define USBPCAP_TRANSFER_ISOCHRONOUS 0 +#define USBPCAP_TRANSFER_INTERRUPT 1 +#define USBPCAP_TRANSFER_CONTROL 2 +#define USBPCAP_TRANSFER_BULK 3 + + uint32_t uph_dlen; /* data length */ +} __attribute__((packed)); + +/* + * Header used when dumping control transfers. + */ +struct usbpcap_ctl_hdr { + struct usbpcap_pkt_hdr uch_hdr; + uint8_t uch_stage; +#define USBPCAP_CONTROL_STAGE_SETUP 0 +#define USBPCAP_CONTROL_STAGE_DATA 1 +#define USBPCAP_CONTROL_STAGE_STATUS 2 +} __attribute__((packed)); + +#endif /* _USBCAP_H_ */ diff --git a/sys/net/bpf.h b/sys/net/bpf.h index cb713eef697..604bdcbab55 100644 --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bpf.h,v 1.64 2018/02/01 12:10:27 dlg Exp $ */ +/* $OpenBSD: bpf.h,v 1.65 2018/02/03 13:37:37 mpi Exp $ */ /* $NetBSD: bpf.h,v 1.15 1996/12/13 07:57:33 mikel Exp $ */ /* @@ -201,6 +201,7 @@ struct bpf_hdr { #define DLT_USER13 160 /* Reserved for private use */ #define DLT_USER14 161 /* Reserved for private use */ #define DLT_USER15 162 /* Reserved for private use */ +#define DLT_USBPCAP 249 /* USBPcap */ #define DLT_MPLS 219 /* MPLS Provider Edge header */ #define DLT_OPENFLOW 267 /* in-kernel OpenFlow, by pcap */ -- 2.20.1