mirror of
https://passt.top/passt
synced 2024-12-22 05:35:23 +00:00
28997fcb29
add virtio and vhost-user functions to connect with QEMU. $ ./passt --vhost-user and # qemu-system-x86_64 ... -m 4G \ -object memory-backend-memfd,id=memfd0,share=on,size=4G \ -numa node,memdev=memfd0 \ -chardev socket,id=chr0,path=/tmp/passt_1.socket \ -netdev vhost-user,id=netdev0,chardev=chr0 \ -device virtio-net,mac=9a:2b:2c:2d:2e:2f,netdev=netdev0 \ ... Signed-off-by: Laurent Vivier <lvivier@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [sbrivio: as suggested by lvivier, include <netinet/if_ether.h> before including <linux/if_ether.h> as C libraries such as musl __UAPI_DEF_ETHHDR in <netinet/if_ether.h> if they already have a definition of struct ethhdr] Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
182 lines
4.2 KiB
C
182 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/* PASST - Plug A Simple Socket Transport
|
|
* for qemu/UNIX domain socket mode
|
|
*
|
|
* PASTA - Pack A Subtle Tap Abstraction
|
|
* for network namespace/tap device mode
|
|
*
|
|
* pcap.c - Packet capture for PASST/PASTA
|
|
*
|
|
* Copyright (c) 2021 Red Hat GmbH
|
|
* Author: Stefano Brivio <sbrivio@redhat.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <net/ethernet.h>
|
|
#include <netinet/in.h>
|
|
#include <unistd.h>
|
|
#include <net/if.h>
|
|
|
|
#include "util.h"
|
|
#include "passt.h"
|
|
#include "log.h"
|
|
#include "pcap.h"
|
|
#include "iov.h"
|
|
|
|
#define PCAP_VERSION_MINOR 4
|
|
|
|
static int pcap_fd = -1;
|
|
|
|
/* See pcap.h from libpcap, or pcap-savefile(5) */
|
|
static const struct {
|
|
uint32_t magic;
|
|
#define PCAP_MAGIC 0xa1b2c3d4
|
|
|
|
uint16_t major;
|
|
#define PCAP_VERSION_MAJOR 2
|
|
|
|
uint16_t minor;
|
|
#define PCAP_VERSION_MINOR 4
|
|
|
|
int32_t thiszone;
|
|
uint32_t sigfigs;
|
|
uint32_t snaplen;
|
|
|
|
uint32_t linktype;
|
|
#define PCAP_LINKTYPE_ETHERNET 1
|
|
} pcap_hdr = {
|
|
PCAP_MAGIC, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR, 0, 0, ETH_MAX_MTU,
|
|
PCAP_LINKTYPE_ETHERNET
|
|
};
|
|
|
|
struct pcap_pkthdr {
|
|
uint32_t tv_sec;
|
|
uint32_t tv_usec;
|
|
uint32_t caplen;
|
|
uint32_t len;
|
|
};
|
|
|
|
/**
|
|
* pcap_frame() - Capture a single frame to pcap file with given timestamp
|
|
* @iov: IO vector containing frame (with L2 headers and tap headers)
|
|
* @iovcnt: Number of buffers (@iov entries) in frame
|
|
* @offset: Byte offset of the L2 headers within @iov
|
|
* @now: Timestamp
|
|
*
|
|
* Returns: 0 on success, -errno on error writing to the file
|
|
*/
|
|
static void pcap_frame(const struct iovec *iov, size_t iovcnt,
|
|
size_t offset, const struct timespec *now)
|
|
{
|
|
size_t l2len = iov_size(iov, iovcnt) - offset;
|
|
struct pcap_pkthdr h = {
|
|
.tv_sec = now->tv_sec,
|
|
.tv_usec = DIV_ROUND_CLOSEST(now->tv_nsec, 1000),
|
|
.caplen = l2len,
|
|
.len = l2len
|
|
};
|
|
|
|
if (write_all_buf(pcap_fd, &h, sizeof(h)) < 0 ||
|
|
write_remainder(pcap_fd, iov, iovcnt, offset) < 0)
|
|
debug_perror("Cannot log packet, length %zu", l2len);
|
|
}
|
|
|
|
/**
|
|
* pcap() - Capture a single frame to pcap file
|
|
* @pkt: Pointer to data buffer, including L2 headers
|
|
* @l2len: L2 frame length
|
|
*/
|
|
void pcap(const char *pkt, size_t l2len)
|
|
{
|
|
struct iovec iov = { (char *)pkt, l2len };
|
|
struct timespec now = { 0 };
|
|
|
|
if (pcap_fd == -1)
|
|
return;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &now))
|
|
err_perror("Failed to get CLOCK_REALTIME time");
|
|
|
|
pcap_frame(&iov, 1, 0, &now);
|
|
}
|
|
|
|
/**
|
|
* pcap_multiple() - Capture multiple frames
|
|
* @iov: IO vector with @frame_parts * @n entries
|
|
* @frame_parts: Number of IO vector items for each frame
|
|
* @n: Number of frames to capture
|
|
* @offset: Offset of the L2 frame within each iovec buffer
|
|
*/
|
|
void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n,
|
|
size_t offset)
|
|
{
|
|
struct timespec now = { 0 };
|
|
unsigned int i;
|
|
|
|
if (pcap_fd == -1)
|
|
return;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &now))
|
|
err_perror("Failed to get CLOCK_REALTIME time");
|
|
|
|
for (i = 0; i < n; i++)
|
|
pcap_frame(iov + i * frame_parts, frame_parts, offset, &now);
|
|
}
|
|
|
|
/*
|
|
* pcap_iov - Write packet data described by an I/O vector
|
|
* to a pcap file descriptor.
|
|
*
|
|
* @iov: Pointer to the array of struct iovec describing the I/O vector
|
|
* containing packet data to write, including L2 header
|
|
* @iovcnt: Number of buffers (@iov entries)
|
|
* @offset: Offset of the L2 frame within the full data length
|
|
*/
|
|
void pcap_iov(const struct iovec *iov, size_t iovcnt, size_t offset)
|
|
{
|
|
struct timespec now = { 0 };
|
|
|
|
if (pcap_fd == -1)
|
|
return;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &now))
|
|
err_perror("Failed to get CLOCK_REALTIME time");
|
|
|
|
pcap_frame(iov, iovcnt, offset, &now);
|
|
}
|
|
|
|
/**
|
|
* pcap_init() - Initialise pcap file
|
|
* @c: Execution context
|
|
*/
|
|
void pcap_init(struct ctx *c)
|
|
{
|
|
if (pcap_fd != -1)
|
|
return;
|
|
|
|
if (!*c->pcap)
|
|
return;
|
|
|
|
pcap_fd = output_file_open(c->pcap, O_WRONLY);
|
|
if (pcap_fd == -1) {
|
|
err_perror("Couldn't open pcap file %s", c->pcap);
|
|
return;
|
|
}
|
|
|
|
info("Saving packet capture to %s", c->pcap);
|
|
|
|
if (write(pcap_fd, &pcap_hdr, sizeof(pcap_hdr)) < 0)
|
|
warn_perror("Cannot write PCAP header");
|
|
}
|