diff --git a/doc/userguide/rules/header-keywords.rst b/doc/userguide/rules/header-keywords.rst index 7f204b8ee7..3697b30d00 100644 --- a/doc/userguide/rules/header-keywords.rst +++ b/doc/userguide/rules/header-keywords.rst @@ -218,13 +218,16 @@ You can match on the following bits:: Matching on this bits can be more specified with the following modifiers:: - + match on the specified bits, plus any others + + match on all the specified bits (plus any others) * match if any of the specified bits are set - ! match if the specified bits are not set + ! or - match if not all the specified bits are set + = match on all the specified bits and only them + +fragbits uses an :ref:`unsigned 16-bits integer ` with bitmasks. Format:: - fragbits:[*+!]<[MDR]>; + fragbits:[*+!=]<[MDR]>; Example of fragbits in a rule: diff --git a/rust/src/detect/fragbits.rs b/rust/src/detect/fragbits.rs new file mode 100644 index 0000000000..d9e77f3e04 --- /dev/null +++ b/rust/src/detect/fragbits.rs @@ -0,0 +1,59 @@ +/* Copyright (C) 2025 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 crate::detect::uint::{detect_parse_uint_bitflags, DetectBitflagModifier, DetectUintData}; + +use std::ffi::CStr; + +#[repr(u16)] +#[derive(EnumStringU16)] +#[allow(non_camel_case_types)] +pub enum Ipv4FragBits { + R = 0x8000, + D = 0x4000, + M = 0x2000, +} + +fn detect_ipv4_fragbits(s: &str) -> Option> { + detect_parse_uint_bitflags::(s, DetectBitflagModifier::Equal, true) +} + +#[no_mangle] +pub unsafe extern "C" fn SCDetectIpv4FragbitsParse( + ustr: *const std::os::raw::c_char, +) -> *mut DetectUintData { + let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe + if let Ok(s) = ft_name.to_str() { + if let Some(ctx) = detect_ipv4_fragbits(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut _; + } + } + return std::ptr::null_mut(); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn fragbits_parse() { + let ctx = detect_ipv4_fragbits("M").unwrap(); + assert_eq!(ctx.arg1, 0x2000); + assert!(detect_ipv4_fragbits("G").is_none()); + } +} diff --git a/rust/src/detect/mod.rs b/rust/src/detect/mod.rs index 5264927c70..e6b51ed052 100644 --- a/rust/src/detect/mod.rs +++ b/rust/src/detect/mod.rs @@ -24,6 +24,7 @@ pub mod entropy; pub mod error; pub mod float; pub mod flow; +pub mod fragbits; pub mod iprep; pub mod parser; pub mod requires; diff --git a/src/detect-fragbits.c b/src/detect-fragbits.c index a36828bd77..0986610cfa 100644 --- a/src/detect-fragbits.c +++ b/src/detect-fragbits.c @@ -27,11 +27,13 @@ #include "suricata-common.h" #include "suricata.h" #include "decode.h" +#include "rust.h" #include "detect.h" #include "detect-parse.h" #include "detect-engine-prefilter.h" #include "detect-engine-prefilter-common.h" +#include "detect-engine-uint.h" #include "flow-var.h" #include "decode-events.h" @@ -46,27 +48,6 @@ #include "host.h" #include "util-profiling.h" -/** - * Regex - * fragbits: [!+*](MDR) - */ -#define PARSE_REGEX "^(?:([\\+\\*!]))?\\s*([MDR]+)" - -/** - * FragBits args[0] *(3) +(2) !(1) - * - */ - -#define MODIFIER_NOT 1 -#define MODIFIER_PLUS 2 -#define MODIFIER_ANY 3 - -#define FRAGBITS_HAVE_MF 0x01 -#define FRAGBITS_HAVE_DF 0x02 -#define FRAGBITS_HAVE_RF 0x04 - -static DetectParseRegex parse_regex; - static int DetectFragBitsMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); static int DetectFragBitsSetup (DetectEngineCtx *, Signature *, const char *); @@ -95,35 +76,7 @@ void DetectFragBitsRegister (void) #endif sigmatch_table[DETECT_FRAGBITS].SetupPrefilter = PrefilterSetupFragBits; sigmatch_table[DETECT_FRAGBITS].SupportsPrefilter = PrefilterFragBitsIsPrefilterable; - - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); -} - -static inline int -FragBitsMatch(const uint8_t pbits, const uint8_t modifier, - const uint8_t dbits) -{ - switch (modifier) { - case MODIFIER_ANY: - if ((pbits & dbits) > 0) - return 1; - return 0; - - case MODIFIER_PLUS: - if (((pbits & dbits) == dbits) && (((pbits - dbits) > 0))) - return 1; - return 0; - - case MODIFIER_NOT: - if ((pbits & dbits) != dbits) - return 1; - return 0; - - default: - if (pbits == dbits) - return 1; - } - return 0; + sigmatch_table[DETECT_FRAGBITS].flags = SIGMATCH_INFO_UINT16 | SIGMATCH_INFO_BITFLAGS_UINT; } /** @@ -146,130 +99,9 @@ static int DetectFragBitsMatch (DetectEngineThreadCtx *det_ctx, if (!ctx || !PacketIsIPv4(p)) return 0; - uint8_t fragbits = 0; - const DetectFragBitsData *de = (const DetectFragBitsData *)ctx; const IPV4Hdr *ip4h = PacketGetIPv4(p); - if (IPV4_GET_RAW_FLAG_MF(ip4h)) - fragbits |= FRAGBITS_HAVE_MF; - if (IPV4_GET_RAW_FLAG_DF(ip4h)) - fragbits |= FRAGBITS_HAVE_DF; - if (IPV4_GET_RAW_FLAG_RF(ip4h)) - fragbits |= FRAGBITS_HAVE_RF; - - return FragBitsMatch(fragbits, de->modifier, de->fragbits); -} - -/** - * \internal - * \brief This function is used to parse fragbits options passed via fragbits: keyword - * - * \param rawstr Pointer to the user provided fragbits options - * - * \retval de pointer to DetectFragBitsData on success - * \retval NULL on failure - */ -static DetectFragBitsData *DetectFragBitsParse (const char *rawstr) -{ - DetectFragBitsData *de = NULL; - int found = 0, res = 0; - size_t pcre2_len; - const char *str_ptr = NULL; - char *args[2] = { NULL, NULL}; - char *ptr; - int i; - pcre2_match_data *match = NULL; - - int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0); - if (ret < 1) { - SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); - goto error; - } - - for (i = 0; i < (ret - 1); i++) { - res = SC_Pcre2SubstringGet(match, i + 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); - if (res < 0) { - SCLogError("pcre2_substring_get_bynumber failed %d", res); - goto error; - } - - args[i] = (char *)str_ptr; - } - - if (args[1] == NULL) { - SCLogError("invalid value"); - goto error; - } - - de = SCCalloc(1, sizeof(DetectFragBitsData)); - if (unlikely(de == NULL)) - goto error; - - /** First parse args[0] */ - - if (args[0] && strlen(args[0])) { - ptr = args[0]; - switch (*ptr) { - case '!': - de->modifier = MODIFIER_NOT; - break; - case '+': - de->modifier = MODIFIER_PLUS; - break; - case '*': - de->modifier = MODIFIER_ANY; - break; - } - } - - /** Second parse first set of fragbits */ - - ptr = args[1]; - - while (*ptr != '\0') { - switch (*ptr) { - case 'M': - case 'm': - de->fragbits |= FRAGBITS_HAVE_MF; - found++; - break; - case 'D': - case 'd': - de->fragbits |= FRAGBITS_HAVE_DF; - found++; - break; - case 'R': - case 'r': - de->fragbits |= FRAGBITS_HAVE_RF; - found++; - break; - default: - found = 0; - break; - } - ptr++; - } - - if(found == 0) - goto error; - - for (i = 0; i < 2; i++) { - if (args[i] != NULL) - pcre2_substring_free((PCRE2_UCHAR8 *)args[i]); - } - pcre2_match_data_free(match); - return de; - -error: - if (match) { - pcre2_match_data_free(match); - } - for (i = 0; i < 2; i++) { - if (args[i] != NULL) - pcre2_substring_free((PCRE2_UCHAR8 *)args[i]); - } - if (de != NULL) - SCFree(de); - return NULL; + DetectU16Data *du16 = (DetectU16Data *)ctx; + return DetectU16Match(IPV4_GET_RAW_IPOFFSET(ip4h), du16); } /** @@ -286,14 +118,12 @@ error: */ static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - DetectFragBitsData *de = NULL; - - de = DetectFragBitsParse(rawstr); - if (de == NULL) + DetectU16Data *du16 = SCDetectIpv4FragbitsParse(rawstr); + if (du16 == NULL) return -1; if (SCSigMatchAppendSMToList( - de_ctx, s, DETECT_FRAGBITS, (SigMatchCtx *)de, DETECT_SM_LIST_MATCH) == NULL) { + de_ctx, s, DETECT_FRAGBITS, (SigMatchCtx *)du16, DETECT_SM_LIST_MATCH) == NULL) { goto error; } s->flags |= SIG_FLAG_REQUIRE_PACKET; @@ -301,21 +131,20 @@ static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, const cha return 0; error: - if (de) - SCFree(de); + if (du16) + DetectFragBitsFree(NULL, du16); return -1; } /** * \internal - * \brief this function will free memory associated with DetectFragBitsData + * \brief this function will free memory associated with DetectU16Data * - * \param de pointer to DetectFragBitsData + * \param de pointer to DetectU16Data */ static void DetectFragBitsFree(DetectEngineCtx *de_ctx, void *de_ptr) { - DetectFragBitsData *de = (DetectFragBitsData *)de_ptr; - if(de) SCFree(de); + SCDetectU16Free(de_ptr); } static void @@ -327,46 +156,21 @@ PrefilterPacketFragBitsMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const vo if (!PacketIsIPv4(p)) return; - uint8_t fragbits = 0; const IPV4Hdr *ip4h = PacketGetIPv4(p); - if (IPV4_GET_RAW_FLAG_MF(ip4h)) - fragbits |= FRAGBITS_HAVE_MF; - if (IPV4_GET_RAW_FLAG_DF(ip4h)) - fragbits |= FRAGBITS_HAVE_DF; - if (IPV4_GET_RAW_FLAG_RF(ip4h)) - fragbits |= FRAGBITS_HAVE_RF; - - if (FragBitsMatch(fragbits, ctx->v1.u8[0], ctx->v1.u8[1])) - { - PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); - } -} - -static void -PrefilterPacketFragBitsSet(PrefilterPacketHeaderValue *v, void *smctx) -{ - const DetectFragBitsData *fb = smctx; - v->u8[0] = fb->modifier; - v->u8[1] = fb->fragbits; -} + DetectU16Data du16; + du16.mode = ctx->v1.u8[0]; + du16.arg1 = ctx->v1.u16[1]; + du16.arg2 = ctx->v1.u16[2]; -static bool -PrefilterPacketFragBitsCompare(PrefilterPacketHeaderValue v, void *smctx) -{ - const DetectFragBitsData *fb = smctx; - if (v.u8[0] == fb->modifier && - v.u8[1] == fb->fragbits) - { - return true; + if (DetectU16Match(IPV4_GET_RAW_IPOFFSET(ip4h), &du16)) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); } - return false; } static int PrefilterSetupFragBits(DetectEngineCtx *de_ctx, SigGroupHead *sgh) { return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FRAGBITS, SIG_MASK_REQUIRE_REAL_PKT, - PrefilterPacketFragBitsSet, PrefilterPacketFragBitsCompare, - PrefilterPacketFragBitsMatch); + PrefilterPacketU16Set, PrefilterPacketU16Compare, PrefilterPacketFragBitsMatch); } static bool PrefilterFragBitsIsPrefilterable(const Signature *s) @@ -389,42 +193,6 @@ static bool PrefilterFragBitsIsPrefilterable(const Signature *s) #include "util-unittest-helper.h" #include "packet.h" -/** - * \test FragBitsTestParse01 is a test for a valid fragbits value - * - * \retval 1 on success - * \retval 0 on failure - */ -static int FragBitsTestParse01 (void) -{ - DetectFragBitsData *de = NULL; - de = DetectFragBitsParse("M"); - if (de && (de->fragbits == FRAGBITS_HAVE_MF) ) { - DetectFragBitsFree(NULL, de); - return 1; - } - - return 0; -} - -/** - * \test FragBitsTestParse02 is a test for an invalid fragbits value - * - * \retval 1 on success - * \retval 0 on failure - */ -static int FragBitsTestParse02 (void) -{ - DetectFragBitsData *de = NULL; - de = DetectFragBitsParse("G"); - if (de) { - DetectFragBitsFree(NULL, de); - return 0; - } - - return 1; -} - /** * \test FragBitsTestParse03 test if DONT FRAG is set. Must return success * @@ -484,9 +252,10 @@ static int FragBitsTestParse03 (void) DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth)); - DetectFragBitsData *de = DetectFragBitsParse("D"); + DetectU16Data *de = SCDetectIpv4FragbitsParse("D"); FAIL_IF(de == NULL); - FAIL_IF(de->fragbits != FRAGBITS_HAVE_DF); + FAIL_IF(de->arg1 != 0x4000); + FAIL_IF(de->mode != DetectUintModeEqual); SigMatch *sm = SigMatchAlloc(); FAIL_IF(sm == NULL); @@ -496,7 +265,7 @@ static int FragBitsTestParse03 (void) int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx); FAIL_IF(ret == 0); - SCFree(de); + DetectFragBitsFree(NULL, de); SCFree(sm); PacketFree(p); @@ -564,10 +333,11 @@ static int FragBitsTestParse04 (void) DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth)); - DetectFragBitsData *de = DetectFragBitsParse("!D"); + DetectU16Data *de = SCDetectIpv4FragbitsParse("!D"); FAIL_IF(de == NULL); - FAIL_IF(de->fragbits != FRAGBITS_HAVE_DF); - FAIL_IF(de->modifier != MODIFIER_NOT); + FAIL_IF(de->arg1 != 0x4000); + FAIL_IF(de->arg2 != 0x4000); + FAIL_IF(de->mode != DetectUintModeNegBitmask); SigMatch *sm = SigMatchAlloc(); FAIL_IF(sm == NULL); @@ -576,7 +346,7 @@ static int FragBitsTestParse04 (void) int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx); FAIL_IF(ret); - SCFree(de); + DetectFragBitsFree(NULL, de); SCFree(sm); PacketFree(p); @@ -590,8 +360,6 @@ static int FragBitsTestParse04 (void) */ static void FragBitsRegisterTests(void) { - UtRegisterTest("FragBitsTestParse01", FragBitsTestParse01); - UtRegisterTest("FragBitsTestParse02", FragBitsTestParse02); UtRegisterTest("FragBitsTestParse03", FragBitsTestParse03); UtRegisterTest("FragBitsTestParse04", FragBitsTestParse04); } diff --git a/src/detect-fragbits.h b/src/detect-fragbits.h index a4b0152a7a..400c109deb 100644 --- a/src/detect-fragbits.h +++ b/src/detect-fragbits.h @@ -24,21 +24,6 @@ #ifndef SURICATA_DETECT_FRAGBITS_H #define SURICATA_DETECT_FRAGBITS_H -/** - * \struct DetectFragBitsData_ - * DetectFragBitsData_ is used to store fragbits: input value - */ - -/** - * \typedef DetectFragBitsData - * A typedef for DetectFragBitsData_ - */ - -typedef struct DetectFragBitsData_ { - uint8_t fragbits; /**< IP fragbits */ - uint8_t modifier; /**< !(1) +(2) *(3) modifiers */ -} DetectFragBitsData; - /** * Registration function for fragbits: keyword */