]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: add vlan.layers keyword 12393/head
authorAlice Akaki <akakialice@gmail.com>
Wed, 8 Jan 2025 21:03:24 +0000 (17:03 -0400)
committerVictor Julien <victor@inliniac.net>
Tue, 14 Jan 2025 14:22:50 +0000 (15:22 +0100)
vlan.layers matches on the number of VLAN layers per packet
It is an unsigned 8-bit integer
Valid range = [0-3]
Supports prefiltering

Ticket: #1065

doc/userguide/rules/vlan-keywords.rst
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-vlan.c
src/detect-vlan.h

index 1ae40f87ec300aae7eca70bc814f267b3cf3cc95..f342b745ad67db82049866bbd5beb8f00309a066 100644 (file)
@@ -83,3 +83,43 @@ It is also possible to use the vlan.id content as a fast_pattern by using the ``
 .. container:: example-rule
 
   alert ip any any -> any any (msg:"Vlan ID is equal to 200 at layer 1"; :example-rule-emphasis:`vlan.id:200,1; prefilter;` sid:1;)
+
+vlan.layers
+-----------
+
+Matches based on the number of layers.
+
+Syntax::
+
+ vlan.layers: [op]number;
+
+It can be matched exactly, or compared using the ``op`` setting::
+
+ vlan.layers:3    # exactly 3 vlan layers
+ vlan.layers:<3   # less than 3 vlan layers
+ vlan.layers:>=2  # more or equal to 2 vlan layers
+
+vlan.layers uses :ref:`unsigned 8-bit integer <rules-integer-keywords>`.
+
+The minimum and maximum values that vlan.layers can be are ``0`` and ``3``.
+
+Examples
+^^^^^^^^
+
+Example of a signature that would alert if a packet has 0 VLAN layers:
+
+.. container:: example-rule
+
+  alert ip any any -> any any (msg:"Packet has 0 vlan layers"; :example-rule-emphasis:`vlan.layers:0;` sid:1;)
+
+Example of a signature that would alert if a packet has more than 1 VLAN layers:
+
+.. container:: example-rule
+
+  alert ip any any -> any any (msg:"Packet has more than 1 vlan layer"; :example-rule-emphasis:`vlan.layers:>1;` sid:1;)
+
+It is also possible to use the vlan.layers content as a fast_pattern by using the ``prefilter`` keyword, as shown in the following example.
+
+.. container:: example-rule
+
+  alert ip any any -> any any (msg:"Packet has 2 vlan layers"; :example-rule-emphasis:`vlan.layers:2; prefilter;` sid:1;)
\ No newline at end of file
index 0ca554149f663660b68ed66004ae6f664ef6543f..b9faff49bae8e8dc75bfe3828ce80df68b57f375 100644 (file)
@@ -728,6 +728,7 @@ void SigTableSetup(void)
     DetectFileHandlerRegister();
 
     DetectVlanIdRegister();
+    DetectVlanLayersRegister();
 
     ScDetectSNMPRegister();
     ScDetectDHCPRegister();
index 37ffd0b3455b73f62ad7e2dcbc75deb565147421..a9e674159e36ee1433a4a09ceba701de2476a028 100644 (file)
@@ -334,6 +334,7 @@ enum DetectKeywordId {
     DETECT_AL_JA4_HASH,
 
     DETECT_VLAN_ID,
+    DETECT_VLAN_LAYERS,
 
     /* make sure this stays last */
     DETECT_TBLSIZE_STATIC,
index 41a7434c6ecc70785e17d0fcf93153ce6cdb9cd8..9b9e085bf9b954505528d71011b9efce0169599e 100644 (file)
@@ -139,4 +139,80 @@ void DetectVlanIdRegister(void)
     sigmatch_table[DETECT_VLAN_ID].Free = DetectVlanIdFree;
     sigmatch_table[DETECT_VLAN_ID].SupportsPrefilter = PrefilterVlanIdIsPrefilterable;
     sigmatch_table[DETECT_VLAN_ID].SetupPrefilter = PrefilterSetupVlanId;
-}
\ No newline at end of file
+}
+
+static int DetectVlanLayersMatch(
+        DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
+{
+    uint8_t nb = p->vlan_idx;
+
+    const DetectU8Data *du8 = (const DetectU8Data *)ctx;
+    return DetectU8Match(nb, du8);
+}
+
+static void DetectVlanLayersFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+    rs_detect_u8_free(ptr);
+}
+
+static int DetectVlanLayersSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+    DetectU8Data *du8 = DetectU8Parse(rawstr);
+
+    if (du8 == NULL) {
+        SCLogError("vlan layers invalid %s", rawstr);
+        return -1;
+    }
+
+    if (du8->arg1 > VLAN_MAX_LAYERS || du8->arg2 > VLAN_MAX_LAYERS) {
+        SCLogError("number of layers out of range %s", rawstr);
+        return -1;
+    }
+
+    if (SigMatchAppendSMToList(
+                de_ctx, s, DETECT_VLAN_LAYERS, (SigMatchCtx *)du8, DETECT_SM_LIST_MATCH) == NULL) {
+        DetectVlanLayersFree(de_ctx, du8);
+        return -1;
+    }
+    s->flags |= SIG_FLAG_REQUIRE_PACKET;
+
+    return 0;
+}
+
+static void PrefilterPacketVlanLayersMatch(
+        DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
+{
+    const PrefilterPacketHeaderCtx *ctx = pectx;
+
+    DetectU8Data du8;
+    du8.mode = ctx->v1.u8[0];
+    du8.arg1 = ctx->v1.u8[1];
+    du8.arg2 = ctx->v1.u8[2];
+
+    if (DetectVlanLayersMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du8)) {
+        PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+    }
+}
+
+static int PrefilterSetupVlanLayers(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+    return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_VLAN_LAYERS, SIG_MASK_REQUIRE_REAL_PKT,
+            PrefilterPacketU8Set, PrefilterPacketU8Compare, PrefilterPacketVlanLayersMatch);
+}
+
+static bool PrefilterVlanLayersIsPrefilterable(const Signature *s)
+{
+    return PrefilterIsPrefilterableById(s, DETECT_VLAN_LAYERS);
+}
+
+void DetectVlanLayersRegister(void)
+{
+    sigmatch_table[DETECT_VLAN_LAYERS].name = "vlan.layers";
+    sigmatch_table[DETECT_VLAN_LAYERS].desc = "match number of vlan layers";
+    sigmatch_table[DETECT_VLAN_LAYERS].url = "/rules/vlan-keywords.html#vlan-layers";
+    sigmatch_table[DETECT_VLAN_LAYERS].Match = DetectVlanLayersMatch;
+    sigmatch_table[DETECT_VLAN_LAYERS].Setup = DetectVlanLayersSetup;
+    sigmatch_table[DETECT_VLAN_LAYERS].Free = DetectVlanLayersFree;
+    sigmatch_table[DETECT_VLAN_LAYERS].SupportsPrefilter = PrefilterVlanLayersIsPrefilterable;
+    sigmatch_table[DETECT_VLAN_LAYERS].SetupPrefilter = PrefilterSetupVlanLayers;
+}
index 301fcff5a691f517e333637ee3e7dd0cf4f5c490..8c449459f283f27dd9720675498b550af6423af0 100644 (file)
@@ -19,5 +19,6 @@
 #define SURICATA_DETECT_VLAN_H
 
 void DetectVlanIdRegister(void);
+void DetectVlanLayersRegister(void);
 
 #endif /* SURICATA_DETECT_VLAN_H */