From 8aafe1c0b7a9751e95dc35297f6d8303629f2a75 Mon Sep 17 00:00:00 2001 From: Alice Akaki Date: Thu, 7 Nov 2024 16:46:33 -0400 Subject: [PATCH 1/2] detect: add vlan.id keyword vlan.id matches on Virtual Local Area Network IDs It is an unsigned 16-bit integer Valid range = [0-4095] Supports prefiltering Ticket: #1065 --- doc/userguide/rules/index.rst | 1 + doc/userguide/rules/vlan-keywords.rst | 85 +++++++++++ rust/src/detect/mod.rs | 1 + rust/src/detect/vlan.rs | 200 ++++++++++++++++++++++++++ src/Makefile.am | 2 + src/detect-engine-register.c | 3 + src/detect-engine-register.h | 2 + src/detect-vlan.c | 142 ++++++++++++++++++ src/detect-vlan.h | 23 +++ 9 files changed, 459 insertions(+) create mode 100644 doc/userguide/rules/vlan-keywords.rst create mode 100644 rust/src/detect/vlan.rs create mode 100644 src/detect-vlan.c create mode 100644 src/detect-vlan.h diff --git a/doc/userguide/rules/index.rst b/doc/userguide/rules/index.rst index c8b586fecaa7..415a6c2c381c 100644 --- a/doc/userguide/rules/index.rst +++ b/doc/userguide/rules/index.rst @@ -49,3 +49,4 @@ Suricata Rules differences-from-snort multi-buffer-matching tag + vlan-keywords diff --git a/doc/userguide/rules/vlan-keywords.rst b/doc/userguide/rules/vlan-keywords.rst new file mode 100644 index 000000000000..1ae40f87ec30 --- /dev/null +++ b/doc/userguide/rules/vlan-keywords.rst @@ -0,0 +1,85 @@ +VLAN Keywords +============= + +.. role:: example-rule-action +.. role:: example-rule-header +.. role:: example-rule-options +.. role:: example-rule-emphasis + +vlan.id +------- + +Suricata has a ``vlan.id`` keyword that can be used in signatures to identify +and filter network packets based on Virtual Local Area Network IDs. By default, +it matches all layers if a packet contains multiple VLAN layers. However, if a +specific layer is defined, it will only match that layer. + +Syntax:: + + vlan.id: [op]id[,layer]; + +The id can be matched exactly, or compared using the ``op`` setting:: + + vlan.id:300 # exactly 300 + vlan.id:<300,0 # smaller than 300 at layer 0 + vlan.id:>=200,1 # greater or equal than 200 at layer 1 + +vlan.id uses :ref:`unsigned 16-bit integer `. + +The valid range for VLAN id values is ``0 - 4095``. + +This keyword also supports ``all`` and ``any`` as arguments for ``layer``. +``all`` matches only if all VLAN layers match and ``any`` matches with any layer. + +.. table:: **Layer values for vlan.id keyword** + + =============== ================================================ + Value Description + =============== ================================================ + [default] Match with any layer + 0 - 2 Match specific layer + ``-3`` - ``-1`` Match specific layer with back to front indexing + all Match only if all layers match + any Match with any layer + =============== ================================================ + +This small illustration shows how indexing works for vlan.id:: + + [ethernet] + [vlan 666 (index 0 and -2)] + [vlan 123 (index 1 and -1)] + [ipv4] + [udp] + +Examples +^^^^^^^^ + +Example of a signature that would alert if any of the VLAN IDs is equal to 300: + +.. container:: example-rule + + alert ip any any -> any any (msg:"Vlan ID is equal to 300"; :example-rule-emphasis:`vlan.id:300;` sid:1;) + +Example of a signature that would alert if the VLAN ID at layer 1 is equal to 300: + +.. container:: example-rule + + alert ip any any -> any any (msg:"Vlan ID is equal to 300 at layer 1"; :example-rule-emphasis:`vlan.id:300,1;` sid:1;) + +Example of a signature that would alert if the VLAN ID at the last layer is equal to 400: + +.. container:: example-rule + + alert ip any any -> any any (msg:"Vlan ID is equal to 400 at the last layer"; :example-rule-emphasis:`vlan.id:400,-1;` sid:1;) + +Example of a signature that would alert only if all the VLAN IDs are greater than 100: + +.. container:: example-rule + + alert ip any any -> any any (msg:"All Vlan IDs are greater than 100"; :example-rule-emphasis:`vlan.id:>100,all;` sid:1;) + +It is also possible to use the vlan.id 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:"Vlan ID is equal to 200 at layer 1"; :example-rule-emphasis:`vlan.id:200,1; prefilter;` sid:1;) diff --git a/rust/src/detect/mod.rs b/rust/src/detect/mod.rs index 1857c22ee2b2..c00f0dfdeb18 100644 --- a/rust/src/detect/mod.rs +++ b/rust/src/detect/mod.rs @@ -29,6 +29,7 @@ pub mod transforms; pub mod uint; pub mod uri; pub mod tojson; +pub mod vlan; use crate::core::AppProto; use std::os::raw::{c_int, c_void}; diff --git a/rust/src/detect/vlan.rs b/rust/src/detect/vlan.rs new file mode 100644 index 000000000000..fb3abbc9b89c --- /dev/null +++ b/rust/src/detect/vlan.rs @@ -0,0 +1,200 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use super::uint::{detect_parse_uint, DetectUintData}; +use std::ffi::CStr; +use std::str::FromStr; + +pub const DETECT_VLAN_ID_ANY: i8 = i8::MIN; +pub const DETECT_VLAN_ID_ALL: i8 = i8::MAX; +pub static VLAN_MAX_LAYERS: i8 = 3; + +#[repr(C)] +#[derive(Debug, PartialEq)] +/// This data structure is also used in detect-vlan.c +pub struct DetectVlanIdData { + /// Vlan id + pub du16: DetectUintData, + /// Layer can be DETECT_VLAN_ID_ANY to match with any vlan layer + /// DETECT_VLAN_ID_ALL to match if all layers match, or an integer + /// within the range -VLAN_MAX_LAYERS to VLAN_MAX_LAYERS-1 for indexing. + /// Negative values represent back to front indexing. + pub layer: i8, +} + +pub fn detect_parse_vlan_id(s: &str) -> Option { + let parts: Vec<&str> = s.split(',').collect(); + let du16 = detect_parse_uint(parts[0]).ok()?.1; + if parts.len() > 2 { + return None; + } + if du16.arg1 > 0xFFF || du16.arg2 > 0xFFF { + // vlan id is encoded on 12 bits + return None; + } + let layer = if parts.len() == 2 { + if parts[1] == "all" { + DETECT_VLAN_ID_ALL + } else if parts[1] == "any" { + DETECT_VLAN_ID_ANY + } else { + let u8_layer = i8::from_str(parts[1]).ok()?; + if !(-VLAN_MAX_LAYERS..=VLAN_MAX_LAYERS - 1).contains(&u8_layer) { + return None; + } + u8_layer + } + } else { + DETECT_VLAN_ID_ANY + }; + return Some(DetectVlanIdData { du16, layer }); +} + +#[no_mangle] +pub unsafe extern "C" fn SCDetectVlanIdParse( + ustr: *const std::os::raw::c_char, +) -> *mut DetectVlanIdData { + let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe + if let Ok(s) = ft_name.to_str() { + if let Some(ctx) = detect_parse_vlan_id(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut _; + } + } + return std::ptr::null_mut(); +} + +#[no_mangle] +pub unsafe extern "C" fn SCDetectVlanIdFree(ctx: &mut DetectVlanIdData) { + // Just unbox... + std::mem::drop(Box::from_raw(ctx)); +} + +#[cfg(test)] +mod test { + use super::*; + use crate::detect::uint::DetectUintMode; + + #[test] + fn test_detect_parse_vlan_id() { + assert_eq!( + detect_parse_vlan_id("300").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 300, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: DETECT_VLAN_ID_ANY + } + ); + assert_eq!( + detect_parse_vlan_id("300,any").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 300, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: DETECT_VLAN_ID_ANY + } + ); + assert_eq!( + detect_parse_vlan_id("300,all").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 300, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: DETECT_VLAN_ID_ALL + } + ); + assert_eq!( + detect_parse_vlan_id("200,1").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: 1 + } + ); + assert_eq!( + detect_parse_vlan_id("200,-1").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: -1 + } + ); + assert_eq!( + detect_parse_vlan_id("!200,2").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 0, + mode: DetectUintMode::DetectUintModeNe, + }, + layer: 2 + } + ); + assert_eq!( + detect_parse_vlan_id(">200,2").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 0, + mode: DetectUintMode::DetectUintModeGt, + }, + layer: 2 + } + ); + assert_eq!( + detect_parse_vlan_id("200-300,0").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 300, + mode: DetectUintMode::DetectUintModeRange, + }, + layer: 0 + } + ); + assert_eq!( + detect_parse_vlan_id("0xC8,2").unwrap(), + DetectVlanIdData { + du16: DetectUintData { + arg1: 200, + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + layer: 2 + } + ); + assert!(detect_parse_vlan_id("200abc").is_none()); + assert!(detect_parse_vlan_id("4096").is_none()); + assert!(detect_parse_vlan_id("600,abc").is_none()); + assert!(detect_parse_vlan_id("600,100").is_none()); + assert!(detect_parse_vlan_id("123,-4").is_none()); + assert!(detect_parse_vlan_id("1,2,3").is_none()); + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 82155d7f825d..615816953542 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -313,6 +313,7 @@ noinst_HEADERS = \ detect-urilen.h \ detect-within.h \ detect-xbits.h \ + detect-vlan.h \ device-storage.h \ feature.h \ flow-bit.h \ @@ -877,6 +878,7 @@ libsuricata_c_a_SOURCES = \ detect-urilen.c \ detect-within.c \ detect-xbits.c \ + detect-vlan.c \ device-storage.c \ feature.c \ flow-bit.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 82153807ad58..4f94e0e9651d 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -249,6 +249,7 @@ #include "detect-ike-nonce-payload-length.h" #include "detect-ike-nonce-payload.h" #include "detect-ike-key-exchange-payload.h" +#include "detect-vlan.h" #include "action-globals.h" #include "tm-threads.h" @@ -701,6 +702,8 @@ void SigTableSetup(void) DetectFileHandlerRegister(); + DetectVlanIdRegister(); + ScDetectSNMPRegister(); ScDetectDHCPRegister(); ScDetectWebsocketRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index f46bf688f0f8..ecbcac9fb3bf 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -333,6 +333,8 @@ enum DetectKeywordId { DETECT_AL_JA4_HASH, + DETECT_VLAN_ID, + /* make sure this stays last */ DETECT_TBLSIZE_STATIC, }; diff --git a/src/detect-vlan.c b/src/detect-vlan.c new file mode 100644 index 000000000000..41a7434c6ecc --- /dev/null +++ b/src/detect-vlan.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "detect-vlan.h" +#include "detect-engine-uint.h" +#include "detect-parse.h" +#include "rust.h" + +static int DetectVlanIdMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + const DetectVlanIdData *vdata = (const DetectVlanIdData *)ctx; + + if (p->vlan_idx == 0) { + return 0; + } + + switch (vdata->layer) { + case DETECT_VLAN_ID_ANY: + for (int i = 0; i < p->vlan_idx; i++) { + if (DetectU16Match(p->vlan_id[i], &vdata->du16)) { + return 1; + } + } + return 0; + case DETECT_VLAN_ID_ALL: + for (int i = 0; i < p->vlan_idx; i++) { + if (!DetectU16Match(p->vlan_id[i], &vdata->du16)) { + return 0; + } + } + return 1; + default: + if (vdata->layer < 0) { // Negative layer values for backward indexing. + if (((int16_t)p->vlan_idx) + vdata->layer < 0) { + return 0; + } + return DetectU16Match(p->vlan_id[p->vlan_idx + vdata->layer], &vdata->du16); + } else { + if (p->vlan_idx < vdata->layer) { + return 0; + } + return DetectU16Match(p->vlan_id[vdata->layer], &vdata->du16); + } + } +} + +static void DetectVlanIdFree(DetectEngineCtx *de_ctx, void *ptr) +{ + SCDetectVlanIdFree(ptr); +} + +static int DetectVlanIdSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + DetectVlanIdData *vdata = SCDetectVlanIdParse(rawstr); + if (vdata == NULL) { + SCLogError("vlan id invalid %s", rawstr); + return -1; + } + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_VLAN_ID, (SigMatchCtx *)vdata, DETECT_SM_LIST_MATCH) == NULL) { + DetectVlanIdFree(de_ctx, vdata); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +} + +static void PrefilterPacketVlanIdMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +{ + const PrefilterPacketHeaderCtx *ctx = pectx; + + DetectVlanIdData vdata; + vdata.du16.mode = ctx->v1.u8[0]; + vdata.layer = ctx->v1.u8[1]; + vdata.du16.arg1 = ctx->v1.u16[2]; + vdata.du16.arg2 = ctx->v1.u16[3]; + + if (p->vlan_idx == 0) + return; + + if (DetectVlanIdMatch(det_ctx, p, NULL, (const SigMatchCtx *)&vdata)) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + } +} + +static void PrefilterPacketVlanIdSet(PrefilterPacketHeaderValue *v, void *smctx) +{ + const DetectVlanIdData *a = smctx; + v->u8[0] = a->du16.mode; + v->u8[1] = a->layer; + v->u16[2] = a->du16.arg1; + v->u16[3] = a->du16.arg2; +} + +static bool PrefilterPacketVlanIdCompare(PrefilterPacketHeaderValue v, void *smctx) +{ + const DetectVlanIdData *a = smctx; + if (v.u8[0] == a->du16.mode && v.u8[1] == a->layer && v.u16[2] == a->du16.arg1 && + v.u16[3] == a->du16.arg2) + return true; + return false; +} + +static int PrefilterSetupVlanId(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_VLAN_ID, SIG_MASK_REQUIRE_REAL_PKT, + PrefilterPacketVlanIdSet, PrefilterPacketVlanIdCompare, PrefilterPacketVlanIdMatch); +} + +static bool PrefilterVlanIdIsPrefilterable(const Signature *s) +{ + return PrefilterIsPrefilterableById(s, DETECT_VLAN_ID); +} + +void DetectVlanIdRegister(void) +{ + sigmatch_table[DETECT_VLAN_ID].name = "vlan.id"; + sigmatch_table[DETECT_VLAN_ID].desc = "match vlan id"; + sigmatch_table[DETECT_VLAN_ID].url = "/rules/vlan-keywords.html#vlan-id"; + sigmatch_table[DETECT_VLAN_ID].Match = DetectVlanIdMatch; + sigmatch_table[DETECT_VLAN_ID].Setup = DetectVlanIdSetup; + 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 diff --git a/src/detect-vlan.h b/src/detect-vlan.h new file mode 100644 index 000000000000..301fcff5a691 --- /dev/null +++ b/src/detect-vlan.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef SURICATA_DETECT_VLAN_H +#define SURICATA_DETECT_VLAN_H + +void DetectVlanIdRegister(void); + +#endif /* SURICATA_DETECT_VLAN_H */ From b3150226fea83d0a4e513d3eada06f12f5803511 Mon Sep 17 00:00:00 2001 From: Alice Akaki Date: Wed, 8 Jan 2025 17:03:24 -0400 Subject: [PATCH 2/2] detect: add vlan.layers keyword 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 | 40 ++++++++++++++ src/detect-engine-register.c | 1 + src/detect-engine-register.h | 1 + src/detect-vlan.c | 78 ++++++++++++++++++++++++++- src/detect-vlan.h | 1 + 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/doc/userguide/rules/vlan-keywords.rst b/doc/userguide/rules/vlan-keywords.rst index 1ae40f87ec30..f342b745ad67 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.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 `. + +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 diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 4f94e0e9651d..7197eaa11e56 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -703,6 +703,7 @@ void SigTableSetup(void) DetectFileHandlerRegister(); DetectVlanIdRegister(); + DetectVlanLayersRegister(); ScDetectSNMPRegister(); ScDetectDHCPRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index ecbcac9fb3bf..455d3735d8bf 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -334,6 +334,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 41a7434c6ecc..9b9e085bf9b9 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 *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; +} diff --git a/src/detect-vlan.h b/src/detect-vlan.h index 301fcff5a691..8c449459f283 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 */