Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow rate initial support/v2 #12236

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/userguide/rules/flow-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,34 @@ Signature example::

In this example, we combine `flow.age` and `flowbits` to get an alert on the first packet after the flow's age is older than one hour.

flow.rate
---------

The rate of the flow calculated by dividing number of bytes by the time in seconds.
So, the unit of the flow rate is bytes/second. Note that it is possible to denote
the rate with units like kb, mb, etc.
Currently, it is implemented as a check against the total number of bytes seen by
the flow in both the directions divided by the age of the flow.

For example, if a flow has seen 5000000 bytes in 10 seconds, a rule for this flow
will be matched against 500000 bytes/s.

flow.rate uses an :ref:`unsigned 64-bit integer <rules-integer-keywords>`.

Syntax::

flow.rate: [op]<number>

The rate can be matched exactly, or compared using the _op_ setting::

flow.rate:10000 # exactly 10000 bytes per second
flow.rate:>20000 # greater than 20000 bytes per second
flow.rate:>=30mb # greater than equal to 30mbps

Signature example::

pass tcp any any -> any any (msg:"Flow rate higher than 50kbps"; flow.rate:>50kb; sid:1; rev:1;)
Copy link
Member Author

@inashivb inashivb Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of mismatching units. Considering keeping the initial syntax something like flow.rate:>500kb,10 where 500kb is the number of bytes and 10 the seconds with which the rate will be calculated.

Copy link
Member Author

@inashivb inashivb Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some other ideas, none of which I'm a fan of:

flow.rate: 50kbps
flow.rate: 50kb,10  # document that first arg is bytes and second one the number of seconds
flow.rate: 50kb,10s
flow.rate: 50kb  # document that this means 50kbytes per second
flow.rate: bytes 50kb, seconds 10  # inspired by threshold keyword

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Between those, I like:

flow.rate: 50kb,10s

The most, I think. I imagine that any format would need documenting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also want packets instead of bytes like flow.rate: packets 50, seconds 10

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you also want milliseconds ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you also want milliseconds ?

@catenacyber I don't know yet. Do you think somebody might want to express rate as 10k bytes in 5ms?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know either, but maybe more granularity/expressivity is always better ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Shall keep that in mind. Thanks a lot for your valuable feedback, both! 🙇🏽‍♀️


flow.pkts_toclient
------------------

Expand Down
9 changes: 8 additions & 1 deletion rust/src/detect/tojson/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,11 @@ pub unsafe extern "C" fn SCDetectU32ToJson(
js: &mut JsonBuilder, du: &DetectUintData<u32>,
) -> bool {
return detect_uint_to_json(js, du).is_ok();
}
}

#[no_mangle]
pub unsafe extern "C" fn SCDetectU64ToJson(
js: &mut JsonBuilder, du: &DetectUintData<u64>,
) -> bool {
return detect_uint_to_json(js, du).is_ok();
}
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ noinst_HEADERS = \
detect-flowbits.h \
detect-flow.h \
detect-flow-age.h \
detect-flow-rate.h \
detect-flow-pkts.h \
detect-flowint.h \
detect-flowvar.h \
Expand Down Expand Up @@ -725,6 +726,7 @@ libsuricata_c_a_SOURCES = \
detect-flowbits.c \
detect-flow.c \
detect-flow-age.c \
detect-flow-rate.c \
detect-flow-pkts.c \
detect-flowint.c \
detect-flowvar.c \
Expand Down
8 changes: 8 additions & 0 deletions src/detect-engine-analyzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "detect-bytetest.h"
#include "detect-isdataat.h"
#include "detect-flow.h"
#include "detect-flow-rate.h"
#include "detect-tcp-flags.h"
#include "detect-tcp-ack.h"
#include "detect-ipopts.h"
Expand Down Expand Up @@ -957,6 +958,13 @@ static void DumpMatches(RuleAnalyzer *ctx, JsonBuilder *js, const SigMatchData *
jb_close(js);
break;
}
case DETECT_FLOW_RATE: {
const DetectFlowRate *fr = (const DetectFlowRate *)smd->ctx;
jb_open_object(js, "flow_rate");
SCDetectU64ToJson(js, fr->rate);
jb_close(js);
break;
}
}
jb_close(js);

Expand Down
2 changes: 2 additions & 0 deletions src/detect-engine-register.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
#include "detect-rev.h"
#include "detect-flow.h"
#include "detect-flow-age.h"
#include "detect-flow-rate.h"
#include "detect-flow-pkts.h"
#include "detect-requires.h"
#include "detect-tcp-window.h"
Expand Down Expand Up @@ -581,6 +582,7 @@ void SigTableSetup(void)
DetectReplaceRegister();
DetectFlowRegister();
DetectFlowAgeRegister();
DetectFlowRateRegister();
DetectFlowPktsToClientRegister();
DetectFlowPktsToServerRegister();
DetectFlowBytesToClientRegister();
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 @@ -131,6 +131,7 @@ enum DetectKeywordId {
DETECT_FLOW_PKTS_TO_SERVER,
DETECT_FLOW_BYTES_TO_CLIENT,
DETECT_FLOW_BYTES_TO_SERVER,
DETECT_FLOW_RATE,

DETECT_REQUIRES,

Expand Down
84 changes: 84 additions & 0 deletions src/detect-flow-rate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* 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.
*/

/**
* \file
*
* \author Shivani Bhardwaj <shivani@oisf.net>
*/

#include "suricata-common.h"
#include "rust.h"
#include "detect-flow-rate.h"
#include "detect-engine.h"
#include "detect-engine-prefilter.h"
#include "detect-engine-uint.h"
#include "detect-parse.h"

static int DetectFlowRateMatch(
DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
{
if (p->flow == NULL) {
return 0;
}

uint64_t age = SCTIME_SECS(p->flow->lastts) - SCTIME_SECS(p->flow->startts);
uint64_t rate = (p->flow->tosrcbytecnt + p->flow->todstbytecnt) / age;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Watch for division by zero ;-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed. an embarrassing mistake. thank you!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also shows lack of tests btw. Something like this should have easily been caught


const DetectFlowRate *expected = (const DetectFlowRate *)ctx;
return DetectU64Match(rate, expected->rate);
}

static int DetectFlowRateSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
DetectU64Data *du64 = DetectU64Parse(rawstr);
if (du64 == NULL)
return -1;

DetectFlowRate *fr = SCCalloc(1, sizeof(DetectFlowRate));
if (fr == NULL)
return -1;

fr->rate = du64;

if (SigMatchAppendSMToList(
de_ctx, s, DETECT_FLOW_RATE, (SigMatchCtx *)fr, DETECT_SM_LIST_MATCH) == NULL) {
return -1;
}
s->flags |= SIG_FLAG_REQUIRE_PACKET;

return 0;
}

static void DetectFlowRateFree(DetectEngineCtx *de_ctx, void *ptr)
{
DetectFlowRate *fr = (DetectFlowRate *)ptr;
if (fr != NULL) {
rs_detect_u64_free(fr->rate);
SCFree(fr);
}
}

void DetectFlowRateRegister(void)
{
sigmatch_table[DETECT_FLOW_RATE].name = "flow.rate";
sigmatch_table[DETECT_FLOW_RATE].desc = "match flow rate";
sigmatch_table[DETECT_FLOW_RATE].url = "/rules/flow-keywords.html#flow-rate";
sigmatch_table[DETECT_FLOW_RATE].Match = DetectFlowRateMatch;
sigmatch_table[DETECT_FLOW_RATE].Setup = DetectFlowRateSetup;
sigmatch_table[DETECT_FLOW_RATE].Free = DetectFlowRateFree;
}
27 changes: 27 additions & 0 deletions src/detect-flow-rate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* 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_FLOW_RATE_H
#define SURICATA_DETECT_FLOW_RATE_H

typedef struct DetectFlowRate_ {
DetectUintData_u64 *rate;
} DetectFlowRate;

void DetectFlowRateRegister(void);

#endif /* SURICATA_DETECT_FLOW_RATE_H */
Loading