• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.5
/src/detect-engine-alert.c
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-engine-alert.h"
23
#include "detect-engine-threshold.h"
24
#include "detect-engine-tag.h"
25

26
#include "decode.h"
27
#include "packet.h"
28

29
#include "flow.h"
30
#include "flow-private.h"
31

32
#ifdef QA_SIMULATION
33
#include "util-exception-policy.h"
34
#endif
35

36
#include "util-profiling.h"
37
#include "util-validate.h"
38

39
#include "action-globals.h"
40
#include "capture-hooks.h"
41

42
/** tag signature we use for tag alerts */
43
static Signature g_tag_signature;
44
/** tag packet alert structure for tag alerts */
45
static PacketAlert g_tag_pa;
46

47
void PacketAlertTagInit(void)
48
{
2✔
49
    memset(&g_tag_signature, 0x00, sizeof(g_tag_signature));
2✔
50

51
    g_tag_signature.id = TAG_SIG_ID;
2✔
52
    g_tag_signature.gid = TAG_SIG_GEN;
2✔
53
    g_tag_signature.iid = TAG_SIG_ID;
2✔
54
    g_tag_signature.rev = 1;
2✔
55
    g_tag_signature.prio = 2;
2✔
56

57
    memset(&g_tag_pa, 0x00, sizeof(g_tag_pa));
2✔
58

59
    g_tag_pa.action = ACTION_ALERT;
2✔
60
    g_tag_pa.s = &g_tag_signature;
2✔
61
}
2✔
62

63
/**
64
 * \brief Handle a packet and check if needs a threshold logic
65
 *        Also apply rule action if necessary.
66
 *
67
 * \param de_ctx Detection Context
68
 * \param sig Signature pointer
69
 * \param p Packet structure
70
 *
71
 * \retval 1 alert is not suppressed
72
 * \retval 0 alert is suppressed
73
 */
74
static int PacketAlertHandle(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
75
        const Signature *s, Packet *p, PacketAlert *pa)
76
{
114,981✔
77
    SCEnter();
114,981✔
78
    int ret = 1;
114,981✔
79
    const DetectThresholdData *td = NULL;
114,981✔
80
    const SigMatchData *smd;
114,981✔
81

82
    if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
114,981✔
83
        SCReturnInt(1);
96✔
84
    }
96✔
85

86
    /* handle suppressions first */
87
    if (s->sm_arrays[DETECT_SM_LIST_SUPPRESS] != NULL) {
114,885✔
UNCOV
88
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_SUPPRESS);
×
UNCOV
89
        smd = NULL;
×
UNCOV
90
        do {
×
UNCOV
91
            td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_SUPPRESS);
×
UNCOV
92
            if (td != NULL) {
×
UNCOV
93
                SCLogDebug("td %p", td);
×
94

95
                /* PacketAlertThreshold returns 2 if the alert is suppressed but
96
                 * we do need to apply rule actions to the packet. */
UNCOV
97
                KEYWORD_PROFILING_START;
×
UNCOV
98
                ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
×
UNCOV
99
                if (ret == 0 || ret == 2) {
×
UNCOV
100
                    KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 0);
×
101
                    /* It doesn't match threshold, remove it */
UNCOV
102
                    SCReturnInt(ret);
×
UNCOV
103
                }
×
UNCOV
104
                KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
×
UNCOV
105
            }
×
UNCOV
106
        } while (smd != NULL);
×
UNCOV
107
    }
×
108

109
    /* if we're still here, consider thresholding */
110
    if (s->sm_arrays[DETECT_SM_LIST_THRESHOLD] != NULL) {
114,885✔
111
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_THRESHOLD);
45✔
112
        smd = NULL;
45✔
113
        do {
45✔
114
            td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_THRESHOLD);
45✔
115
            if (td != NULL) {
45✔
116
                SCLogDebug("td %p", td);
45✔
117

118
                /* PacketAlertThreshold returns 2 if the alert is suppressed but
119
                 * we do need to apply rule actions to the packet. */
120
                KEYWORD_PROFILING_START;
45✔
121
                ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s, pa);
45✔
122
                if (ret == 0 || ret == 2) {
45✔
123
                    KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD ,0);
34✔
124
                    /* It doesn't match threshold, remove it */
125
                    SCReturnInt(ret);
34✔
126
                }
34✔
127
                KEYWORD_PROFILING_END(det_ctx, DETECT_THRESHOLD, 1);
11✔
128
            }
11✔
129
        } while (smd != NULL);
45✔
130
    }
45✔
131
    SCReturnInt(1);
114,885✔
132
}
114,885✔
133

134
#ifdef UNITTESTS
135
/**
136
 * \brief Check if a certain sid alerted, this is used in the test functions
137
 *
138
 * \param p   Packet on which we want to check if the signature alerted or not
139
 * \param sid Signature id of the signature that has to be checked for a match
140
 *
141
 * \retval match A value > 0 on a match; 0 on no match
142
 */
143
int PacketAlertCheck(Packet *p, uint32_t sid)
144
{
145
    int match = 0;
146

147
    for (uint16_t i = 0; i < p->alerts.cnt; i++) {
148
        BUG_ON(p->alerts.alerts[i].s == NULL);
149
        if (p->alerts.alerts[i].s->id == sid)
150
            match++;
151
    }
152

153
    return match;
154
}
155
#endif
156

157
static inline void RuleActionToFlow(const uint8_t action, Flow *f)
158
{
1,373✔
159
    if (action & ACTION_ACCEPT) {
1,373✔
UNCOV
160
        f->flags |= FLOW_ACTION_ACCEPT;
×
UNCOV
161
        SCLogDebug("setting flow action pass");
×
UNCOV
162
    }
×
163

164
    // TODO pass and accept could be set at the same time?
165
    if (action & (ACTION_DROP | ACTION_REJECT_ANY | ACTION_PASS)) {
1,373✔
166
        if (f->flags & (FLOW_ACTION_DROP | FLOW_ACTION_PASS | FLOW_ACTION_ACCEPT)) {
1,373✔
167
            /* drop or pass already set. First to set wins. */
168
            SCLogDebug("not setting %s flow already set to %s",
3✔
169
                    (action & ACTION_PASS) ? "pass" : "drop",
3✔
170
                    (f->flags & FLOW_ACTION_DROP) ? "drop" : "pass");
3✔
171
        } else {
1,370✔
172
            if (action & (ACTION_DROP | ACTION_REJECT_ANY)) {
1,370✔
173
                f->flags |= FLOW_ACTION_DROP;
216✔
174
                SCLogDebug("setting flow action drop");
216✔
175
            }
216✔
176
            if (action & ACTION_PASS) {
1,370✔
177
                f->flags |= FLOW_ACTION_PASS;
1,154✔
178
                SCLogDebug("setting flow action pass");
1,154✔
179
            }
1,154✔
180
        }
1,370✔
181
    }
1,373✔
182
}
1,373✔
183

184
/** \brief Apply action(s) and Set 'drop' sig info,
185
 *         if applicable
186
 *  \param p packet
187
 *  \param s signature -- for id, sig pointer, not actions
188
 *  \param pa packet alert struct -- match, including actions after thresholding (rate_filter) */
189
static void PacketApplySignatureActions(Packet *p, const Signature *s, const PacketAlert *pa)
190
{
114,978✔
191
    SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x", PcapPacketCntGet(p), s->id,
114,978✔
192
            pa->action, pa->flags);
114,978✔
193

194
    /* REJECT also sets ACTION_DROP, just make it more visible with this check */
195
    if (pa->action & ACTION_DROP_REJECT) {
114,978✔
196
        uint8_t drop_reason = PKT_DROP_REASON_RULES;
1,705✔
197
        if (s->detect_table == DETECT_TABLE_PACKET_PRE_STREAM) {
1,705✔
UNCOV
198
            drop_reason = PKT_DROP_REASON_STREAM_PRE_HOOK;
×
199
        } else if (s->detect_table == DETECT_TABLE_PACKET_PRE_FLOW) {
1,705✔
UNCOV
200
            drop_reason = PKT_DROP_REASON_FLOW_PRE_HOOK;
×
UNCOV
201
        }
×
202

203
        /* PacketDrop will update the packet action, too */
204
        PacketDrop(p, pa->action,
1,705✔
205
                (pa->flags & PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED)
1,705✔
206
                        ? PKT_DROP_REASON_RULES_THRESHOLD
1,705✔
207
                        : drop_reason);
1,705✔
208
        SCLogDebug("[packet %p][DROP sid %u]", p, s->id);
1,705✔
209

210
        if (p->alerts.drop.action == 0) {
1,705✔
UNCOV
211
            p->alerts.drop.iid = s->iid;
×
UNCOV
212
            p->alerts.drop.action = pa->action;
×
UNCOV
213
            p->alerts.drop.s = (Signature *)s;
×
UNCOV
214
        }
×
215
        if ((p->flow != NULL) && (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)) {
1,705✔
216
            RuleActionToFlow(pa->action, p->flow);
219✔
217
        }
219✔
218

219
        DEBUG_VALIDATE_BUG_ON(!PacketCheckAction(p, ACTION_DROP));
1,705✔
220
    } else {
113,273✔
221
        if (pa->action & ACTION_PASS) {
113,273✔
222
            SCLogDebug("[packet %p][PASS sid %u]", p, s->id);
3,688✔
223
            // nothing to set in the packet
224
        } else if (pa->action & ACTION_ACCEPT) {
109,585✔
UNCOV
225
            const enum ActionScope as = pa->s->action_scope;
×
UNCOV
226
            SCLogDebug("packet %" PRIu64 ": ACCEPT %u as:%u flags:%02x", PcapPacketCntGet(p), s->id,
×
UNCOV
227
                    as, pa->flags);
×
UNCOV
228
            if (as == ACTION_SCOPE_PACKET || as == ACTION_SCOPE_FLOW ||
×
UNCOV
229
                    (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET)) {
×
UNCOV
230
                SCLogDebug("packet %" PRIu64 ": sid:%u ACCEPT", PcapPacketCntGet(p), s->id);
×
UNCOV
231
                p->action |= ACTION_ACCEPT;
×
UNCOV
232
            }
×
233
        } else if (pa->action & (ACTION_ALERT | ACTION_CONFIG)) {
109,585✔
234
            // nothing to set in the packet
235
        } else if (pa->action != 0) {
106,295✔
236
            DEBUG_VALIDATE_BUG_ON(1); // should be unreachable
×
237
        }
×
238

239
        if ((pa->action & (ACTION_PASS | ACTION_ACCEPT)) && (p->flow != NULL) &&
113,273✔
240
                (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)) {
113,273✔
241
            RuleActionToFlow(pa->action, p->flow);
1,154✔
242
        }
1,154✔
243
    }
113,273✔
244
}
114,978✔
245

246
void AlertQueueInit(DetectEngineThreadCtx *det_ctx)
247
{
35,438✔
248
    det_ctx->alert_queue_size = 0;
35,438✔
249
    det_ctx->alert_queue = SCCalloc(packet_alert_max, sizeof(PacketAlert));
35,438✔
250
    if (det_ctx->alert_queue == NULL) {
35,438✔
251
        FatalError("failed to allocate %" PRIu64 " bytes for the alert queue",
×
252
                (uint64_t)(packet_alert_max * sizeof(PacketAlert)));
×
253
    }
×
254
    det_ctx->alert_queue_capacity = packet_alert_max;
35,438✔
255
    SCLogDebug("alert queue initialized to %u elements (%" PRIu64 " bytes)", packet_alert_max,
35,438✔
256
            (uint64_t)(packet_alert_max * sizeof(PacketAlert)));
35,438✔
257
}
35,438✔
258

259
void AlertQueueFree(DetectEngineThreadCtx *det_ctx)
260
{
35,437✔
261
    SCFree(det_ctx->alert_queue);
35,437✔
262
    det_ctx->alert_queue_capacity = 0;
35,437✔
263
}
35,437✔
264

265
static inline uint16_t AlertQueueExpandDo(DetectEngineThreadCtx *det_ctx, uint16_t new_cap)
266
{
10✔
267
    DEBUG_VALIDATE_BUG_ON(det_ctx->alert_queue_capacity >= new_cap);
10✔
268

269
    void *tmp_queue = SCRealloc(det_ctx->alert_queue, new_cap * sizeof(PacketAlert));
10✔
270
    if (unlikely(tmp_queue == NULL)) {
10✔
271
        /* queue capacity didn't change */
272
        return det_ctx->alert_queue_capacity;
×
273
    }
×
274
    det_ctx->alert_queue = tmp_queue;
10✔
275
    det_ctx->alert_queue_capacity = new_cap;
10✔
276
    SCLogDebug("Alert queue size expanded: %u elements, bytes: %" PRIuMAX "",
10✔
277
            det_ctx->alert_queue_capacity, (uintmax_t)(new_cap * sizeof(PacketAlert)));
10✔
278
    return new_cap;
10✔
279
}
10✔
280

281
/** \internal
282
 * \retval the new capacity
283
 */
284
static uint16_t AlertQueueExpand(DetectEngineThreadCtx *det_ctx)
285
{
10✔
286
#ifdef QA_SIMULATION
287
    if (unlikely(g_eps_is_alert_queue_fail_mode))
288
        return det_ctx->alert_queue_capacity;
289
#endif
290
    if (det_ctx->alert_queue_capacity == UINT16_MAX) {
10✔
UNCOV
291
        return det_ctx->alert_queue_capacity;
×
UNCOV
292
    }
×
293

294
    uint16_t new_cap;
10✔
295
    if (det_ctx->alert_queue_capacity > (UINT16_MAX / 2)) {
10✔
UNCOV
296
        new_cap = UINT16_MAX;
×
297
    } else {
10✔
298
        new_cap = det_ctx->alert_queue_capacity * 2;
10✔
299
    }
10✔
300
    return AlertQueueExpandDo(det_ctx, new_cap);
10✔
301
}
10✔
302

303
static inline int PacketAlertSetContext(
304
        DetectEngineThreadCtx *det_ctx, PacketAlert *pa, const Signature *s)
305
{
120,796✔
306
    pa->json_info = NULL;
120,796✔
307
    if (det_ctx->json_content_len) {
120,796✔
308
        /* We have some JSON attached in the current detection so let's try
309
           to see if some need to be used for current signature. */
UNCOV
310
        struct PacketContextData *current_json = NULL;
×
UNCOV
311
        for (uint8_t i = 0; i < det_ctx->json_content_len; i++) {
×
UNCOV
312
            if (s == det_ctx->json_content[i].id) {
×
UNCOV
313
                SCLogDebug("signature %p, content index %u", s, i);
×
UNCOV
314
                if (current_json == NULL) {
×
315
                    /* Allocate the first one */
UNCOV
316
                    current_json = SCCalloc(1, sizeof(struct PacketContextData));
×
UNCOV
317
                    if (current_json == NULL) {
×
318
                        /* Allocation error, let's return now */
319
                        return -1;
×
320
                    }
×
UNCOV
321
                    if (pa->json_info == NULL) {
×
322
                        /* If this is the first one, set it */
UNCOV
323
                        pa->json_info = current_json;
×
UNCOV
324
                    }
×
UNCOV
325
                    current_json->next = NULL;
×
UNCOV
326
                } else {
×
327
                    /* Allocate the next one */
UNCOV
328
                    struct PacketContextData *next_json =
×
UNCOV
329
                            SCCalloc(1, sizeof(struct PacketContextData));
×
UNCOV
330
                    if (next_json) {
×
UNCOV
331
                        current_json->next = next_json;
×
UNCOV
332
                        current_json = next_json;
×
UNCOV
333
                        current_json->next = NULL;
×
UNCOV
334
                    } else {
×
335
                        /* Allocation error, let's return now */
336
                        return -1;
×
337
                    }
×
UNCOV
338
                }
×
UNCOV
339
                current_json->json_string = SCStrdup(det_ctx->json_content[i].json_content);
×
UNCOV
340
                SCLogDebug("json content %u, value '%s' (%p)", (unsigned int)i,
×
UNCOV
341
                        current_json->json_string, s);
×
UNCOV
342
            }
×
UNCOV
343
        }
×
UNCOV
344
    }
×
345

346
    return 0;
120,796✔
347
}
120,796✔
348

349
/** \internal
350
 */
351
static inline PacketAlert PacketAlertSet(
352
        DetectEngineThreadCtx *det_ctx, const Signature *s, uint64_t tx_id, uint8_t alert_flags)
353
{
120,796✔
354
    PacketAlert pa;
120,796✔
355
    pa.iid = s->iid;
120,796✔
356
    pa.action = s->action;
120,796✔
357
    pa.s = (Signature *)s;
120,796✔
358
    pa.flags = alert_flags;
120,796✔
359
    /* Set tx_id if the frame has it */
360
    pa.tx_id = tx_id;
120,796✔
361
    pa.frame_id = (alert_flags & PACKET_ALERT_FLAG_FRAME) ? det_ctx->frame_id : 0;
120,796✔
362
    PacketAlertSetContext(det_ctx, &pa, s);
120,796✔
363
    return pa;
120,796✔
364
}
120,796✔
365

366
/**
367
 * \brief Append signature to local packet alert queue for later preprocessing
368
 */
369
void AlertQueueAppend(DetectEngineThreadCtx *det_ctx, const Signature *s, Packet *p, uint64_t tx_id,
370
        uint8_t alert_flags)
371
{
118,021✔
372
    /* first time we see a drop action signature, set that in the packet */
373
    /* we do that even before inserting into the queue, so we save it even if appending fails */
374
    if (p->alerts.drop.action == 0 && s->action & ACTION_DROP) {
118,021✔
375
        p->alerts.drop = PacketAlertSet(det_ctx, s, tx_id, alert_flags);
2,775✔
376
        SCLogDebug("Set PacketAlert drop action. s->iid %" PRIu32 "", s->iid);
2,775✔
377
    }
2,775✔
378

379
    uint16_t pos = det_ctx->alert_queue_size;
118,021✔
380
    if (pos == det_ctx->alert_queue_capacity) {
118,021✔
381
        /* we must grow the alert queue */
382
        if (pos == AlertQueueExpand(det_ctx)) {
10✔
383
            /* this means we failed to expand the queue */
UNCOV
384
            p->alerts.discarded++;
×
UNCOV
385
            return;
×
UNCOV
386
        }
×
387
    }
10✔
388
    det_ctx->alert_queue[pos] = PacketAlertSet(det_ctx, s, tx_id, alert_flags);
118,021✔
389

390
    SCLogDebug("Appending sid %" PRIu32 ", s->iid %" PRIu32 " to alert queue", s->id, s->iid);
118,021✔
391
    det_ctx->alert_queue_size++;
118,021✔
392
}
118,021✔
393

394
/** \internal
395
 * \brief sort helper for sorting alerts by priority
396
 *
397
 * Sorting is done first based on num and then using tx_id, if nums are equal.
398
 * The Signature::num field is set based on internal priority. Higher priority
399
 * rules have lower nums.
400
 */
401
static int AlertQueueSortHelperFirewall(const void *a, const void *b)
UNCOV
402
{
×
UNCOV
403
    const PacketAlert *pa0 = a;
×
UNCOV
404
    const PacketAlert *pa1 = b;
×
UNCOV
405
    if (pa0->s->detect_table == pa1->s->detect_table) {
×
UNCOV
406
        if (pa1->iid == pa0->iid) {
×
407
            if (pa1->tx_id == PACKET_ALERT_NOTX) {
×
408
                return -1;
×
409
            } else if (pa0->tx_id == PACKET_ALERT_NOTX) {
×
410
                return 1;
×
411
            }
×
412
            return pa0->tx_id < pa1->tx_id ? 1 : -1;
×
UNCOV
413
        } else {
×
UNCOV
414
            return pa0->iid < pa1->iid ? -1 : 1;
×
UNCOV
415
        }
×
UNCOV
416
    }
×
UNCOV
417
    return pa0->s->detect_table < pa1->s->detect_table ? -1 : 1;
×
UNCOV
418
}
×
419

420
static int AlertQueueSortHelper(const void *a, const void *b)
421
{
86,053✔
422
    const PacketAlert *pa0 = a;
86,053✔
423
    const PacketAlert *pa1 = b;
86,053✔
424
    if (pa1->iid == pa0->iid) {
86,053✔
425
        if (pa1->tx_id == PACKET_ALERT_NOTX) {
2,848✔
426
            return -1;
×
427
        } else if (pa0->tx_id == PACKET_ALERT_NOTX) {
2,848✔
428
            return 1;
×
429
        }
×
430
        return pa0->tx_id < pa1->tx_id ? 1 : -1;
2,848✔
431
    } else {
83,205✔
432
        return pa0->iid < pa1->iid ? -1 : 1;
83,205✔
433
    }
83,205✔
434
}
86,053✔
435

436
/** \internal
437
 * \brief Check if Signature action should be applied to flow and apply
438
 *
439
 */
440
static inline void FlowApplySignatureActions(
441
        Packet *p, PacketAlert *pa, const Signature *s, uint8_t alert_flags)
442
{
114,978✔
443
    /* For DROP and PASS sigs we need to apply the action to the flow if
444
     * - sig is IP or PD only
445
     * - match is in applayer
446
     * - match is in stream */
447
    if (pa->action & (ACTION_DROP | ACTION_PASS | ACTION_ACCEPT)) {
114,978✔
448
        DEBUG_VALIDATE_BUG_ON(s->type == SIG_TYPE_NOT_SET);
5,393✔
449
        DEBUG_VALIDATE_BUG_ON(s->type == SIG_TYPE_MAX);
5,393✔
450

451
        if (s->action_scope == ACTION_SCOPE_FLOW) {
5,393✔
UNCOV
452
            pa->flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW;
×
453
        } else if (s->action_scope == ACTION_SCOPE_AUTO) {
5,393✔
454
            enum SignaturePropertyFlowAction flow_action =
5,393✔
455
                    signature_properties[s->type].flow_action;
5,393✔
456
            if (flow_action == SIG_PROP_FLOW_ACTION_FLOW) {
5,393✔
457
                pa->flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW;
2,662✔
458
            } else if (flow_action == SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL) {
2,731✔
459
                if (pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH)) {
22✔
UNCOV
460
                    pa->flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW;
×
UNCOV
461
                }
×
462
            }
22✔
463
        }
5,393✔
464

465
        if (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW) {
5,393✔
466
            SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x (set "
2,662✔
467
                       "PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)",
2,662✔
468
                    PcapPacketCntGet(p), s->id, s->action, pa->flags);
2,662✔
469
        }
2,662✔
470
    }
5,393✔
471
}
114,978✔
472

473
static inline void PacketAlertFinalizeProcessQueue(
474
        const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
475
{
77,663✔
476
    const bool have_fw_rules = EngineModeIsFirewall();
77,663✔
477

478
    if (det_ctx->alert_queue_size > 1) {
77,663✔
479
        /* sort the alert queue before thresholding and appending to Packet */
480
        qsort(det_ctx->alert_queue, det_ctx->alert_queue_size, sizeof(PacketAlert),
29,800✔
481
                have_fw_rules ? AlertQueueSortHelperFirewall : AlertQueueSortHelper);
29,800✔
482
    }
29,800✔
483

484
    bool alerted = false;
77,663✔
485
    bool dropped = false;
77,663✔
486
    bool skip_td = false;
77,663✔
487
    for (uint16_t i = 0; i < det_ctx->alert_queue_size; i++) {
188,956✔
488
        PacketAlert *pa = &det_ctx->alert_queue[i];
114,981✔
489
        const Signature *s = pa->s;
114,981✔
490

491
        /* if a firewall rule told us to skip, we don't count the skipped
492
         * alerts. */
493
        if (have_fw_rules && skip_td && (s->flags & SIG_FLAG_FIREWALL) == 0) {
114,981✔
494
            continue;
×
495
        }
×
496

497
        int res = PacketAlertHandle(de_ctx, det_ctx, s, p, pa);
114,981✔
498
        if (res > 0) {
114,981✔
499
            /* Now, if we have an alert, we have to check if we want
500
             * to tag this session or src/dst host */
501
            if (s->sm_arrays[DETECT_SM_LIST_TMATCH] != NULL) {
114,978✔
502
                KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_TMATCH);
22✔
503
                SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_TMATCH];
22✔
504
                while (1) {
22✔
505
                    /* tags are set only for alerts */
506
                    KEYWORD_PROFILING_START;
22✔
507
                    sigmatch_table[smd->type].Match(det_ctx, p, (Signature *)s, smd->ctx);
22✔
508
                    KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
22✔
509
                    if (smd->is_last)
22✔
510
                        break;
22✔
UNCOV
511
                    smd++;
×
UNCOV
512
                }
×
513
            }
22✔
514

515
            bool skip_action_set = false;
114,978✔
516
            if ((p->action & (ACTION_DROP | ACTION_ACCEPT)) != 0) {
114,978✔
517
                if (p->action & ACTION_DROP) {
235✔
518
                    if (pa->action & (ACTION_PASS | ACTION_ACCEPT)) {
235✔
UNCOV
519
                        skip_action_set = true;
×
UNCOV
520
                    }
×
521
                } else {
235✔
UNCOV
522
                    if (pa->action & (ACTION_DROP)) {
×
523
                        skip_action_set = true;
×
524
                    }
×
UNCOV
525
                }
×
526
            }
235✔
527
            SCLogDebug("packet %" PRIu64 ": i:%u sid:%u skip_action_set %s", PcapPacketCntGet(p), i,
114,978✔
528
                    s->id, BOOL2STR(skip_action_set));
114,978✔
529
            if (!skip_action_set) {
114,978✔
530
                /* set actions on the flow */
531
                FlowApplySignatureActions(p, pa, s, pa->flags);
114,978✔
532

533
                SCLogDebug("det_ctx->alert_queue[i].action %02x (DROP %s, PASS %s)", pa->action,
114,978✔
534
                        BOOL2STR(pa->action & ACTION_DROP), BOOL2STR(pa->action & ACTION_PASS));
114,978✔
535

536
                /* set actions on packet */
537
                PacketApplySignatureActions(p, s, pa);
114,978✔
538
            }
114,978✔
539
        }
114,978✔
540

541
        /* skip firewall sigs following a drop: IDS mode still shows alerts after an alert. */
542
        if ((s->flags & SIG_FLAG_FIREWALL) && dropped) {
114,981✔
UNCOV
543
            p->alerts.discarded++;
×
544

545
            /* Thresholding removes this alert */
546
        } else if (res == 0 || res == 2 || (s->action & (ACTION_ALERT | ACTION_PASS)) == 0) {
114,981✔
547
            SCLogDebug("sid:%u: skipping alert because of thresholding (res=%d) or NOALERT (%02x)",
3,340✔
548
                    s->id, res, s->action);
3,340✔
549
            /* we will not copy this to the AlertQueue */
550
            p->alerts.suppressed++;
3,340✔
551
        } else if (p->alerts.cnt < packet_alert_max) {
111,641✔
552
            p->alerts.alerts[p->alerts.cnt++] = *pa;
111,462✔
553
            SCLogDebug("appending sid %" PRIu32 " alert to Packet::alerts at pos %u; action:%02x",
111,462✔
554
                    s->id, i, pa->action);
111,462✔
555

556
            if (pa->action & ACTION_ALERT) {
111,462✔
557
                alerted = true;
107,972✔
558
            }
107,972✔
559
            /* pass with alert, we're done. Alert is logged. */
560
            if (pa->action & ACTION_PASS) {
111,462✔
561
                SCLogDebug("sid:%u: is a pass rule, so break out of loop", s->id);
3,688✔
562
                if (!have_fw_rules)
3,688✔
563
                    break;
3,688✔
UNCOV
564
                SCLogDebug("skipping td");
×
UNCOV
565
                skip_td = true;
×
UNCOV
566
                continue;
×
567
            }
3,688✔
568

569
            // TODO we can also drop if alert is suppressed, right?
570
            if (s->action & ACTION_DROP) {
107,774✔
571
                dropped = true;
1,693✔
572
            }
1,693✔
573
        } else {
107,774✔
574
            p->alerts.discarded++;
179✔
575
        }
179✔
576
    }
114,981✔
577

578
    /* Set flag on flow to indicate that it has alerts. We use the bool to
579
     * exclude pass-only entries in `Packet::alerts` */
580
    if (alerted && p->flow != NULL) {
77,663✔
581
        if (!FlowHasAlerts(p->flow)) {
67,457✔
582
            FlowSetHasAlertsFlag(p->flow);
10,046✔
583
            p->flags |= PKT_FIRST_ALERTS;
10,046✔
584
        }
10,046✔
585
    }
67,457✔
586

587
    /* Notify capture layer about packets with real alerts (not pass-only),
588
     * so capture impl can update per-capture context (e.g. pcap-file alert counts). */
589
    if (alerted) {
77,663✔
590
        CaptureHooksOnPacketWithAlerts(p);
72,163✔
591
    }
72,163✔
592
}
77,663✔
593

594
/**
595
 * \brief Check the threshold of the sigs that match, set actions, break on pass action
596
 *        This function iterate the packet alerts array, removing those that didn't match
597
 *        the threshold, and those that match after a signature with the action "pass".
598
 *        The array is sorted by action priority/order
599
 * \param de_ctx detection engine context
600
 * \param det_ctx detection engine thread context
601
 * \param p pointer to the packet
602
 */
603
void PacketAlertFinalize(const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
604
{
1,440,170✔
605
    SCEnter();
1,440,170✔
606

607
    if (det_ctx->alert_queue_size > 0) {
1,440,170✔
608
        PacketAlertFinalizeProcessQueue(de_ctx, det_ctx, p);
77,663✔
609
        if (det_ctx->json_content_len)
77,663✔
UNCOV
610
            p->flags |= PKT_ALERT_CTX_USED;
×
611
    }
77,663✔
612

613
    /* At this point, we should have all the new alerts. Now check the tag
614
     * keyword context for sessions and hosts */
615
    if (!(p->flags & PKT_PSEUDO_STREAM_END))
1,440,170✔
616
        TagHandlePacket(de_ctx, det_ctx, p);
1,429,453✔
617
}
1,440,170✔
618

619
#ifdef UNITTESTS
620
#include "tests/detect-engine-alert.c"
621
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc