Download raw body.
Fix dumping ISOCHRONOUS IN transfers
On 19/12/24(Thu) 02:27, Kirill A. Korinsky wrote:
> tech@,
>
> tcpdump can be used to capture USB traffic, and it works well with one
> exception: ISOCHRONOUS IN transfers.
>
> While OUT transfers work as expected, IN transfers only capture the first
> packet that can be read.
>
> The root cause is that the same array is being used to store both the
> offsets in the data buffer and the resulting lengths. As a result, the dump
> records the length of the packets read from the device as offsets in the
> data buffer.
I don't see any offset being saved. It is just the length of every
frame, no?
> When the device returns full packets, everything works correctly. However,
> when it returns partial packets, only the first packet is useful, and all
> subsequent packets are lost.
I'm not sure what you mean with "lost". Are you saying that as soon as
there is a frame missed the offset calculation is incorrect?
Are you testing on ehci(4) or xhci(4)? If it is xhci(4) is it because
`frlengths' is set to 0 in xhci_device_isoc_start()?
> Index: sys/dev/usb/usb.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/usb/usb.c,v
> diff -u -p -r1.133 usb.c
> --- sys/dev/usb/usb.c 4 Sep 2024 07:54:52 -0000 1.133
> +++ sys/dev/usb/usb.c 19 Dec 2024 01:25:24 -0000
> @@ -952,7 +952,7 @@ usb_tap(struct usbd_bus *bus, struct usb
> struct usbpcap_iso_hdr_full uih;
> } h;
> struct usbpcap_pkt_hdr *uph = &h.uch.uch_hdr;
> - uint32_t nframes, offset;
> + uint32_t nframes;
> unsigned int bpfdir;
> void *data = NULL;
> size_t flen;
> @@ -970,7 +970,6 @@ usb_tap(struct usbd_bus *bus, struct usb
> uph->uph_xfertype = USBPCAP_TRANSFER_CONTROL;
> break;
> case UE_ISOCHRONOUS:
> - offset = 0;
> nframes = xfer->nframes;
> #ifdef DIAGNOSTIC
> if (nframes > _USBPCAP_MAX_ISOFRAMES) {
> @@ -987,11 +986,10 @@ usb_tap(struct usbd_bus *bus, struct usb
> 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_offset = xfer->froffsets[i];
> 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:
> @@ -1049,6 +1047,12 @@ usb_tap(struct usbd_bus *bus, struct usb
> if (xfer->rqflags & URQ_REQUEST)
> h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
> }
> + }
> +
> + /* ISOCHRONOUS IN from device may have gaps, use full buffer */
> + if (bpfdir == BPF_DIRECTION_IN && uph->uph_dlen > 0 &&
> + uph->uph_xfertype == USBPCAP_TRANSFER_ISOCHRONOUS) {
> + uph->uph_dlen = xfer->length;
> }
>
> /* Dump bulk/intr/iso data, ctrl DATA or STATUS stage. */
> Index: sys/dev/usb/usbdi.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/usb/usbdi.c,v
> diff -u -p -r1.111 usbdi.c
> --- sys/dev/usb/usbdi.c 23 May 2024 03:21:09 -0000 1.111
> +++ sys/dev/usb/usbdi.c 18 Dec 2024 22:35:39 -0000
> @@ -482,8 +482,10 @@ usbd_setup_isoc_xfer(struct usbd_xfer *x
> xfer->priv = priv;
> xfer->buffer = 0;
> xfer->length = 0;
> - for (i = 0; i < nframes; i++)
> + for (i = 0; i < nframes; i++) {
> + xfer->froffsets[i] = xfer->length;
> xfer->length += frlengths[i];
> + }
> xfer->actlen = 0;
> xfer->flags = flags;
> xfer->timeout = USBD_NO_TIMEOUT;
> Index: sys/dev/usb/usbdivar.h
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/usb/usbdivar.h,v
> diff -u -p -r1.84 usbdivar.h
> --- sys/dev/usb/usbdivar.h 8 Oct 2024 19:42:31 -0000 1.84
> +++ sys/dev/usb/usbdivar.h 18 Dec 2024 22:35:42 -0000
> @@ -223,6 +223,12 @@ struct usbd_xfer {
> /* For isoc */
> u_int16_t *frlengths;
> int nframes;
> +/*
> + * OpenBSD specific, maximum number of frames per transfer used across
> + * all USB drivers. This allows us to dump ISOC IN.
> + */
> +#define _USBD_MAX_ISOFRAMES 40
> + u_int16_t froffsets[_USBD_MAX_ISOFRAMES];
>
> /* For memory allocation */
> struct usbd_device *device;
>
> --
> wbr, Kirill
>
Fix dumping ISOCHRONOUS IN transfers