Bochs/bochs/iodev/usb/usb_pcap.cc
2022-08-23 21:46:04 +03:00

199 lines
6.4 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2021 Benjamin D Lunt (fys [at] fysnet [dot] net)
// Copyright (C) 2002-2021 The Bochs Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// Notes by Ben Lunt:
//
// Experimental writing of packets to a Packet Capture (pcap) file for WireShark.
// This will create a pcap formatted file of all packets sent to a
// specific device. The using a pcap viewer, such as WireShark, you can
// see what packets have been sent on the "wire" as well as let the viewer
// give you details.
//
// References used:
// https://wiki.wireshark.org/Development/LibpcapFileFormat
// http://www.tcpdump.org/linktypes.html
// https://www.kernel.org/doc/Documentation/usb/usbmon.txt
// Other references (May or may not be added):
// https://desowin.org/usbpcap/captureformat.html
// http://www.tcpdump.org/linktypes/LINKTYPE_USB_DARWIN.html
//
/////////////////////////////////////////////////////////////////////////
#include "iodev.h"
#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
#include "usb_pcap.h"
#include "usb_common.h"
#ifdef linux
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#endif
/*** base class pcap_image_t ***/
pcap_image_t::pcap_image_t()
{
fd = -1;
last_pos = 0;
time_usecs = 0;
}
pcap_image_t::~pcap_image_t()
{
if (fd > -1)
close(fd);
fd = -1;
}
int pcap_image_t::create_pcap(const char *pathname)
{
pcap_hdr_t header;
if (strlen(pathname) == 0)
return 0;
fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC
#ifdef O_BINARY
| O_BINARY
#endif
, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP
);
if (fd < 0)
return fd;
// else write the header
header.magic_number = 0xA1B2C3D4; // noted as little endian
header.version_major = 0x0002; // major version number
header.version_minor = 0x0004; // minor version number
header.thiszone = 0; // GMT to local correction
header.sigfigs = 0; // accuracy of timestamps
header.snaplen = 0x00040000; // max length of captured packets, in octets
header.network = BX_PCAP_LINKTYPE; // data link type (http://www.tcpdump.org/linktypes.html)
write(fd, &header, sizeof(pcap_hdr_t));
// This relies on the fact that the time_t epoc is the same
// epoc that the WireShark uses. We want it in Microseconds as well.
// TODO: I only tested that this epoc is the same for a Windows Host.
// * One will need to test for a non-Win32 host to make sure the epoc is the same.
time_t mtime;
time(&mtime);
time_usecs = (Bit64u) mtime * 1000000; // 'NOW' in mS
#if BX_PCAP_APPEND_PACKETS
last_pos = sizeof(sizeof(pcap_hdr_t));
last_ep = 0xFF;
#endif
return fd;
}
void pcap_image_t::write_packet(void *data, int len, int bus, int dev_addr, int ep, int type, bool is_setup, bool can_append)
{
// if we haven't opened the file, do nothing
if ((fd < 0) || (len == 0) || (data == NULL))
return;
#if BX_PCAP_APPEND_PACKETS
// can we append it to the last packet?
// To make WireShark understand the packets, we need to
// append like packets. i.e.: All IN packets of the GET_DESCRIPTOR request
// need to be one (1) packet here for WireShark.
if (can_append) {
if ((last_packet.xfer_type == type) &&
(last_ep == ep) &&
(last_packet.devnum == dev_addr)) {
last_hdr.incl_len += len;
last_hdr.orig_len += len;
last_packet.length += len;
last_packet.len_cap += len;
lseek(fd, last_pos, SEEK_SET); // seek to last pos
write(fd, &last_hdr, sizeof(pcaprec_hdr_t));
write(fd, &last_packet, sizeof(struct usbmon_packet));
lseek(fd, 0, SEEK_END); // move to end of file again
write(fd, data, len);
return;
}
}
#endif
Bit32u length = sizeof(struct usbmon_packet) + ((!is_setup) ? len : 0);
const Bit64u t = time_usecs + bx_pc_system.time_usec();
Bit64u seconds = (t / 1000000);
Bit32u usecs = (Bit32u) (t % 1000000);
pcaprec_hdr_t header;
header.ts_sec = (Bit32u) seconds; // timestamp seconds
header.ts_usec = usecs; // timestamp microseconds
header.incl_len = length; // number of octets of packet saved in file
header.orig_len = length; // actual length of packet
struct usbmon_packet usb_hdr;
usb_hdr.id = 0;
usb_hdr.type = (ep & 0x80) ? 'C' : 'S';
usb_hdr.xfer_type = type;
usb_hdr.epnum = ep & 0x7F;
usb_hdr.devnum = dev_addr;
usb_hdr.busnum = 0;
usb_hdr.flag_setup = (is_setup) ? 0 : '-';
usb_hdr.flag_data = '='; // ???
usb_hdr.ts_sec = seconds;
usb_hdr.ts_usec = usecs;
usb_hdr.status = 0; // assume success each time?
usb_hdr.length = length - sizeof(struct usbmon_packet);
usb_hdr.len_cap = length;
if (is_setup)
memcpy(usb_hdr.setup, data, 8);
else
memset(usb_hdr.setup, 0, 8);
#if BX_PCAP_LINKTYPE == 220
usb_hdr.interval = 0;
usb_hdr.start_frame = 0;
usb_hdr.xfer_flags = 0;
usb_hdr.ndesc = 0; // we don't support ISO's, so the count is always zero
#endif
#if BX_PCAP_APPEND_PACKETS
// save current position
last_pos = lseek(fd, 0, SEEK_CUR);
#endif
write(fd, &header, sizeof(pcaprec_hdr_t));
write(fd, &usb_hdr, sizeof(struct usbmon_packet));
if (length > sizeof(struct usbmon_packet))
write(fd, data, length - sizeof(struct usbmon_packet));
#if BX_PCAP_APPEND_PACKETS
// save this for next time
last_ep = ep;
memcpy(&last_hdr, &header, sizeof(pcaprec_hdr_t));
memcpy(&last_packet, &usb_hdr, sizeof(struct usbmon_packet));
#endif
}
#endif