Skip to content

Commit

Permalink
detect: add vlan.layers keyword
Browse files Browse the repository at this point in the history
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
  • Loading branch information
AkakiAlice committed Jan 8, 2025
1 parent da8f4d5 commit fa23564
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 1 deletion.
40 changes: 40 additions & 0 deletions doc/userguide/rules/vlan-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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.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;)
1 change: 1 addition & 0 deletions src/detect-engine-register.c
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ void SigTableSetup(void)
DetectFileHandlerRegister();

DetectVlanIdRegister();
DetectVlanLayersRegister();

ScDetectSNMPRegister();
ScDetectDHCPRegister();
Expand Down
1 change: 1 addition & 0 deletions src/detect-engine-register.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ enum DetectKeywordId {
DETECT_AL_JA4_HASH,

DETECT_VLAN_ID,
DETECT_VLAN_LAYERS,

/* make sure this stays last */
DETECT_TBLSIZE_STATIC,
Expand Down
78 changes: 77 additions & 1 deletion src/detect-vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

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;
}
1 change: 1 addition & 0 deletions src/detect-vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
#define SURICATA_DETECT_VLAN_H

void DetectVlanIdRegister(void);
void DetectVlanLayersRegister(void);

#endif /* SURICATA_DETECT_VLAN_H */

0 comments on commit fa23564

Please sign in to comment.