From 855f49b649c7c83a4c0c68f3553e6a42006bd4e1 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Tue, 24 Mar 2026 10:35:30 +0100 Subject: [PATCH] detect/sctp: add sctp.chunk_cnt keyword Add a U8 numeric keyword to match the number of SCTP chunks parsed in a packet with prefilter support. Ticket #4251 --- src/Makefile.am | 2 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-sctp-chunk-cnt.c | 122 +++++++++++++++++++++++++++++++++++ src/detect-sctp-chunk-cnt.h | 27 ++++++++ 5 files changed, 154 insertions(+) create mode 100644 src/detect-sctp-chunk-cnt.c create mode 100644 src/detect-sctp-chunk-cnt.h diff --git a/src/Makefile.am b/src/Makefile.am index 35d5ce145f..2bc3c58d50 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -229,6 +229,7 @@ noinst_HEADERS = \ detect-igmphdr.h \ detect-igmp-type.h \ detect-sctphdr.h \ + detect-sctp-chunk-cnt.h \ detect-sctp-chunk-type.h \ detect-ipaddr.h \ detect-ipopts.h \ @@ -807,6 +808,7 @@ libsuricata_c_a_SOURCES = \ detect-igmphdr.c \ detect-igmp-type.c \ detect-sctphdr.c \ + detect-sctp-chunk-cnt.c \ detect-sctp-chunk-type.c \ detect-ipaddr.c \ detect-ipopts.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 6bc14dfe0b..135795d358 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -157,6 +157,7 @@ #include "detect-igmp-type.h" #include "detect-sctphdr.h" #include "detect-sctp-chunk-type.h" +#include "detect-sctp-chunk-cnt.h" #include "detect-urilen.h" #include "detect-bsize.h" #include "detect-detection-filter.h" @@ -680,6 +681,7 @@ void SigTableSetup(void) DetectIGMPTypeRegister(); DetectSCTPHdrRegister(); DetectSCTPChunkTypeRegister(); + DetectSCTPChunkCntRegister(); DetectTlsRegister(); DetectTlsValidityRegister(); DetectTlsVersionRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index ca77a229c8..814643b04e 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -53,6 +53,7 @@ enum DetectKeywordId { DETECT_IGMP_TYPE, DETECT_SCTPHDR, DETECT_SCTP_CHUNK_TYPE, + DETECT_SCTP_CHUNK_CNT, DETECT_DSIZE, DETECT_FLOW, diff --git a/src/detect-sctp-chunk-cnt.c b/src/detect-sctp-chunk-cnt.c new file mode 100644 index 0000000000..cd54e6fda4 --- /dev/null +++ b/src/detect-sctp-chunk-cnt.c @@ -0,0 +1,122 @@ +/* Copyright (C) 2026 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 + * + * Implements sctp.chunk_cnt keyword + * + * Author: Giuseppe Longo + */ + +#include "suricata-common.h" +#include "decode.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine-prefilter-common.h" +#include "detect-engine-build.h" + +#include "detect-sctp-chunk-cnt.h" +#include "detect-engine-uint.h" + +#include "util-byte.h" +#include "util-debug.h" + +static int DetectSCTPChunkCntMatch( + DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); +static int DetectSCTPChunkCntSetup(DetectEngineCtx *, Signature *, const char *); +void DetectSCTPChunkCntFree(DetectEngineCtx *, void *); + +static int PrefilterSetupSCTPChunkCnt(DetectEngineCtx *de_ctx, SigGroupHead *sgh); +static bool PrefilterSCTPChunkCntIsPrefilterable(const Signature *s); + +void DetectSCTPChunkCntRegister(void) +{ + sigmatch_table[DETECT_SCTP_CHUNK_CNT].name = "sctp.chunk_cnt"; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].desc = "match on the SCTP chunk count"; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].url = "/rules/sctp-keywords.html#sctp-chunk-cnt"; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].Match = DetectSCTPChunkCntMatch; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].Setup = DetectSCTPChunkCntSetup; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].Free = DetectSCTPChunkCntFree; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].flags = SIGMATCH_INFO_UINT8; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].SupportsPrefilter = PrefilterSCTPChunkCntIsPrefilterable; + sigmatch_table[DETECT_SCTP_CHUNK_CNT].SetupPrefilter = PrefilterSetupSCTPChunkCnt; +} + +static int DetectSCTPChunkCntMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p)); + + if (!PacketIsSCTP(p)) { + return 0; + } + + uint8_t val = p->l4.vars.sctp.chunk_cnt; + const DetectU8Data *data = (const DetectU8Data *)ctx; + return DetectU8Match(val, data); +} + +static int DetectSCTPChunkCntSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + DetectU8Data *data = DetectU8Parse(str); + if (data == NULL) + return -1; + + if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_SCTP_CHUNK_CNT, (SigMatchCtx *)data, + DETECT_SM_LIST_MATCH) == NULL) { + DetectSCTPChunkCntFree(de_ctx, data); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; +} + +void DetectSCTPChunkCntFree(DetectEngineCtx *de_ctx, void *ptr) +{ + DetectU8Data *data = (DetectU8Data *)ptr; + SCDetectU8Free(data); +} + +static void PrefilterPacketSCTPChunkCntMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +{ + DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p)); + + if (PacketIsSCTP(p)) { + uint8_t val = p->l4.vars.sctp.chunk_cnt; + const PrefilterPacketU8HashCtx *h = pectx; + const SigsArray *sa = h->array[val]; + if (sa) { + PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt); + } + } +} + +static int PrefilterSetupSCTPChunkCnt(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_SCTP_CHUNK_CNT, + SIG_MASK_REQUIRE_REAL_PKT, PrefilterPacketU8Set, PrefilterPacketU8Compare, + PrefilterPacketSCTPChunkCntMatch); +} + +static bool PrefilterSCTPChunkCntIsPrefilterable(const Signature *s) +{ + return PrefilterIsPrefilterableById(s, DETECT_SCTP_CHUNK_CNT); +} diff --git a/src/detect-sctp-chunk-cnt.h b/src/detect-sctp-chunk-cnt.h new file mode 100644 index 0000000000..1c9e7620b6 --- /dev/null +++ b/src/detect-sctp-chunk-cnt.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2026 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 + */ + +#ifndef SURICATA_DETECT_SCTP_CHUNK_CNT_H +#define SURICATA_DETECT_SCTP_CHUNK_CNT_H + +void DetectSCTPChunkCntRegister(void); + +#endif /* SURICATA_DETECT_SCTP_CHUNK_CNT_H */