suricata
detect.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2025 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Victor Julien <victor@inliniac.net>
22 *
23 * Basic detection engine
24 */
25
26#include "suricata-common.h"
27#include "suricata.h"
28
29#include "decode.h"
30#include "packet.h"
31#include "flow.h"
32#include "stream-tcp.h"
33#include "app-layer.h"
34#include "app-layer-parser.h"
35#include "app-layer-frames.h"
36
37#include "detect.h"
38#include "detect-dsize.h"
39#include "detect-engine.h"
40#include "detect-engine-build.h"
41#include "detect-engine-frame.h"
43
44#include "detect-engine-alert.h"
47#include "detect-engine-proto.h"
48#include "detect-engine-port.h"
49#include "detect-engine-mpm.h"
53#include "detect-engine-state.h"
55
57#include "detect-engine-event.h"
58
59#include "detect-filestore.h"
60#include "detect-flowvar.h"
61#include "detect-replace.h"
62
63#include "util-validate.h"
64#include "util-detect.h"
65#include "util-profiling.h"
66
67#include "action-globals.h"
68
69typedef struct DetectRunScratchpad {
71 const uint8_t flow_flags; /* flow/state flags: STREAM_* */
73 /**
74 * Either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
75 *
76 * ACTION_DROP means the default policy of drop:packet is applied
77 * ACTION_ACCEPT means the default policy of accept:hook is applied
78 */
79 const uint8_t default_action;
82
83/* prototypes */
84static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
85 DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
86 const uint8_t default_action);
87static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
88 DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p);
89static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx,
90 Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch);
91static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
92 DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch);
93static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
94 const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
95 Flow *const pflow, const DetectRunScratchpad *scratch);
96static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx,
97 DetectEngineThreadCtx *det_ctx, Packet *p,
98 Flow *f, DetectRunScratchpad *scratch);
99static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
100 Packet *p, Flow *f, DetectRunScratchpad *scratch);
101static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
102 DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
103 DetectRunScratchpad *scratch);
104static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
105 Packet *p, Flow * const pflow);
106static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p);
107
108/** \internal
109 */
110static void DetectRun(ThreadVars *th_v,
112 Packet *p)
113{
114 SCEnter();
115 SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s pkt_src %s", p->pcap_cnt,
116 p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
117 : "noflow",
118 PktSrcToString(p->pkt_src));
119
120 /* Load the Packet's flow early, even though it might not be needed.
121 * Mark as a constant pointer, although the flow itself can change. */
122 Flow * const pflow = p->flow;
123
124 DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
125
126 /* run the IPonly engine */
127 DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
128
129 /* get our rule group */
130 DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch);
131 /* if we didn't get a sig group head, we
132 * have nothing to do.... */
133 if (scratch.sgh == NULL) {
134 SCLogDebug("no sgh for this packet, nothing to match against");
135 goto end;
136 }
137
138 /* run the prefilters for packets */
139 DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
140
142 /* inspect the rules against the packet */
143 const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
145
146 /* Only FW rules will already have set the action, IDS rules go through PacketAlertFinalize
147 *
148 * If rules told us to drop or accept:packet/accept:flow, we skip app_filter and app_td.
149 *
150 * accept:hook won't have set the pkt_policy, so we simply continue.
151 *
152 * TODO what about app state progression, cleanup and such? */
153 if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
154 goto end;
155 }
156
157 /* run tx/state inspection. Don't call for ICMP error msgs. */
158 if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
159 if (p->proto == IPPROTO_TCP) {
160 if ((p->flags & PKT_STREAM_EST) == 0) {
161 SCLogDebug("packet %" PRIu64 ": skip tcp non-established", p->pcap_cnt);
162 DetectRunAppendDefaultAccept(det_ctx, p);
163 goto end;
164 }
165 const TcpSession *ssn = p->flow->protoctx;
166 bool setting_nopayload = p->flow->alparser &&
170 // we may be right after disabling app-layer (ssh)
171 if (ssn &&
172 ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
173 // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
174 DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
175 // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
176 }
177 // no update to transactions
178 if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
179 ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
180 (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
181 SCLogDebug("packet %" PRIu64 ": no app-layer update", p->pcap_cnt);
182 DetectRunAppendDefaultAccept(det_ctx, p);
183 goto end;
184 }
185 } else if (p->proto == IPPROTO_UDP) {
186 DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
187 }
188
190 DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
192 /* see if we need to increment the inspect_id and reset the de_state */
195 pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL));
197 } else {
198 SCLogDebug("packet %" PRIu64 ": no flow / app-layer", p->pcap_cnt);
199 DetectRunAppendDefaultAccept(det_ctx, p);
200 }
201
202end:
203 DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
204
205 DetectRunCleanup(det_ctx, p, pflow);
206 SCReturn;
207}
208
209/** \internal
210 */
211static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx,
212 DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p)
213{
214 SCEnter();
215 SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s pkt_src %s", p->pcap_cnt,
216 p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
217 : "noflow",
218 PktSrcToString(p->pkt_src));
219
220 /* Load the Packet's flow early, even though it might not be needed.
221 * Mark as a constant pointer, although the flow itself can change. */
222 Flow *const pflow = p->flow;
223
224 DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_ACCEPT);
225 scratch.sgh = sgh;
226
227 /* if we didn't get a sig group head, we
228 * have nothing to do.... */
229 if (scratch.sgh == NULL) {
230 SCLogDebug("no sgh for this packet, nothing to match against");
231 goto end;
232 }
233
234 /* run the prefilters for packets */
235 DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
236
237 // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES); // TODO
238 /* inspect the rules against the packet */
239 const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
240 // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
241 if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
242 goto end;
243 }
244
245end:
246 DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
247
248 DetectRunCleanup(det_ctx, p, pflow);
249 SCReturn;
250}
251
252static void DetectRunPostMatch(ThreadVars *tv,
253 DetectEngineThreadCtx *det_ctx, Packet *p,
254 const Signature *s)
255{
256 /* run the packet match functions */
258 if (smd != NULL) {
260
261 SCLogDebug("running match functions, sm %p", smd);
262
263 while (1) {
265 (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
266 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
267 if (smd->is_last)
268 break;
269 smd++;
270 }
271 }
272}
273
274/**
275 * \brief Get the SigGroupHead for a packet.
276 *
277 * \param de_ctx detection engine context
278 * \param p packet
279 *
280 * \retval sgh the SigGroupHead or NULL if non applies to the packet
281 */
283 const Packet *p)
284{
285 SCEnter();
286 SigGroupHead *sgh = NULL;
287
288 /* if the packet proto is 0 (not set), we're inspecting it against
289 * the decoder events sgh we have. */
290 if (p->proto == 0 && p->events.cnt > 0) {
291 SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
292 } else if (p->proto == 0) {
293 if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
294 /* not IP, so nothing to do */
295 SCReturnPtr(NULL, "SigGroupHead");
296 }
297 }
298
299 /* select the flow_gh */
300 const int dir = (p->flowflags & FLOW_PKT_TOCLIENT) == 0;
301
302 int proto = PacketGetIPProto(p);
303 if (proto == IPPROTO_TCP) {
304 DetectPort *list = de_ctx->flow_gh[dir].tcp;
305 SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", de_ctx->flow_gh[1].tcp,
306 de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[dir].tcp);
307 const uint16_t port = dir ? p->dp : p->sp;
308 SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp);
309 DetectPort *sghport = DetectPortLookupGroup(list, port);
310 if (sghport != NULL)
311 sgh = sghport->sh;
312 SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
313 dir ? "toserver" : "toclient", sghport, sgh);
314 } else if (proto == IPPROTO_UDP) {
315 DetectPort *list = de_ctx->flow_gh[dir].udp;
316 uint16_t port = dir ? p->dp : p->sp;
317 DetectPort *sghport = DetectPortLookupGroup(list, port);
318 if (sghport != NULL)
319 sgh = sghport->sh;
320 SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
321 dir ? "toserver" : "toclient", sghport, sgh);
322 } else {
323 sgh = de_ctx->flow_gh[dir].sgh[proto];
324 }
325
326 SCReturnPtr(sgh, "SigGroupHead");
327}
328
329static inline void DetectPrefilterCopyDeDup(
331{
332 SigIntId *pf_ptr = det_ctx->pmq.rule_id_array;
333 uint32_t final_cnt = det_ctx->pmq.rule_id_array_cnt;
334 Signature **sig_array = de_ctx->sig_array;
335 Signature **match_array = det_ctx->match_array;
336 SigIntId previous_id = (SigIntId)-1;
337 while (final_cnt-- > 0) {
338 SigIntId id = *pf_ptr++;
339 Signature *s = sig_array[id];
340
341 /* As the prefilter list can contain duplicates, check for that here. */
342 if (likely(id != previous_id)) {
343 *match_array++ = s;
344 previous_id = id;
345 }
346 }
347
348 det_ctx->match_array_cnt = (uint32_t)(match_array - det_ctx->match_array);
350 PMQ_RESET(&det_ctx->pmq);
351}
352
353/** \internal
354 * \brief update flow's file tracking flags based on the detection engine
355 * A set of flags is prepared that is sent to the File API. The
356 File API may reject one or more based on the global force settings.
357 */
358static inline void
359DetectPostInspectFileFlagsUpdate(Flow *f, const SigGroupHead *sgh, uint8_t direction)
360{
361 uint16_t flow_file_flags = FLOWFILE_INIT;
362
363 if (sgh == NULL) {
364 SCLogDebug("requesting disabling all file features for flow");
365 flow_file_flags = FLOWFILE_NONE;
366 } else {
367 if (sgh->filestore_cnt == 0) {
368 SCLogDebug("requesting disabling filestore for flow");
369 flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
370 }
371#ifdef HAVE_MAGIC
372 if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
373 SCLogDebug("requesting disabling magic for flow");
374 flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
375 }
376#endif
377 if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
378 SCLogDebug("requesting disabling md5 for flow");
379 flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
380 }
381 if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
382 SCLogDebug("requesting disabling sha1 for flow");
383 flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
384 }
385 if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
386 SCLogDebug("requesting disabling sha256 for flow");
387 flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
388 }
389 if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESIZE)) {
390 SCLogDebug("requesting disabling filesize for flow");
391 flow_file_flags |= (FLOWFILE_NO_SIZE_TS|FLOWFILE_NO_SIZE_TC);
392 }
393 }
394 if (flow_file_flags != 0) {
395 FileUpdateFlowFileFlags(f, flow_file_flags, direction);
396 }
397}
398
399static inline void
400DetectRunPostGetFirstRuleGroup(const Packet *p, Flow *pflow, const SigGroupHead *sgh)
401{
402 if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) {
403 /* first time we see this toserver sgh, store it */
404 pflow->sgh_toserver = sgh;
405 pflow->flags |= FLOW_SGH_TOSERVER;
406
407 if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
408 if (pflow->protoctx != NULL) {
409 TcpSession *ssn = pflow->protoctx;
410 SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.client");
412 }
413 }
414
415 DetectPostInspectFileFlagsUpdate(pflow,
416 pflow->sgh_toserver, STREAM_TOSERVER);
417
418 } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
419 pflow->sgh_toclient = sgh;
420 pflow->flags |= FLOW_SGH_TOCLIENT;
421
422 if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
423 if (pflow->protoctx != NULL) {
424 TcpSession *ssn = pflow->protoctx;
425 SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.server");
427 }
428 }
429
430 DetectPostInspectFileFlagsUpdate(pflow,
431 pflow->sgh_toclient, STREAM_TOCLIENT);
432 }
433}
434
435static inline void DetectRunGetRuleGroup(
436 const DetectEngineCtx *de_ctx,
437 Packet * const p, Flow * const pflow,
438 DetectRunScratchpad *scratch)
439{
440 const SigGroupHead *sgh = NULL;
441
442 if (pflow) {
443 bool use_flow_sgh = false;
444 /* Get the stored sgh from the flow (if any). Make sure we're not using
445 * the sgh for icmp error packets part of the same stream. */
446 if (PacketGetIPProto(p) == pflow->proto) { /* filter out icmp */
448 if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
449 sgh = pflow->sgh_toserver;
450 SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
451 use_flow_sgh = true;
452 } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
453 sgh = pflow->sgh_toclient;
454 SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
455 use_flow_sgh = true;
456 }
458 }
459
460 if (!(use_flow_sgh)) {
464
465 /* HACK: prevent the wrong sgh (or NULL) from being stored in the
466 * flow's sgh pointers */
467 if (PacketIsICMPv4(p) && ICMPV4_DEST_UNREACH_IS_VALID(p)) {
468 ; /* no-op */
469 } else {
470 /* store the found sgh (or NULL) in the flow to save us
471 * from looking it up again for the next packet.
472 * Also run other tasks */
473 DetectRunPostGetFirstRuleGroup(p, pflow, sgh);
474 }
475 }
476 } else { /* p->flags & PKT_HAS_FLOW */
477 /* no flow */
478
482 }
483
484 scratch->sgh = sgh;
485}
486
487static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
488 DetectEngineThreadCtx *det_ctx,
489 Flow * const pflow, Packet * const p)
490{
491 if (pflow) {
493 SCLogDebug("testing against \"ip-only\" signatures");
494
496 IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
498 }
499 } else { /* p->flags & PKT_HAS_FLOW */
500 /* no flow */
501
502 /* Even without flow we should match the packet src/dst */
504 IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
506 }
507}
508
509/** \internal
510 * \brief inspect the rule header: protocol, ports, etc
511 * \retval bool false if no match, true if match */
512static inline bool DetectRunInspectRuleHeader(const Packet *p, const Flow *f, const Signature *s,
513 const uint32_t sflags, const uint8_t s_proto_flags)
514{
515 /* check if this signature has a requirement for flowvars of some type
516 * and if so, if we actually have any in the flow. If not, the sig
517 * can't match and we skip it. */
518 if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) {
519 DEBUG_VALIDATE_BUG_ON(f == NULL);
520
521 /* no flowvars? skip this sig */
522 const bool fv = f->flowvar != NULL;
523 if (!fv) {
524 SCLogDebug("skipping sig as the flow has no flowvars and sig "
525 "has SIG_FLAG_REQUIRE_FLOWVAR flag set.");
526 return false;
527 }
528 }
529
530 if ((s_proto_flags & DETECT_PROTO_IPV4) && !PacketIsIPv4(p)) {
531 SCLogDebug("ip version didn't match");
532 return false;
533 }
534 if ((s_proto_flags & DETECT_PROTO_IPV6) && !PacketIsIPv6(p)) {
535 SCLogDebug("ip version didn't match");
536 return false;
537 }
538
539 if (DetectProtoContainsProto(&s->proto, PacketGetIPProto(p)) == 0) {
540 SCLogDebug("proto didn't match");
541 return false;
542 }
543
544 /* check the source & dst port in the sig */
545 if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
546 if (!(sflags & SIG_FLAG_DP_ANY)) {
547 if (p->flags & PKT_IS_FRAGMENT)
548 return false;
549 const DetectPort *dport = DetectPortLookupGroup(s->dp, p->dp);
550 if (dport == NULL) {
551 SCLogDebug("dport didn't match.");
552 return false;
553 }
554 }
555 if (!(sflags & SIG_FLAG_SP_ANY)) {
556 if (p->flags & PKT_IS_FRAGMENT)
557 return false;
558 const DetectPort *sport = DetectPortLookupGroup(s->sp, p->sp);
559 if (sport == NULL) {
560 SCLogDebug("sport didn't match.");
561 return false;
562 }
563 }
564 } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
565 SCLogDebug("port-less protocol and sig needs ports");
566 return false;
567 }
568
569 /* check the destination address */
570 if (!(sflags & SIG_FLAG_DST_ANY)) {
571 if (PacketIsIPv4(p)) {
573 return false;
574 } else if (PacketIsIPv6(p)) {
576 return false;
577 }
578 }
579 /* check the source address */
580 if (!(sflags & SIG_FLAG_SRC_ANY)) {
581 if (PacketIsIPv4(p)) {
583 return false;
584 } else if (PacketIsIPv6(p)) {
586 return false;
587 }
588 }
589
590 return true;
591}
592
593/** \internal
594 * \brief run packet/stream prefilter engines
595 */
596static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
597 DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch)
598{
599 /* create our prefilter mask */
600 PacketCreateMask(p, &p->sig_mask, scratch->alproto, scratch->app_decoder_events);
601 /* run the prefilter engines */
602 Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags, p->sig_mask);
603 /* create match list if we have non-pf and/or pf */
604 if (det_ctx->pmq.rule_id_array_cnt) {
605#ifdef PROFILING
606 if (tv) {
607 StatsAddUI64(tv, det_ctx->counter_mpm_list, (uint64_t)det_ctx->pmq.rule_id_array_cnt);
608 }
609#endif
611 DetectPrefilterCopyDeDup(de_ctx, det_ctx);
613 }
614}
615
616/** \internal
617 * \brief check if the tx whose id is given is the only one
618 * live transaction for the flow in the given direction
619 *
620 * \param f flow
621 * \param txid transaction id
622 * \param dir direction
623 *
624 * \retval bool true if we are sure this tx is the only one live in said direction
625 */
626static bool IsOnlyTxInDirection(Flow *f, uint64_t txid, uint8_t dir)
627{
628 uint64_t tx_cnt = AppLayerParserGetTxCnt(f, f->alstate);
629 if (tx_cnt == txid + 1) {
630 // only live tx
631 return true;
632 }
633 if (tx_cnt == txid + 2) {
634 // 2 live txs, one after us
635 void *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, txid + 1);
636 if (tx) {
638 // test if the other tx is unidirectional in the other way
639 if ((dir == STREAM_TOSERVER && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TS)) ||
640 (dir == STREAM_TOCLIENT && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TC))) {
641 return true;
642 }
643 }
644 }
645 return false;
646}
647
648static int SortHelper(const void *a, const void *b)
649{
650 const Signature *sa = *(const Signature **)a;
651 const Signature *sb = *(const Signature **)b;
652 if (sa->iid == sb->iid)
653 return 0;
654 return sa->iid > sb->iid ? 1 : -1;
655}
656
657static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
658 const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
659 Flow *const pflow, const DetectRunScratchpad *scratch)
660{
661 uint8_t action = 0;
662 bool fw_verdict = false;
663 const bool have_fw_rules = EngineModeIsFirewall();
664 const Signature *next_s = NULL;
665
666 /* inspect the sigs against the packet */
667 /* Prefetch the next signature. */
668 SigIntId match_cnt = det_ctx->match_array_cnt;
669#ifdef PROFILING
670 if (tv) {
672 (uint64_t)match_cnt);
673 }
674#endif
675 Signature **match_array = det_ctx->match_array;
676
677 SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
678#ifdef PROFILING
679 if (match_cnt >= de_ctx->profile_match_logging_threshold)
680 RulesDumpMatchArray(det_ctx, scratch->sgh, p);
681#endif
682
683 bool skip_fw = false;
684 uint32_t sflags, next_sflags = 0;
685 if (match_cnt) {
686 next_s = *match_array++;
687 next_sflags = next_s->flags;
688 }
689 while (match_cnt--) {
691 bool break_out_of_packet_filter = false;
692 uint8_t alert_flags = 0;
693#ifdef PROFILE_RULES
694 bool smatch = false; /* signature match */
695#endif
696 const Signature *s = next_s;
697 sflags = next_sflags;
698 if (match_cnt) {
699 next_s = *match_array++;
700 next_sflags = next_s->flags;
701 }
702 const uint8_t s_proto_flags = s->proto.flags;
703
704 SCLogDebug("packet %" PRIu64 ": inspecting signature id %" PRIu32 "", p->pcap_cnt, s->id);
705
706 /* if we accept:hook'd the `packet_filter` hook, we skip the rest of the firewall rules. */
707 if (s->flags & SIG_FLAG_FIREWALL) {
708 if (skip_fw) {
709 SCLogDebug("skipping firewall rule %u", s->id);
710 goto next;
711 }
712 } else if (have_fw_rules) {
713 /* fw mode, we skip anything after the fw rules if:
714 * - flow pass is set
715 * - packet pass (e.g. exception policy) */
717 (pflow != NULL && pflow->flags & (FLOW_ACTION_PASS))) {
718 SCLogDebug("skipping firewall rule %u", s->id);
719 break_out_of_packet_filter = true;
720 goto next;
721 }
722 }
723
724 if (s->app_inspect != NULL) {
725 goto next; // handle sig in DetectRunTx
726 }
727 if (s->frame_inspect != NULL) {
728 goto next; // handle sig in DetectRunFrame
729 }
730
731 /* skip pkt sigs for flow end packets */
732 if ((p->flags & PKT_PSEUDO_STREAM_END) != 0 && s->type == SIG_TYPE_PKT)
733 goto next;
734
735 /* don't run mask check for stateful rules.
736 * There we depend on prefilter */
737 if ((s->mask & p->sig_mask) != s->mask) {
738 SCLogDebug("mask mismatch %x & %x != %x", s->mask, p->sig_mask, s->mask);
739 goto next;
740 }
741
742 if (SigDsizePrefilter(p, s, sflags))
743 goto next;
744
745 /* if the sig has alproto and the session as well they should match */
746 if (likely(sflags & SIG_FLAG_APPLAYER)) {
747 if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) {
748 SCLogDebug("alproto mismatch");
749 goto next;
750 }
751 }
752
753 if (!DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags)) {
754 goto next;
755 }
756
757 if (!DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags)) {
758 goto next;
759 }
760
761#ifdef PROFILE_RULES
762 smatch = true;
763#endif
764 DetectRunPostMatch(tv, det_ctx, p, s);
765
766 uint64_t txid = PACKET_ALERT_NOTX;
767 if (pflow && pflow->alstate) {
768 uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER;
770 if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) ||
771 (de_ctx->guess_applayer && IsOnlyTxInDirection(pflow, txid, dir))) {
772 // if there is a UDP specific app-layer signature,
773 // or only one live transaction
774 // try to use the good tx for the packet direction
775 void *tx_ptr =
776 AppLayerParserGetTx(pflow->proto, pflow->alproto, pflow->alstate, txid);
777 AppLayerTxData *txd =
778 tx_ptr ? AppLayerParserGetTxData(pflow->proto, pflow->alproto, tx_ptr)
779 : NULL;
780 if (txd && txd->guessed_applayer_logged < de_ctx->guess_applayer_log_limit) {
781 alert_flags |= PACKET_ALERT_FLAG_TX;
782 if (pflow->proto != IPPROTO_UDP) {
783 alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED;
784 }
785 txd->guessed_applayer_logged++;
786 }
787 }
788 }
789 AlertQueueAppend(det_ctx, s, p, txid, alert_flags);
790
791 if (det_ctx->post_rule_work_queue.len > 0) {
792 /* run post match prefilter engines on work queue */
793 PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, pflow);
794
795 if (det_ctx->pmq.rule_id_array_cnt > 0) {
796 /* undo "prefetch" */
797 if (next_s)
798 match_array--;
799 /* create temporary rule pointer array starting
800 * at where we are in the current match array */
801 const Signature *replace[de_ctx->sig_array_len]; // TODO heap?
802 SCLogDebug("sig_array_len %u det_ctx->pmq.rule_id_array_cnt %u",
804 const Signature **r = replace;
805 for (uint32_t x = 0; x < match_cnt; x++) {
806 *r++ = match_array[x];
807 SCLogDebug("appended %u", match_array[x]->id);
808 }
809 /* append the prefilter results, then sort it */
810 for (uint32_t x = 0; x < det_ctx->pmq.rule_id_array_cnt; x++) {
811 SCLogDebug("adding iid %u", det_ctx->pmq.rule_id_array[x]);
812 Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[x]];
813 SCLogDebug("adding id %u", ts->id);
814 if (ts->app_inspect == NULL) {
815 *r++ = ts;
816 match_cnt++;
817 }
818 }
819 if (match_cnt > 1) {
820 qsort(replace, match_cnt, sizeof(Signature *), SortHelper);
821 }
822 /* rewrite match_array to include the new additions, and deduplicate
823 * while at it. */
824 Signature **m = match_array;
825 Signature *last_sig = NULL;
826 uint32_t skipped = 0;
827 for (uint32_t x = 0; x < match_cnt; x++) {
828 /* de-duplicate */
829 if (last_sig == *m) {
830 skipped++;
831 continue;
832 }
833 last_sig = *m;
834 *m++ = (Signature *)replace[x];
835 }
836 match_cnt -= skipped;
837 /* prefetch next */
838 next_s = *match_array++;
839 next_sflags = next_s->flags;
840 SCLogDebug("%u rules added", det_ctx->pmq.rule_id_array_cnt);
841 det_ctx->post_rule_work_queue.len = 0;
842 PMQ_RESET(&det_ctx->pmq);
843 }
844 }
845
846 /* firewall logic in the packet:filter table:
847 * 1. firewall rules preceed the packet:td rules in the list
848 * 2. if no rule issues an accept, we drop
849 * 3. drop is immediate
850 * 4. accept:
851 * - hook: skip rest of fw rules, inspect packet:td rules
852 * - packet: immediate accept, no packet:td or app:* inspect
853 * - flow: as packet, but applied to all future packets in the
854 * flow as well
855 */
856 if (s->flags & SIG_FLAG_FIREWALL) {
857 if (s->action & (ACTION_ACCEPT)) {
858 fw_verdict = true;
859
860 enum ActionScope as = s->action_scope;
861 if (as == ACTION_SCOPE_HOOK) {
862 /* accept:hook: jump to first TD. Implemented as:
863 * skip until the first TD rule.
864 * Don't update action as we're just continuing to the next hook. */
865 skip_fw = true;
866
867 } else if (as == ACTION_SCOPE_PACKET) {
868 /* accept:packet: break loop, return accept */
869 action |= s->action;
870 break_out_of_packet_filter = true;
871
872 } else if (as == ACTION_SCOPE_FLOW) {
873 /* accept:flow: break loop, return accept */
874 action |= s->action;
875 break_out_of_packet_filter = true;
876
877 /* set immediately, as we're in hook "packet_filter" */
878 if (pflow) {
879 pflow->flags |= FLOW_ACTION_ACCEPT;
880 }
881 }
882 } else if (s->action & ACTION_DROP) {
883 /* apply a drop immediately here */
884 fw_verdict = true;
885 action |= s->action;
886 break_out_of_packet_filter = true;
887 }
888 }
889next:
890 DetectVarProcessList(det_ctx, pflow, p);
891 DetectReplaceFree(det_ctx);
892 RULE_PROFILING_END(det_ctx, s, smatch, p);
893
894 /* fw accept:packet or accept:flow means we're done here */
895 if (break_out_of_packet_filter)
896 break;
897
898 continue;
899 }
900
901 /* if no rule told us to accept, and no rule explicitly dropped, we invoke the default drop
902 * policy
903 */
904 if (have_fw_rules && scratch->default_action == ACTION_DROP) {
905 if (!fw_verdict) {
908 action |= ACTION_DROP;
909 } else {
910 /* apply fw action */
911 p->action |= action;
912 }
913 }
914 return action;
915}
916
917/** \internal
918 * \param default_action either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
919 *
920 * ACTION_DROP means the default policy of drop:packet is applied
921 * ACTION_ACCEPT means the default policy of accept:hook is applied
922 */
923static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
924 DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
925 const uint8_t default_action)
926{
927 AppProto alproto = ALPROTO_UNKNOWN;
928 uint8_t flow_flags = 0; /* flow/state flags */
929 bool app_decoder_events = false;
930
932
933#ifdef UNITTESTS
934 if (RunmodeIsUnittests()) {
935 p->alerts.cnt = 0;
936 p->alerts.discarded = 0;
937 p->alerts.suppressed = 0;
938 }
939#endif
940 det_ctx->filestore_cnt = 0;
941 det_ctx->base64_decoded_len = 0;
942 det_ctx->raw_stream_progress = 0;
943 det_ctx->match_array_cnt = 0;
944 det_ctx->json_content_len = 0;
945
946 det_ctx->alert_queue_size = 0;
947 p->alerts.drop.action = 0;
948
949#ifdef DEBUG
950 if (p->flags & PKT_STREAM_ADD) {
951 det_ctx->pkt_stream_add_cnt++;
952 }
953#endif
954
955 /* grab the protocol state we will detect on */
956 if (p->flags & PKT_HAS_FLOW) {
957 DEBUG_VALIDATE_BUG_ON(pflow == NULL);
958
959 if (p->flowflags & FLOW_PKT_TOSERVER) {
960 flow_flags = STREAM_TOSERVER;
961 SCLogDebug("flag STREAM_TOSERVER set");
962 } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
963 flow_flags = STREAM_TOCLIENT;
964 SCLogDebug("flag STREAM_TOCLIENT set");
965 }
966 SCLogDebug("p->flowflags 0x%02x", p->flowflags);
967
968 if (p->flags & PKT_PSEUDO_STREAM_END) {
969 flow_flags |= STREAM_EOF;
970 SCLogDebug("STREAM_EOF set");
971 }
972
973 /* store tenant_id in the flow so that we can use it
974 * for creating pseudo packets */
975 if (p->tenant_id > 0 && pflow->tenant_id == 0) {
976 pflow->tenant_id = p->tenant_id;
977 }
978
979 /* live ruleswap check for flow updates */
980 if (pflow->de_ctx_version == 0) {
981 /* first time this flow is inspected, set id */
982 pflow->de_ctx_version = de_ctx->version;
983 } else if (pflow->de_ctx_version != de_ctx->version) {
984 /* first time we inspect flow with this de_ctx, reset */
985 pflow->flags &= ~FLOW_SGH_TOSERVER;
986 pflow->flags &= ~FLOW_SGH_TOCLIENT;
987 pflow->sgh_toserver = NULL;
988 pflow->sgh_toclient = NULL;
989
990 pflow->de_ctx_version = de_ctx->version;
991 GenericVarFree(pflow->flowvar);
992 pflow->flowvar = NULL;
993
995 }
996
997 /* Retrieve the app layer state and protocol and the tcp reassembled
998 * stream chunks. */
999 if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
1000 (p->proto == IPPROTO_UDP) ||
1002 {
1003 /* update flow flags with knowledge on disruptions */
1004 flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
1005 alproto = FlowGetAppProtocol(pflow);
1006 if (p->proto == IPPROTO_TCP && pflow->protoctx &&
1009 }
1010 SCLogDebug("alproto %u", alproto);
1011 } else {
1012 SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
1013 }
1014
1015 app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
1016 }
1017
1018 DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
1020 return pad;
1021}
1022
1023static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
1024 DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
1025 DetectRunScratchpad *scratch)
1026{
1027 /* so now let's iterate the alerts and remove the ones after a pass rule
1028 * matched (if any). This is done inside PacketAlertFinalize() */
1029 /* PR: installed "tag" keywords are handled after the threshold inspection */
1030
1032 PacketAlertFinalize(de_ctx, det_ctx, p);
1033 if (p->alerts.cnt > 0) {
1034 StatsAddUI64(tv, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
1035 }
1036 if (p->alerts.discarded > 0) {
1037 StatsAddUI64(tv, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
1038 }
1039 if (p->alerts.suppressed > 0) {
1041 }
1043
1044 /* firewall: "fail" closed if we don't have an ACCEPT. This can happen
1045 * if there was no rule group. */
1046 // TODO review packet src types here
1047 if (EngineModeIsFirewall() && !(p->action & ACTION_ACCEPT) && p->pkt_src == PKT_SRC_WIRE &&
1048 scratch->default_action == ACTION_DROP) {
1049 SCLogDebug("packet %" PRIu64 ": droppit as no ACCEPT set %02x (pkt %s)", p->pcap_cnt,
1050 p->action, PktSrcToString(p->pkt_src));
1052 }
1053}
1054
1055static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
1056 Packet *p, Flow * const pflow)
1057{
1059 InspectionBufferClean(det_ctx);
1060
1061 if (pflow != NULL) {
1062 /* update inspected tracker for raw reassembly */
1063 if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
1066 det_ctx->raw_stream_progress);
1067 }
1068 }
1070 SCReturn;
1071}
1072
1074{
1076 det_ctx->tx_candidates = SCCalloc(size, sizeof(RuleMatchCandidateTx));
1077 if (det_ctx->tx_candidates == NULL) {
1078 FatalError("failed to allocate %" PRIu64 " bytes",
1079 (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
1080 }
1081 det_ctx->tx_candidates_size = size;
1082 SCLogDebug("array initialized to %u elements (%"PRIu64" bytes)",
1083 size, (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
1084}
1085
1087{
1088 SCFree(det_ctx->tx_candidates);
1089 det_ctx->tx_candidates_size = 0;
1090}
1091
1092/* if size >= cur_space */
1093static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1094 const uint32_t need)
1095{
1096 if (det_ctx->tx_candidates_size >= need)
1097 return 1;
1098 return 0;
1099}
1100
1101/* realloc */
1102static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed)
1103{
1104 const uint32_t old_size = det_ctx->tx_candidates_size;
1105 uint32_t new_size = needed;
1106 void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx)));
1107 if (ptmp == NULL) {
1108 FatalError("failed to expand to %" PRIu64 " bytes",
1109 (uint64_t)(new_size * sizeof(RuleMatchCandidateTx)));
1110 // TODO can this be handled more gracefully?
1111 }
1112 det_ctx->tx_candidates = ptmp;
1113 det_ctx->tx_candidates_size = new_size;
1114 SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)",
1115 old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)),
1116 (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size;
1117 return 1;
1118}
1119
1120/** \internal
1121 * \brief sort helper for sorting match candidates by id: ascending
1122 *
1123 * The id field is set from Signature::num, so we sort the candidates to match the signature
1124 * sort order (ascending), where candidates that have flags go first.
1125 */
1126static int
1127DetectRunTxSortHelper(const void *a, const void *b)
1128{
1129 const RuleMatchCandidateTx *s0 = a;
1130 const RuleMatchCandidateTx *s1 = b;
1131 if (s1->id == s0->id) {
1132 if (s1->flags && !s0->flags)
1133 return 1;
1134 else if (!s1->flags && s0->flags)
1135 return -1;
1136 return 0;
1137 } else
1138 return s0->id > s1->id ? 1 : -1;
1139}
1140
1141#if 0
1142#define TRACE_SID_TXS(sid,txs,...) \
1143 do { \
1144 char _trace_buf[2048]; \
1145 snprintf(_trace_buf, sizeof(_trace_buf), __VA_ARGS__); \
1146 SCLogNotice("%p/%"PRIu64"/%u: %s", txs->tx_ptr, txs->tx_id, sid, _trace_buf); \
1147 } while(0)
1148#else
1149#define TRACE_SID_TXS(sid,txs,...)
1150#endif
1151
1152// Get inner transaction for engine
1153void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags)
1154{
1155 if (unlikely(alproto == ALPROTO_DOH2)) {
1156 if (engine_alproto == ALPROTO_DNS) {
1157 // need to get the dns tx pointer
1158 tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags);
1159 } else if (engine_alproto != ALPROTO_HTTP2 && engine_alproto != ALPROTO_UNKNOWN) {
1160 // incompatible engine->alproto with flow alproto
1161 tx_ptr = NULL;
1162 }
1163 } else if (engine_alproto != alproto && engine_alproto != ALPROTO_UNKNOWN) {
1164 // incompatible engine->alproto with flow alproto
1165 tx_ptr = NULL;
1166 }
1167 return tx_ptr;
1168}
1169
1170/** \internal
1171 * \brief inspect a rule against a transaction
1172 *
1173 * Inspect a rule. New detection or continued stateful
1174 * detection.
1175 *
1176 * \param stored_flags pointer to stored flags or NULL.
1177 * If stored_flags is set it means we're continuing
1178 * inspection from an earlier run.
1179 *
1180 * \retval bool true sig matched, false didn't match
1181 */
1182static bool DetectRunTxInspectRule(ThreadVars *tv,
1184 DetectEngineThreadCtx *det_ctx,
1185 Packet *p,
1186 Flow *f,
1187 const uint8_t in_flow_flags, // direction, EOF, etc
1188 void *alstate,
1190 const Signature *s,
1191 uint32_t *stored_flags,
1193 DetectRunScratchpad *scratch)
1194{
1195 const uint8_t flow_flags = in_flow_flags;
1196 const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
1197 uint32_t inspect_flags = stored_flags ? *stored_flags : 0;
1198 int total_matches = 0;
1199 uint16_t file_no_match = 0;
1200 bool retval = false;
1201 bool mpm_before_progress = false; // is mpm engine before progress?
1202 bool mpm_in_progress = false; // is mpm engine in a buffer we will revisit?
1203
1204 TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver");
1205
1206 /* for a new inspection we inspect pkt header and packet matches */
1207 if (likely(stored_flags == NULL)) {
1208 TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
1209 if (!DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags)) {
1210 TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
1211 return false;
1212 }
1213 if (!DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL)) {
1214 TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
1215 return false;
1216 }
1217 /* stream mpm and negated mpm sigs can end up here with wrong proto */
1218 if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) {
1219 TRACE_SID_TXS(s->id, tx, "alproto mismatch");
1220 return false;
1221 }
1222 }
1223
1225 do {
1226 TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
1227 // also if it is not the same direction, but
1228 // this is a transactional signature, and we are toclient
1229 if (!(inspect_flags & BIT_U32(engine->id)) &&
1230 (direction == engine->dir || ((s->flags & SIG_FLAG_TXBOTHDIR) && direction == 1))) {
1231
1232 void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
1233 if (tx_ptr == NULL) {
1234 if (engine->alproto != ALPROTO_UNKNOWN) {
1235 /* special case: file_data on 'alert tcp' will have engines
1236 * in the list that are not for us. */
1237 engine = engine->next;
1238 continue;
1239 } else {
1240 tx_ptr = tx->tx_ptr;
1241 }
1242 }
1243
1244 /* engines are sorted per progress, except that the one with
1245 * mpm/prefilter enabled is first */
1246 if (tx->tx_progress < engine->progress) {
1247 SCLogDebug("tx progress %d < engine progress %d",
1248 tx->tx_progress, engine->progress);
1249 break;
1250 }
1251 if (engine->mpm) {
1252 if (tx->tx_progress > engine->progress) {
1253 TRACE_SID_TXS(s->id, tx,
1254 "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
1255 "mpm_before_progress",
1256 tx->tx_progress, engine->progress);
1257 mpm_before_progress = true;
1258 } else if (tx->tx_progress == engine->progress) {
1259 TRACE_SID_TXS(s->id, tx,
1260 "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
1261 "mpm_in_progress",
1262 tx->tx_progress, engine->progress);
1263 mpm_in_progress = true;
1264 }
1265 }
1266
1267 uint8_t engine_flags = flow_flags;
1268 if (direction != engine->dir) {
1269 engine_flags = flow_flags ^ (STREAM_TOCLIENT | STREAM_TOSERVER);
1270 }
1271 /* run callback: but bypass stream callback if we can */
1272 uint8_t match;
1273 if (unlikely(engine->stream && can->stream_stored)) {
1274 match = can->stream_result;
1275 TRACE_SID_TXS(s->id, tx, "stream skipped, stored result %d used instead", match);
1276 } else if (engine->v2.Callback == NULL) {
1277 /* TODO is this the cleanest way to support a non-app sig on a app hook? */
1278
1279 if (tx->tx_progress > engine->progress) {
1280 mpm_before_progress = true; // TODO needs a new name now
1281 }
1282
1283 /* we don't have to store a "hook" match, also don't want to keep any state to make
1284 * sure the hook gets invoked again until tx progress progresses. */
1285 if (tx->tx_progress <= engine->progress)
1287
1288 /* if progress > engine progress, track state to avoid additional matches */
1290 } else {
1291 KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
1292 DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
1293 match = engine->v2.Callback(
1294 de_ctx, det_ctx, engine, s, f, engine_flags, alstate, tx_ptr, tx->tx_id);
1295 TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
1296 if (engine->stream) {
1297 can->stream_stored = true;
1298 can->stream_result = match;
1299 TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match);
1300 }
1301 }
1302 if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
1303 inspect_flags |= BIT_U32(engine->id);
1304 engine = engine->next;
1305 total_matches++;
1306 continue;
1307 } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
1308 /* if the file engine matched, but indicated more
1309 * files are still in progress, we don't set inspect
1310 * flags as these would end inspection for this tx */
1311 engine = engine->next;
1312 total_matches++;
1313 continue;
1314 } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
1315 inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1316 inspect_flags |= BIT_U32(engine->id);
1317 } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) {
1318 inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1319 inspect_flags |= BIT_U32(engine->id);
1320 file_no_match = 1;
1321 }
1322 /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */
1323 if (engine->mpm && mpm_before_progress) {
1324 inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
1325 inspect_flags |= BIT_U32(engine->id);
1326 }
1327 break;
1328 } else if (!(inspect_flags & BIT_U32(engine->id)) && s->flags & SIG_FLAG_TXBOTHDIR &&
1329 direction != engine->dir) {
1330 // for transactional rules, the engines on the opposite direction
1331 // are ordered by progress on the different side
1332 // so we have a two mixed-up lists, and we skip the elements
1333 if (direction == 0 && engine->next == NULL) {
1334 // do not match yet on request only
1335 break;
1336 }
1337 engine = engine->next;
1338 continue;
1339 }
1340
1341 engine = engine->next;
1342 } while (engine != NULL);
1343 TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
1344 inspect_flags, total_matches, engine);
1345
1346 if (engine == NULL && total_matches) {
1347 inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
1348 TRACE_SID_TXS(s->id, tx, "MATCH");
1349 retval = true;
1350 }
1351
1352 if (stored_flags) {
1353 *stored_flags = inspect_flags;
1354 TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
1355 } else {
1356 // store... or? If tx is done we might not want to come back to this tx
1357
1358 // also... if mpmid tracking is enabled, we won't do a sig again for this tx...
1359 TRACE_SID_TXS(s->id, tx, "start inspect flags %08x", inspect_flags);
1360 if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
1361 if (file_no_match) {
1362 /* if we have a mismatch on a file sig, we need to keep state.
1363 * We may get another file on the same tx (for http and smtp
1364 * at least), so for a new file we need to re-eval the sig.
1365 * Thoughts / TODO:
1366 * - not for some protos that have 1 file per tx (e.g. nfs)
1367 * - maybe we only need this for file sigs that mix with
1368 * other matches? E.g. 'POST + filename', is different than
1369 * just 'filename'.
1370 */
1371 DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1372 inspect_flags, flow_flags, file_no_match);
1373 }
1374 } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) {
1375 TRACE_SID_TXS(s->id, tx, "no need to store match sig, "
1376 "mpm won't trigger for it anymore");
1377
1378 if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
1379 TRACE_SID_TXS(s->id, tx, "except that for new files, "
1380 "we may have to revisit anyway");
1381 DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1382 inspect_flags, flow_flags, file_no_match);
1383 }
1384 } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
1385 TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
1386 "mpm will revisit it");
1387 } else if (inspect_flags != 0 || file_no_match != 0) {
1388 TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
1389 DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
1390 inspect_flags, flow_flags, file_no_match);
1391 }
1392 }
1393
1394 return retval;
1395}
1396
1397#define NO_TX \
1398 { \
1399 NULL, 0, NULL, NULL, 0, 0, 0, 0, \
1400 }
1401
1402/** \internal
1403 * \brief get a DetectTransaction object
1404 * \retval struct filled with relevant info or all nulls/0s
1405 */
1406static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alproto,
1407 const uint64_t tx_id, void *tx_ptr, const int tx_end_state, const uint8_t flow_flags)
1408{
1409 AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr);
1410 const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
1411 bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
1412 if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
1413 DetectTransaction no_tx = NO_TX;
1414 return no_tx;
1415 }
1416 const uint8_t inspected_flag =
1417 (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_INSPECTED_TS : APP_LAYER_TX_INSPECTED_TC;
1418 if (unlikely(txd->flags & inspected_flag)) {
1419 SCLogDebug("%" PRIu64 " tx already fully inspected for %s. Flags %02x", tx_id,
1420 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
1421 DetectTransaction no_tx = NO_TX;
1422 return no_tx;
1423 }
1424 const uint8_t skip_flag = (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_SKIP_INSPECT_TS
1426 if (unlikely(txd->flags & skip_flag)) {
1427 SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %02x", tx_id,
1428 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
1429 DetectTransaction no_tx = NO_TX;
1430 return no_tx;
1431 }
1432
1433 const uint8_t detect_progress =
1434 (flow_flags & STREAM_TOSERVER) ? txd->detect_progress_ts : txd->detect_progress_tc;
1435
1436 const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
1437 DetectEngineState *tx_de_state = txd->de_state;
1438 DetectEngineStateDirection *tx_dir_state =
1439 tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL;
1440 DetectTransaction tx = {
1441 .tx_ptr = tx_ptr,
1442 .tx_id = tx_id,
1443 .tx_data_ptr = (struct AppLayerTxData *)txd,
1444 .de_state = tx_dir_state,
1445 .detect_progress = detect_progress,
1446 .detect_progress_orig = detect_progress,
1447 .tx_progress = tx_progress,
1448 .tx_end_state = tx_end_state,
1449 };
1450 return tx;
1451}
1452
1453static inline void StoreDetectProgress(
1454 DetectTransaction *tx, const uint8_t flow_flags, const uint8_t progress)
1455{
1457 if (flow_flags & STREAM_TOSERVER) {
1458 txd->detect_progress_ts = progress;
1459 } else {
1460 txd->detect_progress_tc = progress;
1461 }
1462}
1463
1464// Merge 'state' rules from the regular prefilter
1465// updates array_idx on the way
1466static inline void RuleMatchCandidateMergeStateRules(
1467 DetectEngineThreadCtx *det_ctx, uint32_t *array_idx)
1468{
1469 // Now, we will merge 2 sorted lists :
1470 // the one in det_ctx->tx_candidates
1471 // and the one in det_ctx->match_array
1472 // For match_array, we take only the relevant elements where s->app_inspect != NULL
1473
1474 // Basically, we iterate at the same time over the 2 lists
1475 // comparing and taking an element from either.
1476
1477 // Trick is to do so in place in det_ctx->tx_candidates,
1478 // so as to minimize the number of moves in det_ctx->tx_candidates.
1479 // For this, the algorithm traverses the lists in reverse order.
1480 // Otherwise, if the first element of match_array was to be put before
1481 // all tx_candidates, we would need to shift all tx_candidates
1482
1483 // Retain the number of elements sorted in tx_candidates before merge
1484 uint32_t j = *array_idx;
1485 // First loop only counting the number of elements to add
1486 for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
1487 const Signature *s = det_ctx->match_array[i];
1488 if (s->app_inspect != NULL) {
1489 (*array_idx)++;
1490 }
1491 }
1492 // Future number of elements in tx_candidates after merge
1493 uint32_t k = *array_idx;
1494
1495 if (k == j) {
1496 // no new element from match_array to merge in tx_candidates
1497 return;
1498 }
1499
1500 // variable i is for all elements of match_array (even not relevant ones)
1501 // variable j is for elements of tx_candidates before merge
1502 // variable k is for elements of tx_candidates after merge
1503 for (uint32_t i = det_ctx->match_array_cnt; i > 0;) {
1504 const Signature *s = det_ctx->match_array[i - 1];
1505 if (s->app_inspect == NULL) {
1506 // no relevant element, get the next one from match_array
1507 i--;
1508 continue;
1509 }
1510 // we have one element from match_array to merge in tx_candidates
1511 k--;
1512 if (j > 0) {
1513 // j > 0 means there is still at least one element in tx_candidates to merge
1514 const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1];
1515 if (s->iid <= s0->id) {
1516 // get next element from previous tx_candidates
1517 j--;
1518 // take the element from tx_candidates before merge
1519 det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
1520 det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
1521 det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
1522 det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
1523 continue;
1524 }
1525 } // otherwise
1526 // get next element from match_array
1527 i--;
1528 // take the element from match_array
1529 det_ctx->tx_candidates[k].s = s;
1530 det_ctx->tx_candidates[k].id = s->iid;
1531 det_ctx->tx_candidates[k].flags = NULL;
1532 det_ctx->tx_candidates[k].stream_reset = 0;
1533 }
1534 // Even if k > 0 or j > 0, the loop is over. (Note that j == k now)
1535 // The remaining elements in tx_candidates up to k were already sorted
1536 // and come before any other element later in the list
1537}
1538
1539/**
1540 * \internal
1541 * \brief Check and update firewall rules state.
1542 *
1543 * \param skip_fw_hook bool to indicate firewall rules skips
1544 * For state `skip_before_progress` should be skipped.
1545 *
1546 * \param skip_before_progress progress value to skip rules before.
1547 * Only used if `skip_fw_hook` is set.
1548 *
1549 * \param last_for_progress[out] set to true if this is the last rule for a progress value
1550 *
1551 * \param fw_next_progress_missing[out] set to true if the next fw rule does not target the next
1552 * progress value, or there is no fw rule for that value.
1553 *
1554 * \retval 0 no action needed
1555 * \retval 1 rest of rules shouldn't inspected
1556 * \retval -1 skip this rule
1557 */
1558static int DetectRunTxCheckFirewallPolicy(DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f,
1559 DetectTransaction *tx, const Signature *s, const uint32_t can_idx, const uint32_t can_size,
1560 bool *skip_fw_hook, const uint8_t skip_before_progress, bool *last_for_progress,
1561 bool *fw_next_progress_missing)
1562{
1563 if (s->flags & SIG_FLAG_FIREWALL) {
1564 /* check if the next sig is on the same progress hook. If not, we need to apply our
1565 * default policy in case the current sig doesn't apply one. If the next sig has a
1566 * progress beyond our progress + 1, it means the next progress has no rules and needs
1567 * the default policy applied. But only after we evaluate the current rule first, as
1568 * that may override it.
1569 * TODO should we do this after dedup below? */
1570
1571 if (can_idx + 1 < can_size) {
1572 const Signature *next_s = det_ctx->tx_candidates[can_idx + 1].s;
1573 SCLogDebug(
1574 "peek: peeking at sid %u / progress %u", next_s->id, next_s->app_progress_hook);
1575 if (next_s->flags & SIG_FLAG_FIREWALL) {
1576 if (s->app_progress_hook != next_s->app_progress_hook) {
1577 SCLogDebug("peek: next sid progress %u != current progress %u, so current "
1578 "is last for progress",
1580 *last_for_progress = true;
1581
1582 if (next_s->app_progress_hook - s->app_progress_hook > 1) {
1583 SCLogDebug("peek: missing progress, so we'll drop that unless we get a "
1584 "sweeping accept first");
1585 *fw_next_progress_missing = true;
1586 }
1587 }
1588 } else {
1589 SCLogDebug("peek: next sid not a fw rule, so current is last for progress");
1590 *last_for_progress = true;
1591 }
1592 } else {
1593 SCLogDebug("peek: no peek beyond last rule");
1594 if (s->app_progress_hook < tx->tx_progress) {
1595 SCLogDebug("peek: there are no rules to allow the state after this rule");
1596 *fw_next_progress_missing = true;
1597 }
1598 }
1599
1600 if ((*skip_fw_hook) == true) {
1601 if (s->app_progress_hook <= skip_before_progress) {
1602 return -1;
1603 }
1604 *skip_fw_hook = false;
1605 }
1606 } else {
1607 /* fw mode, we skip anything after the fw rules if:
1608 * - flow pass is set
1609 * - packet pass (e.g. exception policy) */
1610 if (p->flags & PKT_NOPACKET_INSPECTION || (f->flags & (FLOW_ACTION_PASS))) {
1611 SCLogDebug("skipping firewall rule %u", s->id);
1612 return 1;
1613 }
1614 }
1615 return 0;
1616}
1617
1618// TODO move into det_ctx?
1620static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p)
1621{
1622 if (EngineModeIsFirewall()) {
1623 memset(&default_accept, 0, sizeof(default_accept));
1626 default_accept.iid = UINT32_MAX;
1630 }
1631}
1632
1633/** \internal
1634 * \brief see if the accept rule needs to apply to the packet
1635 */
1636static inline bool ApplyAcceptToPacket(
1637 const uint64_t total_txs, const DetectTransaction *tx, const Signature *s)
1638{
1639 if ((s->flags & SIG_FLAG_FIREWALL) == 0) {
1640 return false;
1641 }
1642 if ((s->action & ACTION_ACCEPT) == 0) {
1643 return false;
1644 }
1645
1646 /* for accept:tx we need:
1647 * - packet will only be accepted if this is set on the last tx
1648 */
1649 if (s->action_scope == ACTION_SCOPE_TX) {
1650 if (total_txs == tx->tx_id + 1) {
1651 return true;
1652 }
1653 }
1654 /* for accept:hook we need a bit more checking:
1655 * - packet will only be accepted if this is set on the last tx
1656 * - the hook accepted should be the last progress available. */
1657 if (s->action_scope == ACTION_SCOPE_HOOK) {
1658 if ((total_txs == tx->tx_id + 1) && /* last tx */
1659 (s->app_progress_hook == tx->tx_progress)) {
1660 return true;
1661 }
1662 }
1663 return false;
1664}
1665
1666/** \internal
1667 * \retval bool true: break_out_of_app_filter, false: don't break out */
1668static bool ApplyAccept(Packet *p, const uint8_t flow_flags, const Signature *s,
1669 DetectTransaction *tx, const int tx_end_state, const bool fw_next_progress_missing,
1670 bool *tx_fw_verdict, bool *skip_fw_hook, uint8_t *skip_before_progress)
1671{
1672 *tx_fw_verdict = true;
1673
1674 const enum ActionScope as = s->action_scope;
1675 /* accept:hook: jump to first rule of next state.
1676 * Implemented as skip until the first rule of next state. */
1677 if (as == ACTION_SCOPE_HOOK) {
1678 *skip_fw_hook = true;
1679 *skip_before_progress = s->app_progress_hook;
1680
1681 /* if there is no fw rule for the next progress value,
1682 * we invoke the default drop policy. */
1683 if (fw_next_progress_missing) {
1684 SCLogDebug("%" PRIu64 ": %s default drop for progress", p->pcap_cnt,
1685 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
1688 return true;
1689 }
1690 return false;
1691 } else if (as == ACTION_SCOPE_TX) {
1692 tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
1693 *skip_fw_hook = true;
1694 *skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
1695 SCLogDebug(
1696 "accept:tx applied, skip_fw_hook, skip_before_progress %u", *skip_before_progress);
1697 return false;
1698 } else if (as == ACTION_SCOPE_PACKET) {
1699 return true;
1700 } else if (as == ACTION_SCOPE_FLOW) {
1701 return true;
1702 }
1703 return false;
1704}
1705
1706static void DetectRunTx(ThreadVars *tv,
1708 DetectEngineThreadCtx *det_ctx,
1709 Packet *p,
1710 Flow *f,
1711 DetectRunScratchpad *scratch)
1712{
1713 const uint8_t flow_flags = scratch->flow_flags;
1714 const SigGroupHead * const sgh = scratch->sgh;
1715 void * const alstate = f->alstate;
1716 const uint8_t ipproto = f->proto;
1717 const AppProto alproto = f->alproto;
1718
1719 const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
1720 uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags);
1721 const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags);
1722
1723 AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto);
1724 AppLayerGetTxIterState state = { 0 };
1725
1726 uint32_t fw_verdicted = 0;
1727 uint32_t tx_inspected = 0;
1728 const bool have_fw_rules = EngineModeIsFirewall();
1729
1730 SCLogDebug("packet %" PRIu64, p->pcap_cnt);
1731
1732 while (1) {
1733 AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
1734 if (ires.tx_ptr == NULL)
1735 break;
1736
1738 GetDetectTx(ipproto, alproto, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
1739 if (tx.tx_ptr == NULL) {
1740 SCLogDebug("%p/%"PRIu64" no transaction to inspect",
1741 tx.tx_ptr, tx_id_min);
1742
1743 tx_id_min++; // next (if any) run look for +1
1744 goto next;
1745 }
1746 tx_id_min = tx.tx_id + 1; // next look for cur + 1
1747 tx_inspected++;
1748
1749 SCLogDebug("%p/%" PRIu64 " txd flags %02x", tx.tx_ptr, tx_id_min, tx.tx_data_ptr->flags);
1750
1751 det_ctx->tx_id = tx.tx_id;
1752 det_ctx->tx_id_set = true;
1753 det_ctx->p = p;
1754
1755 bool do_sort = false; // do we need to sort the tx candidate list?
1756 uint32_t array_idx = 0;
1757 uint32_t total_rules = det_ctx->match_array_cnt;
1758 total_rules += (tx.de_state ? tx.de_state->cnt : 0);
1759
1760 /* run prefilter engines and merge results into a candidates array */
1761 if (sgh->tx_engines) {
1763 DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto,
1764 alstate, &tx);
1766 SCLogDebug("%p/%"PRIu64" rules added from prefilter: %u candidates",
1767 tx.tx_ptr, tx.tx_id, det_ctx->pmq.rule_id_array_cnt);
1768
1769 total_rules += det_ctx->pmq.rule_id_array_cnt;
1770 if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
1771 RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
1772 }
1773
1774 for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
1775 const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
1776 const SigIntId id = s->iid;
1777 det_ctx->tx_candidates[array_idx].s = s;
1778 det_ctx->tx_candidates[array_idx].id = id;
1779 det_ctx->tx_candidates[array_idx].flags = NULL;
1780 det_ctx->tx_candidates[array_idx].stream_reset = 0;
1781 array_idx++;
1782 }
1783 PMQ_RESET(&det_ctx->pmq);
1784 } else {
1785 if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
1786 RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
1787 }
1788 }
1789
1790 /* merge 'state' rules from the regular prefilter */
1791#ifdef PROFILING
1792 uint32_t x = array_idx;
1793#endif
1794 RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);
1795
1796 /* merge stored state into results */
1797 if (tx.de_state != NULL) {
1798 const uint32_t old = array_idx;
1799
1800 /* if tx.de_state->flags has 'new file' set and sig below has
1801 * 'file inspected' flag, reset the file part of the state */
1802 const bool have_new_file = (tx.de_state->flags & DETECT_ENGINE_STATE_FLAG_FILE_NEW);
1803 if (have_new_file) {
1804 SCLogDebug("%p/%"PRIu64" destate: need to consider new file",
1805 tx.tx_ptr, tx.tx_id);
1806 tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW;
1807 }
1808
1809 SigIntId state_cnt = 0;
1810 DeStateStore *tx_store = tx.de_state->head;
1811 for (; tx_store != NULL; tx_store = tx_store->next) {
1812 SCLogDebug("tx_store %p", tx_store);
1813
1814 SigIntId store_cnt = 0;
1815 for (store_cnt = 0;
1816 store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt;
1817 store_cnt++, state_cnt++)
1818 {
1819 DeStateStoreItem *item = &tx_store->store[store_cnt];
1820 SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags);
1821 if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) {
1822 /* remove part of the state. File inspect engine will now
1823 * be able to run again */
1825 SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags);
1826 }
1827 det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid];
1828 det_ctx->tx_candidates[array_idx].id = item->sid;
1829 det_ctx->tx_candidates[array_idx].flags = &item->flags;
1830 det_ctx->tx_candidates[array_idx].stream_reset = 0;
1831 array_idx++;
1832 }
1833 }
1834 do_sort |= (old && old != array_idx); // sort if continue list adds sids
1835 SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id,
1836 array_idx - old);
1837 }
1838 if (do_sort) {
1839 qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx),
1840 DetectRunTxSortHelper);
1841 }
1842
1843#ifdef PROFILING
1844 if (array_idx >= de_ctx->profile_match_logging_threshold)
1845 RulesDumpTxMatchArray(det_ctx, scratch->sgh, p, tx.tx_id, array_idx, x);
1846#endif
1847
1848#ifdef DEBUG
1849 for (uint32_t i = 0; i < array_idx; i++) {
1850 RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1851 const Signature *s = det_ctx->tx_candidates[i].s;
1852 SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags);
1853 }
1854#endif
1855 bool skip_fw_hook = false;
1856 uint8_t skip_before_progress = 0;
1857 bool fw_next_progress_missing = false;
1858
1859 /* if there are no rules / rule candidates, make sure we don't
1860 * invoke the default drop */
1861 if (have_fw_rules && array_idx == 0 && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
1862 fw_verdicted++;
1863
1864 /* current tx is the last we have, append a blank accept:packet */
1865 if (total_txs == tx.tx_id + 1) {
1866 DetectRunAppendDefaultAccept(det_ctx, p);
1867 return;
1868 }
1869 goto next;
1870 }
1871
1872 bool tx_fw_verdict = false;
1873 /* run rules: inspect the match candidates */
1874 for (uint32_t i = 0; i < array_idx; i++) {
1875 RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1876 const Signature *s = det_ctx->tx_candidates[i].s;
1877 uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags;
1878 bool break_out_of_app_filter = false;
1879
1880 SCLogDebug("%" PRIu64 ": sid:%u: %s tx %u/%u/%u sig %u", p->pcap_cnt, s->id,
1881 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.tx_progress,
1882 tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
1883
1884 /* deduplicate: rules_array is sorted, but not deduplicated:
1885 * both mpm and stored state could give us the same sid.
1886 * As they are back to back in that case we can check for it
1887 * here. We select the stored state one as that comes first
1888 * in the array. */
1889 while ((i + 1) < array_idx &&
1890 det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
1891 SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x",
1892 tx.tx_ptr, tx.tx_id, s->id, s->iid, inspect_flags ? *inspect_flags : 0);
1893 i++;
1894 }
1895
1896 /* skip fw rules if we're in accept:tx mode */
1897 if (have_fw_rules && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
1898 /* append a blank accept:packet action for the APP_LAYER_TX_ACCEPT,
1899 * if this is the last tx */
1900 if (!tx_fw_verdict) {
1901 const bool accept_tx_applies_to_packet = total_txs == tx.tx_id + 1;
1902 if (accept_tx_applies_to_packet) {
1903 SCLogDebug("accept:(tx|hook): should be applied to the packet");
1904 DetectRunAppendDefaultAccept(det_ctx, p);
1905 }
1906 }
1907 tx_fw_verdict = true;
1908
1909 if (s->flags & SIG_FLAG_FIREWALL) {
1910 SCLogDebug("APP_LAYER_TX_ACCEPT, so skip rule");
1911 continue;
1912 }
1913
1914 /* threat detect rules will be inspected */
1915 }
1916
1917 SCLogDebug("%p/%" PRIu64 " inspecting: sid %u (%u), flags %08x", tx.tx_ptr, tx.tx_id,
1918 s->id, s->iid, inspect_flags ? *inspect_flags : 0);
1919
1920 if (inspect_flags) {
1921 if (*inspect_flags & DE_STATE_FLAG_FULL_INSPECT) {
1922 SCLogDebug("%p/%" PRIu64
1923 " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_FULL_INSPECT",
1924 tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
1925
1926 /* if we're still in the same progress state as an earlier full
1927 * match, we need to apply the same accept */
1928 if (have_fw_rules && (s->flags & SIG_FLAG_FIREWALL) &&
1930 const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
1931 break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
1932 fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
1933 &skip_before_progress);
1934 if (fw_accept_to_packet)
1935 DetectRunAppendDefaultAccept(det_ctx, p);
1936 if (break_out_of_app_filter)
1937 break;
1938 }
1939 continue;
1940 }
1941 if (*inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
1942 SCLogDebug("%p/%" PRIu64
1943 " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_SIG_CANT_MATCH",
1944 tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
1945 continue;
1946 }
1947 }
1948
1949 if (inspect_flags) {
1950 /* continue previous inspection */
1951 SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id);
1952 } else {
1953 /* start new inspection */
1954 SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
1955 }
1956
1957 bool last_for_progress = false;
1958 if (have_fw_rules) {
1959 int fw_r = DetectRunTxCheckFirewallPolicy(det_ctx, p, f, &tx, s, i, array_idx,
1960 &skip_fw_hook, skip_before_progress, &last_for_progress,
1961 &fw_next_progress_missing);
1962 if (fw_r == -1)
1963 continue;
1964 if (fw_r == 1)
1965 break;
1966 }
1967
1968 /* call individual rule inspection */
1970 const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
1971 alstate, &tx, s, inspect_flags, can, scratch);
1972 if (r == 1) {
1973 /* match */
1974 DetectRunPostMatch(tv, det_ctx, p, s);
1975
1976 /* see if we need to apply tx/hook accept to the packet. This can be needed when
1977 * we've completed the inspection so far for an incomplete tx, and an accept:tx or
1978 * accept:hook is the last match.*/
1979 const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
1980
1981 uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX);
1982 if (fw_accept_to_packet) {
1983 SCLogDebug("accept:(tx|hook): should be applied to the packet");
1985 }
1986
1987 SCLogDebug(
1988 "%p/%" PRIu64 " sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->iid);
1989 AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
1990
1991 if ((s->flags & SIG_FLAG_FIREWALL) && (s->action & ACTION_ACCEPT)) {
1992 break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
1993 fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
1994 &skip_before_progress);
1995 }
1996 } else if (last_for_progress) {
1997 SCLogDebug("sid %u: not a match: %s rule, last_for_progress %s", s->id,
1998 (s->flags & SIG_FLAG_FIREWALL) ? "firewall" : "regular",
1999 BOOL2STR(last_for_progress));
2000 if (s->flags & SIG_FLAG_FIREWALL) {
2001 SCLogDebug("%" PRIu64 ": %s default drop for progress", p->pcap_cnt,
2002 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
2003 /* if this rule was the last for our progress state, and it didn't match,
2004 * we have to invoke the default drop policy. */
2007 break_out_of_app_filter = true;
2008 tx_fw_verdict = true;
2009 }
2010 }
2011 DetectVarProcessList(det_ctx, p->flow, p);
2012 RULE_PROFILING_END(det_ctx, s, r, p);
2013
2014 if (det_ctx->post_rule_work_queue.len > 0) {
2015 SCLogDebug("%p/%" PRIu64 " post_rule_work_queue len %u", tx.tx_ptr, tx.tx_id,
2016 det_ctx->post_rule_work_queue.len);
2017 /* run post match prefilter engines on work queue */
2018 PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, f);
2019
2020 uint32_t prev_array_idx = array_idx;
2021 for (uint32_t j = 0; j < det_ctx->pmq.rule_id_array_cnt; j++) {
2022 const Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[j]];
2023 if (ts->app_inspect != NULL) {
2024 const SigIntId id = ts->iid;
2025 det_ctx->tx_candidates[array_idx].s = ts;
2026 det_ctx->tx_candidates[array_idx].id = id;
2027 det_ctx->tx_candidates[array_idx].flags = NULL;
2028 det_ctx->tx_candidates[array_idx].stream_reset = 0;
2029 array_idx++;
2030
2031 SCLogDebug("%p/%" PRIu64 " rule %u (%u) added from 'post match' prefilter",
2032 tx.tx_ptr, tx.tx_id, ts->id, id);
2033 }
2034 }
2035 SCLogDebug("%p/%" PRIu64 " rules added from 'post match' prefilter: %u", tx.tx_ptr,
2036 tx.tx_id, array_idx - prev_array_idx);
2037 if (prev_array_idx != array_idx) {
2038 /* sort, but only part of array we're still going to process */
2039 qsort(det_ctx->tx_candidates + i, array_idx - i, sizeof(RuleMatchCandidateTx),
2040 DetectRunTxSortHelper);
2041 }
2042 det_ctx->post_rule_work_queue.len = 0;
2043 PMQ_RESET(&det_ctx->pmq);
2044 }
2045
2046 if (break_out_of_app_filter)
2047 break;
2048 }
2049 if (tx_fw_verdict)
2050 fw_verdicted++;
2051
2052 det_ctx->tx_id = 0;
2053 det_ctx->tx_id_set = false;
2054 det_ctx->p = NULL;
2055
2056 /* see if we have any updated state to store in the tx */
2057
2058 /* this side of the tx is done */
2059 if (tx.tx_progress >= tx.tx_end_state) {
2060 SCLogDebug("%" PRIu64 ": %s tx done", p->pcap_cnt,
2061 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
2062 const uint8_t inspected_flag = (flow_flags & STREAM_TOSERVER)
2065 tx.tx_data_ptr->flags |= inspected_flag;
2066 SCLogDebug("%p/%" PRIu64 " tx is done for direction %s. Progress %02x", tx.tx_ptr,
2067 tx.tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
2068 tx.detect_progress);
2069 }
2070
2072 SCLogDebug("%" PRIu64 ": %s tx state change %u -> %u", p->pcap_cnt,
2073 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.detect_progress_orig,
2074 tx.detect_progress);
2075 SCLogDebug("%p/%" PRIu64 " Storing new progress %02x (was %02x)", tx.tx_ptr, tx.tx_id,
2077
2078 StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
2079 }
2080
2081 InspectionBufferClean(det_ctx);
2082
2083 next:
2084 if (!ires.has_next)
2085 break;
2086 }
2087
2088 /* apply default policy if there were txs to inspect, we have fw rules and non of the rules
2089 * applied a policy. */
2090 SCLogDebug("packet %" PRIu64 ": tx_inspected %u fw_verdicted %u", p->pcap_cnt, tx_inspected,
2091 fw_verdicted);
2092 if (tx_inspected && have_fw_rules && tx_inspected != fw_verdicted) {
2093 SCLogDebug("%" PRIu64 ": %s default drop", p->pcap_cnt,
2094 flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
2097 return;
2098 }
2099 /* if all tables have been bypassed, we accept:packet */
2100 if (tx_inspected == 0 && fw_verdicted == 0 && have_fw_rules) {
2101 DetectRunAppendDefaultAccept(det_ctx, p);
2102 }
2103}
2104
2105static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2106 Packet *p, Flow *f, DetectRunScratchpad *scratch)
2107{
2108 const SigGroupHead *const sgh = scratch->sgh;
2109 const AppProto alproto = f->alproto;
2110
2111 /* for TCP, limit inspection to pseudo packets or real packet that did
2112 * an app-layer update. */
2113 if (p->proto == IPPROTO_TCP && !PKT_IS_PSEUDOPKT(p) &&
2114 ((PKT_IS_TOSERVER(p) && (f->flags & FLOW_TS_APP_UPDATED) == 0) ||
2115 (PKT_IS_TOCLIENT(p) && (f->flags & FLOW_TC_APP_UPDATED) == 0))) {
2116 SCLogDebug("pcap_cnt %" PRIu64 ": %s: skip frame inspection for TCP w/o APP UPDATE",
2117 p->pcap_cnt, PKT_IS_TOSERVER(p) ? "toserver" : "toclient");
2118 return;
2119 }
2120 FramesContainer *frames_container = AppLayerFramesGetContainer(f);
2121 if (frames_container == NULL) {
2122 return;
2123 }
2124 Frames *frames;
2125 if (PKT_IS_TOSERVER(p)) {
2126 frames = &frames_container->toserver;
2127 } else {
2128 frames = &frames_container->toclient;
2129 }
2130
2131 for (uint32_t idx = 0; idx < frames->cnt; idx++) {
2132 SCLogDebug("frame %u", idx);
2133 Frame *frame = FrameGetByIndex(frames, idx);
2134 if (frame == NULL) {
2135 continue;
2136 }
2137
2138 det_ctx->frame_inspect_progress = 0;
2139 uint32_t array_idx = 0;
2140 uint32_t total_rules = det_ctx->match_array_cnt;
2141
2142 /* run prefilter engines and merge results into a candidates array */
2143 if (sgh->frame_engines) {
2144 // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
2145 DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto);
2146 // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
2147 SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
2148 det_ctx->pmq.rule_id_array_cnt);
2149
2150 total_rules += det_ctx->pmq.rule_id_array_cnt;
2151
2152 if (!(RuleMatchCandidateTxArrayHasSpace(
2153 det_ctx, total_rules))) { // TODO is it safe to overload?
2154 RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
2155 }
2156
2157 for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
2158 const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
2159 const SigIntId id = s->iid;
2160 det_ctx->tx_candidates[array_idx].s = s;
2161 det_ctx->tx_candidates[array_idx].id = id;
2162 det_ctx->tx_candidates[array_idx].flags = NULL;
2163 det_ctx->tx_candidates[array_idx].stream_reset = 0;
2164 array_idx++;
2165 }
2166 PMQ_RESET(&det_ctx->pmq);
2167 }
2168 /* merge 'state' rules from the regular prefilter */
2169 uint32_t x = array_idx;
2170 for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
2171 const Signature *s = det_ctx->match_array[i];
2172 if (s->frame_inspect != NULL) {
2173 const SigIntId id = s->iid;
2174 det_ctx->tx_candidates[array_idx].s = s;
2175 det_ctx->tx_candidates[array_idx].id = id;
2176 det_ctx->tx_candidates[array_idx].flags = NULL;
2177 det_ctx->tx_candidates[array_idx].stream_reset = 0;
2178 array_idx++;
2179
2180 SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
2181 s->id, id);
2182 }
2183 }
2184 SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
2185 array_idx - x);
2186 (void)x;
2187
2188 /* run rules: inspect the match candidates */
2189 for (uint32_t i = 0; i < array_idx; i++) {
2190 const Signature *s = det_ctx->tx_candidates[i].s;
2191
2192 /* deduplicate: rules_array is sorted, but not deduplicated.
2193 * As they are back to back in that case we can check for it
2194 * here. We select the stored state one as that comes first
2195 * in the array. */
2196 while ((i + 1) < array_idx &&
2197 det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
2198 i++;
2199 }
2200 SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->iid);
2201
2202 /* start new inspection */
2203 SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
2204
2205 /* call individual rule inspection */
2207 bool r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
2208 if (r) {
2209 r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame);
2210 if (r) {
2211 /* match */
2212 DetectRunPostMatch(tv, det_ctx, p, s);
2213
2215 det_ctx->frame_id = frame->id;
2216 SCLogDebug(
2217 "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->iid);
2218 if (frame->flags & FRAME_FLAG_TX_ID_SET) {
2219 alert_flags |= PACKET_ALERT_FLAG_TX;
2220 }
2221 AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags);
2222 }
2223 }
2224 DetectVarProcessList(det_ctx, p->flow, p);
2225 RULE_PROFILING_END(det_ctx, s, r, p);
2226 }
2227
2228 /* update Frame::inspect_progress here instead of in the code above. The reason is that a
2229 * frame might be used more than once in buffers with transforms. */
2230 if (frame->inspect_progress < det_ctx->frame_inspect_progress) {
2231 frame->inspect_progress = det_ctx->frame_inspect_progress;
2232 SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
2233 } else {
2234 SCLogDebug(
2235 "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
2236 }
2237
2238 SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
2239 InspectionBufferClean(det_ctx);
2240 }
2241}
2242
2243static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
2244{
2245 /* technically we need to pass a DetectEngineThreadCtx struct with the
2246 * tenant_id member. But as that member is the first in the struct, we
2247 * can use the id directly. */
2248 return HashTableLookup(h, &id, 0);
2249}
2250
2251static void DetectFlow(ThreadVars *tv,
2253 Packet *p)
2254{
2255 Flow *const f = p->flow;
2256
2257 /* we check the flow drop here, and not the packet drop. This is
2258 * to allow stream engine "invalid" drop packets to still be
2259 * evaluated by the stream event rules. */
2260 if (f->flags & FLOW_ACTION_DROP) {
2262 SCReturn;
2263 }
2264
2265 /* in firewall mode, we still need to run the fw rulesets even for exception policy pass */
2266 bool skip = false;
2267 if (EngineModeIsFirewall()) {
2268 skip = (f->flags & (FLOW_ACTION_ACCEPT));
2269
2270 } else {
2271 skip = (p->flags & PKT_NOPACKET_INSPECTION || f->flags & (FLOW_ACTION_PASS));
2272 }
2273 if (skip) {
2274 /* enfore prior accept:flow */
2275 if (f->flags & FLOW_ACTION_ACCEPT) {
2276 p->action |= ACTION_ACCEPT;
2277 }
2278 /* hack: if we are in pass the entire flow mode, we need to still
2279 * update the inspect_id forward. So test for the condition here,
2280 * and call the update code if necessary. */
2281 const int pass = (f->flags & (FLOW_ACTION_PASS | FLOW_ACTION_ACCEPT));
2282 if (pass) {
2283 uint8_t flags = STREAM_FLAGS_FOR_PACKET(p);
2285 if (f->alstate) {
2287 }
2288 }
2289 SCLogDebug("p->pcap %"PRIu64": no detection on packet, "
2290 "PKT_NOPACKET_INSPECTION is set", p->pcap_cnt);
2291 return;
2292 }
2293
2294 /* see if the packet matches one or more of the sigs */
2295 DetectRun(tv, de_ctx, det_ctx, p);
2296}
2297
2298
2299static void DetectNoFlow(ThreadVars *tv,
2301 Packet *p)
2302{
2303 /* No need to perform any detection on this packet, if the given flag is set.*/
2305 return;
2306 }
2307
2308 /* see if the packet matches one or more of the sigs */
2309 DetectRun(tv, de_ctx, det_ctx, p);
2310}
2311
2313{
2314 const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
2315 const SigGroupHead *sgh = de_ctx->pre_flow_sgh;
2316
2317 SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, p->pcap_cnt, sgh);
2318 DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
2319 return p->action;
2320}
2321
2323{
2324 const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
2325 const int direction = (PKT_IS_TOCLIENT(p) != 0);
2326 const SigGroupHead *sgh = de_ctx->pre_stream_sgh[direction];
2327
2328 SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, p->pcap_cnt, sgh);
2329 DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
2330 return p->action;
2331}
2332
2333/** \brief Detection engine thread wrapper.
2334 * \param tv thread vars
2335 * \param p packet to inspect
2336 * \param data thread specific data
2337 * \param pq packet queue
2338 * \retval TM_ECODE_FAILED error
2339 * \retval TM_ECODE_OK ok
2340 */
2342{
2344
2345 DetectEngineCtx *de_ctx = NULL;
2347 if (det_ctx == NULL) {
2348 printf("ERROR: Detect has no thread ctx\n");
2349 goto error;
2350 }
2351
2352 if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
2353 (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
2354 SCLogDebug("Detect Engine using new det_ctx - %p",
2355 det_ctx);
2356 }
2357
2358 /* if in MT mode _and_ we have tenants registered, use
2359 * MT logic. */
2360 if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL)
2361 {
2362 uint32_t tenant_id = p->tenant_id;
2363 if (tenant_id == 0)
2364 tenant_id = det_ctx->TenantGetId(det_ctx, p);
2365 if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
2366 p->tenant_id = tenant_id;
2367 det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
2368 if (det_ctx == NULL)
2369 return TM_ECODE_OK;
2370 de_ctx = det_ctx->de_ctx;
2371 if (de_ctx == NULL)
2372 return TM_ECODE_OK;
2373
2374 if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
2375 (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
2376 SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
2377 }
2378 } else {
2379 /* use default if no tenants are registered for this packet */
2380 de_ctx = det_ctx->de_ctx;
2381 }
2382 } else {
2383 de_ctx = det_ctx->de_ctx;
2384 }
2385
2386 if (p->flow) {
2387 DetectFlow(tv, de_ctx, det_ctx, p);
2388 } else {
2389 DetectNoFlow(tv, de_ctx, det_ctx, p);
2390 }
2391
2392#ifdef PROFILE_RULES
2393 /* aggregate statistics */
2394 struct timeval ts;
2395 gettimeofday(&ts, NULL);
2396 if (ts.tv_sec != det_ctx->rule_perf_last_sync) {
2397 SCProfilingRuleThreatAggregate(det_ctx);
2398 det_ctx->rule_perf_last_sync = ts.tv_sec;
2399 }
2400#endif
2401
2402 return TM_ECODE_OK;
2403error:
2404 return TM_ECODE_FAILED;
2405}
2406
2407/** \brief disable file features we don't need
2408 * Called if we have no detection engine.
2409 */
2411{
2412 DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER);
2413 DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT);
2414}
2415
2416#ifdef UNITTESTS
2417/**
2418 * \brief wrapper for old tests
2419 */
2422{
2423 if (p->flow) {
2424 DetectFlow(tv, de_ctx, det_ctx, p);
2425 } else {
2426 DetectNoFlow(tv, de_ctx, det_ctx, p);
2427 }
2428}
2429#endif
2430
2431/*
2432 * TESTS
2433 */
2434
2435#ifdef UNITTESTS
2436#include "tests/detect.c"
2437#endif
#define ACTION_ACCEPT
ActionScope
@ ACTION_SCOPE_HOOK
@ ACTION_SCOPE_PACKET
@ ACTION_SCOPE_TX
@ ACTION_SCOPE_FLOW
#define ACTION_DROP
Frame * FrameGetByIndex(Frames *frames, const uint32_t idx)
#define FRAME_FLAG_TX_ID_SET
FramesContainer * AppLayerFramesGetContainer(Flow *f)
struct HtpBodyChunk_ * next
AppLayerTxData * AppLayerParserGetTxData(uint8_t ipproto, AppProto alproto, void *tx)
uint16_t SCAppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint16_t flag)
int AppLayerParserGetStateProgressCompletionStatus(AppProto alproto, uint8_t direction)
uint64_t AppLayerParserGetTxCnt(const Flow *f, void *alstate)
AppLayerGetTxIteratorFunc AppLayerGetTxIterator(const uint8_t ipproto, const AppProto alproto)
uint64_t AppLayerParserGetTransactionInspectId(AppLayerParserState *pstate, uint8_t direction)
bool AppLayerParserHasDecoderEvents(AppLayerParserState *pstate)
void * AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id)
int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, void *alstate, uint8_t flags)
get the progress value for a tx/protocol
void AppLayerParserSetTransactionInspectId(const Flow *f, AppLayerParserState *pstate, void *alstate, const uint8_t flags, bool tag_txs_as_inspected)
#define APP_LAYER_TX_ACCEPT
#define APP_LAYER_TX_INSPECTED_TC
struct AppLayerGetTxIterTuple AppLayerGetTxIterTuple
#define APP_LAYER_PARSER_NO_INSPECTION
struct AppLayerTxData AppLayerTxData
#define APP_LAYER_TX_SKIP_INSPECT_TC
#define APP_LAYER_TX_SKIP_INSPECT_TS
#define APP_LAYER_TX_INSPECTED_TS
AppLayerGetTxIterTuple(* AppLayerGetTxIteratorFunc)(const uint8_t ipproto, const AppProto alproto, void *alstate, uint64_t min_tx_id, uint64_t max_tx_id, AppLayerGetTxIterState *state)
tx iterator prototype
uint16_t AppProto
@ ALPROTO_HTTP2
@ ALPROTO_UNKNOWN
@ ALPROTO_DOH2
@ ALPROTO_DNS
void StatsAddUI64(ThreadVars *tv, uint16_t id, uint64_t x)
Adds a value of type uint64_t to the local counter.
Definition counters.c:146
uint8_t flags
Definition decode-gre.h:0
#define ICMPV4_DEST_UNREACH_IS_VALID(p)
uint8_t proto
#define PKT_HAS_FLOW
Definition decode.h:1266
@ PKT_DROP_REASON_DEFAULT_PACKET_POLICY
Definition decode.h:398
@ PKT_DROP_REASON_DEFAULT_APP_POLICY
Definition decode.h:399
#define PKT_NOPACKET_INSPECTION
Definition decode.h:1247
#define PKT_NOPAYLOAD_INSPECTION
Definition decode.h:1252
#define PKT_PSEUDO_STREAM_END
Definition decode.h:1268
#define PKT_STREAM_ADD
Definition decode.h:1260
#define PKT_IS_TOCLIENT(p)
Definition decode.h:239
#define PKT_DETECT_HAS_STREAMDATA
Definition decode.h:1305
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
#define PKT_IS_TOSERVER(p)
Definition decode.h:238
#define PKT_IS_FRAGMENT
Definition decode.h:1290
#define PKT_STREAM_EST
Definition decode.h:1262
@ PKT_SRC_WIRE
Definition decode.h:52
#define IPPROTO_SCTP
Definition decode.h:1228
int DetectAddressMatchIPv4(const DetectMatchAddressIPv4 *addrs, uint16_t addrs_cnt, const Address *a)
Match a packets address against a signatures addrs array.
int DetectAddressMatchIPv6(const DetectMatchAddressIPv6 *addrs, uint16_t addrs_cnt, const Address *a)
Match a packets address against a signatures addrs array.
void AlertQueueAppend(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint64_t tx_id, uint8_t alert_flags)
Append signature to local packet alert queue for later preprocessing.
void PacketAlertFinalize(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
Check the threshold of the sigs that match, set actions, break on pass action This function iterate t...
void PacketCreateMask(Packet *p, SignatureMask *mask, AppProto alproto, bool app_decoder_events)
bool DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s, Flow *f, Packet *p, const Frames *frames, const Frame *frame)
void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, const Frames *frames, const Frame *frame, const AppProto alproto)
void InspectionBufferClean(DetectEngineThreadCtx *det_ctx)
void IPOnlyMatchPacket(ThreadVars *tv, const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const DetectEngineIPOnlyCtx *io_ctx, Packet *p)
Match a packet against the IP Only detection engine contexts.
DetectPort * DetectPortLookupGroup(DetectPort *dp, uint16_t port)
Function that find the group matching port in a group head.
void PrefilterPostRuleMatch(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, Flow *f)
invoke post-rule match "prefilter" engines
void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, const uint8_t ipproto, const uint8_t flow_flags, const AppProto alproto, void *alstate, DetectTransaction *tx)
run prefilter engines on a transaction
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, const uint8_t flags, const SignatureMask mask)
void RulesDumpTxMatchArray(const DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, const Packet *p, const uint64_t tx_id, const uint32_t rule_cnt, const uint32_t pkt_prefilter_cnt)
void RulesDumpMatchArray(const DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, const Packet *p)
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
#define DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV4
Data structures and function prototypes for keeping state for the detection engine.
#define DETECT_ENGINE_INSPECT_SIG_MATCH
#define DE_STATE_CHUNK_SIZE
#define DETECT_ENGINE_STATE_FLAG_FILE_NEW
#define DE_STATE_FLAG_SIG_CANT_MATCH
#define DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES
#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES
#define DE_STATE_FLAG_FILE_INSPECT
#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
#define DE_STATE_FLAG_FULL_INSPECT
bool DetectEnginePktInspectionRun(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s, Flow *f, Packet *p, uint8_t *alert_flags)
uint32_t id
SigTableElmt * sigmatch_table
void DisableDetectFlowFileFlags(Flow *f)
disable file features we don't need Called if we have no detection engine.
Definition detect.c:2410
uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
Definition detect.c:2312
#define NO_TX
Definition detect.c:1397
const SigGroupHead * SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, const Packet *p)
Get the SigGroupHead for a packet.
Definition detect.c:282
TmEcode Detect(ThreadVars *tv, Packet *p, void *data)
Detection engine thread wrapper.
Definition detect.c:2341
void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size)
Definition detect.c:1073
#define TRACE_SID_TXS(sid, txs,...)
Definition detect.c:1149
thread_local Signature default_accept
Definition detect.c:1619
uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
Definition detect.c:2322
void * DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags)
Definition detect.c:1153
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx)
Definition detect.c:1086
#define SIG_GROUP_HEAD_HAVERAWSTREAM
Definition detect.h:1488
#define SIG_FLAG_SP_ANY
Definition detect.h:243
#define SIG_GROUP_HEAD_HAVEFILESHA1
Definition detect.h:1494
#define PACKET_ALERT_NOTX
Definition detect.h:55
#define SIG_GROUP_HEAD_HAVEFILESHA256
Definition detect.h:1495
#define SIG_FLAG_SRC_ANY
Definition detect.h:241
@ SIG_TYPE_PKT
Definition detect.h:72
#define SIG_FLAG_DST_ANY
Definition detect.h:242
#define SIG_GROUP_HEAD_HAVEFILESIZE
Definition detect.h:1493
#define SIG_FLAG_FIREWALL
Definition detect.h:246
#define SIG_FLAG_REQUIRE_FLOWVAR
Definition detect.h:267
#define SIG_FLAG_DP_ANY
Definition detect.h:244
@ DETECT_SM_LIST_POSTMATCH
Definition detect.h:127
#define SIG_GROUP_HEAD_HAVEFILEMD5
Definition detect.h:1492
#define SIG_FLAG_APPLAYER
Definition detect.h:249
#define SIG_FLAG_TXBOTHDIR
Definition detect.h:250
SCMutex m
Definition flow-hash.h:6
int FlowGetPacketDirection(const Flow *f, const Packet *p)
determine the direction of the packet compared to the flow
Definition flow.c:279
uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags)
get 'disruption' flags: GAP/DEPTH/PASS
Definition flow.c:1141
#define FLOWFILE_NONE
Definition flow.h:168
#define FLOW_SGH_TOCLIENT
Definition flow.h:75
#define FLOW_PKT_TOSERVER
Definition flow.h:233
#define FLOWFILE_NO_SIZE_TC
Definition flow.h:150
#define FLOW_TC_APP_UPDATED
Definition flow.h:120
#define FLOW_ACTION_DROP
Definition flow.h:70
#define FLOWFILE_NO_STORE_TC
Definition flow.h:135
#define FLOW_SGH_TOSERVER
Definition flow.h:73
#define FLOW_ACTION_ACCEPT
Definition flow.h:62
#define FLOWFILE_NO_SIZE_TS
Definition flow.h:149
#define FLOWFILE_NO_SHA1_TS
Definition flow.h:141
#define FLOWFILE_NO_STORE_TS
Definition flow.h:134
#define FLOWFILE_NO_SHA256_TS
Definition flow.h:145
#define FLOW_TS_APP_UPDATED
Definition flow.h:119
#define FLOWFILE_NO_SHA256_TC
Definition flow.h:146
#define FLOW_PKT_TOCLIENT_FIRST
Definition flow.h:237
#define FLOWFILE_INIT
Definition flow.h:127
#define FLOW_PKT_ESTABLISHED
Definition flow.h:235
#define FLOW_PKT_TOSERVER_FIRST
Definition flow.h:236
#define FLOWFILE_NO_SHA1_TC
Definition flow.h:142
#define FLOWFILE_NO_MD5_TS
Definition flow.h:137
#define TOSERVER
Definition flow.h:45
#define FLOWFILE_NO_MAGIC_TS
Definition flow.h:130
#define FLOW_PKT_TOCLIENT
Definition flow.h:234
#define FLOW_ACTION_PASS
Definition flow.h:117
#define FLOWFILE_NO_MD5_TC
Definition flow.h:138
#define FLOWFILE_NO_MAGIC_TC
Definition flow.h:131
ThreadVars * tv
DetectEngineCtx * de_ctx
#define PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET
Definition decode.h:280
#define PACKET_ALERT_FLAG_TX_GUESSED
Definition decode.h:278
#define PACKET_ALERT_FLAG_TX
Definition decode.h:272
#define PACKET_ALERT_FLAG_STATE_MATCH
Definition decode.h:268
#define PACKET_ALERT_FLAG_FRAME
Definition decode.h:276
const char * PktSrcToString(enum PktSrcEnum pkt_src)
Definition decode.c:855
void DetectRunStoreStateTx(const SigGroupHead *sgh, Flow *f, void *tx, uint64_t tx_id, const Signature *s, uint32_t inspect_flags, uint8_t flow_flags, const uint16_t file_no_match)
void DetectEngineStateResetTxs(Flow *f)
Reset de state for active tx' To be used on detect engine reload.
void PacketDrop(Packet *p, const uint8_t action, enum PacketDropReason r)
issue drop action
Definition packet.c:33
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition packet.c:49
uint64_t ts
uint16_t pad
#define STREAMTCP_STREAM_FLAG_DISABLE_RAW
#define STREAMTCP_FLAG_APP_LAYER_DISABLED
bool StreamReassembleRawHasDataReady(TcpSession *ssn, Packet *p)
does the stream engine have data to inspect?
void StreamReassembleRawUpdateProgress(TcpSession *ssn, Packet *p, const uint64_t progress)
update stream engine after detection
#define STREAM_FLAGS_FOR_PACKET(p)
Definition stream.h:30
struct DeStateStore_ * next
DeStateStoreItem store[DE_STATE_CHUNK_SIZE]
struct DetectEngineAppInspectionEngine_ * next
Definition detect.h:441
InspectEngineFuncPtr Callback
Definition detect.h:434
struct DetectEngineAppInspectionEngine_::@90 v2
main detection engine ctx
Definition detect.h:932
DetectEngineIPOnlyCtx io_ctx
Definition detect.h:970
struct SigGroupHead_ * pre_stream_sgh[2]
Definition detect.h:1155
bool guess_applayer
Definition detect.h:979
Signature ** sig_array
Definition detect.h:950
uint32_t profile_match_logging_threshold
Definition detect.h:1049
struct SigGroupHead_ * pre_flow_sgh
Definition detect.h:1160
uint8_t guess_applayer_log_limit
Definition detect.h:976
struct SigGroupHead_ * decoder_event_sgh
Definition detect.h:1017
DetectEngineLookupFlow flow_gh[FLOW_STATES]
Definition detect.h:959
uint32_t version
Definition detect.h:1013
uint32_t sig_array_len
Definition detect.h:951
DetectPort * udp
Definition detect.h:863
DetectPort * tcp
Definition detect.h:862
struct SigGroupHead_ * sgh[256]
Definition detect.h:864
DetectEngineStateDirection dir_state[2]
uint16_t alert_queue_size
Definition detect.h:1329
uint32_t(* TenantGetId)(const void *, const Packet *p)
Definition detect.h:1261
uint32_t tx_candidates_size
Definition detect.h:1343
RuleMatchCandidateTx * tx_candidates
Definition detect.h:1342
uint16_t counter_alerts
Definition detect.h:1289
uint16_t counter_match_list
Definition detect.h:1298
Signature ** match_array
Definition detect.h:1335
HashTable * mt_det_ctxs_hash
Definition detect.h:1256
uint32_t mt_det_ctxs_cnt
Definition detect.h:1254
uint64_t frame_inspect_progress
Definition detect.h:1322
uint16_t counter_alerts_suppressed
Definition detect.h:1293
SigIntId match_array_cnt
Definition detect.h:1340
uint16_t counter_alerts_overflow
Definition detect.h:1291
PostRuleMatchWorkQueue post_rule_work_queue
Definition detect.h:1347
uint64_t raw_stream_progress
Definition detect.h:1265
DetectEngineCtx * de_ctx
Definition detect.h:1364
uint16_t counter_mpm_list
Definition detect.h:1295
PrefilterRuleStore pmq
Definition detect.h:1349
Port structure for detection engine.
Definition detect.h:220
struct SigGroupHead_ * sh
Definition detect.h:231
const uint8_t default_action
Definition detect.c:79
const uint8_t flow_flags
Definition detect.c:71
const SigGroupHead * sgh
Definition detect.c:80
const AppProto alproto
Definition detect.c:70
const bool app_decoder_events
Definition detect.c:72
DetectEngineStateDirection * de_state
struct AppLayerTxData * tx_data_ptr
Flow data structure.
Definition flow.h:356
const struct SigGroupHead_ * sgh_toclient
Definition flow.h:483
uint8_t proto
Definition flow.h:378
uint32_t flags
Definition flow.h:421
GenericVar * flowvar
Definition flow.h:489
uint32_t de_ctx_version
Definition flow.h:464
AppProto alproto
application level protocol
Definition flow.h:450
void * alstate
Definition flow.h:479
uint32_t tenant_id
Definition flow.h:416
const struct SigGroupHead_ * sgh_toserver
Definition flow.h:486
void * protoctx
Definition flow.h:441
AppLayerParserState * alparser
Definition flow.h:478
int64_t id
uint64_t tx_id
uint8_t flags
uint64_t inspect_progress
uint16_t cnt
uint8_t action
Definition decode.h:250
uint16_t suppressed
Definition decode.h:289
uint16_t discarded
Definition decode.h:288
uint16_t cnt
Definition decode.h:287
PacketAlert drop
Definition decode.h:293
uint32_t tenant_id
Definition decode.h:665
uint8_t flowflags
Definition decode.h:532
uint64_t pcap_cnt
Definition decode.h:626
uint8_t action
Definition decode.h:609
uint8_t pkt_src
Definition decode.h:611
Address src
Definition decode.h:505
Port sp
Definition decode.h:508
SignatureMask sig_mask
Definition decode.h:538
uint8_t app_update_direction
Definition decode.h:535
PacketAlerts alerts
Definition decode.h:620
struct Flow_ * flow
Definition decode.h:546
PacketEngineEvents events
Definition decode.h:630
uint32_t flags
Definition decode.h:544
Address dst
Definition decode.h:506
uint8_t proto
Definition decode.h:523
Port dp
Definition decode.h:516
uint8_t stream_result
Definition detect.h:1204
uint32_t * flags
Definition detect.h:1200
const Signature * s
Definition detect.h:1209
uint32_t stream_reset
Definition detect.h:1206
Container for matching data for a signature group.
Definition detect.h:1629
PrefilterEngine * frame_engines
Definition detect.h:1642
PrefilterEngine * tx_engines
Definition detect.h:1641
uint16_t flags
Definition detect.h:1630
uint16_t filestore_cnt
Definition detect.h:1635
Data needed for Match()
Definition detect.h:365
bool is_last
Definition detect.h:367
SigMatchCtx * ctx
Definition detect.h:368
uint16_t type
Definition detect.h:366
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
Signature container.
Definition detect.h:668
DetectEngineFrameInspectionEngine * frame_inspect
Definition detect.h:727
uint8_t action
Definition detect.h:683
DetectPort * sp
Definition detect.h:719
enum SignatureType type
Definition detect.h:671
uint32_t flags
Definition detect.h:669
uint16_t addr_dst_match6_cnt
Definition detect.h:695
uint8_t action_scope
Definition detect.h:690
DetectMatchAddressIPv4 * addr_src_match4
Definition detect.h:708
uint16_t addr_src_match4_cnt
Definition detect.h:694
uint16_t addr_src_match6_cnt
Definition detect.h:696
SigIntId iid
Definition detect.h:680
AppProto alproto
Definition detect.h:673
uint16_t addr_dst_match4_cnt
Definition detect.h:693
DetectEngineAppInspectionEngine * app_inspect
Definition detect.h:725
DetectProto proto
Definition detect.h:687
DetectMatchAddressIPv6 * addr_dst_match6
Definition detect.h:710
DetectPort * dp
Definition detect.h:719
SignatureMask mask
Definition detect.h:679
uint32_t id
Definition detect.h:713
DetectMatchAddressIPv6 * addr_src_match6
Definition detect.h:711
uint8_t app_progress_hook
Definition detect.h:705
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition detect.h:731
DetectMatchAddressIPv4 * addr_dst_match4
Definition detect.h:707
Per thread variable structure.
Definition threadvars.h:58
@ PROF_DETECT_TX_UPDATE
@ PROF_DETECT_GETSGH
@ PROF_DETECT_CLEANUP
@ PROF_DETECT_SETUP
@ PROF_DETECT_RULES
@ PROF_DETECT_PF_SORT2
@ PROF_DETECT_IPONLY
@ PROF_DETECT_PF_TX
@ PROF_DETECT_ALERT
@ PROF_DETECT_TX
#define SigIntId
#define BIT_U32(n)
bool EngineModeIsFirewall(void)
Definition suricata.c:235
int RunmodeIsUnittests(void)
Definition suricata.c:270
@ TM_ECODE_FAILED
@ TM_ECODE_OK
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define BOOL2STR(b)
Definition util-debug.h:535
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnPtr(x, type)
Definition util-debug.h:293
#define SCReturn
Definition util-debug.h:279
void FileUpdateFlowFileFlags(Flow *f, uint16_t set_file_flags, uint8_t direction)
set a flow's file flags
Definition util-file.c:1103
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition util-hash.c:183
#define SCFree(p)
Definition util-mem.h:61
#define SCRealloc(ptr, sz)
Definition util-mem.h:50
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define likely(expr)
#define unlikely(expr)
#define PMQ_RESET(pmq)
#define PACKET_PROFILING_DETECT_START(p, id)
#define KEYWORD_PROFILING_END(ctx, type, m)
#define RULE_PROFILING_END(a, b, c, p)
#define KEYWORD_PROFILING_START
#define KEYWORD_PROFILING_SET_LIST(ctx, list)
#define SGH_PROFILING_RECORD(det_ctx, sgh)
#define PACKET_PROFILING_DETECT_END(p, id)
#define RULE_PROFILING_START(p)
#define DEBUG_VALIDATE_BUG_ON(exp)
#define DEBUG_VALIDATE_PACKET(p)
void GenericVarFree(GenericVar *gv)
Definition util-var.c:48