qemu
1/*
2* usb packet capture
3*
4* Copyright (c) 2021 Gerd Hoffmann <kraxel@redhat.com>
5*
6* This work is licensed under the terms of the GNU GPL, version 2 or later.
7* See the COPYING file in the top-level directory.
8*/
9
10#include "qemu/osdep.h"11#include "hw/usb.h"12
13#define PCAP_MAGIC 0xa1b2c3d414#define PCAP_MAJOR 215#define PCAP_MINOR 416
17/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
18
19struct pcap_hdr {20uint32_t magic_number; /* magic number */21uint16_t version_major; /* major version number */22uint16_t version_minor; /* minor version number */23int32_t thiszone; /* GMT to local correction */24uint32_t sigfigs; /* accuracy of timestamps */25uint32_t snaplen; /* max length of captured packets, in octets */26uint32_t network; /* data link type */27};28
29struct pcaprec_hdr {30uint32_t ts_sec; /* timestamp seconds */31uint32_t ts_usec; /* timestamp microseconds */32uint32_t incl_len; /* number of octets of packet saved in file */33uint32_t orig_len; /* actual length of packet */34};35
36/* https://www.tcpdump.org/linktypes.html */
37/* linux: Documentation/usb/usbmon.rst */
38/* linux: drivers/usb/mon/mon_bin.c */
39
40#define LINKTYPE_USB_LINUX 189 /* first 48 bytes only */41#define LINKTYPE_USB_LINUX_MMAPPED 220 /* full 64 byte header */42
43struct usbmon_packet {44uint64_t id; /* 0: URB ID - from submission to callback */45unsigned char type; /* 8: Same as text; extensible. */46unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */47unsigned char epnum; /* Endpoint number and transfer direction */48unsigned char devnum; /* Device address */49uint16_t busnum; /* 12: Bus number */50char flag_setup; /* 14: Same as text */51char flag_data; /* 15: Same as text; Binary zero is OK. */52int64_t ts_sec; /* 16: gettimeofday */53int32_t ts_usec; /* 24: gettimeofday */54int32_t status; /* 28: */55unsigned int length; /* 32: Length of data (submitted or actual) */56unsigned int len_cap; /* 36: Delivered length */57union { /* 40: */58unsigned char setup[8]; /* Only for Control S-type */59struct iso_rec { /* Only for ISO */60int32_t error_count;61int32_t numdesc;62} iso;63} s;64int32_t interval; /* 48: Only for Interrupt and ISO */65int32_t start_frame; /* 52: For ISO */66uint32_t xfer_flags; /* 56: copy of URB's transfer_flags */67uint32_t ndesc; /* 60: Actual number of ISO descriptors */68}; /* 64 total length */69
70/* ------------------------------------------------------------------------ */
71
72#define CTRL_LEN 409673#define DATA_LEN 25674
75static int usbmon_status(USBPacket *p)76{
77switch (p->status) {78case USB_RET_SUCCESS:79return 0;80case USB_RET_NODEV:81return -19; /* -ENODEV */82default:83return -121; /* -EREMOTEIO */84}85}
86
87static unsigned int usbmon_epnum(USBPacket *p)88{
89unsigned epnum = 0;90
91epnum |= p->ep->nr;92epnum |= (p->pid == USB_TOKEN_IN) ? 0x80 : 0;93return epnum;94}
95
96static unsigned char usbmon_xfer_type[] = {97[USB_ENDPOINT_XFER_CONTROL] = 2,98[USB_ENDPOINT_XFER_ISOC] = 0,99[USB_ENDPOINT_XFER_BULK] = 3,100[USB_ENDPOINT_XFER_INT] = 1,101};102
103static void do_usb_pcap_header(FILE *fp, struct usbmon_packet *packet)104{
105struct pcaprec_hdr header;106struct timeval tv;107
108gettimeofday(&tv, NULL);109packet->ts_sec = tv.tv_sec;110packet->ts_usec = tv.tv_usec;111
112header.ts_sec = packet->ts_sec;113header.ts_usec = packet->ts_usec;114header.incl_len = packet->len_cap;115header.orig_len = packet->length + sizeof(*packet);116fwrite(&header, sizeof(header), 1, fp);117fwrite(packet, sizeof(*packet), 1, fp);118}
119
120static void do_usb_pcap_ctrl(FILE *fp, USBPacket *p, bool setup)121{
122USBDevice *dev = p->ep->dev;123bool in = dev->setup_buf[0] & USB_DIR_IN;124struct usbmon_packet packet = {125.id = 0,126.type = setup ? 'S' : 'C',127.xfer_type = usbmon_xfer_type[USB_ENDPOINT_XFER_CONTROL],128.epnum = in ? 0x80 : 0,129.devnum = dev->addr,130.flag_setup = setup ? 0 : '-',131.flag_data = '=',132.length = dev->setup_len,133};134int data_len = dev->setup_len;135
136if (data_len > CTRL_LEN) {137data_len = CTRL_LEN;138}139if (setup) {140memcpy(packet.s.setup, dev->setup_buf, 8);141} else {142packet.status = usbmon_status(p);143}144
145if (in && setup) {146packet.flag_data = '<';147packet.length = 0;148data_len = 0;149}150if (!in && !setup) {151packet.flag_data = '>';152packet.length = 0;153data_len = 0;154}155
156packet.len_cap = data_len + sizeof(packet);157do_usb_pcap_header(fp, &packet);158if (data_len) {159fwrite(dev->data_buf, data_len, 1, fp);160}161
162fflush(fp);163}
164
165static void do_usb_pcap_data(FILE *fp, USBPacket *p, bool setup)166{
167struct usbmon_packet packet = {168.id = p->id,169.type = setup ? 'S' : 'C',170.xfer_type = usbmon_xfer_type[p->ep->type],171.epnum = usbmon_epnum(p),172.devnum = p->ep->dev->addr,173.flag_setup = '-',174.flag_data = '=',175.length = p->iov.size,176};177int data_len = p->iov.size;178
179if (p->ep->nr == 0) {180/* ignore control pipe packets */181return;182}183
184if (data_len > DATA_LEN) {185data_len = DATA_LEN;186}187if (!setup) {188packet.status = usbmon_status(p);189if (packet.length > p->actual_length) {190packet.length = p->actual_length;191}192if (data_len > p->actual_length) {193data_len = p->actual_length;194}195}196
197if (p->pid == USB_TOKEN_IN && setup) {198packet.flag_data = '<';199packet.length = 0;200data_len = 0;201}202if (p->pid == USB_TOKEN_OUT && !setup) {203packet.flag_data = '>';204packet.length = 0;205data_len = 0;206}207
208packet.len_cap = data_len + sizeof(packet);209do_usb_pcap_header(fp, &packet);210if (data_len) {211void *buf = g_malloc(data_len);212iov_to_buf(p->iov.iov, p->iov.niov, 0, buf, data_len);213fwrite(buf, data_len, 1, fp);214g_free(buf);215}216
217fflush(fp);218}
219
220void usb_pcap_init(FILE *fp)221{
222struct pcap_hdr header = {223.magic_number = PCAP_MAGIC,224.version_major = 2,225.version_minor = 4,226.snaplen = MAX(CTRL_LEN, DATA_LEN) + sizeof(struct usbmon_packet),227.network = LINKTYPE_USB_LINUX_MMAPPED,228};229
230fwrite(&header, sizeof(header), 1, fp);231}
232
233void usb_pcap_ctrl(USBPacket *p, bool setup)234{
235FILE *fp = p->ep->dev->pcap;236
237if (!fp) {238return;239}240
241do_usb_pcap_ctrl(fp, p, setup);242}
243
244void usb_pcap_data(USBPacket *p, bool setup)245{
246FILE *fp = p->ep->dev->pcap;247
248if (!fp) {249return;250}251
252do_usb_pcap_data(fp, p, setup);253}
254