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

Incomplete Port grouping redo/v1 #10162

Closed
wants to merge 6 commits into from
Closed
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
152 changes: 151 additions & 1 deletion src/detect-engine-build.c
Original file line number Diff line number Diff line change
@@ -50,6 +50,15 @@
#define DETECT_PGSCORE_RULE_NO_MPM 55 /* Rule does not contain MPM */
#define DETECT_PGSCORE_RULE_SYN_ONLY 33 /* Rule needs SYN check */

RB_GENERATE(PI, PortInterval, rb, PICompare);

int PICompare(PortInterval *a, PortInterval *b) {
if (a->port > b->port) {
SCReturnInt(1);
}
SCReturnInt(-1);
}

void SigCleanSignatures(DetectEngineCtx *de_ctx)
{
if (de_ctx == NULL)
@@ -1080,7 +1089,7 @@ static int PortIsWhitelisted(const DetectEngineCtx *de_ctx,
while (w) {
/* Make sure the whitelist port falls in the port range of a */
DEBUG_VALIDATE_BUG_ON(a->port > a->port2);
if (w->port >= a->port && w->port <= a->port2) {
if (a->port == w->port && w->port2 == a->port2) {
return 1;
}
w = w->next;
@@ -1129,6 +1138,98 @@ static int RuleSetWhitelist(Signature *s)
return wl;
}

static inline PortIntervals *InitPortIntervals(void)
{
PortIntervals *pis = SCCalloc(1, sizeof(PortIntervals));
if (pis == NULL) {
return NULL;
}

return pis;
}

static inline void PIFree(PortIntervals *pis)
{
PortInterval *pi = NULL, *safe = NULL;
RB_FOREACH_SAFE(pi, PI, &pis->tree, safe) {
PI_RB_REMOVE(&pis->tree, pi);
SCFree(pi);
}
pis->head = NULL;
}

static inline void FreePortIntervals(PortIntervals *pis)
{
if (pis) {
PIFree(pis);
SCFree(pis);
}
}

#if 1
static void PIPrintList(PortIntervals *pis)
{
PortInterval *pi = NULL;
RB_FOREACH(pi, PI, &pis->tree) {
SCLogNotice("pi => port: %d, port2: %d, sgh: %p, color: %d, max: %d", pi->port, pi->port2, pi->sh, RB_COLOR(pi, rb), pi->max);
}
}
#endif

static int PIInsert(PortIntervals *pis, struct PI *head, DetectPort *p)
{
// DEBUG_VALIDATE_BUG_ON(!RB_EMPTY(&pis->tree)); Should only be true in the first run
DEBUG_VALIDATE_BUG_ON(p->port > p->port2);

PortInterval *pi = SCCalloc(1, sizeof(*pi));
PortInterval *left = NULL;
PortInterval *right = NULL;
if (pi == NULL) {
return -1;
}
pi->port = p->port;
pi->port2 = p->port2;
pi->flags = p->flags;
// pi->sh = p->sh; // STODO this is likely wrong, we should prob copy the sgh
// update indeed wrong, ASAN flagged it
if (PI_RB_INSERT(&pis->tree, pi) != NULL) {
SCLogNotice("Node wasn't added to the tree: port: %d, port2: %d", pi->port, pi->port2);
SCFree(pi);
return SC_EINVAL;
}
left = RB_LEFT(pi, rb);
right = RB_RIGHT(pi, rb);
pi->max = pi->port2;
if (left && right)
pi->max = MAX(pi->max, MAX(left->max, right->max));
if (left)
pi->max = MAX(pi->max, left->max);
if (right)
pi->max = MAX(pi->max, right->max);
return SC_OK;
}

static bool PISearchOverlap(uint16_t port, uint16_t port2, struct PI *head, SigGroupHead **sgh_array)
{
PortInterval *ptr = RB_ROOT(head);
PortInterval *left = NULL;
PortInterval *right = NULL;
bool overlaps = false;
while (ptr) {
SCLogNotice("comparing with port: %d, port2: %d", ptr->port, ptr->port2);
left = RB_LEFT(ptr, rb);
right = RB_RIGHT(ptr, rb);
SCLogNotice("Left: [%d, %d]; Right: [%d, %d]", left->port, left->port2, right->port, right->port2);
// STODO following is incomplete, untested
if (port >= ptr->port && port2 < ptr->max && port2 < ptr->port2) {
// Should check left
} else {
// Should check right
}
}
return overlaps;
}

int CreateGroupedPortList(DetectEngineCtx *de_ctx, DetectPort *port_list, DetectPort **newhead, uint32_t unique_groups, int (*CompareFunc)(DetectPort *, DetectPort *), uint32_t max_idx);
int CreateGroupedPortListCmpCnt(DetectPort *a, DetectPort *b);

@@ -1138,10 +1239,13 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u
* rules. Each object will have a SGH with the sigs added
* that belong to the SGH. */
DetectPortHashInit(de_ctx);
PortIntervals *pis = InitPortIntervals();

uint32_t max_idx = 0;
const Signature *s = de_ctx->sig_list;
DetectPort *list = NULL;
bool unique_port_points[65536] = { false };
uint16_t size_unique_port_arr = 0;
while (s) {
/* IP Only rules are handled separately */
if (s->type == SIG_TYPE_IPONLY)
@@ -1194,6 +1298,18 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u
SigGroupHeadAppendSig(de_ctx, &tmp2->sh, s);
tmp2->sh->init->score = pwl;
DetectPortHashAdd(de_ctx, tmp2);
if (!unique_port_points[tmp2->port]) {
unique_port_points[tmp2->port] = true;
size_unique_port_arr++;
}
if (!unique_port_points[tmp2->port2]) {
unique_port_points[tmp2->port2] = true;
size_unique_port_arr++;
}
int ret = 0;
if ((ret = PIInsert(pis, &pis->tree, tmp2)) != SC_OK) {
SCLogNotice("ret: %d", ret);
}
}

p = p->next;
@@ -1203,6 +1319,36 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u
s = s->next;
}

uint16_t *final_unique_points = (uint16_t *)SCCalloc(size_unique_port_arr, sizeof(uint16_t));
for (uint16_t i = 0, j = 0; i < 65535; i++) {
DEBUG_VALIDATE_BUG_ON(j > size_unique_port_arr);
if (unique_port_points[i]) {
final_unique_points[j++] = i;
}
}

#if 0
for (uint16_t i = 0; i < size_unique_port_arr; i++) {
SCLogNotice("arr[%d] := %d", i, final_unique_points[i]);
}
#endif
// free SGH stuff
for (uint16_t i = 0; i < size_unique_port_arr; i++) {
uint16_t port = final_unique_points[i];
// STODO fix interval overlap on boundaries
uint16_t port2 = final_unique_points[i + 1];
SigGroupHead *sgh_array = NULL;
bool overlaps = PISearchOverlap(port, port2, &pis->tree, &sgh_array);
if (overlaps) {
#if 0
// STODO move all this to detect-engine-port.c
DetectPort *dp = DetectPortInit();
dp->port = port;
dp->port2 = port2;
SigGroupHeadCopySigs(de_ctx, sgh_array, &dp->sh);
#endif
}
}
/* step 2: create a list of DetectPort objects */
HashListTableBucket *htb = NULL;
for (htb = HashListTableGetListHead(de_ctx->dport_hash_table);
@@ -1272,6 +1418,10 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u
ipproto == 6 ? "TCP" : "UDP",
direction == SIG_FLAG_TOSERVER ? "toserver" : "toclient",
cnt, own, ref);
#if 1
PIPrintList(pis);
#endif
FreePortIntervals(pis);
return list;
}

22 changes: 22 additions & 0 deletions src/detect.h
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@
#include "detect-metadata.h"
#include "detect-engine-register.h"

#include "tree.h"
#include "util-prefilter.h"
#include "util-mpm.h"
#include "util-spm.h"
@@ -228,6 +229,27 @@ typedef struct DetectPort_ {
struct DetectPort_ *next;
} DetectPort;

/** \brief Port Interval structure */
typedef struct PortInterval {
uint16_t port;
uint16_t port2;
uint16_t max;
uint8_t flags;
struct SigGroupHead_ *sh;
RB_ENTRY(PortInterval) rb;
} __attribute__((__packed__)) PortInterval;

int PICompare(struct PortInterval *a, struct PortInterval *b);

/* red-black tree prototype for Port Groups */
RB_HEAD(PI, PortInterval);
RB_PROTOTYPE(PI, PortInterval, rb, PICompare);

typedef struct PortIntervals_ {
struct PI tree; /* red black tree of Port Intervals */
PortInterval *head; /* rbtree head; should always be same as RB_MIN */
} PortIntervals;
inashivb marked this conversation as resolved.
Show resolved Hide resolved

/* Signature flags */
/** \note: additions should be added to the rule analyzer as well */