From a9e972613c51ef2f82338acd8f5956374264a0da Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 19 Sep 2026 19:35:51 -0600 Subject: [PATCH 01/11] packet: expose ipproto, flow and ts to rust - Rename PacketGetIPProto to SCPacketGetIPProto - Turn SCPacketGetIPProto into linkable function - SCPacketGetTimeAsParts based on existing flow function Ticket: 8597 --- rust/sys/src/sys.rs | 9 +++++++++ src/Makefile.am | 1 + src/alert-fastlog.c | 8 ++++---- src/bindgen.h | 2 ++ src/decode.h | 16 +--------------- src/defrag-hash.c | 4 ++-- src/detect-engine-iponly.c | 2 +- src/detect.c | 6 +++--- src/output-json.c | 6 +++--- src/packet-bindgen.h | 28 ++++++++++++++++++++++++++++ src/packet.c | 35 +++++++++++++++++++++++++++++++++++ src/source-pcap-file.c | 2 +- 12 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 src/packet-bindgen.h diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 67097e5edb..82e44af8f1 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -1958,6 +1958,15 @@ extern "C" { Free: ::std::option::Option, ) -> SCFlowStorageId; } +extern "C" { + pub fn SCPacketGetIPProto(p: *const Packet) -> u8; +} +extern "C" { + pub fn SCPacketGetFlow(p: *const Packet) -> *const Flow; +} +extern "C" { + pub fn SCPacketGetTimeAsParts(p: *const Packet, secs: *mut u64, usecs: *mut u64); +} #[doc = " \\brief Function type for thread intialization callbacks.\n\n Once registered by SCThreadRegisterInitCallback, this function will\n be called for every thread being initialized during Suricata\n startup.\n\n \\param tv The ThreadVars struct that has just been initialized.\n \\param user The user data provided when registering the callback."] pub type SCThreadInitCallbackFn = ::std::option::Option< unsafe extern "C" fn(tv: *mut ThreadVars, user: *mut ::std::os::raw::c_void), diff --git a/src/Makefile.am b/src/Makefile.am index 7e27764a93..3508e82054 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -386,6 +386,7 @@ noinst_HEADERS = \ output-streaming.h \ output-tx.h \ output.h \ + packet-bindgen.h \ packet-queue.h \ packet.h \ pkt-var.h \ diff --git a/src/alert-fastlog.c b/src/alert-fastlog.c index 2392a65049..6a8879ac86 100644 --- a/src/alert-fastlog.c +++ b/src/alert-fastlog.c @@ -125,15 +125,15 @@ int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p) char proto[16] = ""; const char *protoptr; - if (SCProtoNameValid(PacketGetIPProto(p))) { - protoptr = known_proto[PacketGetIPProto(p)]; + if (SCProtoNameValid(SCPacketGetIPProto(p))) { + protoptr = known_proto[SCPacketGetIPProto(p)]; } else { - snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, PacketGetIPProto(p)); + snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, SCPacketGetIPProto(p)); protoptr = proto; } uint16_t src_port_or_icmp = p->sp; uint16_t dst_port_or_icmp = p->dp; - if (PacketGetIPProto(p) == IPPROTO_ICMP || PacketGetIPProto(p) == IPPROTO_ICMPV6) { + if (SCPacketGetIPProto(p) == IPPROTO_ICMP || SCPacketGetIPProto(p) == IPPROTO_ICMPV6) { src_port_or_icmp = p->icmp_s.type; dst_port_or_icmp = p->icmp_s.code; } diff --git a/src/bindgen.h b/src/bindgen.h index 7432437680..ffa5b465f6 100644 --- a/src/bindgen.h +++ b/src/bindgen.h @@ -64,6 +64,8 @@ #include "flow-callbacks.h" #include "flow-storage.h" +#include "packet-bindgen.h" + #include "thread-callbacks.h" #include "thread-storage.h" diff --git a/src/decode.h b/src/decode.h index fb7b1466ef..acefbc4e3b 100644 --- a/src/decode.h +++ b/src/decode.h @@ -32,6 +32,7 @@ #include "threadvars.h" #include "util-debug.h" #include "decode-events.h" +#include "packet-bindgen.h" #include "util-exception-policy-types.h" #include "util-datalink.h" #ifdef PROFILING @@ -734,21 +735,6 @@ static inline IPV4Hdr *PacketSetIPV4(Packet *p, const uint8_t *buf) return p->l3.hdrs.ip4h; } -/* Retrieve proto regardless of IP version */ -static inline uint8_t PacketGetIPProto(const Packet *p) -{ - if (p->proto != 0) { - return p->proto; - } - if (PacketIsIPv4(p)) { - const IPV4Hdr *hdr = PacketGetIPv4(p); - return IPV4_GET_RAW_IPPROTO(hdr); - } else if (PacketIsIPv6(p)) { - return IPV6_GET_L4PROTO(p); - } - return 0; -} - static inline uint8_t PacketGetIPv4IPProto(const Packet *p) { if (PacketGetIPv4(p)) { diff --git a/src/defrag-hash.c b/src/defrag-hash.c index a9fa43ab4a..41718b2134 100644 --- a/src/defrag-hash.c +++ b/src/defrag-hash.c @@ -137,7 +137,7 @@ static void DefragTrackerInit(DefragTracker *dt, Packet *p) dt->id = IPV6_EXTHDR_GET_FH_ID(p); dt->af = AF_INET6; } - dt->proto = PacketGetIPProto(p); + dt->proto = SCPacketGetIPProto(p); memcpy(&dt->vlan_id[0], &p->vlan_id[0], sizeof(dt->vlan_id)); dt->policy = DefragGetOsPolicy(p); dt->host_timeout = DefragPolicyGetHostTimeout(p); @@ -430,7 +430,7 @@ static inline uint32_t DefragHashGetKey(Packet *p) #define CMP_DEFRAGTRACKER(d1, d2, id) \ (((CMP_ADDR(&(d1)->src_addr, &(d2)->src) && CMP_ADDR(&(d1)->dst_addr, &(d2)->dst)) || \ (CMP_ADDR(&(d1)->src_addr, &(d2)->dst) && CMP_ADDR(&(d1)->dst_addr, &(d2)->src))) && \ - (d1)->proto == PacketGetIPProto(d2) && (d1)->id == (id) && \ + (d1)->proto == SCPacketGetIPProto(d2) && (d1)->id == (id) && \ (d1)->vlan_id[0] == (d2)->vlan_id[0] && (d1)->vlan_id[1] == (d2)->vlan_id[1] && \ (d1)->vlan_id[2] == (d2)->vlan_id[2]) diff --git a/src/detect-engine-iponly.c b/src/detect-engine-iponly.c index ae893753b1..11e57efd3a 100644 --- a/src/detect-engine-iponly.c +++ b/src/detect-engine-iponly.c @@ -1053,7 +1053,7 @@ void IPOnlyMatchPacket(ThreadVars *tv, const DetectEngineCtx *de_ctx, continue; } - if (DetectProtoContainsProto(s->proto, PacketGetIPProto(p)) == 0) { + if (DetectProtoContainsProto(s->proto, SCPacketGetIPProto(p)) == 0) { SCLogDebug("proto didn't match"); continue; } diff --git a/src/detect.c b/src/detect.c index 4e5e41d5f4..552f489074 100644 --- a/src/detect.c +++ b/src/detect.c @@ -316,7 +316,7 @@ const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, /* select the flow_gh */ const int dir = (p->flowflags & FLOW_PKT_TOCLIENT) == 0; - int proto = PacketGetIPProto(p); + int proto = SCPacketGetIPProto(p); if (proto == IPPROTO_TCP) { DetectPort *list = de_ctx->flow_gh[dir].tcp; SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", de_ctx->flow_gh[1].tcp, @@ -456,7 +456,7 @@ static inline void DetectRunGetRuleGroup( bool use_flow_sgh = false; /* Get the stored sgh from the flow (if any). Make sure we're not using * the sgh for icmp error packets part of the same stream. */ - if (PacketGetIPProto(p) == pflow->proto) { /* filter out icmp */ + if (SCPacketGetIPProto(p) == pflow->proto) { /* filter out icmp */ PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH); if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) { sgh = pflow->sgh_toserver; @@ -551,7 +551,7 @@ static inline bool DetectRunInspectRuleHeader( SCLogDebug("ip version didn't match"); return false; } - if (DetectProtoContainsProto(s->proto, PacketGetIPProto(p)) == 0) { + if (DetectProtoContainsProto(s->proto, SCPacketGetIPProto(p)) == 0) { SCLogDebug("proto didn't match"); if (PacketIsEthernet(p) && (s_proto_flags & (DETECT_PROTO_ETHERNET | DETECT_PROTO_ARP))) { diff --git a/src/output-json.c b/src/output-json.c index a8c8c30a06..5253d70bd3 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -557,10 +557,10 @@ void JsonAddrInfoInit(const Packet *p, enum SCOutputJsonLogDirection dir, JsonAd break; } - if (SCProtoNameValid(PacketGetIPProto(p))) { - strlcpy(addr->proto, known_proto[PacketGetIPProto(p)], sizeof(addr->proto)); + if (SCProtoNameValid(SCPacketGetIPProto(p))) { + strlcpy(addr->proto, known_proto[SCPacketGetIPProto(p)], sizeof(addr->proto)); } else { - snprintf(addr->proto, sizeof(addr->proto), "%" PRIu32, PacketGetIPProto(p)); + snprintf(addr->proto, sizeof(addr->proto), "%" PRIu32, SCPacketGetIPProto(p)); } } diff --git a/src/packet-bindgen.h b/src/packet-bindgen.h new file mode 100644 index 0000000000..c1eaac494e --- /dev/null +++ b/src/packet-bindgen.h @@ -0,0 +1,28 @@ +/* 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. + */ + +#ifndef SURICATA_PACKET_BINDGEN_H +#define SURICATA_PACKET_BINDGEN_H + +typedef struct Packet_ Packet; +typedef struct Flow_ Flow; + +uint8_t SCPacketGetIPProto(const Packet *p); +const Flow *SCPacketGetFlow(const Packet *p); +void SCPacketGetTimeAsParts(const Packet *p, uint64_t *secs, uint64_t *usecs); + +#endif /* !SURICATA_PACKET_BINDGEN_H */ diff --git a/src/packet.c b/src/packet.c index f2847c3bc4..27efc2c67f 100644 --- a/src/packet.c +++ b/src/packet.c @@ -16,6 +16,7 @@ */ #include "packet.h" +#include "packet-bindgen.h" #include "pkt-var.h" #include "flow.h" #include "host.h" @@ -209,3 +210,37 @@ inline void SCPacketSetSource(Packet *p, enum PktSrcEnum source) { p->pkt_src = (uint8_t)source; } + +/** + * \brief Get IP proto regardless of IP version. + */ +inline uint8_t SCPacketGetIPProto(const Packet *p) +{ + if (p->proto != 0) { + return p->proto; + } + if (PacketIsIPv4(p)) { + const IPV4Hdr *hdr = PacketGetIPv4(p); + return IPV4_GET_RAW_IPPROTO(hdr); + } else if (PacketIsIPv6(p)) { + return IPV6_GET_L4PROTO(p); + } + return 0; +} + +/** + * \brief Get the flow associated with the packet. + */ +const Flow *SCPacketGetFlow(const Packet *p) +{ + return p == NULL ? NULL : p->flow; +} + +/** + * \brief Get packet timestamp as individual values. + */ +void SCPacketGetTimeAsParts(const Packet *p, uint64_t *secs, uint64_t *usecs) +{ + *secs = (uint64_t)SCTIME_SECS(p->ts); + *usecs = (uint64_t)SCTIME_USECS(p->ts); +} diff --git a/src/source-pcap-file.c b/src/source-pcap-file.c index 83ba9edeba..873df04a8c 100644 --- a/src/source-pcap-file.c +++ b/src/source-pcap-file.c @@ -472,7 +472,7 @@ static TmEcode DecodePcapFile(ThreadVars *tv, Packet *p, void *data) #ifdef DEBUG BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR); #endif - DEBUG_VALIDATE_BUG_ON(p->proto != PacketGetIPProto(p)); + DEBUG_VALIDATE_BUG_ON(p->proto != SCPacketGetIPProto(p)); PacketDecodeFinalize(tv, dtv, p); From e7f50a765a144a6c22fd989aafe3d730b6ac490c Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 19 Sep 2026 19:37:07 -0600 Subject: [PATCH 02/11] ndpi: use accessors for ipproto and flow from packet Ticket: 8597 --- plugins/ndpi/ndpi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index c8e5caed1c..488ecf923a 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -134,7 +134,7 @@ static void OnFlowUpdate(ThreadVars *tv, Flow *f, Packet *p, void *_data) /* Ignore packets that have a different protocol than the * flow. This can happen with ICMP unreachable packets. */ - if (p->proto != flow_proto) { + if (SCPacketGetIPProto(p) != flow_proto) { return; } @@ -159,7 +159,9 @@ static void OnFlowUpdate(ThreadVars *tv, Flow *f, Packet *p, void *_data) } if (!flowctx->detection_completed && ip_ptr != NULL && ip_len > 0) { - uint64_t time_ms = ((uint64_t)p->ts.secs) * 1000 + p->ts.usecs / 1000; + uint64_t secs = 0, usecs = 0; + SCPacketGetTimeAsParts(p, &secs, &usecs); + uint64_t time_ms = secs * 1000 + usecs / 1000; SCLogDebug("Performing nDPI detection..."); @@ -225,7 +227,7 @@ static int DetectnDPIProtocolPacketMatch( { SCEnter(); - const Flow *f = p->flow; + const Flow *f = SCPacketGetFlow(p); if (f == NULL) { SCLogDebug("packet %" PRIu64 ": no flow", PcapPacketCntGet(p)); SCReturnInt(0); @@ -360,7 +362,7 @@ static int DetectnDPIRiskPacketMatch( { SCEnter(); - const Flow *f = p->flow; + const Flow *f = SCPacketGetFlow(p); if (f == NULL) { SCLogDebug("packet %" PRIu64 ": no flow", PcapPacketCntGet(p)); SCReturnInt(0); From cb7b659b896465546879bf235f9db9def4045c01 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Thu, 15 Oct 2026 17:27:14 -0600 Subject: [PATCH 03/11] packet: add SCPacketGetL3 accessor for L3 packet data Add SCPacketGetL3 to return a pointer to the L3 header and the length of the L3 packet, regardless of the L3 type. For IPv4 and IPv6 the length comes from the IP header, including the IPv6 base header. For ARP it is the fixed ARP header length. The length is returned as a 32 bit value as an IPv6 packet with the base header included can exceed 16 bits. Expose the function to Rust. Ticket: 8597 --- rust/sys/src/sys.rs | 3 +++ src/packet-bindgen.h | 1 + src/packet.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 82e44af8f1..a11ee0bc5f 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -1961,6 +1961,9 @@ extern "C" { extern "C" { pub fn SCPacketGetIPProto(p: *const Packet) -> u8; } +extern "C" { + pub fn SCPacketGetL3(p: *const Packet, len: *mut u32) -> *const u8; +} extern "C" { pub fn SCPacketGetFlow(p: *const Packet) -> *const Flow; } diff --git a/src/packet-bindgen.h b/src/packet-bindgen.h index c1eaac494e..a36f6d7a5b 100644 --- a/src/packet-bindgen.h +++ b/src/packet-bindgen.h @@ -22,6 +22,7 @@ typedef struct Packet_ Packet; typedef struct Flow_ Flow; uint8_t SCPacketGetIPProto(const Packet *p); +const uint8_t *SCPacketGetL3(const Packet *p, uint32_t *len); const Flow *SCPacketGetFlow(const Packet *p); void SCPacketGetTimeAsParts(const Packet *p, uint64_t *secs, uint64_t *usecs); diff --git a/src/packet.c b/src/packet.c index 27efc2c67f..fa4c6a5d02 100644 --- a/src/packet.c +++ b/src/packet.c @@ -228,6 +228,45 @@ inline uint8_t SCPacketGetIPProto(const Packet *p) return 0; } +/** + * \brief Get the L3 packet data, starting at the L3 header. + * + * The returned data is borrowed from p and is valid only while its packet + * buffer remains valid and unchanged. For IPv4 and IPv6 the length comes + * from the IP header and includes the IPv6 base header. For ARP it is the + * fixed ARP header length. Trailing padding is excluded. The decoder has + * already validated the length against the captured packet length. + * + * \param p Packet to inspect, or NULL. + * \param len Non-NULL output pointer, set to zero when no data is returned. + * \retval NULL The packet is NULL or has no decoded L3 layer. + */ +const uint8_t *SCPacketGetL3(const Packet *p, uint32_t *len) +{ + *len = 0; + if (p == NULL) + return NULL; + + switch (p->l3.type) { + case PACKET_L3_IPV4: { + const IPV4Hdr *ip4h = PacketGetIPv4(p); + *len = IPV4_GET_RAW_IPLEN(ip4h); + return (const uint8_t *)ip4h; + } + case PACKET_L3_IPV6: { + const IPV6Hdr *ip6h = PacketGetIPv6(p); + *len = IPV6_HEADER_LEN + IPV6_GET_RAW_PLEN(ip6h); + return (const uint8_t *)ip6h; + } + case PACKET_L3_ARP: + *len = ARP_HEADER_LEN; + return (const uint8_t *)PacketGetARP(p); + case PACKET_L3_UNKNOWN: + break; + } + return NULL; +} + /** * \brief Get the flow associated with the packet. */ From 4b653833c1f649f41034d7f66d04d1b38fd69e26 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Thu, 15 Oct 2026 17:27:14 -0600 Subject: [PATCH 04/11] ndpi: use SCPacketGetL3 to get the IP packet for nDPI Replace the inline IPv4/IPv6 header handling with SCPacketGetL3, and return early for packets that are not IPv4 or IPv6 as nDPI only handles IP packets. Ticket: 8597 --- plugins/ndpi/ndpi.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index 488ecf923a..76143c813d 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -138,8 +138,7 @@ static void OnFlowUpdate(ThreadVars *tv, Flow *f, Packet *p, void *_data) return; } - uint16_t ip_len = 0; - void *ip_ptr = NULL; + uint32_t ip_len = 0; struct NdpiThreadContext *threadctx = NdpiGetThreadContext(tv); struct NdpiFlowContext *flowctx = NdpiGetFlowContext(f); @@ -147,16 +146,10 @@ static void OnFlowUpdate(ThreadVars *tv, Flow *f, Packet *p, void *_data) return; if (flowctx == NULL || flowctx->ndpi_flow == NULL) return; + if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) + return; - if (PacketIsIPv4(p)) { - const IPV4Hdr *ip4h = PacketGetIPv4(p); - ip_len = IPV4_GET_RAW_IPLEN(ip4h); - ip_ptr = (void *)PacketGetIPv4(p); - } else if (PacketIsIPv6(p)) { - const IPV6Hdr *ip6h = PacketGetIPv6(p); - ip_len = IPV6_HEADER_LEN + IPV6_GET_RAW_PLEN(ip6h); - ip_ptr = (void *)PacketGetIPv6(p); - } + const uint8_t *ip_ptr = SCPacketGetL3(p, &ip_len); if (!flowctx->detection_completed && ip_ptr != NULL && ip_len > 0) { uint64_t secs = 0, usecs = 0; From a244b8fcd6714a032c960727c9fd8d73d3cfed7c Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 11:47:39 -0600 Subject: [PATCH 05/11] detect: expose packet match callback registration to Rust Add SCDetectHelperKeywordPacketMatchRegister as a companion to keyword registration, and expose it through the generated Rust bindings. Plugins can install a packet match callback without accessing sigmatch_table or changing the existing SCSigTableAppLiteElmt layout. Use case: nDPI wasn't using the helper functions due to missing Match parameter, and this allows for that without a more invasive change. Related to ticket: 7499 --- rust/sys/src/sys.rs | 13 +++++++++++++ src/detect-engine-helper.c | 12 ++++++++++++ src/detect-engine-helper.h | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index a11ee0bc5f..9a363caa89 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -886,6 +886,19 @@ extern "C" { extern "C" { pub fn SCDetectHelperKeywordRegister(kw: *const SCSigTableAppLiteElmt) -> u16; } +extern "C" { + pub fn SCDetectHelperKeywordPacketMatchRegister( + kwid: u16, + Match: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut DetectEngineThreadCtx, + arg2: *mut Packet, + arg3: *const Signature, + arg4: *const SigMatchCtx, + ) -> ::std::os::raw::c_int, + >, + ); +} extern "C" { pub fn SCDetectHelperKeywordAliasRegister(kwid: u16, alias: *const ::std::os::raw::c_char); } diff --git a/src/detect-engine-helper.c b/src/detect-engine-helper.c index 65ef028555..f6de45844c 100644 --- a/src/detect-engine-helper.c +++ b/src/detect-engine-helper.c @@ -202,6 +202,18 @@ uint16_t SCDetectHelperKeywordRegister(const SCSigTableAppLiteElmt *kw) return (uint16_t)keyword_id; } +/** + * \brief Register a packet match callback for an existing keyword. + * + * Must be called during keyword initialization, after successfully registering + * the keyword with SCDetectHelperKeywordRegister(). + */ +void SCDetectHelperKeywordPacketMatchRegister(uint16_t kwid, + int (*Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)) +{ + sigmatch_table[kwid].Match = Match; +} + void SCDetectHelperKeywordAliasRegister(uint16_t kwid, const char *alias) { sigmatch_table[kwid].alias = alias; diff --git a/src/detect-engine-helper.h b/src/detect-engine-helper.h index fb914f8577..f8eb5a5ac0 100644 --- a/src/detect-engine-helper.h +++ b/src/detect-engine-helper.h @@ -29,6 +29,8 @@ // type from flow.h with only forward declarations for bindgen typedef struct Flow_ Flow; +// type from decode.h with only a forward declaration for bindgen +typedef struct Packet_ Packet; // types from detect.h with only forward declarations for bindgen // could be #ifndef SURICATA_BINDGEN_H #include "detect.h" #endif typedef struct DetectEngineCtx_ DetectEngineCtx; @@ -79,6 +81,8 @@ typedef struct SCTransformTableElmt { int SCDetectHelperNewKeywordId(void); uint16_t SCDetectHelperKeywordRegister(const SCSigTableAppLiteElmt *kw); +void SCDetectHelperKeywordPacketMatchRegister(uint16_t kwid, + int (*Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)); void SCDetectHelperKeywordAliasRegister(uint16_t kwid, const char *alias); typedef struct SCJsonBuilder SCJsonBuilder; void SCDetectHelperKeywordJsonInfoRegister(uint16_t kwid, void (*cb)(struct SCJsonBuilder *)); From 4fc0cd8bfa94d425458b07673ed4e009733c8bc9 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 11:47:41 -0600 Subject: [PATCH 06/11] ndpi: use helpers to register packet keywords Register ndpi-protocol and ndpi-risk through SCDetectHelperKeywordRegister and install their packet callbacks with the new companion function. This removes direct sigmatch_table writes while retaining the keyword metadata, flags, setup functions, and match callbacks. Note: Check the registration helper's UINT16_MAX failure result before installing the packet callback. Related to ticket: 7499 --- plugins/ndpi/ndpi.c | 52 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index 76143c813d..4fec47768e 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -533,34 +533,36 @@ static void EveCallback(ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder static void NdpInitRiskKeyword(void) { - /* SCSigTableAppLiteElmt and SCDetectHelperKeywordRegister don't yet - * support all the fields required to register the nDPI keywords, - * missing the (packet) Match callback, - * so we'll just register with an empty keyword specifier to get - * the ID, then fill in the ID. */ - ndpi_protocol_keyword_id = SCDetectHelperNewKeywordId(); + const SCSigTableAppLiteElmt protocol_kw = { + .name = "ndpi-protocol", + .desc = "match on the detected nDPI protocol", + .url = "/rules/ndpi-protocol.html", + .flags = SIGMATCH_QUOTES_OPTIONAL | SIGMATCH_HANDLE_NEGATION, + .Setup = DetectnDPIProtocolSetup, + .Free = DetectnDPIProtocolFree, + }; + ndpi_protocol_keyword_id = SCDetectHelperKeywordRegister(&protocol_kw); + if (ndpi_protocol_keyword_id == UINT16_MAX) { + FatalError("Failed to register ndpi-protocol keyword"); + } + SCDetectHelperKeywordPacketMatchRegister( + ndpi_protocol_keyword_id, DetectnDPIProtocolPacketMatch); SCLogDebug("Registered new ndpi-protocol keyword with ID %" PRIu32, ndpi_protocol_keyword_id); - sigmatch_table[ndpi_protocol_keyword_id].name = "ndpi-protocol"; - sigmatch_table[ndpi_protocol_keyword_id].desc = "match on the detected nDPI protocol"; - sigmatch_table[ndpi_protocol_keyword_id].url = "/rules/ndpi-protocol.html"; - sigmatch_table[ndpi_protocol_keyword_id].Match = DetectnDPIProtocolPacketMatch; - sigmatch_table[ndpi_protocol_keyword_id].Setup = DetectnDPIProtocolSetup; - sigmatch_table[ndpi_protocol_keyword_id].Free = DetectnDPIProtocolFree; - sigmatch_table[ndpi_protocol_keyword_id].flags = - (SIGMATCH_QUOTES_OPTIONAL | SIGMATCH_HANDLE_NEGATION); - - ndpi_risk_keyword_id = SCDetectHelperNewKeywordId(); + const SCSigTableAppLiteElmt risk_kw = { + .name = "ndpi-risk", + .desc = "match on the detected nDPI risk", + .url = "/rules/ndpi-risk.html", + .flags = SIGMATCH_QUOTES_OPTIONAL | SIGMATCH_HANDLE_NEGATION, + .Setup = DetectnDPIRiskSetup, + .Free = DetectnDPIRiskFree, + }; + ndpi_risk_keyword_id = SCDetectHelperKeywordRegister(&risk_kw); + if (ndpi_risk_keyword_id == UINT16_MAX) { + FatalError("Failed to register ndpi-risk keyword"); + } + SCDetectHelperKeywordPacketMatchRegister(ndpi_risk_keyword_id, DetectnDPIRiskPacketMatch); SCLogDebug("Registered new ndpi-risk keyword with ID %" PRIu32, ndpi_risk_keyword_id); - - sigmatch_table[ndpi_risk_keyword_id].name = "ndpi-risk"; - sigmatch_table[ndpi_risk_keyword_id].desc = "match on the detected nDPI risk"; - sigmatch_table[ndpi_risk_keyword_id].url = "/rules/ndpi-risk.html"; - sigmatch_table[ndpi_risk_keyword_id].Match = DetectnDPIRiskPacketMatch; - sigmatch_table[ndpi_risk_keyword_id].Setup = DetectnDPIRiskSetup; - sigmatch_table[ndpi_risk_keyword_id].Free = DetectnDPIRiskFree; - sigmatch_table[ndpi_risk_keyword_id].flags = - (SIGMATCH_QUOTES_OPTIONAL | SIGMATCH_HANDLE_NEGATION); } static void NdpiInit(void) From 95585aa4e5e359054c5b891eee7e714860808f9d Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 15:19:34 -0600 Subject: [PATCH 07/11] detect: expose keyword negation to plugins and Rust Add SCDetectSignatureIsNegated so a keyword Setup callback can check if its option was negated without reading Signature internals, and expose it through the generated Rust bindings. Keywords registered with SIGMATCH_HANDLE_NEGATION get the '!' stripped from their option by the rule parser, which records the negation in Signature::init_data. Plugins, and keywords implemented in Rust, can't read that field as Signature is opaque to them. Related to ticket: 7499 --- rust/sys/src/sys.rs | 3 +++ src/detect-parse.c | 16 ++++++++++++++++ src/detect-parse.h | 1 + 3 files changed, 20 insertions(+) diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 9a363caa89..673b07346b 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -1045,6 +1045,9 @@ extern "C" { s: *mut Signature, alproto: AppProto, ) -> ::std::os::raw::c_int; } +extern "C" { + pub fn SCDetectSignatureIsNegated(s: *const Signature) -> bool; +} #[repr(u32)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum SCError { diff --git a/src/detect-parse.c b/src/detect-parse.c index d79642ffd0..34a8cc5ebb 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -2610,6 +2610,22 @@ int DetectSignatureSetMultiAppProto(Signature *s, const AppProto *alprotos) return 0; } +/** + * \brief Check if the keyword option being set up was negated. + * + * Only set for keywords registered with the SIGMATCH_HANDLE_NEGATION flag, + * in which case the leading '!' is stripped from the option value before it + * is passed to the keyword's Setup callback. + * + * \param s signature being set up + * + * \retval true if the option was prefixed with '!' + */ +bool SCDetectSignatureIsNegated(const Signature *s) +{ + return s->init_data->negated; +} + int SCDetectSignatureSetAppProto(Signature *s, AppProto alproto) { if (!AppProtoIsValid(alproto)) { diff --git a/src/detect-parse.h b/src/detect-parse.h index 196ead6606..757f4cecab 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -86,6 +86,7 @@ SigMatch *DetectGetLastSMByListPtr(const Signature *s, SigMatch *sm_list, ...); SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...); int WARN_UNUSED SCDetectSignatureSetAppProto(Signature *s, AppProto alproto); +bool SCDetectSignatureIsNegated(const Signature *s); int WARN_UNUSED DetectSignatureSetMultiAppProto(Signature *s, const AppProto *alprotos); /* parse regex setup and free util funcs */ From c92af118673a9ec08a82f65aba62fd63ec800318 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 15:19:34 -0600 Subject: [PATCH 08/11] ndpi: use SCDetectSignatureIsNegated for keyword negation Use the accessor instead of reading s->init_data->negated, removing the Signature struct dependency from the keyword setup functions. Related to ticket: 7499 --- plugins/ndpi/ndpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index 4fec47768e..52be62347d 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -316,7 +316,7 @@ static bool nDPIProtocolDataHasConflicts( static int DetectnDPIProtocolSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { - DetectnDPIProtocolData *data = DetectnDPIProtocolParse(arg, s->init_data->negated); + DetectnDPIProtocolData *data = DetectnDPIProtocolParse(arg, SCDetectSignatureIsNegated(s)); if (data == NULL) goto error; @@ -456,7 +456,7 @@ static bool nDPIRiskDataHasConflicts(const DetectnDPIRiskData *us, const Detectn static int DetectnDPIRiskSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { - DetectnDPIRiskData *data = DetectnDPIRiskParse(arg, s->init_data->negated); + DetectnDPIRiskData *data = DetectnDPIRiskParse(arg, SCDetectSignatureIsNegated(s)); if (data == NULL) goto error; From c2d192db92e4083ff9f19439f934f27db8f6a578 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 15:21:19 -0600 Subject: [PATCH 09/11] detect: expose opaque SigMatch lookup to plugins and Rust Add accessors so a keyword Setup callback can inspect earlier instances of its keyword in the signature being set up without depending on the Signature or SigMatch layout: - SCDetectSignatureGetLastSMByType: last SigMatch of a keyword in a list - SCSigMatchGetPrevByType: previous SigMatch of the same keyword - SCSigMatchGetCtx: the keyword context of a SigMatch This is what keywords like app-layer-protocol do to reject duplicate or conflicting options, by walking the match list directly. Plugins, and keywords implemented in Rust, can't do that as these types are opaque to them. The existing SCDetectGetLastSMFromLists is not a substitute as it ignores the packet match list once a sticky buffer is active in the rule, and gives no access to the context. The lookup is limited to the static lists from DetectSigmatchListEnum. A dynamic buffer list can have several instances in a signature, each with its own SigMatch chain, so the previous-SigMatch walk could not cover it, and DetectGetLastSMByListId would search every buffer rather than the requested one. Expose the new functions through the generated Rust bindings. Related to ticket: 7499 --- rust/sys/src/sys.rs | 11 ++++++++++ src/detect-parse.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/detect-parse.h | 3 +++ 3 files changed, 65 insertions(+) diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 673b07346b..72aa482fbe 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -1040,6 +1040,17 @@ extern "C" { extern "C" { pub fn SCDetectGetLastSMFromLists(s: *const Signature, ...) -> *mut SigMatch; } +extern "C" { + pub fn SCDetectSignatureGetLastSMByType( + s: *const Signature, list_id: ::std::os::raw::c_int, type_: u16, + ) -> *const SigMatch; +} +extern "C" { + pub fn SCSigMatchGetPrevByType(sm: *const SigMatch, type_: u16) -> *const SigMatch; +} +extern "C" { + pub fn SCSigMatchGetCtx(sm: *const SigMatch) -> *const SigMatchCtx; +} extern "C" { pub fn SCDetectSignatureSetAppProto( s: *mut Signature, alproto: AppProto, diff --git a/src/detect-parse.c b/src/detect-parse.c index 34a8cc5ebb..6e507aabcc 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -737,6 +737,57 @@ SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...) return sm_last; } +/** + * \brief Get the last SigMatch of keyword \a type in list \a list_id. + * + * Lets a keyword Setup callback inspect earlier instances of a keyword in + * the signature being set up, together with SCSigMatchGetPrevByType() and + * SCSigMatchGetCtx(), without depending on the SigMatch layout. + * + * Only the static lists from DetectSigmatchListEnum are supported. A dynamic + * buffer list can have several instances in a signature, each with its own + * SigMatch chain, so there is no single list to walk. + * + * \param s signature being set up + * \param list_id static list to search, e.g. DETECT_SM_LIST_MATCH + * \param type keyword id, as returned by SCDetectHelperKeywordRegister() + * + * \retval sm last SigMatch of \a type in the list, or NULL if there is none + * or \a list_id is not a static list + */ +const SigMatch *SCDetectSignatureGetLastSMByType(const Signature *s, int list_id, uint16_t type) +{ + DEBUG_VALIDATE_BUG_ON(list_id < 0 || list_id >= DETECT_SM_LIST_MAX); + if (list_id < 0 || list_id >= DETECT_SM_LIST_MAX) + return NULL; + return DetectGetLastSMByListId(s, list_id, type, -1); +} + +/** + * \brief Get the previous SigMatch of keyword \a type before \a sm. + * + * \param sm SigMatch to search before, must not be NULL + * \param type keyword id + * + * \retval sm previous SigMatch of \a type in the same list, or NULL if there + * is none + */ +const SigMatch *SCSigMatchGetPrevByType(const SigMatch *sm, uint16_t type) +{ + return SigMatchGetLastSMByType(sm->prev, type); +} + +/** + * \brief Get the keyword context of \a sm, as passed to + * SCSigMatchAppendSMToList() by the keyword's Setup callback. + * + * \param sm SigMatch, must not be NULL + */ +const SigMatchCtx *SCSigMatchGetCtx(const SigMatch *sm) +{ + return sm->ctx; +} + /** * \brief Returns the sm with the largest index (added latest) from this sig * diff --git a/src/detect-parse.h b/src/detect-parse.h index 757f4cecab..fa89519d8f 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -84,6 +84,9 @@ SigMatch *DetectGetLastSMFromMpmLists(const DetectEngineCtx *de_ctx, const Signa SigMatch *SCDetectGetLastSMFromLists(const Signature *s, ...); SigMatch *DetectGetLastSMByListPtr(const Signature *s, SigMatch *sm_list, ...); SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...); +const SigMatch *SCDetectSignatureGetLastSMByType(const Signature *s, int list_id, uint16_t type); +const SigMatch *SCSigMatchGetPrevByType(const SigMatch *sm, uint16_t type); +const SigMatchCtx *SCSigMatchGetCtx(const SigMatch *sm); int WARN_UNUSED SCDetectSignatureSetAppProto(Signature *s, AppProto alproto); bool SCDetectSignatureIsNegated(const Signature *s); From 4651b30bd32d81805c2e3a92fcd7f63932a2f439 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 15:21:19 -0600 Subject: [PATCH 10/11] ndpi: use SigMatch accessors for keyword conflict checks Walk earlier ndpi-protocol and ndpi-risk instances with SCDetectSignatureGetLastSMByType, SCSigMatchGetPrevByType and SCSigMatchGetCtx instead of reading the signature's match list and SigMatch fields directly. The list is now walked newest first, which makes no difference to the conflict checks. No Signature or SigMatch internals are accessed by the plugin anymore. Related to ticket: 7499 --- plugins/ndpi/ndpi.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index 52be62347d..3b4710534f 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -320,16 +320,15 @@ static int DetectnDPIProtocolSetup(DetectEngineCtx *de_ctx, Signature *s, const if (data == NULL) goto error; - SigMatch *tsm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; - for (; tsm != NULL; tsm = tsm->next) { - if (tsm->type == ndpi_protocol_keyword_id) { - const DetectnDPIProtocolData *them = (const DetectnDPIProtocolData *)tsm->ctx; - - if (nDPIProtocolDataHasConflicts(data, them)) { - SCLogError("can't mix " - "positive ndpi-protocol match with negated"); - goto error; - } + const SigMatch *tsm = + SCDetectSignatureGetLastSMByType(s, DETECT_SM_LIST_MATCH, ndpi_protocol_keyword_id); + for (; tsm != NULL; tsm = SCSigMatchGetPrevByType(tsm, ndpi_protocol_keyword_id)) { + const DetectnDPIProtocolData *them = (const DetectnDPIProtocolData *)SCSigMatchGetCtx(tsm); + + if (nDPIProtocolDataHasConflicts(data, them)) { + SCLogError("can't mix " + "positive ndpi-protocol match with negated"); + goto error; } } @@ -460,16 +459,15 @@ static int DetectnDPIRiskSetup(DetectEngineCtx *de_ctx, Signature *s, const char if (data == NULL) goto error; - SigMatch *tsm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; - for (; tsm != NULL; tsm = tsm->next) { - if (tsm->type == ndpi_risk_keyword_id) { - const DetectnDPIRiskData *them = (const DetectnDPIRiskData *)tsm->ctx; + const SigMatch *tsm = + SCDetectSignatureGetLastSMByType(s, DETECT_SM_LIST_MATCH, ndpi_risk_keyword_id); + for (; tsm != NULL; tsm = SCSigMatchGetPrevByType(tsm, ndpi_risk_keyword_id)) { + const DetectnDPIRiskData *them = (const DetectnDPIRiskData *)SCSigMatchGetCtx(tsm); - if (nDPIRiskDataHasConflicts(data, them)) { - SCLogError("can't mix " - "positive ndpi-risk match with negated"); - goto error; - } + if (nDPIRiskDataHasConflicts(data, them)) { + SCLogError("can't mix " + "positive ndpi-risk match with negated"); + goto error; } } From 1a73276acb2f01340b172f53da53e7a1693ef6a4 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sat, 17 Oct 2026 15:22:52 -0600 Subject: [PATCH 11/11] detect: expose the sigmatch list ids to Rust Move enum DetectSigmatchListEnum to detect-engine-register.h, which is already included by detect.h and the bindgen header, and allowlist it in the Rust bindings. Packet keywords implemented in Rust or in plugins need DETECT_SM_LIST_MATCH to add their SigMatch and to look up earlier ones, and had to hardcode its value. Related to ticket: 7499 --- rust/Makefile.am | 1 + rust/sys/src/sys.rs | 17 +++++++++++++++++ src/detect-engine-register.h | 29 +++++++++++++++++++++++++++++ src/detect.h | 29 ----------------------------- 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/rust/Makefile.am b/rust/Makefile.am index aa069e916e..db68c0dbf0 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -157,6 +157,7 @@ if HAVE_BINDGEN --allowlist-function 'SC.*' \ --allowlist-var 'SC.*' \ --allowlist-var 'SIGMATCH.*' \ + --allowlist-type 'DetectSigmatchListEnum' \ --opaque-type 'SCConfNode' \ --allowlist-type 'ThreadVars.?' \ --opaque-type 'ThreadVars.?' \ diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 72aa482fbe..6659bf931f 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -635,6 +635,23 @@ extern "C" { tv: *mut ThreadVars, p: *const Packet, f: *mut Flow, jb: *mut SCJsonBuilder, ); } +impl DetectSigmatchListEnum { + pub const DETECT_SM_LIST_DYNAMIC_START: DetectSigmatchListEnum = + DetectSigmatchListEnum::DETECT_SM_LIST_MAX; +} +#[repr(u32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum DetectSigmatchListEnum { + DETECT_SM_LIST_MATCH = 0, + DETECT_SM_LIST_PMATCH = 1, + DETECT_SM_LIST_BASE64_DATA = 2, + DETECT_SM_LIST_POSTMATCH = 3, + #[doc = "< post-detection tagging"] + DETECT_SM_LIST_TMATCH = 4, + DETECT_SM_LIST_SUPPRESS = 5, + DETECT_SM_LIST_THRESHOLD = 6, + DETECT_SM_LIST_MAX = 7, +} extern "C" { pub fn SCSigTablePreRegister( KeywordsRegister: ::std::option::Option, diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 8a247f3600..837cb22de4 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -298,6 +298,35 @@ enum DetectKeywordId { DETECT_TBLSIZE_STATIC, }; +/* holds the values for different possible lists in struct Signature. + * These codes are access points to particular lists in the array + * Signature->init_data->smlists[DETECT_SM_LIST_MAX]. */ +enum DetectSigmatchListEnum { + /* list for non-payload per packet matches, e.g. ttl, flow keyword */ + DETECT_SM_LIST_MATCH = 0, + /* list for payload and stream match */ + DETECT_SM_LIST_PMATCH, + + /* base64_data keyword uses some hardcoded logic so consider + * built-in + * TODO convert to inspect engine */ + DETECT_SM_LIST_BASE64_DATA, + + /* list for post match actions: flowbit set, flowint increment, etc */ + DETECT_SM_LIST_POSTMATCH, + + DETECT_SM_LIST_TMATCH, /**< post-detection tagging */ + + /* lists for alert thresholding and suppression */ + DETECT_SM_LIST_SUPPRESS, + DETECT_SM_LIST_THRESHOLD, + + DETECT_SM_LIST_MAX, + + /* start of dynamically registered lists */ + DETECT_SM_LIST_DYNAMIC_START = DETECT_SM_LIST_MAX, +}; + extern int DETECT_TBLSIZE; extern int DETECT_TBLSIZE_IDX; // step for reallocating sigmatch_table diff --git a/src/detect.h b/src/detect.h index 605b54693a..0d3671e2ea 100644 --- a/src/detect.h +++ b/src/detect.h @@ -110,35 +110,6 @@ extern const struct SignatureProperties signature_properties[SIG_TYPE_MAX]; -- Protocol */ -/* holds the values for different possible lists in struct Signature. - * These codes are access points to particular lists in the array - * Signature->init_data->smlists[DETECT_SM_LIST_MAX]. */ -enum DetectSigmatchListEnum { - /* list for non-payload per packet matches, e.g. ttl, flow keyword */ - DETECT_SM_LIST_MATCH = 0, - /* list for payload and stream match */ - DETECT_SM_LIST_PMATCH, - - /* base64_data keyword uses some hardcoded logic so consider - * built-in - * TODO convert to inspect engine */ - DETECT_SM_LIST_BASE64_DATA, - - /* list for post match actions: flowbit set, flowint increment, etc */ - DETECT_SM_LIST_POSTMATCH, - - DETECT_SM_LIST_TMATCH, /**< post-detection tagging */ - - /* lists for alert thresholding and suppression */ - DETECT_SM_LIST_SUPPRESS, - DETECT_SM_LIST_THRESHOLD, - - DETECT_SM_LIST_MAX, - - /* start of dynamically registered lists */ - DETECT_SM_LIST_DYNAMIC_START = DETECT_SM_LIST_MAX, -}; - /* used for Signature->list, which indicates which list * we're adding keywords to in cases of sticky buffers like * file_data */