Add support for dumping USB transfers via bpf(4) using USBPcap headers.
authormpi <mpi@openbsd.org>
Sat, 3 Feb 2018 13:37:37 +0000 (13:37 +0000)
committermpi <mpi@openbsd.org>
Sat, 3 Feb 2018 13:37:37 +0000 (13:37 +0000)
ok deraadt@, dlg@

sys/dev/usb/usb.c
sys/dev/usb/usbdi.c
sys/dev/usb/usbdivar.h
sys/dev/usb/usbpcap.h [new file with mode: 0644]
sys/net/bpf.h

index 8139db6..a9b530e 100644 (file)
@@ -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 <machine/bus.h>
 
 #include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usbpcap.h>
 
 #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
+}
index b0d3f38..29ff9e4 100644 (file)
@@ -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.
index 08462e6..cc653bc 100644 (file)
@@ -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 $   */
 
 #ifndef _USBDIVAR_H_
 #define _USBDIVAR_H_
 
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
 #include <sys/timeout.h>
 
 /* 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 (file)
index 0000000..890c830
--- /dev/null
@@ -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_ */
index cb713ee..604bdcb 100644 (file)
@@ -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 */