099ace64ce
For clock_gettime(), we shouldn't ignore errors if they happen at initialisation phase, because something is seriously wrong and it's not helpful if we proceed as if nothing happened. As we're up and running, though, it's probably better to report the error and use a stale value than to terminate altogether. Make sure we use a zero value if we don't have a stale one somewhere. For timerfd_gettime() and timerfd_settime() failures, just report an error, there isn't much else we can do. 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
|
|
*/
|
|
/* cppcheck-suppress unusedFunction */
|
|
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");
|
|
}
|