]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
pcap-log: add bpf filter for packets that are logged
authorJason Ish <jason.ish@oisf.net>
Tue, 1 Apr 2025 21:18:54 +0000 (15:18 -0600)
committerVictor Julien <victor@inliniac.net>
Mon, 7 Apr 2025 12:21:37 +0000 (14:21 +0200)
Add an optional bpf filter to pcap-log. If set, packets must match the
filter to be logged, otherwise they will be ignored.

This allows a user to limit what is logged to disk if they have pcap-log
enabled, but still inspect all data captured.

Ticket: #6832

doc/userguide/configuration/suricata-yaml.rst
src/log-pcap.c
suricata.yaml.in

index fe3e7e59f186f27028fefdb285beb26d64f77a58..16b208613b5306cfe56afb0f078d12c1cdb9263a 100644 (file)
@@ -497,6 +497,8 @@ By default all packets are logged except:
 
 - TCP streams beyond stream.reassembly.depth
 - encrypted streams after the key exchange
+- If a ``bpf-filter`` is set, packets that don't match the filter will
+  not be logged
 
 It is possible to do conditional pcap logging by using the `conditional`
 option in the pcap-log section. By default the variable is set to `all`
@@ -520,6 +522,11 @@ the alert.
       mode: normal # "normal" or multi
       conditional: alerts
 
+      # A BPF filter that will be applied to all packets being
+      # logged. If set, packets must match this filter otherwise they
+      # will not be logged.
+      #bpf-filter:
+
 In ``normal`` mode a pcap file "filename" is created in the default-log-dir or as
 specified by "dir". ``normal`` mode is generally not as performant as ``multi``
 mode.
index c92eb399de8e5acf1c84ec17b85317a4ee44cec1..48261b3fe01a8a407ef81262ad801d5de9793cfb 100644 (file)
@@ -140,6 +140,7 @@ typedef struct PcapLogCompressionData_ {
 typedef struct PcapLogData_ {
     int use_stream_depth;       /**< use stream depth i.e. ignore packets that reach limit */
     int honor_pass_rules;       /**< don't log if pass rules have matched */
+    char *bpf_filter;           /**< bpf filter to apply to output */
     SCMutex plog_lock;
     uint64_t pkt_cnt;              /**< total number of packets */
     struct pcap_pkthdr *h;      /**< pcap header struct */
@@ -150,6 +151,7 @@ typedef struct PcapLogData_ {
     uint64_t size_limit;        /**< file size limit */
     pcap_t *pcap_dead_handle;   /**< pcap_dumper_t needs a handle */
     pcap_dumper_t *pcap_dumper; /**< actually writes the packets */
+    struct bpf_program *bpfp;   /**< compiled bpf program */
     uint64_t profile_data_size; /**< track in bytes how many bytes we wrote */
     uint32_t file_cnt;          /**< count of pcap files we currently have */
     uint32_t max_files;         /**< maximum files to use in ring buffer mode */
@@ -391,6 +393,18 @@ static int PcapLogOpenHandles(PcapLogData *pl, const Packet *p)
             SCLogDebug("Error opening dead pcap handle");
             return TM_ECODE_FAILED;
         }
+
+        if (pl->bpfp == NULL && pl->bpf_filter) {
+            struct bpf_program bpfp;
+            if (pcap_compile(pl->pcap_dead_handle, &bpfp, pl->bpf_filter, 0,
+                        PCAP_NETMASK_UNKNOWN) == PCAP_ERROR) {
+                FatalError("Failed to compile BPF filter, aborting: %s: %s", pl->bpf_filter,
+                        pcap_geterr(pl->pcap_dead_handle));
+            } else {
+                pl->bpfp = SCCalloc(1, sizeof(*pl->bpfp));
+                *pl->bpfp = bpfp;
+            }
+        }
     }
 
     if (pl->pcap_dumper == NULL) {
@@ -483,6 +497,14 @@ static inline int PcapWrite(
 {
     struct timeval current_dump;
     gettimeofday(&current_dump, NULL);
+
+    if (pl->bpfp) {
+        if (pcap_offline_filter(pl->bpfp, pl->h, data) == 0) {
+            SCLogDebug("Packet doesn't match filter, will not be logged.");
+            return TM_ECODE_OK;
+        }
+    }
+
     pcap_dump((u_char *)pl->pcap_dumper, pl->h, data);
     if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_NONE) {
         pl->size_current += len;
@@ -1142,6 +1164,11 @@ static void PcapLogDataFree(PcapLogData *pl)
         pcap_close(pl->pcap_dead_handle);
     }
 
+    if (pl->bpfp) {
+        pcap_freecode(pl->bpfp);
+        SCFree(pl->bpfp);
+    }
+
 #ifdef HAVE_LIBLZ4
     if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) {
         SCFree(pl->compression.buffer);
@@ -1608,6 +1635,8 @@ static OutputInitResult PcapLogInitCtx(SCConfNode *conf)
         }
     }
 
+    pl->bpf_filter = conf == NULL ? NULL : (char *)SCConfNodeLookupChildValue(conf, "bpf-filter");
+
     /* create the output ctx and send it back */
 
     OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
index 8ecab2d95d0f0ff760cd1fc193271be394e3c20c..66c9ef5fdc9fe51115e745ac6296a98a806b276e 100644 (file)
@@ -448,6 +448,11 @@ outputs:
       # to log only flow tagged via the "tag" keyword
       #conditional: all
 
+      # A BPF filter that will be applied to all packets being
+      # logged. If set, packets must match this filter otherwise they
+      # will not be logged.
+      #bpf-filter:
+
   # a full alert log containing much information for signature writers
   # or for investigating suspected false positives.
   - alert-debug: