]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/entropy: Add calculated entropy value to flowvars
authorJeff Lucovsky <jlucovsky@oisf.net>
Sat, 31 May 2025 14:18:32 +0000 (10:18 -0400)
committerVictor Julien <victor@inliniac.net>
Fri, 6 Jun 2025 18:08:46 +0000 (20:08 +0200)
When the entropy keyword is used, record the calculated entropy value to
a flow variable for logging use.

etc/schema.json
rust/src/detect/entropy.rs
src/detect-entropy.c
src/flow-var.c
src/flow-var.h
src/output-json.c
src/util-var.h

index 53ba9e23509523646916d8f23cf3fbbb5885afd8..d1a5139589c0abeb0d2a741250ca20026e1f333b 100644 (file)
             "type": "object",
             "additionalProperties": false,
             "properties": {
+                "entropy": {
+                    "type": "object",
+                    "suricata": {
+                        "keywords": [
+                            "entropy"
+                        ]
+                    }
+                },
                 "flowbits": {
                     "type": "array",
                     "minItems": 1,
index 3a63363c658a435bc69b01220b2155e512ad066d..6816dc723122f48a993827f5eca5a383c376157f 100644 (file)
@@ -27,13 +27,15 @@ use nom7::sequence::preceded;
 use nom7::{Err, IResult};
 
 use std::ffi::CStr;
-use std::os::raw::{c_char, c_void};
+use std::os::raw::{c_double, c_char, c_void};
 use std::slice;
 
+#[repr(C)]
 #[derive(Debug)]
 pub struct DetectEntropyData {
     offset: i32,
     nbytes: i32,
+    fv_idx: i32,
     value: DetectFloatData<f64>,
 }
 
@@ -42,6 +44,7 @@ impl Default for DetectEntropyData {
         DetectEntropyData {
             offset: 0,
             nbytes: 0,
+            fv_idx: 0,
             value: DetectFloatData::<f64>::default(),
         }
     }
@@ -166,6 +169,7 @@ fn calculate_entropy(data: &[u8]) -> f64 {
 #[no_mangle]
 pub unsafe extern "C" fn SCDetectEntropyMatch(
     c_data: *const c_void, length: i32, ctx: &DetectEntropyData,
+    calculated_entropy: *mut c_double,
 ) -> bool {
     if c_data.is_null() {
         return false;
@@ -200,7 +204,11 @@ pub unsafe extern "C" fn SCDetectEntropyMatch(
     let entropy = calculate_entropy(data_slice);
     SCLogDebug!("entropy is {}", entropy);
 
-    // Use a hypothetical `detect_entropy_match` function to check entropy
+    // Return entropy on request
+    if !calculated_entropy.is_null() {
+        *calculated_entropy = entropy;
+    }
+
     detect_match_float::<f64>(&ctx.value, entropy)
 }
 
@@ -250,6 +258,7 @@ mod tests {
         let ded = DetectEntropyData {
             offset,
             nbytes,
+            fv_idx: 0,
             value: ctx,
         };
 
index 4b21a7ac0b5f364c334ae10a0016edccf25141ed..a79f629108b978db717c9341aede22303deea120 100644 (file)
@@ -23,6 +23,8 @@
 #include "detect-engine-buffer.h"
 
 #include "detect-entropy.h"
+#include "util-var-name.h"
+#include "flow-var.h"
 
 #include "rust.h"
 
@@ -39,6 +41,8 @@ static int DetectEntropySetup(DetectEngineCtx *de_ctx, Signature *s, const char
             goto error;
 
         sm_list = s->init_data->list;
+        ded->fv_idx = VarNameStoreRegister(
+                DetectEngineBufferTypeGetNameById(de_ctx, sm_list), VAR_TYPE_FLOW_FLOAT);
     }
 
     if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_ENTROPY, (SigMatchCtx *)ded, sm_list) != NULL) {
@@ -57,13 +61,25 @@ error:
 
 static void DetectEntropyFree(DetectEngineCtx *de_ctx, void *ptr)
 {
-    SCDetectEntropyFree(ptr);
+    if (ptr) {
+        DetectEntropyData *ded = (DetectEntropyData *)ptr;
+        VarNameStoreUnregister(ded->fv_idx, VAR_TYPE_FLOW_FLOAT);
+        SCDetectEntropyFree(ptr);
+    }
 }
 
 bool DetectEntropyDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s,
         const SigMatchCtx *ctx, const uint8_t *buffer, const uint32_t buffer_len)
 {
-    return SCDetectEntropyMatch(buffer, buffer_len, (const DetectEntropyData *)ctx);
+    double entropy = -1.0;
+    bool rc = SCDetectEntropyMatch(buffer, buffer_len, (const DetectEntropyData *)ctx, &entropy);
+
+    if (entropy != -1.0) {
+        DetectEntropyData *ded = (DetectEntropyData *)ctx;
+        FlowVarAddFloat(det_ctx->p->flow, ded->fv_idx, entropy);
+    }
+
+    return rc;
 }
 
 void DetectEntropyRegister(void)
index 37e3f267e6693449a1bd5c9676e240e80944972e..cba6385b842876d37da404bffe3f7e5b70f1b474 100644 (file)
@@ -47,6 +47,12 @@ static void FlowVarUpdateInt(FlowVar *fv, uint32_t value)
     fv->data.fv_int.value = value;
 }
 
+/* puts a new value into a flowvar */
+static void FlowVarUpdateFloat(FlowVar *fv, double value)
+{
+    fv->data.fv_float.value = value;
+}
+
 /** \brief get the flowvar with index 'idx' from the flow
  *  \note flow is not locked by this function, caller is
  *        responsible
@@ -132,6 +138,26 @@ void FlowVarAddIdValue(Flow *f, uint32_t idx, uint8_t *value, uint16_t size)
     }
 }
 
+/* add a flowvar to the flow, or update it */
+void FlowVarAddFloat(Flow *f, uint32_t idx, double value)
+{
+    FlowVar *fv = FlowVarGet(f, idx);
+    if (fv == NULL) {
+        fv = SCMalloc(sizeof(FlowVar));
+        if (unlikely(fv == NULL))
+            return;
+
+        fv->type = DETECT_FLOWVAR;
+        fv->datatype = FLOWVAR_TYPE_FLOAT;
+        fv->idx = idx;
+        fv->data.fv_float.value = value;
+        fv->next = NULL;
+
+        GenericVarAppend(&f->flowvar, (GenericVar *)fv);
+    } else {
+        FlowVarUpdateFloat(fv, value);
+    }
+}
 /* add a flowvar to the flow, or update it */
 void FlowVarAddIntNoLock(Flow *f, uint32_t idx, uint32_t value)
 {
index d409e54a6bebe32277bdc3b0e81bf0249545b976..e0d35e8d312498516dfe559bb02ad03de0411fa6 100644 (file)
@@ -32,6 +32,7 @@
 
 #define FLOWVAR_TYPE_STR 1
 #define FLOWVAR_TYPE_INT 2
+#define FLOWVAR_TYPE_FLOAT 3
 
 typedef uint8_t FlowVarKeyLenType;
 /** Struct used to hold the string data type for flowvars */
@@ -45,6 +46,11 @@ typedef struct FlowVarTypeInt_ {
     uint32_t value;
 } FlowVarTypeInt;
 
+/** Struct used to hold the integer data type for flowvars */
+typedef struct FlowVarTypeFloat_ {
+    double value;
+} FlowVarTypeFloat;
+
 /** Generic Flowvar Structure */
 typedef struct FlowVar_ {
     uint16_t type; /* type, DETECT_FLOWVAR in this case */
@@ -57,6 +63,7 @@ typedef struct FlowVar_ {
     union {
         FlowVarTypeStr fv_str;
         FlowVarTypeInt fv_int;
+        FlowVarTypeFloat fv_float;
     } data;
     uint8_t *key;
 } FlowVar;
@@ -69,6 +76,7 @@ void FlowVarAddKeyValue(
 
 void FlowVarAddIntNoLock(Flow *, uint32_t, uint32_t);
 void FlowVarAddInt(Flow *, uint32_t, uint32_t);
+void FlowVarAddFloat(Flow *, uint32_t, double);
 FlowVar *FlowVarGet(Flow *, uint32_t);
 FlowVar *FlowVarGetByKey(Flow *f, const uint8_t *key, FlowVarKeyLenType keylen);
 void FlowVarFree(FlowVar *);
index bf724fc8526ae99a5bcaff3784f728a6fa4e4670..d902f8bd17b2d4dee8067208fa0dc7602afd3407 100644 (file)
@@ -247,6 +247,7 @@ static void EveAddFlowVars(const Flow *f, SCJsonBuilder *js_root, SCJsonBuilder
     SCJsonBuilder *js_traffic_id = NULL;
     SCJsonBuilder *js_traffic_label = NULL;
     SCJsonBuilder *js_flowints = NULL;
+    SCJsonBuilder *js_entropyvals = NULL;
     SCJsonBuilder *js_flowbits = NULL;
     GenericVar *gv = f->flowvar;
     while (gv != NULL) {
@@ -292,6 +293,17 @@ static void EveAddFlowVars(const Flow *f, SCJsonBuilder *js_root, SCJsonBuilder
                 SCJbStartObject(js_flowvars);
                 SCJbSetString(js_flowvars, (const char *)keybuf, (char *)printable_buf);
                 SCJbClose(js_flowvars);
+            } else if (fv->datatype == FLOWVAR_TYPE_FLOAT) {
+                const char *varname = VarNameStoreLookupById(fv->idx, VAR_TYPE_FLOW_FLOAT);
+                if (varname) {
+                    if (js_entropyvals == NULL) {
+                        js_entropyvals = SCJbNewObject();
+                        if (js_entropyvals == NULL)
+                            break;
+                    }
+                    SCJbSetFloat(js_entropyvals, varname, fv->data.fv_float.value);
+                }
+
             } else if (fv->datatype == FLOWVAR_TYPE_INT) {
                 const char *varname = VarNameStoreLookupById(fv->idx,
                         VAR_TYPE_FLOW_INT);
@@ -303,7 +315,6 @@ static void EveAddFlowVars(const Flow *f, SCJsonBuilder *js_root, SCJsonBuilder
                     }
                     SCJbSetUint(js_flowints, varname, fv->data.fv_int.value);
                 }
-
             }
         } else if (gv->type == DETECT_FLOWBITS) {
             FlowBit *fb = (FlowBit *)gv;
@@ -348,6 +359,11 @@ static void EveAddFlowVars(const Flow *f, SCJsonBuilder *js_root, SCJsonBuilder
         SCJbSetObject(js_root, "flowints", js_flowints);
         SCJbFree(js_flowints);
     }
+    if (js_entropyvals) {
+        SCJbClose(js_entropyvals);
+        SCJbSetObject(js_root, "entropy", js_entropyvals);
+        SCJbFree(js_entropyvals);
+    }
     if (js_flowvars) {
         SCJbClose(js_flowvars);
         SCJbSetObject(js_root, "flowvars", js_flowvars);
index 30048084aadf2f04ce63621472a19873e8872654..9b40c869216fbd4a5ec087193b7e986ef1dcb890 100644 (file)
@@ -35,6 +35,7 @@ enum VarTypes {
 
     VAR_TYPE_FLOW_BIT,
     VAR_TYPE_FLOW_INT,
+    VAR_TYPE_FLOW_FLOAT,
     VAR_TYPE_FLOW_VAR,
 
     VAR_TYPE_HOST_BIT,