diff --git a/plugins/ndpi/ndpi.c b/plugins/ndpi/ndpi.c index c8e5caed1c..3b4710534f 100644 --- a/plugins/ndpi/ndpi.c +++ b/plugins/ndpi/ndpi.c @@ -134,12 +134,11 @@ 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; } - 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,19 +146,15 @@ 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 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 +220,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); @@ -321,20 +316,19 @@ 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; - 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; + 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; - } + if (nDPIProtocolDataHasConflicts(data, them)) { + SCLogError("can't mix " + "positive ndpi-protocol match with negated"); + goto error; } } @@ -360,7 +354,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); @@ -461,20 +455,19 @@ 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; - 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; } } @@ -538,34 +531,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) diff --git a/rust/Makefile.am b/rust/Makefile.am index cbb1a9cb1b..d6720d7052 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -158,6 +158,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 67097e5edb..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, @@ -886,6 +903,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); } @@ -1027,11 +1057,25 @@ 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, ) -> ::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 { @@ -1958,6 +2002,18 @@ extern "C" { Free: ::std::option::Option, ) -> SCFlowStorageId; } +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; +} +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 a606981fe1..debd0d5aaf 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-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 *)); diff --git a/src/detect-engine-iponly.c b/src/detect-engine-iponly.c index d58686c53f..3f6689bdbf 100644 --- a/src/detect-engine-iponly.c +++ b/src/detect-engine-iponly.c @@ -1054,7 +1054,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-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-parse.c b/src/detect-parse.c index 045f660f04..d1c77b0ffe 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 * @@ -2616,6 +2667,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..fa89519d8f 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -84,8 +84,12 @@ 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); int WARN_UNUSED DetectSignatureSetMultiAppProto(Signature *s, const AppProto *alprotos); /* parse regex setup and free util funcs */ 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/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 */ 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..a36f6d7a5b --- /dev/null +++ b/src/packet-bindgen.h @@ -0,0 +1,29 @@ +/* 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 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); + +#endif /* !SURICATA_PACKET_BINDGEN_H */ diff --git a/src/packet.c b/src/packet.c index f2847c3bc4..fa4c6a5d02 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,76 @@ 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 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. + */ +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);