Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/flow-worker.c
Line
Count
Source
1
/* Copyright (C) 2016-2024 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
 * Flow Workers are single thread modules taking care of (almost)
24
 * everything related to packets with flows:
25
 *
26
 * - Lookup/creation
27
 * - Stream tracking, reassembly
28
 * - Applayer update
29
 * - Detection
30
 *
31
 * This all while holding the flow lock.
32
 */
33
34
#include "suricata-common.h"
35
#include "suricata.h"
36
37
#include "action-globals.h"
38
#include "packet.h"
39
#include "decode.h"
40
#include "detect.h"
41
#include "stream-tcp.h"
42
#include "app-layer.h"
43
#include "detect-engine.h"
44
#include "output.h"
45
#include "app-layer-parser.h"
46
#include "app-layer-frames.h"
47
48
#include "util-profiling.h"
49
#include "util-validate.h"
50
#include "util-time.h"
51
#include "tmqh-packetpool.h"
52
53
#include "flow-util.h"
54
#include "flow-manager.h"
55
#include "flow-timeout.h"
56
#include "flow-spare-pool.h"
57
#include "flow-worker.h"
58
59
typedef DetectEngineThreadCtx *DetectEngineThreadCtxPtr;
60
61
typedef struct FlowTimeoutCounters {
62
    uint32_t flows_aside_needs_work;
63
    uint32_t flows_aside_pkt_inject;
64
} FlowTimeoutCounters;
65
66
typedef struct FlowWorkerThreadData_ {
67
    DecodeThreadVars *dtv;
68
69
    union {
70
        StreamTcpThread *stream_thread;
71
        void *stream_thread_ptr;
72
    };
73
74
    SC_ATOMIC_DECLARE(DetectEngineThreadCtxPtr, detect_thread);
75
76
    void *output_thread; /* Output thread data. */
77
    void *output_thread_flow; /* Output thread data. */
78
79
    uint16_t local_bypass_pkts;
80
    uint16_t local_bypass_bytes;
81
    uint16_t both_bypass_pkts;
82
    uint16_t both_bypass_bytes;
83
    /** Queue to put pseudo packets that have been created by the stream (RST response) and by the
84
     * flush logic following a protocol change. */
85
    PacketQueueNoLock pq;
86
    FlowLookupStruct fls;
87
88
    struct {
89
        uint16_t flows_injected;
90
        uint16_t flows_injected_max;
91
        uint16_t flows_removed;
92
        uint16_t flows_aside_needs_work;
93
        uint16_t flows_aside_pkt_inject;
94
    } cnt;
95
    FlowEndCounters fec;
96
97
} FlowWorkerThreadData;
98
99
static void FlowWorkerFlowTimeout(
100
        ThreadVars *tv, Packet *p, FlowWorkerThreadData *fw, DetectEngineThreadCtx *det_ctx);
101
102
/**
103
 * \internal
104
 * \brief Forces reassembly for flow if it needs it.
105
 *
106
 *        The function requires flow to be locked beforehand.
107
 *
108
 * \param f Pointer to the flow.
109
 *
110
 * \retval cnt number of packets injected
111
 */
112
static int FlowFinish(ThreadVars *tv, Flow *f, FlowWorkerThreadData *fw, void *detect_thread)
113
128k
{
114
128k
    const int server = f->ffr_tc;
115
128k
    const int client = f->ffr_ts;
116
128k
    int cnt = 0;
117
118
    /* Get the tcp session for the flow */
119
128k
    const TcpSession *ssn = (TcpSession *)f->protoctx;
120
121
    /* insert a pseudo packet in the toserver direction */
122
128k
    if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
123
122k
        Packet *p = FlowPseudoPacketGet(0, f, ssn);
124
122k
        if (p != NULL) {
125
122k
            PKT_SET_SRC(p, PKT_SRC_FFR);
126
122k
            if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NONE) {
127
23.7k
                p->flowflags |= FLOW_PKT_LAST_PSEUDO;
128
23.7k
            }
129
122k
            FlowWorkerFlowTimeout(tv, p, fw, detect_thread);
130
122k
            PacketPoolReturnPacket(p);
131
122k
            cnt++;
132
122k
        }
133
122k
    }
134
135
    /* handle toclient */
136
128k
    if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
137
104k
        Packet *p = FlowPseudoPacketGet(1, f, ssn);
138
104k
        if (p != NULL) {
139
104k
            PKT_SET_SRC(p, PKT_SRC_FFR);
140
104k
            p->flowflags |= FLOW_PKT_LAST_PSEUDO;
141
104k
            FlowWorkerFlowTimeout(tv, p, fw, detect_thread);
142
104k
            PacketPoolReturnPacket(p);
143
104k
            f->flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
144
104k
            cnt++;
145
104k
        }
146
104k
    }
147
148
128k
    if (cnt > 0) {
149
128k
        f->flags |= FLOW_TIMEOUT_REASSEMBLY_DONE;
150
128k
    }
151
128k
    return cnt;
152
128k
}
153
154
/** \param[in] max_work Max flows to process. 0 if unlimited. */
155
static void CheckWorkQueue(ThreadVars *tv, FlowWorkerThreadData *fw, FlowTimeoutCounters *counters,
156
        FlowQueuePrivate *fq, const uint32_t max_work)
157
50.7k
{
158
50.7k
    FlowQueuePrivate ret_queue = { NULL, NULL, 0 };
159
50.7k
    uint32_t i = 0;
160
50.7k
    Flow *f;
161
101k
    while ((f = FlowQueuePrivateGetFromTop(fq)) != NULL) {
162
50.9k
        FLOWLOCK_WRLOCK(f);
163
50.9k
        f->flow_end_flags |= FLOW_END_FLAG_TIMEOUT; //TODO emerg
164
165
50.9k
        if (f->proto == IPPROTO_TCP) {
166
50.2k
            if (!(f->flags & (FLOW_TIMEOUT_REASSEMBLY_DONE | FLOW_ACTION_DROP)) &&
167
50.2k
                    !FlowIsBypassed(f) && FlowNeedsReassembly(f) && f->ffr != 0) {
168
                /* read detect thread in case we're doing a reload */
169
32.7k
                void *detect_thread = SC_ATOMIC_GET(fw->detect_thread);
170
32.7k
                int cnt = FlowFinish(tv, f, fw, detect_thread);
171
32.7k
                counters->flows_aside_pkt_inject += cnt;
172
32.7k
                counters->flows_aside_needs_work++;
173
32.7k
            }
174
50.2k
        }
175
176
        /* no one is referring to this flow, removed from hash
177
         * so we can unlock it and pass it to the flow recycler */
178
179
50.9k
        if (fw->output_thread_flow != NULL)
180
50.9k
            (void)OutputFlowLog(tv, fw->output_thread_flow, f);
181
182
50.9k
        FlowEndCountersUpdate(tv, &fw->fec, f);
183
50.9k
        if (f->proto == IPPROTO_TCP && f->protoctx != NULL) {
184
44.7k
            StatsDecr(tv, fw->dtv->counter_tcp_active_sessions);
185
44.7k
        }
186
50.9k
        StatsDecr(tv, fw->dtv->counter_flow_active);
187
188
50.9k
        FlowClearMemory (f, f->protomap);
189
50.9k
        FLOWLOCK_UNLOCK(f);
190
191
50.9k
        if (fw->fls.spare_queue.len >= (FLOW_SPARE_POOL_BLOCK_SIZE * 2)) {
192
0
            FlowQueuePrivatePrependFlow(&ret_queue, f);
193
0
            if (ret_queue.len == FLOW_SPARE_POOL_BLOCK_SIZE) {
194
0
                FlowSparePoolReturnFlows(&ret_queue);
195
0
            }
196
50.9k
        } else {
197
50.9k
            FlowQueuePrivatePrependFlow(&fw->fls.spare_queue, f);
198
50.9k
        }
199
200
50.9k
        if (max_work != 0 && ++i == max_work)
201
179
            break;
202
50.9k
    }
203
50.7k
    if (ret_queue.len > 0) {
204
0
        FlowSparePoolReturnFlows(&ret_queue);
205
0
    }
206
207
50.7k
    StatsAddUI64(tv, fw->cnt.flows_removed, (uint64_t)i);
208
50.7k
}
209
210
/** \brief handle flow for packet
211
 *
212
 *  Handle flow creation/lookup
213
 */
214
static inline TmEcode FlowUpdate(ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p)
215
12.2M
{
216
12.2M
    FlowHandlePacketUpdate(p->flow, p, tv, fw->dtv);
217
218
12.2M
    int state = p->flow->flow_state;
219
12.2M
    switch (state) {
220
#ifdef CAPTURE_OFFLOAD
221
        case FLOW_STATE_CAPTURE_BYPASSED: {
222
            StatsAddUI64(tv, fw->both_bypass_pkts, 1);
223
            StatsAddUI64(tv, fw->both_bypass_bytes, GET_PKT_LEN(p));
224
            Flow *f = p->flow;
225
            FlowDeReference(&p->flow);
226
            FLOWLOCK_UNLOCK(f);
227
            return TM_ECODE_DONE;
228
        }
229
#endif
230
77
        case FLOW_STATE_LOCAL_BYPASSED: {
231
77
            StatsAddUI64(tv, fw->local_bypass_pkts, 1);
232
77
            StatsAddUI64(tv, fw->local_bypass_bytes, GET_PKT_LEN(p));
233
77
            Flow *f = p->flow;
234
77
            FlowDeReference(&p->flow);
235
77
            FLOWLOCK_UNLOCK(f);
236
77
            return TM_ECODE_DONE;
237
0
        }
238
12.2M
        default:
239
12.2M
            return TM_ECODE_OK;
240
12.2M
    }
241
12.2M
}
242
243
static TmEcode FlowWorkerThreadDeinit(ThreadVars *tv, void *data);
244
245
static TmEcode FlowWorkerThreadInit(ThreadVars *tv, const void *initdata, void **data)
246
5
{
247
5
    FlowWorkerThreadData *fw = SCCalloc(1, sizeof(*fw));
248
5
    if (fw == NULL)
249
0
        return TM_ECODE_FAILED;
250
251
5
    SC_ATOMIC_INITPTR(fw->detect_thread);
252
5
    SC_ATOMIC_SET(fw->detect_thread, NULL);
253
254
5
    fw->local_bypass_pkts = StatsRegisterCounter("flow_bypassed.local_pkts", tv);
255
5
    fw->local_bypass_bytes = StatsRegisterCounter("flow_bypassed.local_bytes", tv);
256
5
    fw->both_bypass_pkts = StatsRegisterCounter("flow_bypassed.local_capture_pkts", tv);
257
5
    fw->both_bypass_bytes = StatsRegisterCounter("flow_bypassed.local_capture_bytes", tv);
258
259
5
    fw->cnt.flows_aside_needs_work = StatsRegisterCounter("flow.wrk.flows_evicted_needs_work", tv);
260
5
    fw->cnt.flows_aside_pkt_inject = StatsRegisterCounter("flow.wrk.flows_evicted_pkt_inject", tv);
261
5
    fw->cnt.flows_removed = StatsRegisterCounter("flow.wrk.flows_evicted", tv);
262
5
    fw->cnt.flows_injected = StatsRegisterCounter("flow.wrk.flows_injected", tv);
263
5
    fw->cnt.flows_injected_max = StatsRegisterMaxCounter("flow.wrk.flows_injected_max", tv);
264
265
5
    fw->fls.dtv = fw->dtv = DecodeThreadVarsAlloc(tv);
266
5
    if (fw->dtv == NULL) {
267
0
        FlowWorkerThreadDeinit(tv, fw);
268
0
        return TM_ECODE_FAILED;
269
0
    }
270
271
    /* setup TCP */
272
5
    if (StreamTcpThreadInit(tv, NULL, &fw->stream_thread_ptr) != TM_ECODE_OK) {
273
0
        FlowWorkerThreadDeinit(tv, fw);
274
0
        return TM_ECODE_FAILED;
275
0
    }
276
277
5
    if (DetectEngineEnabled()) {
278
        /* setup DETECT */
279
5
        void *detect_thread = NULL;
280
5
        if (DetectEngineThreadCtxInit(tv, NULL, &detect_thread) != TM_ECODE_OK) {
281
0
            FlowWorkerThreadDeinit(tv, fw);
282
0
            return TM_ECODE_FAILED;
283
0
        }
284
5
        SC_ATOMIC_SET(fw->detect_thread, detect_thread);
285
5
    }
286
287
    /* Setup outputs for this thread. */
288
5
    if (OutputLoggerThreadInit(tv, initdata, &fw->output_thread) != TM_ECODE_OK) {
289
0
        FlowWorkerThreadDeinit(tv, fw);
290
0
        return TM_ECODE_FAILED;
291
0
    }
292
5
    if (OutputFlowLogThreadInit(tv, &fw->output_thread_flow) != TM_ECODE_OK) {
293
0
        SCLogError("initializing flow log API for thread failed");
294
0
        FlowWorkerThreadDeinit(tv, fw);
295
0
        return TM_ECODE_FAILED;
296
0
    }
297
298
5
    DecodeRegisterPerfCounters(fw->dtv, tv);
299
5
    AppLayerRegisterThreadCounters(tv);
300
5
    FlowEndCountersRegister(tv, &fw->fec);
301
302
    /* setup pq for stream end pkts */
303
5
    memset(&fw->pq, 0, sizeof(PacketQueueNoLock));
304
5
    *data = fw;
305
5
    return TM_ECODE_OK;
306
5
}
307
308
static TmEcode FlowWorkerThreadDeinit(ThreadVars *tv, void *data)
309
0
{
310
0
    FlowWorkerThreadData *fw = data;
311
312
0
    DecodeThreadVarsFree(tv, fw->dtv);
313
314
    /* free TCP */
315
0
    StreamTcpThreadDeinit(tv, (void *)fw->stream_thread);
316
317
    /* free DETECT */
318
0
    void *detect_thread = SC_ATOMIC_GET(fw->detect_thread);
319
0
    if (detect_thread != NULL) {
320
0
        DetectEngineThreadCtxDeinit(tv, detect_thread);
321
0
        SC_ATOMIC_SET(fw->detect_thread, NULL);
322
0
    }
323
324
    /* Free output. */
325
0
    OutputLoggerThreadDeinit(tv, fw->output_thread);
326
0
    OutputFlowLogThreadDeinit(tv, fw->output_thread_flow);
327
328
    /* free pq */
329
0
    BUG_ON(fw->pq.len);
330
331
0
    Flow *f;
332
0
    while ((f = FlowQueuePrivateGetFromTop(&fw->fls.spare_queue)) != NULL) {
333
0
        FlowFree(f);
334
0
    }
335
336
0
    SCFree(fw);
337
0
    return TM_ECODE_OK;
338
0
}
339
340
TmEcode Detect(ThreadVars *tv, Packet *p, void *data);
341
TmEcode StreamTcp (ThreadVars *, Packet *, void *, PacketQueueNoLock *pq);
342
343
static inline void UpdateCounters(ThreadVars *tv,
344
        FlowWorkerThreadData *fw, const FlowTimeoutCounters *counters)
345
175k
{
346
175k
    if (counters->flows_aside_needs_work) {
347
127k
        StatsAddUI64(tv, fw->cnt.flows_aside_needs_work,
348
127k
                (uint64_t)counters->flows_aside_needs_work);
349
127k
    }
350
175k
    if (counters->flows_aside_pkt_inject) {
351
127k
        StatsAddUI64(tv, fw->cnt.flows_aside_pkt_inject,
352
127k
                (uint64_t)counters->flows_aside_pkt_inject);
353
127k
    }
354
175k
}
355
356
/** \brief update stream engine
357
 *
358
 *  We can be called from both the flow timeout path as well as from the
359
 *  "real" traffic path. If in the timeout path any additional packets we
360
 *  forge for flushing pipelines should not leave our scope. If the original
361
 *  packet is real (or related to a real packet) we need to push the packets
362
 *  on, so IPS logic stays valid.
363
 */
364
static inline void FlowWorkerStreamTCPUpdate(ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p,
365
        DetectEngineThreadCtx *det_ctx, const bool timeout)
366
11.1M
{
367
11.1M
    if (det_ctx != NULL && det_ctx->de_ctx->PreStreamHook != NULL) {
368
0
        const uint8_t action = det_ctx->de_ctx->PreStreamHook(tv, det_ctx, p);
369
0
        if (action & ACTION_DROP) {
370
0
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FW_STREAM_PRE_HOOK);
371
0
            return;
372
0
        }
373
0
    }
374
375
11.1M
    FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_STREAM);
376
11.1M
    StreamTcp(tv, p, fw->stream_thread, &fw->pq);
377
11.1M
    FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_STREAM);
378
379
    // this is the first packet that sets no payload inspection
380
11.1M
    bool setting_nopayload =
381
11.1M
            p->flow->alparser &&
382
7.20M
            SCAppLayerParserStateIssetFlag(p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
383
12.9k
            !(p->flags & PKT_NOPAYLOAD_INSPECTION);
384
11.1M
    if (FlowChangeProto(p->flow) || setting_nopayload) {
385
24.4k
        StreamTcpDetectLogFlush(tv, fw->stream_thread, p->flow, p, &fw->pq);
386
24.4k
        if (setting_nopayload) {
387
104
            FlowSetNoPayloadInspectionFlag(p->flow);
388
104
        }
389
24.4k
        SCAppLayerParserStateSetFlag(p->flow->alparser, APP_LAYER_PARSER_EOF_TS);
390
24.4k
        SCAppLayerParserStateSetFlag(p->flow->alparser, APP_LAYER_PARSER_EOF_TC);
391
24.4k
    }
392
393
    /* Packets here can safely access p->flow as it's locked */
394
11.1M
    SCLogDebug("packet %"PRIu64": extra packets %u", p->pcap_cnt, fw->pq.len);
395
11.1M
    Packet *x;
396
11.2M
    while ((x = PacketDequeueNoLock(&fw->pq))) {
397
48.8k
        SCLogDebug("packet %"PRIu64" extra packet %p", p->pcap_cnt, x);
398
399
48.8k
        if (det_ctx != NULL) {
400
48.8k
            FLOWWORKER_PROFILING_START(x, PROFILE_FLOWWORKER_DETECT);
401
48.8k
            Detect(tv, x, det_ctx);
402
48.8k
            FLOWWORKER_PROFILING_END(x, PROFILE_FLOWWORKER_DETECT);
403
48.8k
        }
404
405
48.8k
        OutputLoggerLog(tv, x, fw->output_thread);
406
407
48.8k
        FramesPrune(x->flow, x);
408
        /*  Release tcp segments. Done here after alerting can use them. */
409
48.8k
        FLOWWORKER_PROFILING_START(x, PROFILE_FLOWWORKER_TCPPRUNE);
410
48.8k
        StreamTcpPruneSession(
411
48.8k
                x->flow, x->flowflags & FLOW_PKT_TOSERVER ? STREAM_TOSERVER : STREAM_TOCLIENT);
412
48.8k
        FLOWWORKER_PROFILING_END(x, PROFILE_FLOWWORKER_TCPPRUNE);
413
414
        /* no need to keep a flow ref beyond this point */
415
48.8k
        FlowDeReference(&x->flow);
416
417
        /* no further work to do for this pseudo packet, so we can return
418
         * it to the pool immediately. */
419
48.8k
        if (timeout) {
420
36
            PacketPoolReturnPacket(x);
421
48.7k
        } else {
422
            /* to support IPS verdict logic, in the non-timeout case we need to do a bit more */
423
48.7k
            TmqhOutputPacketpool(tv, x);
424
48.7k
        }
425
48.8k
    }
426
11.1M
    if (FlowChangeProto(p->flow) && p->flow->flags & FLOW_ACTION_DROP) {
427
        // in case f->flags & FLOW_ACTION_DROP was set by one of the dequeued packets
428
39
        if (p->flow->aux_flags & FLOW_AUX_ACTION_BY_FIREWALL) {
429
0
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FW_FLOW_DROP);
430
39
        } else {
431
39
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FLOW_DROP);
432
39
        }
433
39
    }
434
11.1M
}
435
436
static void FlowWorkerFlowTimeout(
437
        ThreadVars *tv, Packet *p, FlowWorkerThreadData *fw, DetectEngineThreadCtx *det_ctx)
438
227k
{
439
227k
    DEBUG_VALIDATE_BUG_ON(p->pkt_src != PKT_SRC_FFR);
440
441
227k
    SCLogDebug("packet %"PRIu64" is TCP. Direction %s", p->pcap_cnt, PKT_IS_TOSERVER(p) ? "TOSERVER" : "TOCLIENT");
442
227k
    DEBUG_VALIDATE_BUG_ON(!(p->flow && PacketIsTCP(p)));
443
227k
    DEBUG_ASSERT_FLOW_LOCKED(p->flow);
444
445
    /* handle TCP and app layer */
446
227k
    FlowWorkerStreamTCPUpdate(tv, fw, p, det_ctx, true);
447
448
227k
    PacketUpdateEngineEventCounters(tv, fw->dtv, p);
449
450
    /* handle Detect */
451
227k
    SCLogDebug("packet %"PRIu64" calling Detect", p->pcap_cnt);
452
227k
    if (det_ctx != NULL) {
453
227k
        FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_DETECT);
454
227k
        Detect(tv, p, det_ctx);
455
227k
        FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_DETECT);
456
227k
    }
457
458
    // Outputs.
459
227k
    OutputLoggerLog(tv, p, fw->output_thread);
460
461
227k
    FramesPrune(p->flow, p);
462
463
    /*  Release tcp segments. Done here after alerting can use them. */
464
227k
    FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_TCPPRUNE);
465
227k
    StreamTcpPruneSession(p->flow, p->flowflags & FLOW_PKT_TOSERVER ?
466
116k
            STREAM_TOSERVER : STREAM_TOCLIENT);
467
227k
    FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_TCPPRUNE);
468
469
    /* run tx cleanup last */
470
227k
    AppLayerParserTransactionsCleanup(p->flow, STREAM_FLAGS_FOR_PACKET(p));
471
472
227k
    FlowDeReference(&p->flow);
473
    /* flow is unlocked later in FlowFinish() */
474
227k
}
475
476
/** \internal
477
 *  \brief process flows injected into our queue by other threads
478
 */
479
static inline void FlowWorkerProcessInjectedFlows(
480
        ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p)
481
16.0M
{
482
    /* take injected flows and append to our work queue */
483
16.0M
    FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_FLOW_INJECTED);
484
16.0M
    FlowQueuePrivate injected = { NULL, NULL, 0 };
485
16.0M
    if (SC_ATOMIC_GET(tv->flow_queue->non_empty))
486
0
        injected = FlowQueueExtractPrivate(tv->flow_queue);
487
16.0M
    if (injected.len > 0) {
488
0
        StatsAddUI64(tv, fw->cnt.flows_injected, (uint64_t)injected.len);
489
0
        if (p->pkt_src == PKT_SRC_WIRE)
490
0
            StatsSetUI64(tv, fw->cnt.flows_injected_max, (uint64_t)injected.len);
491
492
        /* move to local queue so we can process over the course of multiple packets */
493
0
        FlowQueuePrivateAppendPrivate(&fw->fls.work_queue, &injected);
494
0
    }
495
16.0M
    FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_FLOW_INJECTED);
496
16.0M
}
497
498
/** \internal
499
 *  \brief process flows set aside locally during flow lookup
500
 */
501
static inline void FlowWorkerProcessLocalFlows(ThreadVars *tv, FlowWorkerThreadData *fw, Packet *p)
502
16.0M
{
503
16.0M
    uint32_t max_work = 2;
504
16.0M
    if (p->pkt_src == PKT_SRC_SHUTDOWN_FLUSH || p->pkt_src == PKT_SRC_CAPTURE_TIMEOUT)
505
0
        max_work = 0;
506
507
16.0M
    FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_FLOW_EVICTED);
508
16.0M
    if (fw->fls.work_queue.len) {
509
175k
        FlowTimeoutCounters counters = { 0, 0, };
510
175k
        CheckWorkQueue(tv, fw, &counters, &fw->fls.work_queue, max_work);
511
175k
        UpdateCounters(tv, fw, &counters);
512
175k
    }
513
16.0M
    FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_FLOW_EVICTED);
514
16.0M
}
515
516
/** \internal
517
 *  \brief apply Packet::app_update_direction to the flow flags
518
 */
519
static void PacketAppUpdate2FlowFlags(Packet *p)
520
11.8M
{
521
11.8M
    switch ((enum StreamUpdateDir)p->app_update_direction) {
522
8.52M
        case UPDATE_DIR_NONE: // NONE implies pseudo packet
523
8.52M
            SCLogDebug("pcap_cnt %" PRIu64 ", UPDATE_DIR_NONE", p->pcap_cnt);
524
8.52M
            break;
525
816k
        case UPDATE_DIR_PACKET:
526
816k
            if (PKT_IS_TOSERVER(p)) {
527
691k
                p->flow->flags |= FLOW_TS_APP_UPDATED;
528
691k
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TS_APP_UPDATED set", p->pcap_cnt);
529
691k
            } else {
530
125k
                p->flow->flags |= FLOW_TC_APP_UPDATED;
531
125k
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TC_APP_UPDATED set", p->pcap_cnt);
532
125k
            }
533
816k
            break;
534
9.70k
        case UPDATE_DIR_BOTH:
535
9.70k
            if (PKT_IS_TOSERVER(p)) {
536
5.99k
                p->flow->flags |= FLOW_TS_APP_UPDATED | FLOW_TC_APP_UPDATE_NEXT;
537
5.99k
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TS_APP_UPDATED|FLOW_TC_APP_UPDATE_NEXT set",
538
5.99k
                        p->pcap_cnt);
539
5.99k
            } else {
540
3.70k
                p->flow->flags |= FLOW_TC_APP_UPDATED | FLOW_TS_APP_UPDATE_NEXT;
541
3.70k
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TC_APP_UPDATED|FLOW_TS_APP_UPDATE_NEXT set",
542
3.70k
                        p->pcap_cnt);
543
3.70k
            }
544
            /* fall through */
545
2.50M
        case UPDATE_DIR_OPPOSING:
546
2.50M
            if (PKT_IS_TOSERVER(p)) {
547
1.46M
                p->flow->flags |= FLOW_TC_APP_UPDATED | FLOW_TS_APP_UPDATE_NEXT;
548
1.46M
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TC_APP_UPDATED|FLOW_TS_APP_UPDATE_NEXT set",
549
1.46M
                        p->pcap_cnt);
550
1.46M
            } else {
551
1.04M
                p->flow->flags |= FLOW_TS_APP_UPDATED | FLOW_TC_APP_UPDATE_NEXT;
552
1.04M
                SCLogDebug("pcap_cnt %" PRIu64 ", FLOW_TS_APP_UPDATED|FLOW_TC_APP_UPDATE_NEXT set",
553
1.04M
                        p->pcap_cnt);
554
1.04M
            }
555
2.50M
            break;
556
11.8M
    }
557
11.8M
}
558
559
static TmEcode FlowWorker(ThreadVars *tv, Packet *p, void *data)
560
16.0M
{
561
16.0M
    FlowWorkerThreadData *fw = data;
562
16.0M
    DetectEngineThreadCtx *det_ctx = SC_ATOMIC_GET(fw->detect_thread);
563
564
16.0M
    DEBUG_VALIDATE_BUG_ON(p == NULL);
565
16.0M
    DEBUG_VALIDATE_BUG_ON(tv->flow_queue == NULL);
566
567
16.0M
    SCLogDebug("packet %"PRIu64, p->pcap_cnt);
568
569
    /* handle Flow */
570
16.0M
    if (det_ctx != NULL && det_ctx->de_ctx->PreFlowHook != NULL) {
571
123
        const uint8_t action = det_ctx->de_ctx->PreFlowHook(tv, det_ctx, p);
572
123
        if (action & ACTION_DROP) {
573
3
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FW_FLOW_PRE_HOOK);
574
3
            goto pre_flow_drop;
575
3
        }
576
123
    }
577
578
16.0M
    if (p->flags & PKT_WANTS_FLOW) {
579
12.2M
        FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_FLOW);
580
581
12.2M
        FlowHandlePacket(tv, &fw->fls, p);
582
12.2M
        if (likely(p->flow != NULL)) {
583
12.2M
            DEBUG_ASSERT_FLOW_LOCKED(p->flow);
584
12.2M
            if (FlowUpdate(tv, fw, p) == TM_ECODE_DONE) {
585
                /* update time */
586
77
                if (!(PKT_IS_PSEUDOPKT(p))) {
587
77
                    TimeSetByThread(tv->id, p->ts);
588
77
                }
589
77
                goto housekeeping;
590
77
            }
591
12.2M
        }
592
        /* Flow is now LOCKED */
593
594
12.2M
        FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_FLOW);
595
596
    /* if PKT_WANTS_FLOW is not set, but PKT_HAS_FLOW is, then this is a
597
     * pseudo packet created by the flow manager. */
598
12.2M
    } else if (p->flags & PKT_HAS_FLOW) {
599
0
        FLOWLOCK_WRLOCK(p->flow);
600
0
        DEBUG_VALIDATE_BUG_ON(p->pkt_src != PKT_SRC_FFR);
601
0
    }
602
603
    /* update time */
604
16.0M
    if (!(PKT_IS_PSEUDOPKT(p))) {
605
16.0M
        TimeSetByThread(tv->id, p->ts);
606
16.0M
    }
607
608
16.0M
    SCLogDebug("packet %"PRIu64" has flow? %s", p->pcap_cnt, p->flow ? "yes" : "no");
609
610
    /* handle TCP and app layer */
611
16.0M
    if (p->flow) {
612
12.2M
        SCLogDebug("packet %" PRIu64
613
12.2M
                   ": direction %s FLOW_TS_APP_UPDATE_NEXT %s FLOW_TC_APP_UPDATE_NEXT %s",
614
12.2M
                p->pcap_cnt, PKT_IS_TOSERVER(p) ? "toserver" : "toclient",
615
12.2M
                BOOL2STR((p->flow->flags & FLOW_TS_APP_UPDATE_NEXT) != 0),
616
12.2M
                BOOL2STR((p->flow->flags & FLOW_TC_APP_UPDATE_NEXT) != 0));
617
        /* see if need to consider flags set by prev packets */
618
12.2M
        if (PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATE_NEXT)) {
619
1.42M
            p->flow->flags |= FLOW_TS_APP_UPDATED;
620
1.42M
            p->flow->flags &= ~FLOW_TS_APP_UPDATE_NEXT;
621
1.42M
            SCLogDebug("FLOW_TS_APP_UPDATED");
622
10.8M
        } else if (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATE_NEXT)) {
623
995k
            p->flow->flags |= FLOW_TC_APP_UPDATED;
624
995k
            p->flow->flags &= ~FLOW_TC_APP_UPDATE_NEXT;
625
995k
            SCLogDebug("FLOW_TC_APP_UPDATED");
626
995k
        }
627
628
12.2M
        if (PacketIsTCP(p)) {
629
10.9M
            SCLogDebug("packet %" PRIu64 " is TCP. Direction %s", p->pcap_cnt,
630
10.9M
                    PKT_IS_TOSERVER(p) ? "TOSERVER" : "TOCLIENT");
631
10.9M
            DEBUG_ASSERT_FLOW_LOCKED(p->flow);
632
633
            /* if detect is disabled, we need to apply file flags to the flow
634
             * here on the first packet. */
635
10.9M
            if (det_ctx == NULL &&
636
0
                    ((PKT_IS_TOSERVER(p) && (p->flowflags & FLOW_PKT_TOSERVER_FIRST)) ||
637
0
                            (PKT_IS_TOCLIENT(p) && (p->flowflags & FLOW_PKT_TOCLIENT_FIRST)))) {
638
0
                DisableDetectFlowFileFlags(p->flow);
639
0
            }
640
641
10.9M
            FlowWorkerStreamTCPUpdate(tv, fw, p, det_ctx, false);
642
10.9M
            PacketAppUpdate2FlowFlags(p);
643
644
            /* handle the app layer part of the UDP packet payload */
645
10.9M
        } else if (p->proto == IPPROTO_UDP && !PacketCheckAction(p, ACTION_DROP)) {
646
878k
            FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_APPLAYERUDP);
647
878k
            AppLayerHandleUdp(tv, fw->stream_thread->ra_ctx->app_tctx, p, p->flow);
648
878k
            FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_APPLAYERUDP);
649
878k
            PacketAppUpdate2FlowFlags(p);
650
878k
        }
651
12.2M
    }
652
653
16.0M
    PacketUpdateEngineEventCounters(tv, fw->dtv, p);
654
655
    /* handle Detect */
656
16.0M
    DEBUG_ASSERT_FLOW_LOCKED(p->flow);
657
16.0M
    SCLogDebug("packet %"PRIu64" calling Detect", p->pcap_cnt);
658
16.0M
    if (det_ctx != NULL) {
659
16.0M
        FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_DETECT);
660
16.0M
        Detect(tv, p, det_ctx);
661
16.0M
        FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_DETECT);
662
16.0M
    }
663
664
16.0M
pre_flow_drop:
665
    // Outputs.
666
16.0M
    OutputLoggerLog(tv, p, fw->output_thread);
667
668
    /*  Release tcp segments. Done here after alerting can use them. */
669
16.0M
    if (p->flow != NULL) {
670
12.2M
        DEBUG_ASSERT_FLOW_LOCKED(p->flow);
671
672
12.2M
        if (FlowIsBypassed(p->flow)) {
673
12
            FlowCleanupAppLayer(p->flow);
674
12
            if (p->proto == IPPROTO_TCP) {
675
3
                StreamTcpSessionCleanup(p->flow->protoctx);
676
3
            }
677
12.2M
        } else if (p->proto == IPPROTO_TCP && p->flow->protoctx && p->flags & PKT_STREAM_EST) {
678
9.15M
            if ((p->flow->flags & FLOW_TS_APP_UPDATED) && PKT_IS_TOSERVER(p)) {
679
1.89M
                FramesPrune(p->flow, p);
680
7.25M
            } else if ((p->flow->flags & FLOW_TC_APP_UPDATED) && PKT_IS_TOCLIENT(p)) {
681
1.81M
                FramesPrune(p->flow, p);
682
1.81M
            }
683
9.15M
            FLOWWORKER_PROFILING_START(p, PROFILE_FLOWWORKER_TCPPRUNE);
684
9.15M
            StreamTcpPruneSession(p->flow, p->flowflags & FLOW_PKT_TOSERVER ?
685
5.17M
                    STREAM_TOSERVER : STREAM_TOCLIENT);
686
9.15M
            FLOWWORKER_PROFILING_END(p, PROFILE_FLOWWORKER_TCPPRUNE);
687
9.15M
        } else if (p->proto == IPPROTO_UDP) {
688
879k
            FramesPrune(p->flow, p);
689
879k
        }
690
691
12.2M
        if ((PKT_IS_PSEUDOPKT(p)) ||
692
12.2M
                (p->flow->flags & (FLOW_TS_APP_UPDATED | FLOW_TC_APP_UPDATED))) {
693
6.11M
            if ((p->flags & PKT_STREAM_EST) || p->proto != IPPROTO_TCP) {
694
6.02M
                if (PKT_IS_TOSERVER(p)) {
695
3.44M
                    if (PKT_IS_PSEUDOPKT(p) || (p->flow->flags & (FLOW_TS_APP_UPDATED))) {
696
2.57M
                        AppLayerParserTransactionsCleanup(p->flow, STREAM_TOSERVER);
697
2.57M
                        p->flow->flags &= ~FLOW_TS_APP_UPDATED;
698
2.57M
                        SCLogDebug("~FLOW_TS_APP_UPDATED");
699
2.57M
                    }
700
3.44M
                } else {
701
2.57M
                    if (PKT_IS_PSEUDOPKT(p) || (p->flow->flags & (FLOW_TC_APP_UPDATED))) {
702
1.93M
                        AppLayerParserTransactionsCleanup(p->flow, STREAM_TOCLIENT);
703
1.93M
                        p->flow->flags &= ~FLOW_TC_APP_UPDATED;
704
1.93M
                        SCLogDebug("~FLOW_TC_APP_UPDATED");
705
1.93M
                    }
706
2.57M
                }
707
6.02M
            }
708
6.14M
        } else {
709
6.14M
            SCLogDebug("not pseudo, no app update: skip");
710
6.14M
        }
711
712
12.2M
        if (p->flow->flags & FLOW_ACTION_DROP) {
713
3.83k
            SCLogDebug("flow drop in place: remove app update flags");
714
3.83k
            p->flow->flags &= ~(FLOW_TS_APP_UPDATED | FLOW_TC_APP_UPDATED);
715
3.83k
        }
716
717
12.2M
        Flow *f = p->flow;
718
12.2M
        FlowDeReference(&p->flow);
719
12.2M
        FLOWLOCK_UNLOCK(f);
720
12.2M
    }
721
722
16.0M
housekeeping:
723
724
    /* take injected flows and add them to our local queue */
725
16.0M
    FlowWorkerProcessInjectedFlows(tv, fw, p);
726
727
    /* process local work queue */
728
16.0M
    FlowWorkerProcessLocalFlows(tv, fw, p);
729
730
16.0M
    return TM_ECODE_OK;
731
16.0M
}
732
733
void FlowWorkerReplaceDetectCtx(void *flow_worker, void *detect_ctx)
734
113k
{
735
113k
    FlowWorkerThreadData *fw = flow_worker;
736
737
113k
    SC_ATOMIC_SET(fw->detect_thread, detect_ctx);
738
113k
}
739
740
void *FlowWorkerGetDetectCtxPtr(void *flow_worker)
741
113k
{
742
113k
    FlowWorkerThreadData *fw = flow_worker;
743
744
113k
    return SC_ATOMIC_GET(fw->detect_thread);
745
113k
}
746
747
void *FlowWorkerGetThreadData(void *flow_worker)
748
0
{
749
0
    return (FlowWorkerThreadData *)flow_worker;
750
0
}
751
752
const char *ProfileFlowWorkerIdToString(enum ProfileFlowWorkerId fwi)
753
0
{
754
0
    switch (fwi) {
755
0
        case PROFILE_FLOWWORKER_FLOW:
756
0
            return "flow";
757
0
        case PROFILE_FLOWWORKER_STREAM:
758
0
            return "stream";
759
0
        case PROFILE_FLOWWORKER_APPLAYERUDP:
760
0
            return "app-layer";
761
0
        case PROFILE_FLOWWORKER_DETECT:
762
0
            return "detect";
763
0
        case PROFILE_FLOWWORKER_TCPPRUNE:
764
0
            return "tcp-prune";
765
0
        case PROFILE_FLOWWORKER_FLOW_INJECTED:
766
0
            return "flow-inject";
767
0
        case PROFILE_FLOWWORKER_FLOW_EVICTED:
768
0
            return "flow-evict";
769
0
        case PROFILE_FLOWWORKER_SIZE:
770
0
            return "size";
771
0
    }
772
0
    return "error";
773
0
}
774
775
static bool FlowWorkerIsBusy(ThreadVars *tv, void *flow_worker)
776
0
{
777
0
    FlowWorkerThreadData *fw = flow_worker;
778
0
    if (fw->pq.len)
779
0
        return true;
780
0
    if (fw->fls.work_queue.len)
781
0
        return true;
782
783
0
    if (tv->flow_queue) {
784
0
        FQLOCK_LOCK(tv->flow_queue);
785
0
        bool fq_done = (tv->flow_queue->qlen == 0);
786
0
        FQLOCK_UNLOCK(tv->flow_queue);
787
0
        if (!fq_done) {
788
0
            return true;
789
0
        }
790
0
    }
791
792
0
    return false;
793
0
}
794
795
void TmModuleFlowWorkerRegister (void)
796
11
{
797
11
    tmm_modules[TMM_FLOWWORKER].name = "FlowWorker";
798
11
    tmm_modules[TMM_FLOWWORKER].ThreadInit = FlowWorkerThreadInit;
799
11
    tmm_modules[TMM_FLOWWORKER].Func = FlowWorker;
800
11
    tmm_modules[TMM_FLOWWORKER].ThreadBusy = FlowWorkerIsBusy;
801
11
    tmm_modules[TMM_FLOWWORKER].ThreadDeinit = FlowWorkerThreadDeinit;
802
11
    tmm_modules[TMM_FLOWWORKER].cap_flags = 0;
803
11
    tmm_modules[TMM_FLOWWORKER].flags = TM_FLAG_FLOWWORKER_TM;
804
11
}