detect/ipv4: make fragbits a generic uint16 bitflags keyword

Ticket: 6724

Allows to use numerical values
pull/14067/head
Philippe Antoine 1 year ago committed by Victor Julien
parent 633180c93f
commit 1f9236a6d8

@ -218,13 +218,16 @@ You can match on the following bits::
Matching on this bits can be more specified with the following Matching on this bits can be more specified with the following
modifiers:: 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 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 <rules-integer-keywords>` with bitmasks.
Format:: Format::
fragbits:[*+!]<[MDR]>; fragbits:[*+!=]<[MDR]>;
Example of fragbits in a rule: Example of fragbits in a rule:

@ -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<DetectUintData<u16>> {
detect_parse_uint_bitflags::<u16, Ipv4FragBits>(s, DetectBitflagModifier::Equal, true)
}
#[no_mangle]
pub unsafe extern "C" fn SCDetectIpv4FragbitsParse(
ustr: *const std::os::raw::c_char,
) -> *mut DetectUintData<u16> {
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());
}
}

@ -24,6 +24,7 @@ pub mod entropy;
pub mod error; pub mod error;
pub mod float; pub mod float;
pub mod flow; pub mod flow;
pub mod fragbits;
pub mod iprep; pub mod iprep;
pub mod parser; pub mod parser;
pub mod requires; pub mod requires;

@ -27,11 +27,13 @@
#include "suricata-common.h" #include "suricata-common.h"
#include "suricata.h" #include "suricata.h"
#include "decode.h" #include "decode.h"
#include "rust.h"
#include "detect.h" #include "detect.h"
#include "detect-parse.h" #include "detect-parse.h"
#include "detect-engine-prefilter.h" #include "detect-engine-prefilter.h"
#include "detect-engine-prefilter-common.h" #include "detect-engine-prefilter-common.h"
#include "detect-engine-uint.h"
#include "flow-var.h" #include "flow-var.h"
#include "decode-events.h" #include "decode-events.h"
@ -46,27 +48,6 @@
#include "host.h" #include "host.h"
#include "util-profiling.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 *, static int DetectFragBitsMatch (DetectEngineThreadCtx *, Packet *,
const Signature *, const SigMatchCtx *); const Signature *, const SigMatchCtx *);
static int DetectFragBitsSetup (DetectEngineCtx *, Signature *, const char *); static int DetectFragBitsSetup (DetectEngineCtx *, Signature *, const char *);
@ -95,35 +76,7 @@ void DetectFragBitsRegister (void)
#endif #endif
sigmatch_table[DETECT_FRAGBITS].SetupPrefilter = PrefilterSetupFragBits; sigmatch_table[DETECT_FRAGBITS].SetupPrefilter = PrefilterSetupFragBits;
sigmatch_table[DETECT_FRAGBITS].SupportsPrefilter = PrefilterFragBitsIsPrefilterable; sigmatch_table[DETECT_FRAGBITS].SupportsPrefilter = PrefilterFragBitsIsPrefilterable;
sigmatch_table[DETECT_FRAGBITS].flags = SIGMATCH_INFO_UINT16 | SIGMATCH_INFO_BITFLAGS_UINT;
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;
} }
/** /**
@ -146,130 +99,9 @@ static int DetectFragBitsMatch (DetectEngineThreadCtx *det_ctx,
if (!ctx || !PacketIsIPv4(p)) if (!ctx || !PacketIsIPv4(p))
return 0; return 0;
uint8_t fragbits = 0;
const DetectFragBitsData *de = (const DetectFragBitsData *)ctx;
const IPV4Hdr *ip4h = PacketGetIPv4(p); const IPV4Hdr *ip4h = PacketGetIPv4(p);
if (IPV4_GET_RAW_FLAG_MF(ip4h)) DetectU16Data *du16 = (DetectU16Data *)ctx;
fragbits |= FRAGBITS_HAVE_MF; return DetectU16Match(IPV4_GET_RAW_IPOFFSET(ip4h), du16);
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;
} }
/** /**
@ -286,14 +118,12 @@ error:
*/ */
static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{ {
DetectFragBitsData *de = NULL; DetectU16Data *du16 = SCDetectIpv4FragbitsParse(rawstr);
if (du16 == NULL)
de = DetectFragBitsParse(rawstr);
if (de == NULL)
return -1; return -1;
if (SCSigMatchAppendSMToList( 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; goto error;
} }
s->flags |= SIG_FLAG_REQUIRE_PACKET; s->flags |= SIG_FLAG_REQUIRE_PACKET;
@ -301,21 +131,20 @@ static int DetectFragBitsSetup (DetectEngineCtx *de_ctx, Signature *s, const cha
return 0; return 0;
error: error:
if (de) if (du16)
SCFree(de); DetectFragBitsFree(NULL, du16);
return -1; return -1;
} }
/** /**
* \internal * \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) static void DetectFragBitsFree(DetectEngineCtx *de_ctx, void *de_ptr)
{ {
DetectFragBitsData *de = (DetectFragBitsData *)de_ptr; SCDetectU16Free(de_ptr);
if(de) SCFree(de);
} }
static void static void
@ -327,46 +156,21 @@ PrefilterPacketFragBitsMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const vo
if (!PacketIsIPv4(p)) if (!PacketIsIPv4(p))
return; return;
uint8_t fragbits = 0;
const IPV4Hdr *ip4h = PacketGetIPv4(p); const IPV4Hdr *ip4h = PacketGetIPv4(p);
if (IPV4_GET_RAW_FLAG_MF(ip4h)) DetectU16Data du16;
fragbits |= FRAGBITS_HAVE_MF; du16.mode = ctx->v1.u8[0];
if (IPV4_GET_RAW_FLAG_DF(ip4h)) du16.arg1 = ctx->v1.u16[1];
fragbits |= FRAGBITS_HAVE_DF; du16.arg2 = ctx->v1.u16[2];
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;
}
static bool if (DetectU16Match(IPV4_GET_RAW_IPOFFSET(ip4h), &du16)) {
PrefilterPacketFragBitsCompare(PrefilterPacketHeaderValue v, void *smctx) PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
{
const DetectFragBitsData *fb = smctx;
if (v.u8[0] == fb->modifier &&
v.u8[1] == fb->fragbits)
{
return true;
} }
return false;
} }
static int PrefilterSetupFragBits(DetectEngineCtx *de_ctx, SigGroupHead *sgh) static int PrefilterSetupFragBits(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{ {
return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FRAGBITS, SIG_MASK_REQUIRE_REAL_PKT, return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FRAGBITS, SIG_MASK_REQUIRE_REAL_PKT,
PrefilterPacketFragBitsSet, PrefilterPacketFragBitsCompare, PrefilterPacketU16Set, PrefilterPacketU16Compare, PrefilterPacketFragBitsMatch);
PrefilterPacketFragBitsMatch);
} }
static bool PrefilterFragBitsIsPrefilterable(const Signature *s) static bool PrefilterFragBitsIsPrefilterable(const Signature *s)
@ -389,42 +193,6 @@ static bool PrefilterFragBitsIsPrefilterable(const Signature *s)
#include "util-unittest-helper.h" #include "util-unittest-helper.h"
#include "packet.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 * \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)); DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth));
DetectFragBitsData *de = DetectFragBitsParse("D"); DetectU16Data *de = SCDetectIpv4FragbitsParse("D");
FAIL_IF(de == NULL); FAIL_IF(de == NULL);
FAIL_IF(de->fragbits != FRAGBITS_HAVE_DF); FAIL_IF(de->arg1 != 0x4000);
FAIL_IF(de->mode != DetectUintModeEqual);
SigMatch *sm = SigMatchAlloc(); SigMatch *sm = SigMatchAlloc();
FAIL_IF(sm == NULL); FAIL_IF(sm == NULL);
@ -496,7 +265,7 @@ static int FragBitsTestParse03 (void)
int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx); int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx);
FAIL_IF(ret == 0); FAIL_IF(ret == 0);
SCFree(de); DetectFragBitsFree(NULL, de);
SCFree(sm); SCFree(sm);
PacketFree(p); PacketFree(p);
@ -564,10 +333,11 @@ static int FragBitsTestParse04 (void)
DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth)); DecodeEthernet(&tv, &dtv, p, raw_eth, sizeof(raw_eth));
DetectFragBitsData *de = DetectFragBitsParse("!D"); DetectU16Data *de = SCDetectIpv4FragbitsParse("!D");
FAIL_IF(de == NULL); FAIL_IF(de == NULL);
FAIL_IF(de->fragbits != FRAGBITS_HAVE_DF); FAIL_IF(de->arg1 != 0x4000);
FAIL_IF(de->modifier != MODIFIER_NOT); FAIL_IF(de->arg2 != 0x4000);
FAIL_IF(de->mode != DetectUintModeNegBitmask);
SigMatch *sm = SigMatchAlloc(); SigMatch *sm = SigMatchAlloc();
FAIL_IF(sm == NULL); FAIL_IF(sm == NULL);
@ -576,7 +346,7 @@ static int FragBitsTestParse04 (void)
int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx); int ret = DetectFragBitsMatch(NULL, p, NULL, sm->ctx);
FAIL_IF(ret); FAIL_IF(ret);
SCFree(de); DetectFragBitsFree(NULL, de);
SCFree(sm); SCFree(sm);
PacketFree(p); PacketFree(p);
@ -590,8 +360,6 @@ static int FragBitsTestParse04 (void)
*/ */
static void FragBitsRegisterTests(void) static void FragBitsRegisterTests(void)
{ {
UtRegisterTest("FragBitsTestParse01", FragBitsTestParse01);
UtRegisterTest("FragBitsTestParse02", FragBitsTestParse02);
UtRegisterTest("FragBitsTestParse03", FragBitsTestParse03); UtRegisterTest("FragBitsTestParse03", FragBitsTestParse03);
UtRegisterTest("FragBitsTestParse04", FragBitsTestParse04); UtRegisterTest("FragBitsTestParse04", FragBitsTestParse04);
} }

@ -24,21 +24,6 @@
#ifndef SURICATA_DETECT_FRAGBITS_H #ifndef SURICATA_DETECT_FRAGBITS_H
#define 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 * Registration function for fragbits: keyword
*/ */

Loading…
Cancel
Save