pcap: refactor delete-when-done to support non-alerts
Some checks are pending
builds / Prepare dependencies (push) Waiting to run
builds / Prepare cbindgen (push) Waiting to run
builds / AlmaLinux 10 (schema, plugins) (push) Blocked by required conditions
builds / AlmaLinux 9 (schema, rust-checks) (push) Blocked by required conditions
builds / AlmaLinux 9 Test Templates (push) Blocked by required conditions
builds / Build RPMs (push) Blocked by required conditions
builds / AlmaLinux 8 (push) Blocked by required conditions
builds / CentOS Stream 9 (push) Blocked by required conditions
builds / Fedora 43 (Suricata Verify codecov) (push) Blocked by required conditions
builds / Fedora 43 (clang, debug, asan, wshadow, rust-strict, systemd) (push) Blocked by required conditions
builds / Fedora 43 (gcc, debug, flto, asan, wshadow, rust-strict) (push) Blocked by required conditions
builds / Fedora (non-root, debug, clang, asan, wshadow, rust-strict, no-ja) (push) Blocked by required conditions
builds / AlmaLinux 9 (no jansson) (push) Blocked by required conditions
builds / AlmaLinux 9 (Minimal/Recommended Build) (push) Blocked by required conditions
builds / Ubuntu 24.04 (cocci) (push) Blocked by required conditions
builds / Ubuntu 24.04 (RUSTC+CARGO vars) (push) Blocked by required conditions
builds / Ubuntu 24.04 (unittests coverage) (push) Blocked by required conditions
builds / Ubuntu 22.04 (unix socket mode coverage) (push) Blocked by required conditions
builds / Ubuntu 22.04 (afpacket and dpdk coverage) (push) Blocked by required conditions
builds / Ubuntu 24.04 (pcap unix socket ASAN) (push) Blocked by required conditions
builds / Ubuntu 24.04 (afpacket IPS tests in namespaces) (push) Blocked by required conditions
builds / Ubuntu 24.04 (afpacket and dpdk live tests with ASAN) (push) Blocked by required conditions
builds / Ubuntu 22.04 (fuzz corpus coverage) (push) Blocked by required conditions
builds / Ubuntu 20.04 (-DNDEBUG) (push) Blocked by required conditions
builds / Ubuntu 20.04 (unsupported rust) (push) Blocked by required conditions
builds / Ubuntu 22.04 (Debug Validation) (push) Blocked by required conditions
builds / Ubuntu 22.04 (Fuzz) (push) Blocked by required conditions
builds / Ubuntu 22.04 (Netmap build) (push) Blocked by required conditions
builds / Ubuntu 22.04 (Minimal/Recommended Build) (push) Blocked by required conditions
builds / Ubuntu 22.04 (DPDK Build) (push) Blocked by required conditions
builds / Debian 12 (xdp) (push) Blocked by required conditions
builds / Debian 13 (xdp) (push) Blocked by required conditions
builds / Ubuntu 22.04 Dist Builder (push) Blocked by required conditions
builds / Debian 12 MSRV (push) Blocked by required conditions
builds / Debian 11 (push) Blocked by required conditions
builds / MacOS Latest (push) Blocked by required conditions
builds / Windows MSYS2 MINGW64 (NPcap) (push) Blocked by required conditions
builds / Windows MSYS2 MINGW64 (libpcap) (push) Blocked by required conditions
builds / Windows MSYS2 UCRT64 (libpcap) (push) Blocked by required conditions
builds / Windows MSYS2 MINGW64 (WinDivert) (push) Blocked by required conditions
builds / PF_RING (push) Blocked by required conditions
CodeQL (Rust/C) / Analyze (push) Waiting to run
docs / Prepare dependencies (push) Waiting to run
docs / Prepare cbindgen (push) Waiting to run
docs / Ubuntu 22.04 Dist Builder (push) Blocked by required conditions
Nix Env Build / tests (push) Waiting to run
Scan-build / Scan-build (push) Waiting to run
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run

Refactor pcap file deletion to use a single delete-when-done option
with three values instead of separate boolean options:
- false (default): No deletion
- true: Always delete files
- "non-alerts": Delete only files with no alerts

Also account for alerts produced by pseudo packets (flow timeout / shutdown flush):
- Introduce small capture hooks and invoke on pseudo-packet creation so the
  capture layer can retain references and observe alerts emitted after the last
  live packet
- Call the hook from both TmThreadDisableReceiveThreads and TmThreadDrainPacketThreads

Key changes:
- Replace should_delete/delete_non_alerts_only bools with enum
- Move alert counter from global to per-file PcapFileFileVars
- Relocate alert counting from PacketAlertFinalize to pcap module
- Ensure thread safety for both single and continuous pcap modes
- Add unit tests for configuration parsing and pseudo-packet alert path

The --pcap-file-delete command line option overrides YAML config
and forces "always delete" mode for backward compatibility.

Documentation updated to reflect the new three-value configuration.

Fixes OISF#7786
This commit is contained in:
Ofer Dagan 2025-06-25 13:30:38 +03:00 committed by Victor Julien
parent 539e4ee665
commit e1f9d8a067
13 changed files with 996 additions and 33 deletions

View file

@ -15,6 +15,8 @@ Configuration
checksum-checks: auto
# buffer-size: 128 KiB
# tenant-id: none
# Applies to file and directory. Options: false (no deletion), true (always delete),
# "non-alerts" (delete only files with no alerts)
# delete-when-done: false
# recursive: false
# continuous: false
@ -85,9 +87,22 @@ Other options
**delete-when-done**
- If ``true``, Suricata deletes the PCAP file after processing.
- The command-line option is
:ref:`--pcap-file-delete <cmdline-option-pcap-file-delete>`
Controls when PCAP files are deleted after processing. Three values are supported:
- ``false`` (default): Files are never deleted
- ``true``: Files are always deleted after processing
- ``"non-alerts"``: Files are deleted only if they didn't generate any alerts
.. note::
The command-line option :ref:`--pcap-file-delete <cmdline-option-pcap-file-delete>`
overrides this configuration and forces "always delete" mode (``true``).
.. warning::
When using ``"non-alerts"`` mode, file deletion is deferred until thread
cleanup to ensure alert counts are finalized. This may delay deletion
compared to other modes.
**BPF filter**

View file

@ -80,10 +80,23 @@
.. option:: --pcap-file-delete
Used with the -r option to indicate that the mode should delete pcap files
after they have been processed. This is useful with pcap-file-continuous to
continuously feed files to a directory and have them cleaned up when done. If
this option is not set, pcap files will not be deleted after processing.
Used with the -r option to force deletion of pcap files after they have been
processed. This is useful with pcap-file-continuous to continuously feed files
to a directory and have them cleaned up when done.
**command-line vs Configuration**: This command-line option overrides the
``pcap-file.delete-when-done`` configuration option in ``suricata.yaml`` and
forces "always delete" mode (equivalent to ``delete-when-done: true``).
**For more control**, use the ``pcap-file.delete-when-done`` configuration
option instead, which supports three values:
- ``false`` (default): No files are deleted
- ``true``: All files are deleted after processing
- ``"non-alerts"``: Only files that generated no alerts are deleted
If neither ``--pcap-file-delete`` nor ``delete-when-done`` is configured,
pcap files will not be deleted after processing.
.. _cmdline-option-pcap-file-buffer-size:

View file

@ -51,6 +51,7 @@ noinst_HEADERS = \
app-layer.h \
bindgen.h \
build-info.h \
capture-hooks.h \
conf-yaml-loader.h \
conf.h \
counters.h \
@ -636,6 +637,7 @@ libsuricata_c_a_SOURCES = \
app-layer-ssl.c \
app-layer-tftp.c \
app-layer.c \
capture-hooks.c \
conf-yaml-loader.c \
conf.c \
counters.c \

65
src/capture-hooks.c Normal file
View file

@ -0,0 +1,65 @@
/* Copyright (C) 2007-2026 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
* Lightweight indirection layer for capture-related callbacks.
*
* This module lets the capture implementation register small hooks that the
* generic engine can invoke without hard dependencies. Two hooks are used:
* - on-alerts: invoked when a packet produced alerts so capture can update
* per-input stats (e.g., deciding if a pcap should be deleted or kept).
* - on-pseudo-created: invoked when the engine creates pseudo packets (e.g.,
* flow timeout or shutdown flush). This allows capture to retain references
* or track alert outcomes tied to those pseudo packets.
*/
#include "suricata-common.h"
#include "capture-hooks.h"
static CaptureOnPacketWithAlertsHook g_on_alerts_hook = NULL;
static CaptureOnPseudoPacketCreatedHook g_on_pseudo_created_hook = NULL;
void CaptureHooksSet(
CaptureOnPacketWithAlertsHook OnAlerts, CaptureOnPseudoPacketCreatedHook OnPseudoCreated)
{
/* Allow re-setting the same hooks (e.g. during pcap file reload), but BUG_ON
* if overwriting with different ones without clearing. */
if (g_on_alerts_hook != NULL) {
BUG_ON(g_on_alerts_hook != OnAlerts);
}
if (g_on_pseudo_created_hook != NULL) {
BUG_ON(g_on_pseudo_created_hook != OnPseudoCreated);
}
g_on_alerts_hook = OnAlerts;
g_on_pseudo_created_hook = OnPseudoCreated;
}
void CaptureHooksOnPacketWithAlerts(const Packet *p)
{
if (g_on_alerts_hook != NULL) {
g_on_alerts_hook(p);
}
}
void CaptureHooksOnPseudoPacketCreated(Packet *p)
{
if (g_on_pseudo_created_hook != NULL) {
g_on_pseudo_created_hook(p);
}
}

43
src/capture-hooks.h Normal file
View file

@ -0,0 +1,43 @@
/* Copyright (C) 2007-2026 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file capture-hooks.h
* Small hook interface for capture modules to react to events in the
* generic engine without creating circular dependencies.
*/
#ifndef SURICATA_CAPTURE_HOOKS_H
#define SURICATA_CAPTURE_HOOKS_H
#include "suricata-common.h"
struct Packet_;
typedef struct Packet_ Packet;
typedef void (*CaptureOnPacketWithAlertsHook)(const Packet *p);
typedef void (*CaptureOnPseudoPacketCreatedHook)(Packet *p);
/* Register/clear hooks (called by capture implementations) */
void CaptureHooksSet(
CaptureOnPacketWithAlertsHook OnAlerts, CaptureOnPseudoPacketCreatedHook OnPseudoCreated);
/* Invoke hooks (called from generic code, safe if unset) */
void CaptureHooksOnPacketWithAlerts(const Packet *p);
void CaptureHooksOnPseudoPacketCreated(Packet *p);
#endif /* SURICATA_CAPTURE_HOOKS_H */

View file

@ -37,6 +37,7 @@
#include "util-validate.h"
#include "action-globals.h"
#include "capture-hooks.h"
/** tag signature we use for tag alerts */
static Signature g_tag_signature;
@ -582,6 +583,12 @@ static inline void PacketAlertFinalizeProcessQueue(
p->flags |= PKT_FIRST_ALERTS;
}
}
/* Notify capture layer about packets with real alerts (not pass-only),
* so capture impl can update per-capture context (e.g. pcap-file alert counts). */
if (alerted) {
CaptureHooksOnPacketWithAlerts(p);
}
}
/**

View file

@ -116,6 +116,7 @@
#include "decode-vntag.h"
#include "decode-vxlan.h"
#include "decode-pppoe.h"
#include "source-pcap-file-helper.h"
#include "output-json-stats.h"
@ -212,6 +213,7 @@ static void RegisterUnittests(void)
StreamingBufferRegisterTests();
MacSetRegisterTests();
FlowRateRegisterTests();
SourcePcapFileHelperRegisterTests();
#ifdef OS_WIN32
Win32SyscallRegisterTests();
#endif

View file

@ -28,34 +28,77 @@
#include "util-datalink.h"
#include "util-checksum.h"
#include "util-profiling.h"
#include "source-pcap-file.h"
#include "util-exception-policy.h"
#include "conf-yaml-loader.h"
#include "capture-hooks.h"
#include "threads.h"
extern uint32_t max_pending_packets;
extern PcapFileGlobalVars pcap_g;
static PcapFileFileVars *pcap_current_pfv = NULL;
static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt);
static void PcapFileReleasePacket(Packet *p)
{
PcapFileFileVars *pfv = p->pcap_v.pfv;
if (pfv != NULL) {
PcapFileFinalizePacket(pfv);
}
PacketFreeOrRelease(p);
}
void PcapFileReleasePseudoPacket(Packet *p)
{
PcapFileFileVars *pfv = p->pcap_v.pfv;
/* Alerts are counted in PacketAlertFinalize via PcapFileAddAlertCount, so
* avoid double-counting here. Decrement refcount if we held one. */
if (pfv != NULL) {
uint32_t prev = SC_ATOMIC_SUB(pfv->ref_cnt, 1);
if (prev == 1 && pfv->cleanup_requested) {
CleanupPcapFileFileVars(pfv);
}
}
PacketFreeOrRelease(p);
}
void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
{
if (pfv != NULL) {
if (pfv->pcap_handle != NULL) {
pcap_close(pfv->pcap_handle);
pfv->pcap_handle = NULL;
}
if (pfv->filename != NULL) {
if (pfv->shared != NULL && pfv->shared->should_delete) {
SCLogDebug("Deleting pcap file %s", pfv->filename);
if (unlink(pfv->filename) != 0) {
SCLogWarning("Failed to delete %s: %s", pfv->filename, strerror(errno));
}
}
SCFree(pfv->filename);
pfv->filename = NULL;
}
pfv->shared = NULL;
SCFree(pfv);
if (pfv == NULL) {
return;
}
/* If there are still packets in flight, defer ALL cleanup actions, including
* the deletion decision, until the last packet completes. */
if (SC_ATOMIC_GET(pfv->ref_cnt) != 0) {
pfv->cleanup_requested = true;
return;
}
/* No packets in flight anymore: it's now safe to close, decide, and delete. */
if (pfv->pcap_handle != NULL) {
pcap_close(pfv->pcap_handle);
pfv->pcap_handle = NULL;
}
if (pfv->filename != NULL) {
if (PcapFileShouldDeletePcapFile(pfv)) {
SCLogDebug("Deleting pcap file %s", pfv->filename);
if (unlink(pfv->filename) != 0) {
SCLogWarning("Failed to delete %s: %s", pfv->filename, strerror(errno));
}
}
SCFree(pfv->filename);
pfv->filename = NULL;
}
pfv->shared = NULL;
if (pcap_current_pfv == pfv) {
pcap_current_pfv = NULL;
}
SCFree(pfv);
}
void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
@ -74,6 +117,10 @@ void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
if (unlikely(p == NULL)) {
SCReturn;
}
SC_ATOMIC_ADD(ptv->ref_cnt, 1);
SCLogDebug("pcap-file: got packet, pfv=%p filename=%s ref_cnt now=%u p=%p", (void *)ptv,
ptv->filename, SC_ATOMIC_GET(ptv->ref_cnt), (void *)p);
PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
PKT_SET_SRC(p, PKT_SRC_WIRE);
@ -83,9 +130,12 @@ void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
p->pcap_v.pcap_cnt = ++pcap_g.cnt;
p->pcap_v.tenant_id = ptv->shared->tenant_id;
p->pcap_v.pfv = ptv;
ptv->shared->pkts++;
ptv->shared->bytes += h->caplen;
p->ReleasePacket = PcapFileReleasePacket;
if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
TmqhOutputPacketpool(ptv->shared->tv, p);
PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
@ -120,6 +170,16 @@ const char *PcapFileGetFilename(void)
return pcap_filename;
}
void PcapFileSetCurrentPfv(PcapFileFileVars *pfv)
{
pcap_current_pfv = pfv;
}
PcapFileFileVars *PcapFileGetCurrentPfv(void)
{
return pcap_current_pfv;
}
/**
* \brief Main PCAP file reading Loop function
*/
@ -127,6 +187,7 @@ TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
{
SCEnter();
PcapFileSetCurrentPfv(ptv);
/* initialize all the thread's initial timestamp */
if (likely(ptv->first_pkt_hdr != NULL)) {
TmThreadsInitThreadsTimestamp(SCTIME_FROM_TIMEVAL(&ptv->first_pkt_ts));
@ -236,6 +297,14 @@ TmEcode InitPcapFile(PcapFileFileVars *pfv)
pcap_freecode(&pfv->filter);
}
SC_ATOMIC_INIT(pfv->alerts_count);
SC_ATOMIC_SET(pfv->alerts_count, 0);
SC_ATOMIC_INIT(pfv->ref_cnt);
SC_ATOMIC_SET(pfv->ref_cnt, 0);
pfv->cleanup_requested = false;
pfv->datalink = pcap_datalink(pfv->pcap_handle);
SCLogDebug("datalink %" PRId32 "", pfv->datalink);
DatalinkSetGlobalType(pfv->datalink);
@ -285,3 +354,687 @@ TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn)
SCReturnInt(TM_ECODE_OK);
}
bool PcapFileShouldDeletePcapFile(PcapFileFileVars *pfv)
{
if (pfv == NULL || pfv->shared == NULL) {
return false;
}
if (pfv->shared->delete_mode == PCAP_FILE_DELETE_NONE) {
return false;
}
if (pfv->shared->delete_mode == PCAP_FILE_DELETE_ALWAYS) {
return true;
}
/* PCAP_FILE_DELETE_NON_ALERTS mode */
uint64_t file_alerts = SC_ATOMIC_GET(pfv->alerts_count);
if (file_alerts != 0) {
SCLogDebug("Skipping deletion of %s due to %" PRIu64 " alert(s) generated.", pfv->filename,
file_alerts);
return false;
}
SCLogDebug("pcap-file: will delete %s (no alerts counted)", pfv->filename);
return true;
}
void PcapFileFinalizePacket(PcapFileFileVars *pfv)
{
if (pfv != NULL) {
/* decrease ref count as packet is done */
uint32_t prev = SC_ATOMIC_SUB(pfv->ref_cnt, 1);
SCLogDebug("pcap-file: packet done pfv=%p filename=%s ref_cnt was=%u now=%u", (void *)pfv,
pfv->filename, prev, prev - 1);
if (prev == 1) {
if (pfv->cleanup_requested) {
CleanupPcapFileFileVars(pfv);
}
}
}
}
void PcapFileAddAlertCount(PcapFileFileVars *pfv, uint16_t alert_count)
{
if (pfv != NULL && alert_count > 0) {
SC_ATOMIC_ADD(pfv->alerts_count, alert_count);
}
}
static void PcapCaptureOnPacketWithAlerts(const Packet *p)
{
PcapFileFileVars *pfv = p->pcap_v.pfv;
if (pfv == NULL) {
pfv = PcapFileGetCurrentPfv();
}
if (pfv != NULL) {
/* alerts.cnt is uint16_t; count alerts for delete-on-non-alerts logic */
PcapFileAddAlertCount(pfv, p->alerts.cnt);
}
}
static void PcapCaptureOnPseudoPacketCreated(Packet *p)
{
/* For pseudo packets created by generic layers, associate with current pfv
* and ensure refcount held so deletion defers. */
if (p->pcap_v.pfv == NULL) {
PcapFileFileVars *pfv = PcapFileGetCurrentPfv();
if (pfv != NULL) {
p->pcap_v.pfv = pfv;
p->ReleasePacket = PcapFileReleasePseudoPacket;
SC_ATOMIC_ADD(pfv->ref_cnt, 1);
}
}
}
void PcapFileInstallCaptureHooks(void)
{
CaptureHooksSet(PcapCaptureOnPacketWithAlerts, PcapCaptureOnPseudoPacketCreated);
}
PcapFileDeleteMode PcapFileParseDeleteMode(void)
{
PcapFileDeleteMode delete_mode = PCAP_FILE_DELETE_NONE;
const char *delete_when_done_str = NULL;
if (SCConfGet("pcap-file.delete-when-done", &delete_when_done_str) == 1) {
if (strcmp(delete_when_done_str, "non-alerts") == 0) {
delete_mode = PCAP_FILE_DELETE_NON_ALERTS;
} else {
int delete_always = 0;
if (SCConfGetBool("pcap-file.delete-when-done", &delete_always) == 1) {
if (delete_always == 1) {
delete_mode = PCAP_FILE_DELETE_ALWAYS;
}
}
}
}
return delete_mode;
}
#ifdef UNITTESTS
#include "util-unittest-helper.h"
/**
* \test Tests that the PcapFileShouldDeletePcapFile function correctly applies the
* delete mode configuration.
*/
static int SourcePcapFileHelperTest01(void)
{
PcapFileSharedVars shared;
memset(&shared, 0, sizeof(shared));
shared.delete_mode = PCAP_FILE_DELETE_ALWAYS;
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
pfv.shared = &shared;
pfv.filename = SCStrdup("test.pcap");
SC_ATOMIC_INIT(pfv.alerts_count);
SC_ATOMIC_SET(pfv.alerts_count, 0);
/* Test case 1: Always delete mode */
int result1 = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF_NOT(result1);
/* Test case 2: Non-alerts mode with no alerts */
shared.delete_mode = PCAP_FILE_DELETE_NON_ALERTS;
int result2 = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF_NOT(result2);
/* Test case 3: Non-alerts mode with alerts */
SC_ATOMIC_ADD(pfv.alerts_count, 1);
int result3 = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF(result3);
/* Test case 4: Always delete mode with alerts */
shared.delete_mode = PCAP_FILE_DELETE_ALWAYS;
int result4 = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF_NOT(result4);
/* Test case 5: No delete mode */
shared.delete_mode = PCAP_FILE_DELETE_NONE;
int result5 = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF(result5);
SCFree(pfv.filename);
PASS;
}
/**
* \test Test PcapFileFinalizePacket function with reference counting
*/
static int SourcePcapFileHelperTest02(void)
{
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
SC_ATOMIC_INIT(pfv.alerts_count);
SC_ATOMIC_SET(pfv.alerts_count, 0);
SC_ATOMIC_INIT(pfv.ref_cnt);
SC_ATOMIC_SET(pfv.ref_cnt, 0);
pfv.cleanup_requested = false;
/* Test adding alerts with reference counting */
SC_ATOMIC_ADD(pfv.ref_cnt, 1); /* simulate packet in flight */
PcapFileAddAlertCount(&pfv, 5);
PcapFileFinalizePacket(&pfv);
uint64_t count = SC_ATOMIC_GET(pfv.alerts_count);
FAIL_IF_NOT(count == 5);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv.ref_cnt) == 0); /* should be decremented */
/* Test adding more alerts */
SC_ATOMIC_ADD(pfv.ref_cnt, 1);
PcapFileAddAlertCount(&pfv, 3);
PcapFileFinalizePacket(&pfv);
count = SC_ATOMIC_GET(pfv.alerts_count);
FAIL_IF_NOT(count == 8);
/* Test with zero alerts (should not increment count) */
SC_ATOMIC_ADD(pfv.ref_cnt, 1);
PcapFileFinalizePacket(&pfv);
count = SC_ATOMIC_GET(pfv.alerts_count);
FAIL_IF_NOT(count == 8);
/* Test with NULL pfv (should not crash) */
PcapFileFinalizePacket(NULL);
PASS;
}
/* Mock for configuration testing */
static int SetupYamlConf(const char *conf_string)
{
SCConfCreateContextBackup();
SCConfInit();
return SCConfYamlLoadString(conf_string, strlen(conf_string));
}
static void CleanupYamlConf(void)
{
SCConfDeInit();
SCConfRestoreContextBackup();
}
/**
* \test Test PcapFileParseDeleteMode with all configuration combinations
*/
static int SourcePcapFileHelperTest03(void)
{
/* Test 1: No configuration (should default to NONE) */
SCConfCreateContextBackup();
SCConfInit();
PcapFileDeleteMode result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_NONE);
SCConfDeInit();
SCConfRestoreContextBackup();
/* Test 2: "false" configuration */
const char *conf_false = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: false\n";
SetupYamlConf(conf_false);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_NONE);
CleanupYamlConf();
/* Test 3: "true" configuration */
const char *conf_true = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: true\n";
SetupYamlConf(conf_true);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_ALWAYS);
CleanupYamlConf();
/* Test 4: "non-alerts" configuration */
const char *conf_non_alerts = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: \"non-alerts\"\n";
SetupYamlConf(conf_non_alerts);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_NON_ALERTS);
CleanupYamlConf();
/* Test 5: Invalid configuration (should default to NONE) */
const char *conf_invalid = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: \"invalid-value\"\n";
SetupYamlConf(conf_invalid);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_NONE);
CleanupYamlConf();
PASS;
}
/**
* \test pfv is NULL.
*/
static int SourcePcapFileHelperTest04(void)
{
int rc = PcapFileShouldDeletePcapFile(NULL);
FAIL_IF(rc);
PASS;
}
/**
* \test pfv->shared is NULL.
*/
static int SourcePcapFileHelperTest05(void)
{
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
int rc = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF(rc);
PASS;
}
/**
* \test Test cleanup with reference counting and deferred deletion
*/
static int SourcePcapFileHelperTest06(void)
{
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
SC_ATOMIC_INIT(pfv.alerts_count);
SC_ATOMIC_SET(pfv.alerts_count, 0);
SC_ATOMIC_INIT(pfv.ref_cnt);
SC_ATOMIC_SET(pfv.ref_cnt, 2); /* simulate 2 packets in flight */
pfv.cleanup_requested = false;
/* Simulate first packet completion - should not cleanup yet */
SC_ATOMIC_SUB(pfv.ref_cnt, 1);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv.ref_cnt) == 1);
/* Request cleanup while packets are still in flight */
pfv.cleanup_requested = true;
/* Simulate second packet completion - should trigger cleanup */
if (SC_ATOMIC_SUB(pfv.ref_cnt, 1) == 1) {
FAIL_IF_NOT(pfv.cleanup_requested); /* cleanup should have been requested */
/* In real code, CleanupPcapFileFileVars would be called here */
}
PASS;
}
/**
* \test Test edge cases and error conditions
*/
static int SourcePcapFileHelperTest07(void)
{
/* Test 1: PcapFileShouldDeletePcapFile with very high alert count */
PcapFileSharedVars shared;
memset(&shared, 0, sizeof(shared));
shared.delete_mode = PCAP_FILE_DELETE_NON_ALERTS;
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
pfv.shared = &shared;
pfv.filename = SCStrdup("test.pcap");
SC_ATOMIC_INIT(pfv.alerts_count);
SC_ATOMIC_SET(pfv.alerts_count, UINT64_MAX); /* max value */
int result = PcapFileShouldDeletePcapFile(&pfv);
FAIL_IF(result); /* should not delete with max alerts */
/* Test 2: PcapFileFinalizePacket with max alert count */
SC_ATOMIC_INIT(pfv.ref_cnt);
SC_ATOMIC_ADD(pfv.ref_cnt, 1);
PcapFileAddAlertCount(&pfv, UINT16_MAX); /* max uint16_t */
PcapFileFinalizePacket(&pfv);
/* Should not overflow or crash */
SCFree(pfv.filename);
PASS;
}
/**
* \test Test command-line --pcap-file-delete override behavior
*/
static int SourcePcapFileHelperTest08(void)
{
/* Test 1: Command line overrides YAML "false" */
const char *conf_false = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: false\n";
SetupYamlConf(conf_false);
/* Simulate --pcap-file-delete command line option */
int set_result = SCConfSetFinal("pcap-file.delete-when-done", "true");
FAIL_IF_NOT(set_result == 1);
PcapFileDeleteMode result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_ALWAYS); /* Should override YAML false */
CleanupYamlConf();
/* Test 2: Command line overrides YAML "non-alerts" */
const char *conf_non_alerts = "%YAML 1.1\n"
"---\n"
"pcap-file:\n"
" delete-when-done: \"non-alerts\"\n";
SetupYamlConf(conf_non_alerts);
/* Simulate --pcap-file-delete command line option */
set_result = SCConfSetFinal("pcap-file.delete-when-done", "true");
FAIL_IF_NOT(set_result == 1);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_ALWAYS); /* Should override YAML "non-alerts" */
CleanupYamlConf();
/* Test 3: Command line overrides no YAML config */
SCConfCreateContextBackup();
SCConfInit();
/* Simulate --pcap-file-delete command line option with no YAML config */
set_result = SCConfSetFinal("pcap-file.delete-when-done", "true");
FAIL_IF_NOT(set_result == 1);
result = PcapFileParseDeleteMode();
FAIL_IF_NOT(result == PCAP_FILE_DELETE_ALWAYS); /* Should set to always delete */
SCConfDeInit();
SCConfRestoreContextBackup();
PASS;
}
/**
* \test Test that cleanup defers while packets are in flight and that a file
* with alerts is not deleted in NON_ALERTS mode.
*/
static int SourcePcapFileHelperTest09(void)
{
PcapFileSharedVars *shared = SCCalloc(1, sizeof(*shared));
FAIL_IF_NULL(shared);
shared->delete_mode = PCAP_FILE_DELETE_NON_ALERTS;
PcapFileFileVars *pfv = SCCalloc(1, sizeof(*pfv));
FAIL_IF_NULL(pfv);
pfv->shared = shared;
pfv->filename = SCStrdup("unit_del_test.pcap");
FAIL_IF_NULL(pfv->filename);
SC_ATOMIC_INIT(pfv->alerts_count);
SC_ATOMIC_SET(pfv->alerts_count, 0);
SC_ATOMIC_INIT(pfv->ref_cnt);
SC_ATOMIC_SET(pfv->ref_cnt, 2); /* two packets in flight */
pfv->cleanup_requested = false;
/* Request cleanup while packets still in flight: should defer. */
CleanupPcapFileFileVars(pfv);
FAIL_IF_NOT(pfv->cleanup_requested);
FAIL_IF_NULL(pfv->filename); /* not freed yet */
/* First packet completes and generates an alert. */
PcapFileAddAlertCount(pfv, 1);
PcapFileFinalizePacket(pfv);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv->alerts_count) == 1);
/* Second (last) packet completes: triggers final cleanup. */
PcapFileFinalizePacket(pfv);
/* pfv memory is freed at this point; only free shared. */
SCFree(shared);
PASS;
}
/**
* \test Cover unlink-on-ALWAYS branch (ref_cnt == 0) and deferred deletion when ref_cnt > 0
*/
static int SourcePcapFileHelperTest10(void)
{
/* Create a temporary file that we expect to be deleted. */
const char *tmpname = "suri_ut_delete_always.pcap";
const uint8_t dummy[] = { 0x00 };
int rc = TestHelperBufferToFile(tmpname, dummy, sizeof(dummy));
FAIL_IF_NOT(rc >= 0);
/* Case 1: delete ALWAYS with no packets in flight -> file unlinked immediately */
PcapFileSharedVars *shared1 = SCCalloc(1, sizeof(*shared1));
FAIL_IF_NULL(shared1);
shared1->delete_mode = PCAP_FILE_DELETE_ALWAYS;
PcapFileFileVars *pfv1 = SCCalloc(1, sizeof(*pfv1));
FAIL_IF_NULL(pfv1);
pfv1->shared = shared1;
pfv1->filename = SCStrdup(tmpname);
FAIL_IF_NULL(pfv1->filename);
SC_ATOMIC_INIT(pfv1->alerts_count);
SC_ATOMIC_SET(pfv1->alerts_count, 0);
SC_ATOMIC_INIT(pfv1->ref_cnt);
SC_ATOMIC_SET(pfv1->ref_cnt, 0);
/* Provide a closable handle to cover close path. */
pfv1->pcap_handle = pcap_open_dead(DLT_EN10MB, 65535);
FAIL_IF_NULL(pfv1->pcap_handle);
CleanupPcapFileFileVars(pfv1);
/* File should be gone. */
FILE *f = fopen(tmpname, "rb");
FAIL_IF_NOT_NULL(f);
if (f != NULL)
fclose(f);
/* Case 2: delete ALWAYS but ref_cnt > 0 -> defer until finalize. */
/* Recreate the file. */
rc = TestHelperBufferToFile(tmpname, dummy, sizeof(dummy));
FAIL_IF_NOT(rc >= 0);
PcapFileSharedVars *shared2 = SCCalloc(1, sizeof(*shared2));
FAIL_IF_NULL(shared2);
shared2->delete_mode = PCAP_FILE_DELETE_ALWAYS;
PcapFileFileVars *pfv2 = SCCalloc(1, sizeof(*pfv2));
FAIL_IF_NULL(pfv2);
pfv2->shared = shared2;
pfv2->filename = SCStrdup(tmpname);
FAIL_IF_NULL(pfv2->filename);
SC_ATOMIC_INIT(pfv2->alerts_count);
SC_ATOMIC_SET(pfv2->alerts_count, 0);
SC_ATOMIC_INIT(pfv2->ref_cnt);
SC_ATOMIC_SET(pfv2->ref_cnt, 1);
pfv2->pcap_handle = pcap_open_dead(DLT_EN10MB, 65535);
FAIL_IF_NULL(pfv2->pcap_handle);
CleanupPcapFileFileVars(pfv2);
/* Still exists now because deletion should be deferred. */
f = fopen(tmpname, "rb");
FAIL_IF_NULL(f);
if (f != NULL)
fclose(f);
/* Finalize the last packet which should trigger final cleanup & unlink. */
PcapFileFinalizePacket(pfv2);
/* Now the file should be gone. */
f = fopen(tmpname, "rb");
FAIL_IF_NOT_NULL(f);
if (f != NULL)
fclose(f);
SCFree(shared1);
SCFree(shared2);
PASS;
}
/**
* \test Test PcapFileReleasePseudoPacket refcount decrement without cleanup
*/
static int SourcePcapFileHelperTest11(void)
{
/* Setup pfv with ref_cnt=2 so release does not trigger cleanup */
PcapFileFileVars pfv;
memset(&pfv, 0, sizeof(pfv));
SC_ATOMIC_INIT(pfv.ref_cnt);
SC_ATOMIC_SET(pfv.ref_cnt, 2);
pfv.cleanup_requested = false;
/* Allocate a packet from the pool to allow safe release */
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
p->pcap_v.pfv = &pfv;
/* Call release and ensure ref count decremented by 1 */
PcapFileReleasePseudoPacket(p);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv.ref_cnt) == 1);
PASS;
}
/**
* \test Test adding alert count and that cleanup after refcnt reaches zero
* does not delete when alerts exist in NON_ALERTS mode.
*/
static int SourcePcapFileHelperTest12(void)
{
PcapFileSharedVars shared;
memset(&shared, 0, sizeof(shared));
shared.delete_mode = PCAP_FILE_DELETE_NON_ALERTS;
PcapFileFileVars *pfv = SCCalloc(1, sizeof(*pfv));
FAIL_IF_NULL(pfv);
pfv->shared = &shared;
pfv->filename = SCStrdup("ut_non_alerts.pcap");
FAIL_IF_NULL(pfv->filename);
SC_ATOMIC_INIT(pfv->alerts_count);
SC_ATOMIC_SET(pfv->alerts_count, 0);
SC_ATOMIC_INIT(pfv->ref_cnt);
SC_ATOMIC_SET(pfv->ref_cnt, 1);
pfv->cleanup_requested = true;
/* Simulate pseudo alert */
PcapFileAddAlertCount(pfv, 2);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv->alerts_count) == 2);
/* Simulate last ref release triggering cleanup; file shouldn't be deleted
* due to alerts > 0. We cannot check unlink here; rely on return value. */
if (SC_ATOMIC_SUB(pfv->ref_cnt, 1) == 1) {
CleanupPcapFileFileVars(pfv);
}
/* Success if no crash. */
PASS;
}
/**
* \test Test global current pfv pointer lifecycle
*/
static int SourcePcapFileHelperTest13(void)
{
PcapFileFileVars *pfv = SCCalloc(1, sizeof(*pfv));
FAIL_IF_NULL(pfv);
pfv->filename = SCStrdup("ut_global_clear.pcap");
FAIL_IF_NULL(pfv->filename);
PcapFileSetCurrentPfv(pfv);
FAIL_IF_NOT(PcapFileGetCurrentPfv() == pfv);
/* Cleanup should clear global reference when pointing to this pfv */
CleanupPcapFileFileVars(pfv);
/* Global accessor must be NULL after cleanup. */
FAIL_IF_NOT(PcapFileGetCurrentPfv() == NULL);
PASS;
}
/**
* \test Exercise unlink failure branch in CleanupPcapFileFileVars
*/
static int SourcePcapFileHelperTest14(void)
{
PcapFileSharedVars shared;
memset(&shared, 0, sizeof(shared));
shared.delete_mode = PCAP_FILE_DELETE_ALWAYS;
PcapFileFileVars *pfv = SCCalloc(1, sizeof(*pfv));
FAIL_IF_NULL(pfv);
pfv->shared = &shared;
pfv->filename = SCStrdup("does-not-exist-ut.pcap");
FAIL_IF_NULL(pfv->filename);
SC_ATOMIC_INIT(pfv->ref_cnt);
SC_ATOMIC_SET(pfv->ref_cnt, 0);
/* Attempt cleanup; unlink should fail but must not crash */
CleanupPcapFileFileVars(pfv);
PASS;
}
/**
* \test Cover alerts hook fallback using current PFV
*/
static int SourcePcapFileHelperTest15(void)
{
PcapFileInstallCaptureHooks();
PcapFileFileVars *pfv = SCCalloc(1, sizeof(*pfv));
FAIL_IF_NULL(pfv);
SC_ATOMIC_INIT(pfv->alerts_count);
SC_ATOMIC_SET(pfv->alerts_count, 0);
/* Set current PFV to exercise fallback path */
PcapFileSetCurrentPfv(pfv);
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
p->alerts.cnt = 3; /* simulate 3 alerts */
p->pcap_v.pfv = NULL; /* force fallback */
/* Call hook: it should update pfv->alerts_count */
CaptureHooksOnPacketWithAlerts(p);
FAIL_IF_NOT(SC_ATOMIC_GET(pfv->alerts_count) == 3);
/* Cleanup */
PacketFreeOrRelease(p);
CleanupPcapFileFileVars(pfv);
PASS;
}
/**
* \brief Register unit tests for pcap file helper
*/
void SourcePcapFileHelperRegisterTests(void)
{
UtRegisterTest("SourcePcapFileHelperTest01", SourcePcapFileHelperTest01);
UtRegisterTest("SourcePcapFileHelperTest02", SourcePcapFileHelperTest02);
UtRegisterTest("SourcePcapFileHelperTest03", SourcePcapFileHelperTest03);
UtRegisterTest("SourcePcapFileHelperTest04", SourcePcapFileHelperTest04);
UtRegisterTest("SourcePcapFileHelperTest05", SourcePcapFileHelperTest05);
UtRegisterTest("SourcePcapFileHelperTest06", SourcePcapFileHelperTest06);
UtRegisterTest("SourcePcapFileHelperTest07", SourcePcapFileHelperTest07);
UtRegisterTest("SourcePcapFileHelperTest08", SourcePcapFileHelperTest08);
UtRegisterTest("SourcePcapFileHelperTest09", SourcePcapFileHelperTest09);
UtRegisterTest("SourcePcapFileHelperTest10", SourcePcapFileHelperTest10);
UtRegisterTest("SourcePcapFileHelperTest11", SourcePcapFileHelperTest11);
UtRegisterTest("SourcePcapFileHelperTest12", SourcePcapFileHelperTest12);
UtRegisterTest("SourcePcapFileHelperTest13", SourcePcapFileHelperTest13);
UtRegisterTest("SourcePcapFileHelperTest14", SourcePcapFileHelperTest14);
UtRegisterTest("SourcePcapFileHelperTest15", SourcePcapFileHelperTest15);
}
#endif /* UNITTESTS */

View file

@ -23,10 +23,17 @@
#include "suricata-common.h"
#include "tm-threads.h"
#include "util-atomic.h"
#ifndef SURICATA_SOURCE_PCAP_FILE_HELPER_H
#define SURICATA_SOURCE_PCAP_FILE_HELPER_H
typedef enum {
PCAP_FILE_DELETE_NONE = 0,
PCAP_FILE_DELETE_ALWAYS,
PCAP_FILE_DELETE_NON_ALERTS
} PcapFileDeleteMode;
typedef struct PcapFileGlobalVars_ {
uint64_t cnt; /** packet counter */
ChecksumValidationMode conf_checksum_mode;
@ -46,7 +53,7 @@ typedef struct PcapFileSharedVars_
struct timespec last_processed;
bool should_delete;
PcapFileDeleteMode delete_mode;
ThreadVars *tv;
TmSlot *slot;
@ -76,6 +83,14 @@ typedef struct PcapFileFileVars_
PcapFileSharedVars *shared;
SC_ATOMIC_DECLARE(uint64_t, alerts_count);
/* Reference count for outstanding users (e.g., pseudo packets created
* during shutdown/timeout handling). Ensures the per-file state is not
* cleaned up or the file deleted before all dependent processing that may
* still raise alerts has completed. */
SC_ATOMIC_DECLARE(uint32_t, ref_cnt);
bool cleanup_requested;
/* fields used to get the first packet's timestamp early,
* so it can be used to setup the time subsys. */
const u_char *first_pkt_data;
@ -121,4 +136,23 @@ TmEcode ValidateLinkType(int datalink, DecoderFunc *decoder);
const char *PcapFileGetFilename(void);
bool PcapFileShouldDeletePcapFile(PcapFileFileVars *pfv);
void PcapFileFinalizePacket(PcapFileFileVars *pfv);
PcapFileDeleteMode PcapFileParseDeleteMode(void);
void PcapFileAddAlertCount(PcapFileFileVars *pfv, uint16_t alert_count);
void PcapFileReleasePseudoPacket(Packet *p);
void PcapFileSetCurrentPfv(PcapFileFileVars *pfv);
PcapFileFileVars *PcapFileGetCurrentPfv(void);
void PcapFileInstallCaptureHooks(void);
#ifdef UNITTESTS
void SourcePcapFileHelperRegisterTests(void);
#endif
#endif /* SURICATA_SOURCE_PCAP_FILE_HELPER_H */

View file

@ -33,6 +33,7 @@
#include "suricata.h"
#include "conf.h"
#include "util-misc.h"
#include "capture-hooks.h"
extern uint32_t max_pending_packets;
PcapFileGlobalVars pcap_g;
@ -148,6 +149,8 @@ void PcapFileGlobalInit(void)
memset(&pcap_g, 0x00, sizeof(pcap_g));
SC_ATOMIC_INIT(pcap_g.invalid_checksums);
PcapFileInstallCaptureHooks();
#if defined(HAVE_SETVBUF) && defined(OS_LINUX)
pcap_g.read_buffer_size = PCAP_FILE_BUFFER_SIZE_DEFAULT;
@ -204,8 +207,11 @@ TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot)
if(ptv->is_directory == 0) {
SCLogInfo("Starting file run for %s", ptv->behavior.file->filename);
/* Hold a reference for the duration of file processing, including
* post-dispatch flow draining during EngineStop, so deletion decision
* is deferred until all pseudo packets have been processed. */
SC_ATOMIC_ADD(ptv->behavior.file->ref_cnt, 1);
status = PcapFileDispatch(ptv->behavior.file);
CleanupPcapFileFromThreadVars(ptv, ptv->behavior.file);
} else {
SCLogInfo("Starting directory run for %s", ptv->behavior.directory->filename);
PcapDirectoryDispatch(ptv->behavior.directory);
@ -215,6 +221,19 @@ TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot)
SCLogDebug("Pcap file loop complete with status %u", status);
status = PcapFileExit(status, &ptv->shared.last_processed);
/* Release the hold set before dispatch and trigger deferred cleanup
* if requested and this was the final reference. */
if (ptv->is_directory == 0) {
/* Ensure current pfv global is set for any pseudo packets emitted
* during EngineStop/flow drain. */
PcapFileSetCurrentPfv(ptv->behavior.file);
uint32_t prev = SC_ATOMIC_SUB(ptv->behavior.file->ref_cnt, 1);
if (prev == 1 && ptv->behavior.file->cleanup_requested) {
CleanupPcapFileFileVars(ptv->behavior.file);
ptv->behavior.file = NULL;
}
}
SCReturnInt(status);
}
@ -261,11 +280,7 @@ TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **d
}
}
int should_delete = 0;
ptv->shared.should_delete = false;
if (SCConfGetBool("pcap-file.delete-when-done", &should_delete) == 1) {
ptv->shared.should_delete = should_delete == 1;
}
ptv->shared.delete_mode = PcapFileParseDeleteMode();
DIR *directory = NULL;
SCLogDebug("checking file or directory %s", (char*)initdata);
@ -422,6 +437,11 @@ TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data)
SCEnter();
if(data != NULL) {
PcapFileThreadVars *ptv = (PcapFileThreadVars *) data;
if (!ptv->is_directory && ptv->behavior.file != NULL) {
CleanupPcapFileFromThreadVars(ptv, ptv->behavior.file);
}
CleanupPcapFileThreadVars(ptv);
}
SCReturnInt(TM_ECODE_OK);

View file

@ -24,6 +24,8 @@
#ifndef SURICATA_SOURCE_PCAP_H
#define SURICATA_SOURCE_PCAP_H
typedef struct PcapFileFileVars_ PcapFileFileVars;
void TmModuleReceivePcapRegister (void);
void TmModuleDecodePcapRegister (void);
void PcapTranslateIPToDevice(char *pcap_dev, size_t len);
@ -35,6 +37,7 @@ void PcapTranslateIPToDevice(char *pcap_dev, size_t len);
typedef struct PcapPacketVars_ {
uint64_t pcap_cnt;
uint32_t tenant_id;
PcapFileFileVars *pfv;
} PcapPacketVars;
/** needs to be able to contain Windows adapter id's, so

View file

@ -35,6 +35,7 @@
#include "tm-queues.h"
#include "tm-queuehandlers.h"
#include "tm-threads.h"
#include "capture-hooks.h"
#include "tmqh-packetpool.h"
#include "threads.h"
#include "util-affinity.h"
@ -46,6 +47,7 @@
#include "util-signal.h"
#include "queue.h"
#include "util-validate.h"
#include "source-pcap-file-helper.h"
#ifdef PROFILE_LOCKING
thread_local uint64_t mutex_lock_contention;
@ -1358,6 +1360,7 @@ again:
if (p != NULL) {
p->flags |= PKT_PSEUDO_STREAM_END;
PKT_SET_SRC(p, PKT_SRC_SHUTDOWN_FLUSH);
CaptureHooksOnPseudoPacketCreated(p);
PacketQueue *q = tv->stream_pq;
SCMutexLock(&q->mutex_q);
PacketEnqueue(q, p);
@ -1441,6 +1444,7 @@ again:
if (p != NULL) {
p->flags |= PKT_PSEUDO_STREAM_END;
PKT_SET_SRC(p, PKT_SRC_SHUTDOWN_FLUSH);
CaptureHooksOnPseudoPacketCreated(p);
PacketQueue *q = tv->stream_pq;
SCMutexLock(&q->mutex_q);
PacketEnqueue(q, p);

View file

@ -896,7 +896,9 @@ pcap-file:
# buffer-size: 128 KiB
# tenant-id: none # applies in multi-tenant environment with "direct" selector
# delete-when-done: false # applies to file and directory
# Applies to file and directory. Options: false (no deletion), true (always delete),
# "non-alerts" (delete only files with no alerts)
# delete-when-done: false
# PCAP Directory Processing options
# recursive: false