// SPDX-License-Identifier: AGPL-3.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 */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "passt.h" #define PCAP_PREFIX "/tmp/passt_" #define PCAP_PREFIX_PASTA "/tmp/pasta_" #define PCAP_ISO8601_FORMAT "%FT%H:%M:%SZ" #define PCAP_ISO8601_STR "YYYY-MM-ddTHH:mm:ssZ" #define PCAP_VERSION_MINOR 4 static int pcap_fd = -1; /* See pcap.h from libpcap, or pcap-savefile(5) */ static 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() - Capture a single frame to pcap file * @pkt: Pointer to data buffer, including L2 headers * @len: L2 packet length */ void pcap(char *pkt, size_t len) { struct pcap_pkthdr h; struct timeval tv; if (pcap_fd == -1) return; gettimeofday(&tv, NULL); h.tv_sec = tv.tv_sec; h.tv_usec = tv.tv_usec; h.caplen = h.len = len; write(pcap_fd, &h, sizeof(h)); write(pcap_fd, pkt, len); } /** * pcapm() - Capture multiple frames from message header to pcap file * @mh: Pointer to sendmsg() message header buffer */ void pcapm(struct msghdr *mh) { struct pcap_pkthdr h; struct timeval tv; unsigned int i; if (pcap_fd == -1) return; gettimeofday(&tv, NULL); h.tv_sec = tv.tv_sec; h.tv_usec = tv.tv_usec; for (i = 0; i < mh->msg_iovlen; i++) { struct iovec *iov = &mh->msg_iov[i]; h.caplen = h.len = iov->iov_len - 4; write(pcap_fd, &h, sizeof(h)); write(pcap_fd, (char *)iov->iov_base + 4, iov->iov_len - 4); } } /** * pcapm() - Capture multiple frames from multiple message headers to pcap file * @mmh: Pointer to first sendmmsg() header */ void pcapmm(struct mmsghdr *mmh, unsigned int vlen) { struct pcap_pkthdr h; struct timeval tv; unsigned int i, j; if (pcap_fd == -1) return; gettimeofday(&tv, NULL); h.tv_sec = tv.tv_sec; h.tv_usec = tv.tv_usec; for (i = 0; i < vlen; i++) { struct msghdr *mh = &mmh[i].msg_hdr; for (j = 0; j < mh->msg_iovlen; j++) { struct iovec *iov = &mh->msg_iov[j]; h.caplen = h.len = iov->iov_len - 4; write(pcap_fd, &h, sizeof(h)); write(pcap_fd, (char *)iov->iov_base + 4, iov->iov_len - 4); } } } /** * pcap_init() - Initialise pcap file * @c: Execution context * @index: pcap name index: passt instance number or pasta target pid */ void pcap_init(struct ctx *c, int index) { char name[] = PCAP_PREFIX PCAP_ISO8601_STR STR(UINT_MAX) ".pcap"; struct timeval tv; struct tm *tm; if (pcap_fd != -1) close(pcap_fd); if (!*c->pcap) return; if (*c->pcap == 1) { if (c->mode == MODE_PASTA) memcpy(name, PCAP_PREFIX_PASTA, sizeof(PCAP_PREFIX_PASTA)); gettimeofday(&tv, NULL); tm = localtime(&tv.tv_sec); strftime(name + strlen(PCAP_PREFIX), sizeof(PCAP_ISO8601_STR) - 1, PCAP_ISO8601_FORMAT, tm); snprintf(name + strlen(PCAP_PREFIX) + strlen(PCAP_ISO8601_STR), sizeof(name) - strlen(PCAP_PREFIX) - strlen(PCAP_ISO8601_STR), "_%i.pcap", index); strncpy(c->pcap, name, PATH_MAX); } pcap_fd = open(c->pcap, O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (pcap_fd == -1) { perror("open"); return; } info("Saving packet capture at %s", c->pcap); write(pcap_fd, &pcap_hdr, sizeof(pcap_hdr)); }