On Tue, Jan 30, 2018 at 04:32:11PM +1000, David Gwynne wrote:
> On Fri, Jan 26, 2018 at 07:22:20AM +0100, Martin Pieuchot wrote:
> > On 25/01/18(Thu) 14:55, Bryan Steele wrote:
> > > On Thu, Jan 25, 2018 at 07:00:43PM +0100, Martin Pieuchot wrote:
> > > > Diff below adds support for dumping USB transfers via bpf(4), including
> > > > the tcpdump(8) bits.
> > > >
> > > > I'd like special review of the new bpf_tap(9) function I'm introducing:
> > > > - is there a way to pass the header as a different buffer?
> > > > - is _bpf_tap() the best way to share code with bpf_mtap(9)?
> > > >
> > > > Concerning the kernel/userland interface, in looked at the existing
> > > > DLT_USB*, namely:
> > > > - DLT_USB_FREEBSD (a.k.a DLT_USB)
> > > > - DLT_USB_LINUX
> > > > - DLT_USB_LINUX_MMAPPED
> > > > - DLT_USBPCAP
> > > > - DLT_USB_DARWIN
> > > >
> > > > I decided to follow DLT_USBPCAP which is the most generic one beside
> > > > being originated from Windows. Wireshark has some support for it
> > > > (currently untested) so it is a nice fit.
> > > > The current implementation is small and I'd like to continue developing
> > > > it in tree.
> > > >
> > > > Here's an excerpt of what's happening when plugging a USB key:
> > > >
> > > > # tcpdump -xXi usb0
> > > > tcpdump: listening on usb0, link-type USBPCAP
> > > > [...]
> > > > 18:56:26.261293 bus 0 > addr 5: ep0 ctrl 0
> > > >
> > > > 18:56:26.261409 bus 0 < addr 5: ep0 ctrl 20
> > > > 0000: 1403 5300 6c00 6900 6d00 2000 4c00 6900 ..S.l.i.m. .L.i.
> > > > 0010: 6e00 6500 n.e.
> > > >
> > > > 18:56:26.261418 bus 0 > addr 5: ep0 ctrl 0
> > > >
> > > > 18:56:26.261547 bus 0 < addr 5: ep0 ctrl 2
> > > > 0000: 2203 ".
> > > >
> > > > 18:56:26.261552 bus 0 > addr 5: ep0 ctrl 0
> > > >
> > > > 18:56:26.261746 bus 0 < addr 5: ep0 ctrl 34
> > > > 0000: 2203 3000 3700 3100 3000 3500 3900 3600 ".0.7.1.0.5.9.6.
> > > > 0010: 3100 4100 3400 3800 4400 3600 4100 3900 1.A.4.8.D.6.A.9.
> > > > 0020: 3500 5.
> > > >
> > > > 18:56:26.261761 bus 0 > addr 5: ep2 bulk 31
> > > > 0000: 5553 4243 4c00 0000 0000 0000 0000 0600 USBCL...........
> > > > 0010: 0000 0000 0000 0000 0000 0000 0000 00 ...............
> > > >
> > > > Ok to put it in tree?
> > >
> > >
> > > This looks really cool, hopefully might be useful for diagnosing the
> > > constantly reattaching issues I've been seeing with USB mouses..
> > >
> > > Looks like you forgot to add print-usbpcap.c for tcpdump.
> >
> > Indeed, full diff below.
>
> bpfsdetach takes the reference to a bpf interface that bpfsattach
> returns. the manpage likely isnt clear. the diff below fixes it.
>
> ive also added a bpf_tap_hdr function that lets you pass a header
> along with a flat buffer. internally it shoves them into mbufs, but
> the caller doesnt have to know that. it also doesnt have to allocate
> a temporary buffer to pass along.
>
> ive also made usb_tap return early if bpf hasnt set the bpf pointer
> yet.
oops, this diff was stale and had bufs in bpf_tap_hdr.
Index: sys/net/bpf.c
===================================================================
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.166
diff -u -p -r1.166 bpf.c
--- sys/net/bpf.c 24 Jan 2018 00:25:17 -0000 1.166
+++ sys/net/bpf.c 30 Jan 2018 06:36:50 -0000
@@ -1291,6 +1291,45 @@ _bpf_mtap(caddr_t arg, const struct mbuf
}
/*
+ * Incoming linkage from device drivers, where a data buffer should be
+ * prepended by an arbitrary header. In this situation we already have a
+ * way of representing a chain of memory buffers, ie, mbufs, so reuse
+ * the existing functionality by attaching the buffers to mbufs.
+ *
+ * Con up a minimal mbuf chain to pacify bpf by allocating (only) a
+ * struct m_hdr each for the header and data on the stack.
+ */
+int
+bpf_tap_hdr(caddr_t arg, const void *hdr, unsigned int hdrlen,
+ const void *buf, unsigned int buflen, u_int direction)
+{
+ struct m_hdr mh, md;
+ struct mbuf *m0 = NULL;
+ struct mbuf **mp = &m0;
+
+ if (hdr != NULL) {
+ mh.mh_flags = 0;
+ mh.mh_next = NULL;
+ mh.mh_len = hdrlen;
+ mh.mh_data = (void *)hdr;
+
+ *mp = (struct mbuf *)&mh;
+ mp = &mh.mh_next;
+ }
+
+ if (buf != NULL) {
+ md.mh_flags = 0;
+ md.mh_next = NULL;
+ md.mh_len = buflen;
+ md.mh_data = (void *)buf;
+
+ *mp = (struct mbuf *)&md;
+ }
+
+ return _bpf_mtap(arg, m0, direction, bpf_mcopy);
+}
+
+/*
* Incoming linkage from device drivers, when packet is in an mbuf chain.
*/
int
Index: sys/net/bpf.h
===================================================================
RCS file: /cvs/src/sys/net/bpf.h,v
retrieving revision 1.63
diff -u -p -r1.63 bpf.h
--- sys/net/bpf.h 24 Jan 2018 00:25:17 -0000 1.63
+++ sys/net/bpf.h 30 Jan 2018 06:36:50 -0000
@@ -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 */
@@ -311,6 +312,7 @@ int bpf_mtap_hdr(caddr_t, caddr_t, u_in
void (*)(const void *, void *, size_t));
int bpf_mtap_af(caddr_t, u_int32_t, const struct mbuf *, u_int);
int bpf_mtap_ether(caddr_t, const struct mbuf *, u_int);
+int bpf_tap_hdr(caddr_t, const void *, u_int, const void *, u_int, u_int);
void bpfattach(caddr_t *, struct ifnet *, u_int, u_int);
void bpfdetach(struct ifnet *);
void *bpfsattach(caddr_t *, const char *, u_int, u_int);
Index: usr.sbin/tcpdump/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/Makefile,v
retrieving revision 1.62
diff -u -p -r1.62 Makefile
--- usr.sbin/tcpdump/Makefile 30 Oct 2017 10:07:44 -0000 1.62
+++ usr.sbin/tcpdump/Makefile 30 Jan 2018 06:36:50 -0000
@@ -49,7 +49,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p
print-etherip.c print-lwres.c print-lldp.c print-cdp.c print-pflog.c \
print-pfsync.c pf_print_state.c print-ofp.c ofp_map.c \
print-udpencap.c print-carp.c \
- print-802_11.c print-iapp.c print-mpls.c print-slow.c \
+ print-802_11.c print-iapp.c print-mpls.c print-slow.c print-usbpcap.c \
gmt2local.c savestr.c setsignal.c in_cksum.c
# TCP OS Fingerprinting
Index: usr.sbin/tcpdump/interface.h
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.69
diff -u -p -r1.69 interface.h
--- usr.sbin/tcpdump/interface.h 16 Nov 2016 13:47:27 -0000 1.69
+++ usr.sbin/tcpdump/interface.h 30 Jan 2018 06:36:50 -0000
@@ -276,6 +276,8 @@ extern void slow_print(const u_char *, u
extern void gtp_print(const u_char *, u_int, u_short, u_short);
extern void ofp_print(const u_char *, u_int);
extern void ofp_if_print(u_char *, const struct pcap_pkthdr *, const u_char *);
+extern void usbpcap_if_print(u_char *, const struct pcap_pkthdr *,
+ const u_char *);
#ifdef INET6
extern void ip6_print(const u_char *, u_int);
Index: usr.sbin/tcpdump/print-usbpcap.c
===================================================================
RCS file: usr.sbin/tcpdump/print-usbpcap.c
diff -N usr.sbin/tcpdump/print-usbpcap.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ usr.sbin/tcpdump/print-usbpcap.c 30 Jan 2018 06:36:50 -0000
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Martin Pieuchot <[email protected]>
+ *
+ * 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.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbpcap.h>
+
+#include <pcap.h>
+
+#include "interface.h"
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+const char *usbpcap_xfer_type[] = {"isoc", "intr", "ctrl", "bulk"};
+
+void
+usbpcap_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ const struct usbpcap_hdr *uph;
+
+ ts_print(&h->ts);
+
+ /* check length */
+ if (caplen < sizeof(uint16_t)) {
+ printf("[|usb]");
+ goto out;
+ }
+ uph = (struct usbpcap_hdr *)p;
+ if (uph->hlen < sizeof(*uph)) {
+ printf("[usb: invalid header length %u!]", uph->hlen);
+ goto out;
+ }
+
+ if (caplen < uph->hlen) {
+ printf("[|usb]");
+ goto out;
+ }
+
+ printf("bus %u %c addr %u: ep%u",
+ uph->bus, ((uph->info & USBPCAP_INFO_PDO_TO_FDO) ? '<' : '>'),
+ uph->devaddr, UE_GET_ADDR(uph->epaddr));
+
+ if (uph->xfertype < nitems(usbpcap_xfer_type))
+ printf(" %s", usbpcap_xfer_type[uph->xfertype]);
+ else
+ printf(" ??");
+
+ printf(" %u", uph->dlen);
+
+ if (xflag)
+ default_print(p + sizeof(*uph), length - sizeof(*uph));
+out:
+ putchar('\n');
+}
Index: usr.sbin/tcpdump/tcpdump.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/tcpdump.c,v
retrieving revision 1.81
diff -u -p -r1.81 tcpdump.c
--- usr.sbin/tcpdump/tcpdump.c 8 Dec 2017 17:04:15 -0000 1.81
+++ usr.sbin/tcpdump/tcpdump.c 30 Jan 2018 06:36:50 -0000
@@ -127,6 +127,7 @@ static struct printer printers[] = {
{ ieee802_11_if_print, DLT_IEEE802_11 },
{ ieee802_11_radio_if_print, DLT_IEEE802_11_RADIO },
{ ofp_if_print, DLT_OPENFLOW },
+ { usbpcap_if_print, DLT_USBPCAP },
{ NULL, 0 },
};
Index: lib/libpcap/gencode.c
===================================================================
RCS file: /cvs/src/lib/libpcap/gencode.c,v
retrieving revision 1.46
diff -u -p -r1.46 gencode.c
--- lib/libpcap/gencode.c 20 Nov 2016 12:45:26 -0000 1.46
+++ lib/libpcap/gencode.c 30 Jan 2018 06:36:50 -0000
@@ -787,6 +787,8 @@ init_linktype(type)
off_nl = 12;
return;
+ case DLT_USBPCAP:
+ /* FALLTHROUGH */
case DLT_RAW:
off_linktype = -1;
off_nl = 0;
Index: lib/libpcap/pcap.c
===================================================================
RCS file: /cvs/src/lib/libpcap/pcap.c,v
retrieving revision 1.20
diff -u -p -r1.20 pcap.c
--- lib/libpcap/pcap.c 16 Nov 2016 13:47:27 -0000 1.20
+++ lib/libpcap/pcap.c 30 Jan 2018 06:36:50 -0000
@@ -326,6 +326,7 @@ DLT_CHOICE(DLT_IEEE802_11, "IEEE 802.11
DLT_CHOICE(DLT_PFLOG, "Packet filter logging, by pcap people"),
DLT_CHOICE(DLT_IEEE802_11_RADIO, "IEEE 802.11 plus WLAN header"),
DLT_CHOICE(DLT_OPENFLOW, "OpenFlow"),
+DLT_CHOICE(DLT_USBPCAP, "USB"),
#undef DLT_CHOICE
{ NULL, NULL, -1}
};
Index: sys/dev/usb/usb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usb.c,v
retrieving revision 1.114
diff -u -p -r1.114 usb.c
--- sys/dev/usb/usb.c 29 Jul 2017 18:26:14 -0000 1.114
+++ sys/dev/usb/usb.c 30 Jan 2018 06:36:50 -0000
@@ -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
}
printf("\n");
+#if NBPFILTER > 0
+ sc->sc_bus->bpfif = bpfsattach(&sc->sc_bus->bpf, sc->sc_dev.dv_xname,
+ DLT_USBPCAP, sizeof(struct usbpcap_hdr));
+#endif
+
/* Make sure not to use tsleep() if we are cold booting. */
if (cold)
sc->sc_bus->use_polling++;
@@ -973,5 +980,72 @@ usb_detach(struct device *self, int flag
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_hdr uph;
+ unsigned int bpfdir;
+ void *buf;
+ unsigned int buflen;
+ caddr_t bpf;
+ uint8_t info = 0;
+
+ bpf = bus->bpf;
+ if (bpf == NULL)
+ return;
+
+ buf = KERNADDR(&xfer->dmabuf, 0);
+ buflen = xfer->length;
+
+ if (dir == USBTAP_DIR_OUT) {
+ bpfdir = BPF_DIRECTION_OUT;
+ if (usbd_xfer_isread(xfer)) {
+ buf = NULL;
+ buflen = 0;
+ }
+ } else { /* USBTAP_DIR_IN */
+ info = USBPCAP_INFO_PDO_TO_FDO;
+ bpfdir = BPF_DIRECTION_IN;
+ if (!usbd_xfer_isread(xfer)) {
+ buf = NULL;
+ buflen = 0;
+ }
+ }
+
+ uph.hlen = htole16(sizeof(uph));
+ uph.id = 0; /* not yet used */
+ uph.status = htole32(xfer->status);
+ uph.function = 0; /* not yet used */
+ uph.info = info;
+ uph.bus = htole32(sc->sc_dev.dv_unit);
+ uph.devaddr = htole16(xfer->device->address);
+ uph.epaddr = ed->bEndpointAddress;
+ uph.dlen = buflen;
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_CONTROL:
+ uph.xfertype = USBPCAP_TRANSFER_CONTROL;
+ break;
+ case UE_ISOCHRONOUS:
+ uph.xfertype = USBPCAP_TRANSFER_ISOCHRONOUS;
+ break;
+ case UE_BULK:
+ uph.xfertype = USBPCAP_TRANSFER_BULK;
+ break;
+ case UE_INTERRUPT:
+ uph.xfertype = USBPCAP_TRANSFER_INTERRUPT;
+ break;
+ }
+
+ bpf_tap_hdr(bpf, &uph, sizeof(uph), buf, buflen, bpfdir);
+#endif
}
Index: sys/dev/usb/usbdi.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.96
diff -u -p -r1.96 usbdi.c
--- sys/dev/usb/usbdi.c 21 Sep 2017 07:44:06 -0000 1.96
+++ sys/dev/usb/usbdi.c 30 Jan 2018 06:36:50 -0000
@@ -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 *
/* 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 *
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;
@@ -788,6 +791,8 @@ usb_transfer_complete(struct usbd_xfer *
xfer->actlen, xfer->length));
xfer->status = USBD_SHORT_XFER;
}
+
+ usb_tap(bus, xfer, USBTAP_DIR_IN);
/*
* We cannot dereference ``xfer'' after calling the callback as
Index: sys/dev/usb/usbdivar.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.72
diff -u -p -r1.72 usbdivar.h
--- sys/dev/usb/usbdivar.h 8 Apr 2017 02:57:25 -0000 1.72
+++ sys/dev/usb/usbdivar.h 30 Jan 2018 06:36:50 -0000
@@ -35,6 +35,11 @@
#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 *, s
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
Index: sys/dev/usb/usbpcap.h
===================================================================
RCS file: sys/dev/usb/usbpcap.h
diff -N sys/dev/usb/usbpcap.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/dev/usb/usbpcap.h 30 Jan 2018 06:36:50 -0000
@@ -0,0 +1,45 @@
+/* $OpenBSD$ */
+
+/*
+ * 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_
+
+/*
+ * DLT_USBPCAP header.
+ */
+struct usbpcap_hdr {
+ uint16_t hlen; /* header length */
+ uint64_t id; /* request ID */
+ uint32_t status; /* USB status code */
+ uint16_t function; /* stack function ID */
+ uint8_t info; /* info flags */
+#define USBPCAP_INFO_PDO_TO_FDO (1 << 0)
+
+ uint16_t bus; /* bus number */
+ uint16_t devaddr; /* device address */
+ uint8_t epaddr; /* endpoint's `bEndpointAddress' */
+ uint8_t 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 dlen; /* data length */
+} __attribute__((packed));
+
+#endif /* _USBCAP_H_ */