From dda7945ca9c9d2371fc37cfaed688f92bd627224 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 28 Feb 2024 12:52:04 +1100 Subject: pcap: Handle short writes in pcap_frame() Currently pcap_frame() assumes that if write() doesn't return an error, it has written everything we want. That's not necessarily true, because it could return a short write. That's not likely to happen on a regular file, but there's not a lot of reason not to be robust here; it's conceivable we might want to direct the pcap fd at a named pipe or similar. So, make pcap_frame() handle short frames by using the write_remainder() helper. Signed-off-by: David Gibson [sbrivio: Formatting fix, and avoid gcc warning in pcap_frame()] Signed-off-by: Stefano Brivio --- pcap.c | 48 +++++++++++++++++++++++++++++------------------- pcap.h | 3 ++- tap.c | 2 +- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/pcap.c b/pcap.c index 5cd7a8c..a4057b5 100644 --- a/pcap.c +++ b/pcap.c @@ -31,6 +31,7 @@ #include "util.h" #include "passt.h" #include "log.h" +#include "pcap.h" #define PCAP_VERSION_MINOR 4 @@ -67,25 +68,32 @@ struct pcap_pkthdr { /** * pcap_frame() - Capture a single frame to pcap file with given timestamp - * @iov: IO vector referencing buffer containing frame (with L2 headers) - * @offset: Offset of the frame from @iov->iov_base + * @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 * @tv: Timestamp * * Returns: 0 on success, -errno on error writing to the file */ -static void pcap_frame(const struct iovec *iov, size_t offset, - const struct timeval *tv) +static void pcap_frame(const struct iovec *iov, size_t iovcnt, + size_t offset, const struct timeval *tv) { size_t len = iov->iov_len - offset; - struct pcap_pkthdr h; - - h.tv_sec = tv->tv_sec; - h.tv_usec = tv->tv_usec; - h.caplen = h.len = len; - - if (write(pcap_fd, &h, sizeof(h)) < 0 || - write(pcap_fd, (char *)iov->iov_base + offset, len) < 0) - debug("Cannot log packet, length %zu", len); + struct pcap_pkthdr h = { + .tv_sec = tv->tv_sec, + .tv_usec = tv->tv_usec, + .caplen = len, + .len = len + }; + struct iovec hiov = { &h, sizeof(h) }; + + (void)iovcnt; + + if (write_remainder(pcap_fd, &hiov, 1, 0) < 0 || + write_remainder(pcap_fd, iov, 1, offset) < 0) { + debug("Cannot log packet, length %zu: %s", + len, strerror(errno)); + } } /** @@ -102,16 +110,18 @@ void pcap(const char *pkt, size_t len) return; gettimeofday(&tv, NULL); - pcap_frame(&iov, 0, &tv); + pcap_frame(&iov, 1, 0, &tv); } /** * pcap_multiple() - Capture multiple frames - * @iov: Array of iovecs, one entry per frame - * @n: Number of frames to capture - * @offset: Offset of the frame within each iovec buffer + * @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, unsigned int n, size_t offset) +void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n, + size_t offset) { struct timeval tv; unsigned int i; @@ -122,7 +132,7 @@ void pcap_multiple(const struct iovec *iov, unsigned int n, size_t offset) gettimeofday(&tv, NULL); for (i = 0; i < n; i++) - pcap_frame(iov + i, offset, &tv); + pcap_frame(iov + i * frame_parts, frame_parts, offset, &tv); } /** diff --git a/pcap.h b/pcap.h index da5a7e8..85fc58e 100644 --- a/pcap.h +++ b/pcap.h @@ -7,7 +7,8 @@ #define PCAP_H void pcap(const char *pkt, size_t len); -void pcap_multiple(const struct iovec *iov, unsigned int n, size_t offset); +void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n, + size_t offset); void pcap_init(struct ctx *c); #endif /* PCAP_H */ diff --git a/tap.c b/tap.c index dd11d1d..87d176b 100644 --- a/tap.c +++ b/tap.c @@ -433,7 +433,7 @@ size_t tap_send_frames(const struct ctx *c, const struct iovec *iov, size_t n) if (m < n) debug("tap: failed to send %zu frames of %zu", n - m, n); - pcap_multiple(iov, m, c->mode == MODE_PASST ? sizeof(uint32_t) : 0); + pcap_multiple(iov, 1, n, c->mode == MODE_PASST ? sizeof(uint32_t) : 0); return m; } -- cgit v1.2.3