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.
-Bryan.
> 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 25 Jan 2018 14:02:58 -0000
> @@ -1226,35 +1226,24 @@ bpf_mcopy(const void *src_arg, void *dst
> }
> }
>
> -/*
> - * like bpf_mtap, but copy fn can be given. used by various bpf_mtap*
> - */
> int
> -_bpf_mtap(caddr_t arg, const struct mbuf *m, u_int direction,
> - void (*cpfn)(const void *, void *, size_t))
> +_bpf_tap(caddr_t arg, u_char *pkt, size_t pktlen, u_int direction,
> + void (*cpfn)(const void *, void *, size_t), int mfilter)
> {
> struct bpf_if *bp = (struct bpf_if *)arg;
> struct srp_ref sr;
> struct bpf_d *d;
> - size_t pktlen, slen;
> - const struct mbuf *m0;
> + size_t slen;
> struct timeval tv;
> int gottime = 0;
> int drop = 0;
>
> - if (m == NULL)
> + if (pkt == NULL)
> return (0);
>
> - if (cpfn == NULL)
> - cpfn = bpf_mcopy;
> -
> if (bp == NULL)
> return (0);
>
> - pktlen = 0;
> - for (m0 = m; m0 != NULL; m0 = m0->m_next)
> - pktlen += m0->m_len;
> -
> SRPL_FOREACH(d, &sr, &bp->bif_dlist, bd_next) {
> atomic_inc_long(&d->bd_rcount);
>
> @@ -1268,7 +1257,11 @@ _bpf_mtap(caddr_t arg, const struct mbuf
> bf = srp_enter(&bsr, &d->bd_rfilter);
> if (bf != NULL)
> fcode = bf->bf_insns;
> - slen = bpf_mfilter(fcode, m, pktlen);
> + if (mfilter) {
> + const struct mbuf *m = (const struct mbuf *)pkt;
> + slen = bpf_mfilter(fcode, m, pktlen);
> + } else
> + slen = bpf_filter(fcode, pkt, pktlen, pktlen);
> srp_leave(&bsr);
> }
>
> @@ -1277,8 +1270,7 @@ _bpf_mtap(caddr_t arg, const struct mbuf
> microtime(&tv);
>
> mtx_enter(&d->bd_mtx);
> - bpf_catchpacket(d, (u_char *)m, pktlen, slen, cpfn,
> - &tv);
> + bpf_catchpacket(d, pkt, pktlen, slen, cpfn, &tv);
> mtx_leave(&d->bd_mtx);
>
> if (d->bd_fildrop)
> @@ -1290,6 +1282,35 @@ _bpf_mtap(caddr_t arg, const struct mbuf
> return (drop);
> }
>
> +int
> +bpf_tap(caddr_t arg, u_char *pkt, size_t pktlen, u_int direction)
> +{
> + return _bpf_tap(arg, pkt, pktlen, direction, bcopy, 0);
> +}
> +
> +/*
> + * like bpf_mtap, but copy fn can be given. used by various bpf_mtap*
> + */
> +int
> +_bpf_mtap(caddr_t arg, const struct mbuf *m, u_int direction,
> + void (*cpfn)(const void *, void *, size_t))
> +{
> + const struct mbuf *m0;
> + size_t pktlen;
> +
> + if (m == NULL)
> + return (0);
> +
> + if (cpfn == NULL)
> + cpfn = bpf_mcopy;
> +
> + pktlen = 0;
> + for (m0 = m; m0 != NULL; m0 = m0->m_next)
> + pktlen += m0->m_len;
> +
> + return _bpf_tap(arg, (u_char *)m, pktlen, direction, cpfn, 1);
> +}
> +
> /*
> * Incoming linkage from device drivers, when packet is in an mbuf chain.
> */
> @@ -1790,7 +1811,7 @@ bpf_mbuf_copy(const struct mbuf *m, u_in
>
> for (;;) {
> count = min(m->m_len - off, len);
> -
> +
> memcpy(cp, m->m_data + off, count);
> len -= count;
>
> 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 25 Jan 2018 16:06:35 -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 */
>
> @@ -306,6 +307,7 @@ struct ifnet;
> struct mbuf;
>
> int bpf_validate(struct bpf_insn *, int);
> +int bpf_tap(caddr_t arg, u_char *, size_t, u_int);
> int bpf_mtap(caddr_t, const struct mbuf *, u_int);
> int bpf_mtap_hdr(caddr_t, caddr_t, u_int, const struct mbuf *, u_int,
> void (*)(const void *, void *, size_t));
> 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 25 Jan 2018 17:10:15 -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 25 Jan 2018 17:13:09 -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/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 25 Jan 2018 17:10:08 -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 25 Jan 2018 17:09:17 -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 25 Jan 2018 17:09:23 -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 25 Jan 2018 17:41:40 -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
> + 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,75 @@ usb_detach(struct device *self, int flag
> sc->sc_bus->soft = NULL;
> }
>
> +#if NBPFILTER > 0
> + bpfsdetach(&sc->sc_bus->bpf);
> +#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;
> + uint32_t len, dlen;
> + uint8_t *buf, info = 0;
> +
> + if (dir == USBTAP_DIR_OUT) {
> + bpfdir = BPF_DIRECTION_OUT;
> + if (usbd_xfer_isread(xfer))
> + dlen = 0;
> + else
> + dlen = xfer->length;
> + } else { /* USBTAP_DIR_IN */
> + info = USBPCAP_INFO_PDO_TO_FDO;
> + bpfdir = BPF_DIRECTION_IN;
> + if (usbd_xfer_isread(xfer))
> + dlen = xfer->actlen;
> + else
> + dlen = 0;
> + }
> +
> + len = sizeof(*uph) + dlen;
> +
> + buf = malloc(len, M_TEMP, M_NOWAIT);
> + if (buf == NULL)
> + return;
> +
> + uph = (struct usbpcap_hdr *)buf;
> + uph->hlen = htole32(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;
> +
> + 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;
> + }
> +
> + uph->dlen = dlen;
> + if (dlen)
> + memcpy(buf + sizeof(*uph), KERNADDR(&xfer->dmabuf, 0), dlen);
> +
> + bpf_tap(bus->bpf, buf, len, bpfdir);
> +
> + free(buf, M_TEMP, len);
> +#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 25 Jan 2018 16:59:55 -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 25 Jan 2018 17:02:27 -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,9 @@ struct usbd_bus {
> /* Filled by HC driver */
> struct device bdev; /* base device, host adapter */
> struct usbd_bus_methods *methods;
> +#if NBPFILTER > 0
> + 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 +261,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 25 Jan 2018 17:05:08 -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_ */
>
>