From e9bef0595aa26b71442f62fdc215192d08d2e578 Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Fri, 12 Jun 2026 08:22:36 -0500 Subject: [PATCH] pcap-file: skip setvbuf on non-seekable streams Reading a pcap from /dev/stdin or a named pipe currently fails with "failed to get first packet timestamp. pcap_next_ex(): -1" because InitPcapFile calls setvbuf on the FILE* underlying the pcap handle after libpcap has already consumed the pcap header. On a non-seekable fd glibc cannot recover from that and the next read returns -1. Detect non-regular files via fstat and skip setvbuf in that case so the read keeps working on pipes, fifos and stdin. Accept pcap-file.buffer-size values of 0, which disables setvbuf buffering as an explicit opt-out, or PCAP_FILE_BUFFER_SIZE_MIN (4 KiB) to PCAP_FILE_BUFFER_SIZE_MAX (64 MiB). Treat any non-zero setvbuf return value as an error, not just negative values. When pcap-file.buffer-size fails to parse, retain the default buffer size instead of falling through and setting it to 0. The branches are now mutually exclusive so only one of the parse-error, accepted, or out-of-range messages is logged. Update the user guide: --pcap-file-buffer-size now documents valid values of 0 (disables setvbuf buffering) or 4 KiB to 64 MiB, and pcap-file.rst notes that 0 is the opt-out for non-seekable sources such as stdin and named pipes. Bug: #8464. --- doc/userguide/capture-hardware/pcap-file.rst | 4 ++++ doc/userguide/partials/options.rst | 3 ++- src/source-pcap-file-helper.c | 14 ++++++++++---- src/source-pcap-file.c | 16 +++++++++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/doc/userguide/capture-hardware/pcap-file.rst b/doc/userguide/capture-hardware/pcap-file.rst index d5ed628dde36..2d0dde98ca15 100644 --- a/doc/userguide/capture-hardware/pcap-file.rst +++ b/doc/userguide/capture-hardware/pcap-file.rst @@ -33,6 +33,10 @@ This can improve performance, especially for large files. The size can be specified through the command line option, see :ref:`--pcap-file-buffer-size ` +Setting ``buffer-size`` to ``0`` disables ``setvbuf`` buffering. This is the +explicit opt-out for non-seekable sources such as ``/dev/stdin`` or named +pipes, where buffering the underlying file descriptor is not supported. + Directory-related options ------------------------- diff --git a/doc/userguide/partials/options.rst b/doc/userguide/partials/options.rst index 5264db813337..bcbca0f62206 100644 --- a/doc/userguide/partials/options.rst +++ b/doc/userguide/partials/options.rst @@ -103,7 +103,8 @@ .. option:: --pcap-file-buffer-size Set read buffer size using ``setvbuf`` to speed up pcap reading. Valid values - are 4 KiB to 64 MiB. Default value is 128 KiB. Supported on Linux only. + are 0, which disables ``setvbuf`` buffering, or 4 KiB to 64 MiB. Default + value is 128 KiB. Supported on Linux only. .. option:: -i diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index e88db6d51547..70cb2f550bb5 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -271,10 +271,16 @@ TmEcode InitPcapFile(PcapFileFileVars *pfv) #if defined(HAVE_SETVBUF) && defined(OS_LINUX) if (pcap_g.read_buffer_size > 0) { - errno = 0; - if (setvbuf(pcap_file(pfv->pcap_handle), pfv->buffer, _IOFBF, pcap_g.read_buffer_size) < - 0) { - SCLogWarning("Failed to setvbuf on PCAP file handle: %s", strerror(errno)); + struct stat sb; + int fd = fileno(pcap_file(pfv->pcap_handle)); + if (fd >= 0 && fstat(fd, &sb) == 0 && !S_ISREG(sb.st_mode)) { + SCLogInfo("%s: skipping setvbuf, underlying fd is not a regular file", pfv->filename); + } else { + errno = 0; + if (setvbuf(pcap_file(pfv->pcap_handle), pfv->buffer, _IOFBF, + pcap_g.read_buffer_size) != 0) { + SCLogWarning("Failed to setvbuf on PCAP file handle: %s", strerror(errno)); + } } } #endif diff --git a/src/source-pcap-file.c b/src/source-pcap-file.c index 808fe27b69cb..b32a0cecea59 100644 --- a/src/source-pcap-file.c +++ b/src/source-pcap-file.c @@ -158,13 +158,19 @@ void PcapFileGlobalInit(void) if (SCConfGet("pcap-file.buffer-size", &str) == 1) { uint32_t value = 0; if (ParseSizeStringU32(str, &value) < 0) { - SCLogWarning("failed to parse pcap-file.buffer-size %s", str); - } - if (value >= PCAP_FILE_BUFFER_SIZE_MIN && value <= PCAP_FILE_BUFFER_SIZE_MAX) { - SCLogInfo("Pcap-file will use %u buffer size", value); + SCLogWarning("failed to parse pcap-file.buffer-size %s; keeping default %u", str, + PCAP_FILE_BUFFER_SIZE_DEFAULT); + } else if (value == 0 || + (value >= PCAP_FILE_BUFFER_SIZE_MIN && value <= PCAP_FILE_BUFFER_SIZE_MAX)) { + if (value == 0) { + SCLogInfo("Pcap-file buffering disabled"); + } else { + SCLogInfo("Pcap-file will use %u buffer size", value); + } pcap_g.read_buffer_size = value; } else { - SCLogWarning("pcap-file.buffer-size value of %u is invalid. Valid range is %u-%u", + SCLogWarning("pcap-file.buffer-size value of %u is invalid. Valid values are 0 to " + "disable buffering, or %u-%u", value, PCAP_FILE_BUFFER_SIZE_MIN, PCAP_FILE_BUFFER_SIZE_MAX); } }