suricata
detect-engine-alert.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2026 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 #include "suricata-common.h"
19 #include "suricata.h"
20 
21 #include "detect.h"
22 #include "detect-parse.h"
23 #include "detect-engine-alert.h"
25 #include "detect-engine-tag.h"
26 
27 #include "decode.h"
28 #include "packet.h"
29 
30 #include "flow.h"
31 #include "flow-private.h"
32 
33 #ifdef QA_SIMULATION
34 #include "util-exception-policy.h"
35 #endif
36 
37 #include "util-profiling.h"
38 #include "util-validate.h"
39 
40 #include "action-globals.h"
41 #include "capture-hooks.h"
42 
43 /** tag signature we use for tag alerts */
44 static Signature g_tag_signature;
45 /** tag packet alert structure for tag alerts */
46 static PacketAlert g_tag_pa;
47 
49 {
50  memset(&g_tag_signature, 0x00, sizeof(g_tag_signature));
51 
52  g_tag_signature.id = TAG_SIG_ID;
53  g_tag_signature.gid = TAG_SIG_GEN;
54  g_tag_signature.iid = TAG_SIG_ID;
55  g_tag_signature.rev = 1;
56  g_tag_signature.prio = 2;
57 
58  memset(&g_tag_pa, 0x00, sizeof(g_tag_pa));
59 
60  g_tag_pa.action = ACTION_ALERT;
61  g_tag_pa.s = &g_tag_signature;
62 }
63 
64 /**
65  * \brief Handle a packet and check if needs a threshold logic
66  * Also apply rule action if necessary.
67  *
68  * \param de_ctx Detection Context
69  * \param sig Signature pointer
70  * \param p Packet structure
71  *
72  * \retval 1 alert is not suppressed
73  * \retval 0 alert is suppressed
74  */
75 static int PacketAlertHandle(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
76  const Signature *s, Packet *p, PacketAlert *pa)
77 {
78  SCEnter();
79  int ret = 1;
80  const DetectThresholdData *td = NULL;
81  const SigMatchData *smd;
82 
83  if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
84  SCReturnInt(1);
85  }
86 
87  /* handle suppressions first */
88  if (s->sm_arrays[DETECT_SM_LIST_SUPPRESS] != NULL) {
90  smd = NULL;
91  do {
93  if (td != NULL) {
94  SCLogDebug("td %p", td);
95 
96  /* PacketAlertThreshold returns 2 if the alert is suppressed but
97  * we do need to apply rule actions to the packet. */
99  ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
100  if (ret == 0 || ret == 2) {
102  /* It doesn't match threshold, remove it */
103  SCReturnInt(ret);
104  }
106  }
107  } while (smd != NULL);
108  }
109 
110  /* if we're still here, consider thresholding */
111  if (s->sm_arrays[DETECT_SM_LIST_THRESHOLD] != NULL) {
113  smd = NULL;
114  do {
116  if (td != NULL) {
117  SCLogDebug("td %p", td);
118 
119  /* PacketAlertThreshold returns 2 if the alert is suppressed but
120  * we do need to apply rule actions to the packet. */
122  ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
123  if (ret == 0 || ret == 2) {
125  /* It doesn't match threshold, remove it */
126  SCReturnInt(ret);
127  }
129  }
130  } while (smd != NULL);
131  }
132  SCReturnInt(1);
133 }
134 
135 #ifdef UNITTESTS
136 /**
137  * \brief Check if a certain sid alerted, this is used in the test functions
138  *
139  * \param p Packet on which we want to check if the signature alerted or not
140  * \param sid Signature id of the signature that has to be checked for a match
141  *
142  * \retval match A value > 0 on a match; 0 on no match
143  */
144 int PacketAlertCheck(Packet *p, uint32_t sid)
145 {
146  int match = 0;
147 
148  for (uint16_t i = 0; i < p->alerts.cnt; i++) {
149  BUG_ON(p->alerts.alerts[i].s == NULL);
150  if (p->alerts.alerts[i].s->id == sid)
151  match++;
152  }
153 
154  return match;
155 }
156 #endif
157 
158 static inline void RuleActionToFlow(const uint8_t action, Flow *f, const bool fw_rule)
159 {
160  if (action & ACTION_ACCEPT) {
161  DEBUG_VALIDATE_BUG_ON(!fw_rule);
163  SCLogDebug("setting flow action accept");
164  }
165 
166  /* pass:flow can be set if accept:flow is present */
167  if (action & ACTION_PASS) {
169  /* drop or pass already set. First to set wins. */
170  SCLogDebug("not setting %s flow already set to %s",
171  (action & ACTION_PASS) ? "pass" : "drop",
172  (f->flags & FLOW_ACTION_DROP) ? "drop" : "pass");
173  } else {
175  SCLogDebug("setting flow action pass");
176  }
177 
178  // TODO firewall drop:flow should override FLOW_ACTION_PASS
179  } else if (action & (ACTION_DROP | ACTION_REJECT_ANY)) {
180  /* drop:flow from TD rules will override a accept:flow from
181  * firewall rules. */
182  if (f->flags & FLOW_ACTION_ACCEPT) {
184  FlowSetDropAction(f, fw_rule ? FLOW_ACTION_BY_FIREWALL : 0);
185  SCLogDebug("replaced FLOW_ACTION_ACCEPT with FLOW_ACTION_DROP");
186  }
188  /* drop or pass already set. First to set wins. */
189  SCLogDebug("not setting %s flow already set to %s",
190  (action & ACTION_PASS) ? "pass" : "drop",
191  (f->flags & FLOW_ACTION_DROP) ? "drop" : "pass");
192  } else {
193  FlowSetDropAction(f, 0);
194  SCLogDebug("setting flow action drop");
195  }
196  }
197 
198  if (fw_rule) {
200  }
201 }
202 
203 /** \brief Apply action(s) and Set 'drop' sig info,
204  * if applicable
205  * \param p packet
206  * \param s signature -- for id, sig pointer, not actions
207  * \param pa packet alert struct -- match, including actions after thresholding (rate_filter) */
208 static void PacketApplySignatureActions(Packet *p, const Signature *s, const PacketAlert *pa)
209 {
210  SCLogDebug("packet %" PRIu64 ", sid %u: action %02x alert_flags %02x", PcapPacketCntGet(p),
211  s->id, pa->action, pa->flags);
212 
213  const bool is_fw_rule = s->flags & SIG_FLAG_FIREWALL ? true : false;
214  /* REJECT also sets ACTION_DROP, just make it more visible with this check */
215  if (pa->action & ACTION_DROP_REJECT) {
216  uint8_t drop_reason = is_fw_rule ? PKT_DROP_REASON_FW_RULES : PKT_DROP_REASON_RULES;
217  if (is_fw_rule) {
220  } else if (s->detect_table == DETECT_TABLE_PACKET_PRE_FLOW) {
221  drop_reason = PKT_DROP_REASON_FW_FLOW_PRE_HOOK;
222  }
223  }
224  /* PacketDrop will update the packet action, too */
225  PacketDrop(p, pa->action,
228  : drop_reason);
229  SCLogDebug("[packet %p][DROP sid %u]", p, s->id);
230 
231  if (p->alerts.drop.action == 0) {
232  p->alerts.drop.iid = s->iid;
233  p->alerts.drop.action = pa->action;
234  p->alerts.drop.s = (Signature *)s;
235  }
236  if ((p->flow != NULL) && (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)) {
237  RuleActionToFlow(pa->action, p->flow, is_fw_rule);
238  }
239 
241  } else {
242  if (pa->action & ACTION_ACCEPT) {
243  const enum ActionScope as = pa->s->action_scope;
244  SCLogDebug("packet %" PRIu64 ": ACCEPT %u as:%u flags:%02x", PcapPacketCntGet(p), s->id,
245  as, pa->flags);
246  if (as == ACTION_SCOPE_PACKET || as == ACTION_SCOPE_FLOW ||
248  SCLogDebug("packet %" PRIu64 ": sid:%u ACCEPT", PcapPacketCntGet(p), s->id);
249  p->action |= ACTION_ACCEPT;
250  }
251  } else if (pa->action & (ACTION_ALERT | ACTION_CONFIG)) {
252  // nothing to set in the packet
253  } else if (pa->action & ACTION_PASS) {
254  SCLogDebug("[packet %p][PASS sid %u]", p, s->id);
255  // nothing to set in the packet
256  } else if (pa->action != 0) {
257  DEBUG_VALIDATE_BUG_ON(1); // should be unreachable
258  }
259 
260  if ((pa->action & (ACTION_PASS | ACTION_ACCEPT)) && (p->flow != NULL) &&
262  RuleActionToFlow(pa->action, p->flow, is_fw_rule);
263  }
264  }
265 }
266 
268 {
269  det_ctx->alert_queue_size = 0;
270  det_ctx->alert_queue = SCCalloc(packet_alert_max, sizeof(PacketAlert));
271  if (det_ctx->alert_queue == NULL) {
272  FatalError("failed to allocate %" PRIu64 " bytes for the alert queue",
273  (uint64_t)(packet_alert_max * sizeof(PacketAlert)));
274  }
276  SCLogDebug("alert queue initialized to %u elements (%" PRIu64 " bytes)", packet_alert_max,
277  (uint64_t)(packet_alert_max * sizeof(PacketAlert)));
278 }
279 
281 {
282  SCFree(det_ctx->alert_queue);
283  det_ctx->alert_queue_capacity = 0;
284 }
285 
286 static inline uint16_t AlertQueueExpandDo(DetectEngineThreadCtx *det_ctx, uint16_t new_cap)
287 {
288  DEBUG_VALIDATE_BUG_ON(det_ctx->alert_queue_capacity >= new_cap);
289 
290  void *tmp_queue = SCRealloc(det_ctx->alert_queue, new_cap * sizeof(PacketAlert));
291  if (unlikely(tmp_queue == NULL)) {
292  /* queue capacity didn't change */
293  return det_ctx->alert_queue_capacity;
294  }
295  det_ctx->alert_queue = tmp_queue;
296  det_ctx->alert_queue_capacity = new_cap;
297  SCLogDebug("Alert queue size expanded: %u elements, bytes: %" PRIuMAX "",
298  det_ctx->alert_queue_capacity, (uintmax_t)(new_cap * sizeof(PacketAlert)));
299  return new_cap;
300 }
301 
302 /** \internal
303  * \retval the new capacity
304  */
305 static uint16_t AlertQueueExpand(DetectEngineThreadCtx *det_ctx)
306 {
307 #ifdef QA_SIMULATION
308  if (unlikely(g_eps_is_alert_queue_fail_mode))
309  return det_ctx->alert_queue_capacity;
310 #endif
311  if (det_ctx->alert_queue_capacity == UINT16_MAX) {
312  return det_ctx->alert_queue_capacity;
313  }
314 
315  uint16_t new_cap;
316  if (det_ctx->alert_queue_capacity > (UINT16_MAX / 2)) {
317  new_cap = UINT16_MAX;
318  } else {
319  new_cap = det_ctx->alert_queue_capacity * 2;
320  }
321  return AlertQueueExpandDo(det_ctx, new_cap);
322 }
323 
324 static inline int PacketAlertSetContext(
325  DetectEngineThreadCtx *det_ctx, PacketAlert *pa, const Signature *s)
326 {
327  pa->json_info = NULL;
328  if (det_ctx->json_content_len) {
329  /* We have some JSON attached in the current detection so let's try
330  to see if some need to be used for current signature. */
331  struct PacketContextData *current_json = NULL;
332  for (uint8_t i = 0; i < det_ctx->json_content_len; i++) {
333  if (s == det_ctx->json_content[i].id) {
334  SCLogDebug("signature %p, content index %u", s, i);
335  if (current_json == NULL) {
336  /* Allocate the first one */
337  current_json = SCCalloc(1, sizeof(struct PacketContextData));
338  if (current_json == NULL) {
339  /* Allocation error, let's return now */
340  return -1;
341  }
342  if (pa->json_info == NULL) {
343  /* If this is the first one, set it */
344  pa->json_info = current_json;
345  }
346  current_json->next = NULL;
347  } else {
348  /* Allocate the next one */
349  struct PacketContextData *next_json =
350  SCCalloc(1, sizeof(struct PacketContextData));
351  if (next_json) {
352  current_json->next = next_json;
353  current_json = next_json;
354  current_json->next = NULL;
355  } else {
356  /* Allocation error, let's return now */
357  return -1;
358  }
359  }
360  current_json->json_string = SCStrdup(det_ctx->json_content[i].json_content);
361  if (current_json->json_string == NULL) {
362  return -1;
363  }
364  SCLogDebug("json content %u, value '%s' (%p)", (unsigned int)i,
365  current_json->json_string, s);
366  }
367  }
368  }
369 
370  return 0;
371 }
372 
373 /** \internal
374  * \brief Free the alert context a PacketAlert owns and clear the reference.
375  */
376 static void PacketAlertContextRelease(PacketAlert *pa)
377 {
378  struct PacketContextData *json = pa->json_info;
379  while (json != NULL) {
380  struct PacketContextData *next = json->next;
381  SCFree(json->json_string);
382  SCFree(json);
383  json = next;
384  }
385  pa->json_info = NULL;
386 }
387 
388 /** \internal
389  * \brief Release the alert context of queue entries for alerts that never reached the packet.
390  */
391 static void AlertQueueReleaseContext(DetectEngineThreadCtx *det_ctx)
392 {
393  for (uint16_t i = 0; i < det_ctx->alert_queue_size; i++) {
394  PacketAlert *pa = &det_ctx->alert_queue[i];
395  if (pa->json_info != NULL) {
396  PacketAlertContextRelease(pa);
397  }
398  }
399 }
400 
401 /** \internal
402  */
403 static inline PacketAlert PacketAlertSet(DetectEngineThreadCtx *det_ctx, const Signature *s,
404  uint64_t tx_id, const uint8_t sub_state, uint8_t alert_flags)
405 {
406  PacketAlert pa;
407  pa.iid = s->iid;
408  pa.action = s->action;
409  pa.s = (Signature *)s;
410  pa.flags = alert_flags;
411  /* Set tx_id if the frame has it */
412  pa.tx_id = tx_id;
413  pa.sub_state = sub_state;
414  pa.frame_id = (alert_flags & PACKET_ALERT_FLAG_FRAME) ? det_ctx->frame_id : 0;
415  PacketAlertSetContext(det_ctx, &pa, s);
416  return pa;
417 }
418 
419 /**
420  * \brief Append signature to local packet alert queue for later preprocessing
421  */
422 static void AlertQueueAppend(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p,
423  const uint64_t tx_id, const uint8_t sub_state, uint8_t alert_flags)
424 {
425  /* first time we see a drop action signature, set that in the packet */
426  /* we do that even before inserting into the queue, so we save it even if appending fails */
427  if (p->alerts.drop.action == 0 && s->action & ACTION_DROP) {
428  /* an empty drop slot owns no context: both reset sites release the chain
429  * before clearing `action`, so overwriting the whole slot here is safe. */
431  p->alerts.drop = PacketAlertSet(det_ctx, s, tx_id, sub_state, alert_flags);
432  SCLogDebug("sid %u: set PacketAlert drop action. s->iid %" PRIu32 "", s->id, s->iid);
433  }
434 
435  uint16_t pos = det_ctx->alert_queue_size;
436  if (pos == det_ctx->alert_queue_capacity) {
437  /* we must grow the alert queue */
438  if (pos == AlertQueueExpand(det_ctx)) {
439  /* this means we failed to expand the queue */
440  p->alerts.discarded++;
441  return;
442  }
443  }
444  det_ctx->alert_queue[pos] = PacketAlertSet(det_ctx, s, tx_id, sub_state, alert_flags);
445 
446  SCLogDebug("packet %" PRIu64 ": appending sid %" PRIu32 ", s->iid %" PRIu32 " to alert queue",
447  PcapPacketCntGet(p), s->id, s->iid);
448  det_ctx->alert_queue_size++;
449 }
450 
451 /**
452  * \brief Append signature to local packet alert queue for later preprocessing
453  */
455  uint64_t tx_id, uint8_t sub_state, uint8_t alert_flags)
456 {
458  return AlertQueueAppend(det_ctx, s, p, tx_id, sub_state, alert_flags);
459 }
460 
461 /**
462  * \brief Append signature to local packet alert queue for later preprocessing
463  * This does not automatically set PACKET_ALERT_FLAG_STATE_MATCH as this
464  * comes from the packet alert path.
465  */
467  uint64_t tx_id, const uint8_t sub_state, uint8_t alert_flags)
468 {
469  alert_flags |= PACKET_ALERT_FLAG_TX;
470  return AlertQueueAppend(det_ctx, s, p, tx_id, sub_state, alert_flags);
471 }
472 
473 /**
474  * \brief Append signature to local packet alert queue for later preprocessing
475  */
477  DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint8_t alert_flags)
478 {
479  return AlertQueueAppend(det_ctx, s, p, PACKET_ALERT_NOTX, 0, alert_flags);
480 }
481 
482 /** \internal
483  * \brief sort helper for sorting alerts by priority
484  *
485  * Sorting is done first based on num and then using tx_id, if nums are equal.
486  * The Signature::num field is set based on internal priority. Higher priority
487  * rules have lower nums.
488  */
489 static int AlertQueueSortHelperFirewall(const void *a, const void *b)
490 {
491  const PacketAlert *pa0 = a;
492  const PacketAlert *pa1 = b;
493  if (pa0->s->detect_table == pa1->s->detect_table) {
494  if (pa1->iid == pa0->iid) {
495  if (pa1->tx_id == PACKET_ALERT_NOTX) {
496  return -1;
497  } else if (pa0->tx_id == PACKET_ALERT_NOTX) {
498  return 1;
499  }
500  return pa0->tx_id < pa1->tx_id ? 1 : -1;
501  } else {
502  return pa0->iid < pa1->iid ? -1 : 1;
503  }
504  }
505  return pa0->s->detect_table < pa1->s->detect_table ? -1 : 1;
506 }
507 
508 static int AlertQueueSortHelper(const void *a, const void *b)
509 {
510  const PacketAlert *pa0 = a;
511  const PacketAlert *pa1 = b;
512  if (pa1->iid == pa0->iid) {
513  if (pa1->tx_id == PACKET_ALERT_NOTX) {
514  return -1;
515  } else if (pa0->tx_id == PACKET_ALERT_NOTX) {
516  return 1;
517  }
518  return pa0->tx_id < pa1->tx_id ? 1 : -1;
519  } else {
520  return pa0->iid < pa1->iid ? -1 : 1;
521  }
522 }
523 
524 /** \internal
525  * \brief Check if Signature action should be applied to flow and apply
526  *
527  */
528 static inline void FlowApplySignatureActions(
529  Packet *p, PacketAlert *pa, const Signature *s, uint8_t alert_flags)
530 {
531  /* For DROP and PASS sigs we need to apply the action to the flow if
532  * - sig is IP or PD only
533  * - match is in applayer
534  * - match is in stream */
535  if (pa->action & (ACTION_DROP | ACTION_PASS | ACTION_ACCEPT)) {
538 
539  if (s->action_scope == ACTION_SCOPE_FLOW) {
541  } else if (s->action_scope == ACTION_SCOPE_AUTO) {
542  enum SignaturePropertyFlowAction flow_action =
544  if (flow_action == SIG_PROP_FLOW_ACTION_FLOW) {
546  } else if (flow_action == SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL) {
549  }
550  }
551  }
552 
554  SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x (set "
555  "PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)",
556  PcapPacketCntGet(p), s->id, s->action, pa->flags);
557  }
558  }
559 }
560 
561 /** \internal
562  * \brief handle immediate actions for a firewall rule match
563  *
564  * Delayed action (accept) is handled after TD has also run, as TD drop
565  * can overrule a FW accept. So returning `accept` here means "will accept if TD agrees".
566  *
567  * \retval pol firewall policy with action that is (drop) or should possibly be (accept)
568  * applied to the packet and flow.
569  */
570 static struct DetectFirewallPolicy HandleFirewallRule(
572 {
573  const Signature *s = pa->s;
574  struct DetectFirewallPolicy pol = { .action = 0, .action_scope = 0 };
575  int res = PacketAlertHandle(de_ctx, det_ctx, s, p, pa);
576  SCLogDebug("packet %" PRIu64 ": fw sid %u: res %d", PcapPacketCntGet(p), s->id, res);
577  if (res > 0) {
578  /* Now, if we have an alert, we have to check if we want
579  * to tag this session or src/dst host */
580  if (s->sm_arrays[DETECT_SM_LIST_TMATCH] != NULL) {
583  while (1) {
584  /* tags are set only for alerts */
586  sigmatch_table[smd->type].Match(det_ctx, p, (Signature *)s, smd->ctx);
587  KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
588  if (smd->is_last)
589  break;
590  smd++;
591  }
592  }
593  /* if this is a terminating action, pass the action to the caller. */
594  if (s->action_scope != ACTION_SCOPE_HOOK ||
596  pol.action = s->action;
597  pol.action_scope = s->action_scope;
598  if (pol.action & ACTION_DROP) {
599  uint8_t drop_reason = PKT_DROP_REASON_FW_RULES;
602  } else if (s->detect_table == DETECT_TABLE_PACKET_PRE_FLOW) {
603  drop_reason = PKT_DROP_REASON_FW_FLOW_PRE_HOOK;
604  }
605  PacketDrop(p, pol.action, drop_reason);
606  if (p->flow && pol.action_scope == ACTION_SCOPE_FLOW) {
607  FlowSetDropAction(p->flow, FLOW_ACTION_BY_FIREWALL);
608  SCLogDebug("packet %" PRIu64 ": FLOW_ACTION_DROP set by firewall by sid %u",
609  PcapPacketCntGet(p), s->id);
610  }
611  }
612  if (pol.action & ACTION_PASS) {
613  if (p->flow && pol.action_scope == ACTION_SCOPE_FLOW) {
615  SCLogDebug("packet %" PRIu64 ": FLOW_ACTION_PASS set by firewall by sid %u",
616  PcapPacketCntGet(p), s->id);
617  }
618  }
619  }
620  /* add the alert for logging if required. */
621  if (s->action & ACTION_ALERT) {
622  if (p->alerts.cnt < packet_alert_max) {
623  p->alerts.alerts[p->alerts.cnt++] = *pa;
624  pa->json_info = NULL; // ownership moved to the packet
625  } else {
627  }
628  }
629  }
630  return pol;
631 }
632 
633 /*
634  * Queue order after sorting:
635  *
636  * Firewall use case: rules are sorted by Signature::detect_table, Signature::iid (which reflects
637  * order in the rule file(s)) This means:
638  * - pre_flow
639  * - pre_stream
640  * - packet:filter (firewall)
641  * - packet:td (IDS/IPS rules)
642  * - app:filter (firewall)
643  * - app:td (IDS/IPS rules)
644  *
645  * Firewall DROPs are immediate.
646  * Firewall ACCEPTs depend on TD not dropping.
647  * Firewall rules are not affected by PASS.
648  * TD rules are affected by PASS, both set by Firewall and TD rules.
649  *
650  * Non-Firewall use case: rules are sorted by Signature::iid (which reflect order based on flowbits,
651  * action-order, priority, etc)
652  */
653 static inline void PacketAlertFinalizeProcessQueue(
655 {
656  const bool have_fw_rules = EngineModeIsFirewall();
657 
658  if (det_ctx->alert_queue_size > 1) {
659  /* sort the alert queue before thresholding and appending to Packet */
660  qsort(det_ctx->alert_queue, det_ctx->alert_queue_size, sizeof(PacketAlert),
661  have_fw_rules ? AlertQueueSortHelperFirewall : AlertQueueSortHelper);
662  }
663 
664  bool alerted = false;
665  bool dropped = false;
666  bool skip_td = false;
667  bool skip_fw = false;
668  bool fw_accept_packet = false; // same as skip_fw?
669  bool fw_accept_flow = false;
670 
671 #ifdef DEBUG
672  SCLogDebug("packet %" PRIu64 ": starting alert event queue", PcapPacketCntGet(p));
673  for (uint16_t x = 0; x < det_ctx->alert_queue_size; x++) {
674  const PacketAlert *pa = &det_ctx->alert_queue[x];
675  const Signature *s = pa->s;
676  SCLogDebug("(list) %s sid %u: action %02x scope %u iid %u detect_table %u",
677  (s->flags & SIG_FLAG_FIREWALL) ? "fw" : "td", s->id, s->action, s->action_scope,
678  s->iid, s->detect_table);
679  }
680 #endif /* DEBUG */
681  uint8_t skip_table_id = 0;
682  bool skip_table = false;
683  for (uint16_t i = 0; i < det_ctx->alert_queue_size; i++) {
684  PacketAlert *pa = &det_ctx->alert_queue[i];
685  const Signature *s = pa->s;
686 
687  if (s->flags & SIG_FLAG_FIREWALL) {
688  if (skip_fw) {
689  continue;
690  }
691  /* skip for this table, but not for later. Mostly for showing
692  * alerts for both packet:filter and app:filter together. */
693  if (skip_table) {
694  if (s->detect_table <= skip_table_id)
695  continue;
696  skip_table = false;
697  }
698 
699  if (dropped) {
700  SCLogDebug("Skipping firewall signature after a drop.");
701  continue;
702  }
703 
704  const uint16_t pre_alert_cnt = p->alerts.cnt;
705  struct DetectFirewallPolicy pol = HandleFirewallRule(de_ctx, det_ctx, p, pa);
706  /* drop is immediate */
707  if (pol.action & ACTION_DROP) {
708  dropped = true;
709  } else if (pol.action & ACTION_ACCEPT) {
710  SCLogDebug("fw sid %u setting skip_fw for action %02x scope %s", s->id, pol.action,
712  /* see if we want to skip other alerts for this table */
713  if (pol.action_scope == ACTION_SCOPE_HOOK) {
714  skip_table_id = s->detect_table;
715  skip_table = true;
716  } else {
717  skip_fw = true;
718  }
719  fw_accept_packet = true;
720  if (pol.action_scope == ACTION_SCOPE_FLOW)
721  fw_accept_flow = true;
722  }
723  if (pol.action & ACTION_PASS) {
724  skip_td = true;
725  }
726  if (pre_alert_cnt < p->alerts.cnt)
727  alerted = true;
728  if (dropped)
729  goto fw_dropped;
730 
731  continue;
732  }
733 
734  SCLogDebug("sid: %u, action %02x, firewall? %s", s->id, pa->action,
736 
737  /* if a firewall rule told us to skip, we don't count the skipped
738  * alerts. */
739  if (have_fw_rules && skip_td) {
740  continue;
741  }
742 
743  int res = PacketAlertHandle(de_ctx, det_ctx, s, p, pa);
744  SCLogDebug("sid %u: res %d", pa->s->id, res);
745  if (res > 0) {
746  /* Now, if we have an alert, we have to check if we want
747  * to tag this session or src/dst host */
748  if (s->sm_arrays[DETECT_SM_LIST_TMATCH] != NULL) {
751  while (1) {
752  /* tags are set only for alerts */
754  sigmatch_table[smd->type].Match(det_ctx, p, (Signature *)s, smd->ctx);
755  KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
756  if (smd->is_last)
757  break;
758  smd++;
759  }
760  }
761 
762  bool skip_action_set = false;
763  if ((p->action & (ACTION_DROP | ACTION_ACCEPT)) != 0) {
764  if (p->action & ACTION_DROP) {
765  if (pa->action & (ACTION_PASS | ACTION_ACCEPT)) {
766  skip_action_set = true;
767  }
768  } else {
769  if (pa->action & (ACTION_DROP)) {
770  skip_action_set = true;
771  }
772  }
773  }
774  SCLogDebug("packet %" PRIu64 ": i:%u sid:%u skip_action_set %s", PcapPacketCntGet(p), i,
775  s->id, BOOL2STR(skip_action_set));
776  if (!skip_action_set) {
777  /* set actions on the flow */
778  FlowApplySignatureActions(p, pa, s, pa->flags);
779 
780  SCLogDebug("sid %u: action %02x (DROP %s, PASS %s)", pa->s->id, pa->action,
782 
783  /* set actions on packet */
784  PacketApplySignatureActions(p, s, pa);
785  }
786  }
787 
788  /* Thresholding removes this alert */
789  if (res == 0 || res == 2 || (s->action & (ACTION_ALERT | ACTION_PASS)) == 0) {
790  SCLogDebug("sid:%u: skipping alert because of thresholding (res=%d) or NOALERT (%02x)",
791  s->id, res, s->action);
792  /* we will not copy this to the AlertQueue */
793  p->alerts.suppressed++;
794  } else if (p->alerts.cnt < packet_alert_max) {
795  p->alerts.alerts[p->alerts.cnt++] = *pa;
796  pa->json_info = NULL; // ownership moved to the packet
797  SCLogDebug("appending sid %" PRIu32 " alert to Packet::alerts at pos %u; action:%02x",
798  s->id, i, pa->action);
799 
800  if (pa->action & ACTION_ALERT) {
801  alerted = true;
802  }
803  /* pass with alert, we're done. Alert is logged. */
804  if (pa->action & ACTION_PASS) {
805  SCLogDebug("sid:%u: is a pass rule, so break out of loop", s->id);
806  if (!have_fw_rules)
807  break;
808  SCLogDebug("skipping td");
809  skip_td = true;
810  continue;
811  }
812  } else {
813  p->alerts.discarded++;
814  }
815 
816  if (s->action & ACTION_DROP) {
817  dropped = true;
818  }
819  }
820 
821 fw_dropped:
822  /* release the context of the alerts we did not hand over to the packet:
823  * suppressed, discarded, skipped or simply never reached. */
824  AlertQueueReleaseContext(det_ctx);
825 
826  /* after threat detection has been handled, see if the fw intended to accept (drop is handled
827  * immediately by the fw), as fw accept can be overruled by td drop. */
828  if (have_fw_rules) {
829  if (p->action & ACTION_DROP) {
830  SCLogDebug("packet %" PRIu64 ": dropped by TD", PcapPacketCntGet(p));
831  } else if (fw_accept_packet) {
832  p->action |= ACTION_ACCEPT;
833  if (p->flow && fw_accept_flow) {
835  SCLogDebug("packet %" PRIu64 ": FLOW_ACTION_ACCEPT set from firewall",
837  }
838  }
839  }
840 
841  /* Set flag on flow to indicate that it has alerts. We use the bool to
842  * exclude pass-only entries in `Packet::alerts` */
843  if (alerted && p->flow != NULL) {
844  if (!FlowHasAlerts(p->flow)) {
847  }
848  }
849 
850  /* Notify capture layer about packets with real alerts (not pass-only),
851  * so capture impl can update per-capture context (e.g. pcap-file alert counts). */
852  if (alerted) {
854  }
855 }
856 
857 /**
858  * \brief Check the threshold of the sigs that match, set actions, break on pass action
859  * This function iterate the packet alerts array, removing those that didn't match
860  * the threshold, and those that match after a signature with the action "pass".
861  * The array is sorted by action priority/order
862  * \param de_ctx detection engine context
863  * \param det_ctx detection engine thread context
864  * \param p pointer to the packet
865  */
867 {
868  SCEnter();
869 
870  if (det_ctx->alert_queue_size > 0) {
871  PacketAlertFinalizeProcessQueue(de_ctx, det_ctx, p);
872  if (det_ctx->json_content_len)
874  }
875 
876  /* At this point, we should have all the new alerts. Now check the tag
877  * keyword context for sessions and hosts */
878  if (!(p->flags & PKT_PSEUDO_STREAM_END))
879  TagHandlePacket(de_ctx, det_ctx, p);
880 }
881 
882 #ifdef UNITTESTS
884 #endif
PacketCheckAction
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition: packet.c:50
PKT_DROP_REASON_RULES_THRESHOLD
@ PKT_DROP_REASON_RULES_THRESHOLD
Definition: decode.h:412
DetectEngineThreadCtx_::alert_queue_size
uint16_t alert_queue_size
Definition: detect.h:1386
FlowSetHasAlertsFlag
void FlowSetHasAlertsFlag(Flow *f)
Set flag to indicate that flow has alerts.
Definition: flow.c:151
PacketAlerts_::firewall_discarded
uint16_t firewall_discarded
Definition: decode.h:307
FLOW_ACTION_BY_FIREWALL
#define FLOW_ACTION_BY_FIREWALL
Definition: flow.h:125
PacketAlert_::s
const struct Signature_ * s
Definition: decode.h:270
AlertQueueFree
void AlertQueueFree(DetectEngineThreadCtx *det_ctx)
Definition: detect-engine-alert.c:280
Flow_::flags
uint64_t flags
Definition: flow.h:410
DETECT_SM_LIST_SUPPRESS
@ DETECT_SM_LIST_SUPPRESS
Definition: detect-engine-register.h:321
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:78
PACKET_ALERT_FLAG_STATE_MATCH
#define PACKET_ALERT_FLAG_STATE_MATCH
Definition: decode.h:286
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
ACTION_PASS
#define ACTION_PASS
Definition: action-globals.h:34
PKT_DROP_REASON_FW_RULES
@ PKT_DROP_REASON_FW_RULES
Definition: decode.h:425
KEYWORD_PROFILING_SET_LIST
#define KEYWORD_PROFILING_SET_LIST(ctx, list)
Definition: util-profiling.h:46
PcapPacketCntGet
uint64_t PcapPacketCntGet(const Packet *p)
Definition: decode.c:1244
DETECT_TABLE_PACKET_PRE_STREAM
@ DETECT_TABLE_PACKET_PRE_STREAM
Definition: detect.h:538
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
SigMatchData_::is_last
bool is_last
Definition: detect.h:342
ActionScopeToString
const char * ActionScopeToString(enum ActionScope s)
Definition: detect-parse.c:4265
PacketAlerts_::cnt
uint16_t cnt
Definition: decode.h:305
PacketAlertCheck
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
Definition: detect-engine-alert.c:144
SigMatchData_::ctx
SigMatchCtx * ctx
Definition: detect.h:343
action-globals.h
Packet_::flags
uint32_t flags
Definition: decode.h:588
Packet_::action
uint8_t action
Definition: decode.h:650
flow-private.h
Flow_
Flow data structure.
Definition: flow.h:360
TAG_SIG_ID
#define TAG_SIG_ID
Definition: detect-engine-tag.h:42
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:966
PacketAlerts_::alerts
PacketAlert * alerts
Definition: decode.h:309
ACTION_SCOPE_FLOW
@ ACTION_SCOPE_FLOW
Definition: action-globals.h:45
ACTION_REJECT_ANY
#define ACTION_REJECT_ANY
Definition: action-globals.h:38
PacketAlerts_::drop
PacketAlert drop
Definition: decode.h:312
CaptureHooksOnPacketWithAlerts
void CaptureHooksOnPacketWithAlerts(const Packet *p)
Definition: capture-hooks.c:53
ACTION_DROP_REJECT
#define ACTION_DROP_REJECT
Definition: action-globals.h:40
FLOW_ACTION_DROP
#define FLOW_ACTION_DROP
Definition: flow.h:69
p
Packet * p
Definition: fuzz_dataset.c:30
Signature_::sm_arrays
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition: detect.h:730
DETECT_SM_LIST_THRESHOLD
@ DETECT_SM_LIST_THRESHOLD
Definition: detect-engine-register.h:322
EngineModeIsFirewall
bool EngineModeIsFirewall(void)
Definition: suricata.c:240
SigMatchData_
Data needed for Match()
Definition: detect.h:340
KEYWORD_PROFILING_START
#define KEYWORD_PROFILING_START
Definition: util-profiling.h:50
SigMatchData_::type
uint16_t type
Definition: detect.h:341
DetectFirewallPolicy::action
uint8_t action
Definition: detect.h:918
Packet_::alerts
PacketAlerts alerts
Definition: decode.h:663
KEYWORD_PROFILING_END
#define KEYWORD_PROFILING_END(ctx, type, m)
Definition: util-profiling.h:64
PacketAlert_::tx_id
uint64_t tx_id
Definition: decode.h:271
PacketAlert_::action
uint8_t action
Definition: decode.h:267
SIG_FLAG_FIREWALL
#define SIG_FLAG_FIREWALL
Definition: detect.h:220
AlertQueueInit
void AlertQueueInit(DetectEngineThreadCtx *det_ctx)
Definition: detect-engine-alert.c:267
Signature_::gid
uint32_t gid
Definition: detect.h:713
FLOW_ACTION_PASS
#define FLOW_ACTION_PASS
Definition: flow.h:116
SignaturePropertyFlowAction
SignaturePropertyFlowAction
Definition: detect.h:84
capture-hooks.h
PacketAlert_::json_info
struct PacketContextData * json_info
Definition: decode.h:273
decode.h
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
DetectEngineThreadCtx_
Definition: detect.h:1295
DETECT_THRESHOLD
@ DETECT_THRESHOLD
Definition: detect-engine-register.h:67
AlertQueueAppendAppTxFromPacket
void AlertQueueAppendAppTxFromPacket(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint64_t tx_id, const uint8_t sub_state, uint8_t alert_flags)
Append signature to local packet alert queue for later preprocessing This does not automatically set ...
Definition: detect-engine-alert.c:466
PKT_PSEUDO_STREAM_END
#define PKT_PSEUDO_STREAM_END
Definition: decode.h:1328
util-exception-policy.h
PKT_DROP_REASON_FW_FLOW_PRE_HOOK
@ PKT_DROP_REASON_FW_FLOW_PRE_HOOK
Definition: decode.h:429
BOOL2STR
#define BOOL2STR(b)
Definition: util-debug.h:542
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
detect.h
PacketContextData
Definition: decode.h:258
DETECT_TABLE_PACKET_PRE_FLOW
@ DETECT_TABLE_PACKET_PRE_FLOW
Definition: detect.h:537
DETECT_SM_LIST_TMATCH
@ DETECT_SM_LIST_TMATCH
Definition: detect-engine-register.h:318
PacketAlerts_::discarded
uint16_t discarded
Definition: decode.h:306
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:322
detect-engine-tag.h
PKT_DROP_REASON_RULES
@ PKT_DROP_REASON_RULES
Definition: decode.h:411
Signature_::action
uint8_t action
Definition: detect.h:678
util-profiling.h
PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW
#define PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW
Definition: decode.h:284
Signature_::flags
uint32_t flags
Definition: detect.h:664
PKT_ALERT_CTX_USED
#define PKT_ALERT_CTX_USED
Definition: decode.h:1315
ACTION_ALERT
#define ACTION_ALERT
Definition: action-globals.h:29
Packet_
Definition: decode.h:534
DetectEngineThreadCtx_::frame_id
int64_t frame_id
Definition: detect.h:1378
ACTION_SCOPE_AUTO
@ ACTION_SCOPE_AUTO
Definition: action-globals.h:43
detect-engine-alert.h
DetectEngineThreadCtx_::alert_queue_capacity
uint16_t alert_queue_capacity
Definition: detect.h:1387
SigTableElmt_::Match
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1504
PacketAlert_::iid
SigIntId iid
Definition: decode.h:266
signature_properties
const struct SignatureProperties signature_properties[SIG_TYPE_MAX]
Definition: detect-engine.c:119
PACKET_ALERT_NOTX
#define PACKET_ALERT_NOTX
Definition: detect.h:56
SIG_TYPE_NOT_SET
@ SIG_TYPE_NOT_SET
Definition: detect.h:66
PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET
#define PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET
Definition: decode.h:298
PacketAlertFinalize
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...
Definition: detect-engine-alert.c:866
PACKET_ALERT_FLAG_STREAM_MATCH
#define PACKET_ALERT_FLAG_STREAM_MATCH
Definition: decode.h:288
TagHandlePacket
void TagHandlePacket(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
Search tags for src and dst. Update entries of the tag, remove if necessary.
Definition: detect-engine-tag.c:520
PACKET_ALERT_FLAG_FRAME
#define PACKET_ALERT_FLAG_FRAME
Definition: decode.h:294
SCRealloc
#define SCRealloc(ptr, sz)
Definition: util-mem.h:50
PacketAlert_::frame_id
int64_t frame_id
Definition: decode.h:272
PacketAlert_::flags
uint8_t flags
Definition: decode.h:268
PacketAlert_::sub_state
uint8_t sub_state
Definition: decode.h:269
Packet_::flow
struct Flow_ * flow
Definition: decode.h:590
suricata-common.h
SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL
@ SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL
Definition: detect.h:87
ActionScope
ActionScope
Definition: action-globals.h:42
DetectThresholdData_
Definition: detect-threshold.h:62
ACTION_SCOPE_HOOK
@ ACTION_SCOPE_HOOK
Definition: action-globals.h:46
Signature_::action_scope
uint8_t action_scope
Definition: detect.h:685
packet.h
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
Signature_::rev
uint32_t rev
Definition: detect.h:714
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
FatalError
#define FatalError(...)
Definition: util-debug.h:517
ACTION_CONFIG
#define ACTION_CONFIG
Definition: action-globals.h:35
PacketContextData::next
struct PacketContextData * next
Definition: decode.h:260
PKT_DROP_REASON_FW_STREAM_PRE_HOOK
@ PKT_DROP_REASON_FW_STREAM_PRE_HOOK
Definition: decode.h:428
SIG_TYPE_MAX
@ SIG_TYPE_MAX
Definition: detect.h:80
Signature_::prio
int prio
Definition: detect.h:715
AlertQueueAppendPacket
void AlertQueueAppendPacket(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint8_t alert_flags)
Append signature to local packet alert queue for later preprocessing.
Definition: detect-engine-alert.c:476
SigGetThresholdTypeIter
const DetectThresholdData * SigGetThresholdTypeIter(const Signature *sig, const SigMatchData **psm, int list)
Return next DetectThresholdData for signature.
Definition: detect-engine-threshold.c:750
FLOW_ACTION_ACCEPT
#define FLOW_ACTION_ACCEPT
Definition: flow.h:63
util-validate.h
DetectEngineThreadCtx_::alert_queue
PacketAlert * alert_queue
Definition: detect.h:1388
PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED
#define PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED
Definition: decode.h:292
DetectEngineThreadCtx_::json_content
SigJsonContent * json_content
Definition: detect.h:1334
PacketAlerts_::suppressed
uint16_t suppressed
Definition: decode.h:308
DetectFirewallPolicy
Definition: detect.h:917
Signature_::iid
SigIntId iid
Definition: detect.h:675
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-engine-alert.c
Signature_::id
uint32_t id
Definition: detect.h:712
ACTION_SCOPE_PACKET
@ ACTION_SCOPE_PACKET
Definition: action-globals.h:44
detect-parse.h
Signature_
Signature container.
Definition: detect.h:663
PACKET_ALERT_FLAG_TX
#define PACKET_ALERT_FLAG_TX
Definition: decode.h:290
PacketAlertTagInit
void PacketAlertTagInit(void)
Definition: detect-engine-alert.c:48
PacketDrop
void PacketDrop(Packet *p, const uint8_t action, enum PacketDropReason r)
issue drop action
Definition: packet.c:34
ACTION_ACCEPT
#define ACTION_ACCEPT
Definition: action-globals.h:36
suricata.h
PacketAlert_
Definition: decode.h:265
Signature_::detect_table
uint8_t detect_table
Definition: detect.h:697
FlowHasAlerts
int FlowHasAlerts(const Flow *f)
Check if flow has alerts.
Definition: flow.c:162
packet_alert_max
uint16_t packet_alert_max
Definition: decode.c:82
DetectEngineThreadCtx_::json_content_len
uint8_t json_content_len
Definition: detect.h:1336
TAG_SIG_GEN
#define TAG_SIG_GEN
Definition: detect-engine-tag.h:41
PacketContextData::json_string
char * json_string
Definition: decode.h:259
SIG_PROP_FLOW_ACTION_FLOW
@ SIG_PROP_FLOW_ACTION_FLOW
Definition: detect.h:86
PacketAlertThreshold
int PacketAlertThreshold(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const DetectThresholdData *td, Packet *p, const Signature *s, PacketAlert *pa)
Make the threshold logic for signatures.
Definition: detect-engine-threshold.c:1270
flow.h
PKT_FIRST_ALERTS
#define PKT_FIRST_ALERTS
Definition: decode.h:1375
DetectFirewallPolicy::action_scope
uint8_t action_scope
Definition: detect.h:919
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
AlertQueueAppendAppTx
void AlertQueueAppendAppTx(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint64_t tx_id, uint8_t sub_state, uint8_t alert_flags)
Append signature to local packet alert queue for later preprocessing.
Definition: detect-engine-alert.c:454
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
Signature_::type
enum SignatureType type
Definition: detect.h:666
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
SignatureProperties::flow_action
enum SignaturePropertyFlowAction flow_action
Definition: detect.h:91
SigJsonContent::json_content
char json_content[SIG_JSON_CONTENT_ITEM_LEN]
Definition: detect.h:1281
SigJsonContent::id
void * id
Definition: detect.h:1280
detect-engine-threshold.h
f
Flow f
Definition: fuzz_dataset.c:32