From 4ba11bd8f61508559d6013c795a0888688136394 Mon Sep 17 00:00:00 2001 From: mpi Date: Mon, 26 Feb 2018 13:06:49 +0000 Subject: [PATCH] Support dumping isochronous frames via bpf(4) using the USBPcap protocol. ok deraadt@, ratchov@ --- sys/dev/usb/usb.c | 57 +++++++++++++++++++++++++++++++++---------- sys/dev/usb/usbpcap.h | 33 ++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c index a2f3c568bc8..0c62cb2cb6a 100644 --- a/sys/dev/usb/usb.c +++ b/sys/dev/usb/usb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usb.c,v 1.117 2018/02/19 09:20:45 jsg Exp $ */ +/* $OpenBSD: usb.c,v 1.118 2018/02/26 13:06:49 mpi Exp $ */ /* $NetBSD: usb.c,v 1.77 2003/01/01 00:10:26 thorpej Exp $ */ /* @@ -992,11 +992,17 @@ 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; + union { + struct usbpcap_ctl_hdr uch; + struct usbpcap_iso_hdr_full uih; + } h; + struct usbpcap_pkt_hdr *uph = &h.uch.uch_hdr; + uint32_t nframes, offset; unsigned int bpfdir; void *data = NULL; + size_t flen; caddr_t bpf; + int i; bpf = bus->bpf; if (bpf == NULL) @@ -1004,13 +1010,34 @@ usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) switch (UE_GET_XFERTYPE(ed->bmAttributes)) { case UE_CONTROL: - /* Control transfer headers include an extra byte. */ - uph->uph_hlen = htole16(sizeof(uch)); + /* Control transfer headers include an extra byte */ + uph->uph_hlen = htole16(sizeof(struct usbpcap_ctl_hdr)); uph->uph_xfertype = USBPCAP_TRANSFER_CONTROL; break; case UE_ISOCHRONOUS: - uph->uph_hlen = htole16(sizeof(*uph)); + offset = 0; + nframes = xfer->nframes; +#ifdef DIAGNOSTIC + if (nframes > _USBPCAP_MAX_ISOFRAMES) { + printf("%s: too many frames: %d > %d\n", __func__, + xfer->nframes, _USBPCAP_MAX_ISOFRAMES); + nframes = _USBPCAP_MAX_ISOFRAMES; + } +#endif + /* Isochronous transfer headers include space for one frame */ + flen = (nframes - 1) * sizeof(struct usbpcap_iso_pkt); + uph->uph_hlen = htole16(sizeof(struct usbpcap_iso_hdr) + flen); uph->uph_xfertype = USBPCAP_TRANSFER_ISOCHRONOUS; + h.uih.uih_startframe = 0; /* not yet used */ + h.uih.uih_nframes = nframes; + h.uih.uih_errors = 0; /* we don't have per-frame error */ + for (i = 0; i < nframes; i++) { + h.uih.uih_frames[i].uip_offset = offset; + h.uih.uih_frames[i].uip_length = xfer->frlengths[i]; + /* See above, we don't have per-frame error */ + h.uih.uih_frames[i].uip_status = 0; + offset += xfer->frlengths[i]; + } break; case UE_BULK: uph->uph_hlen = htole16(sizeof(*uph)); @@ -1034,7 +1061,7 @@ usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) /* Outgoing control requests start with a STAGE dump. */ if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_OUT)) { - uch.uch_stage = USBPCAP_CONTROL_STAGE_SETUP; + h.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); @@ -1045,11 +1072,13 @@ usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) if (!usbd_xfer_isread(xfer)) { data = KERNADDR(&xfer->dmabuf, 0); uph->uph_dlen = xfer->length; - uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; + if (xfer->rqflags & URQ_REQUEST) + h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; } else { data = NULL; uph->uph_dlen = 0; - uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; + if (xfer->rqflags & URQ_REQUEST) + h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; } } else { /* USBTAP_DIR_IN */ bpfdir = BPF_DIRECTION_IN; @@ -1057,11 +1086,13 @@ usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) if (usbd_xfer_isread(xfer)) { data = KERNADDR(&xfer->dmabuf, 0); uph->uph_dlen = xfer->actlen; - uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; + if (xfer->rqflags & URQ_REQUEST) + h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA; } else { data = NULL; uph->uph_dlen = 0; - uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; + if (xfer->rqflags & URQ_REQUEST) + h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; } } @@ -1070,8 +1101,8 @@ usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir) /* 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; + (h.uch.uch_stage == USBPCAP_CONTROL_STAGE_DATA)) { + h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS; uph->uph_dlen = 0; bpf_tap_hdr(bpf, uph, uph->uph_hlen, NULL, 0, BPF_DIRECTION_IN); } diff --git a/sys/dev/usb/usbpcap.h b/sys/dev/usb/usbpcap.h index 890c830707e..c29361d973d 100644 --- a/sys/dev/usb/usbpcap.h +++ b/sys/dev/usb/usbpcap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbpcap.h,v 1.1 2018/02/03 13:37:37 mpi Exp $ */ +/* $OpenBSD: usbpcap.h,v 1.2 2018/02/26 13:06:49 mpi Exp $ */ /* * Copyright (c) 2018 Martin Pieuchot @@ -53,4 +53,35 @@ struct usbpcap_ctl_hdr { #define USBPCAP_CONTROL_STAGE_STATUS 2 } __attribute__((packed)); +struct usbpcap_iso_pkt { + uint32_t uip_offset; + uint32_t uip_length; + uint32_t uip_status; +} __attribute__((packed)); + +/* + * Header used when dumping isochronous transfers. + */ +struct usbpcap_iso_hdr { + struct usbpcap_pkt_hdr uih_hdr; + uint32_t uih_startframe; + uint32_t uih_nframes; /* number of frame */ + uint32_t uih_errors; /* error count */ + struct usbpcap_iso_pkt uih_frames[1]; +} __attribute__((packed)); + +#ifdef _KERNEL +/* + * OpenBSD specific, maximum number of frames per transfer used across + * all USB drivers. This allows us to setup the header on the stack. + */ +#define _USBPCAP_MAX_ISOFRAMES 40 +struct usbpcap_iso_hdr_full { + struct usbpcap_pkt_hdr uih_hdr; + uint32_t uih_startframe; + uint32_t uih_nframes; + uint32_t uih_errors; + struct usbpcap_iso_pkt uih_frames[_USBPCAP_MAX_ISOFRAMES]; +} __attribute__((packed)); +#endif /* _KERNEL */ #endif /* _USBCAP_H_ */ -- 2.20.1