diff --git a/doc/userguide/rules/vlan-keywords.rst b/doc/userguide/rules/vlan-keywords.rst index e3671572188..9b23a8c7fcf 100644 --- a/doc/userguide/rules/vlan-keywords.rst +++ b/doc/userguide/rules/vlan-keywords.rst @@ -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.id: [op]number; + +It can be matched exactly, or compared using the ``op`` setting:: + + vlan.layer:3 # exactly 3 vlan layers + vlan.layer:<3 # less than 3 vlan layers + vlan.layer:>=2 # more or equal to 2 vlan layers + +vlan.layer uses :ref:`unsigned 8-bit integer `. + +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.id: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 more than 1 vlan layer"; :example-rule-emphasis:`vlan.id:>1;` sid:1;) + +It is also possible to use the vlan.layer 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.id:2; prefilter;` sid:1;) \ No newline at end of file diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 17827d05f19..86b25596186 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -701,6 +701,7 @@ void SigTableSetup(void) DetectFileHandlerRegister(); DetectVlanIdRegister(); + DetectVlanLayersRegister(); ScDetectSNMPRegister(); ScDetectDHCPRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 79071af056f..945d0fe6355 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -332,6 +332,7 @@ enum DetectKeywordId { DETECT_AL_JA4_HASH, DETECT_VLAN_ID, + DETECT_VLAN_LAYERS, /* make sure this stays last */ DETECT_TBLSIZE_STATIC, diff --git a/src/detect-vlan.c b/src/detect-vlan.c index 4c8316d25ab..5d6c650d7aa 100644 --- a/src/detect-vlan.c +++ b/src/detect-vlan.c @@ -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 *du32 = (const DetectU8Data *)ctx; + return DetectU8Match(nb, du32); +} + +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; +} diff --git a/src/detect-vlan.h b/src/detect-vlan.h index 301fcff5a69..8c449459f28 100644 --- a/src/detect-vlan.h +++ b/src/detect-vlan.h @@ -19,5 +19,6 @@ #define SURICATA_DETECT_VLAN_H void DetectVlanIdRegister(void); +void DetectVlanLayersRegister(void); #endif /* SURICATA_DETECT_VLAN_H */