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

OISF / suricata / 23350763619

20 Mar 2026 03:49PM UTC coverage: 76.429% (-2.9%) from 79.315%
23350763619

Pull #15029

github

web-flow
Merge 5fca25ef1 into 6587e363a
Pull Request #15029: Fw updates/v5

379 of 449 new or added lines in 26 files covered. (84.41%)

13267 existing lines in 297 files now uncovered.

244170 of 319471 relevant lines covered (76.43%)

3147764.06 hits per line

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

94.25
/src/flow-timeout.c
1
/* Copyright (C) 2007-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 Anoop Saldanha <anoopsaldanha@gmail.com>
22
 */
23

24
#include "suricata-common.h"
25
#include "suricata.h"
26
#include "decode.h"
27
#include "conf.h"
28
#include "threadvars.h"
29
#include "tm-threads.h"
30
#include "runmodes.h"
31

32
#include "util-random.h"
33
#include "util-time.h"
34

35
#include "flow.h"
36
#include "flow-queue.h"
37
#include "flow-hash.h"
38
#include "flow-util.h"
39
#include "flow-var.h"
40
#include "flow-private.h"
41
#include "flow-manager.h"
42
#include "flow-timeout.h"
43
#include "pkt-var.h"
44
#include "host.h"
45

46
#include "stream-tcp-private.h"
47
#include "stream-tcp-reassemble.h"
48
#include "stream-tcp.h"
49

50
#include "util-unittest.h"
51
#include "util-unittest-helper.h"
52
#include "util-byte.h"
53

54
#include "util-debug.h"
55
#include "util-privs.h"
56
#include "util-datalink.h"
57

58
#include "detect.h"
59
#include "detect-engine-state.h"
60
#include "stream.h"
61

62
#include "rust.h"
63
#include "app-layer-frames.h"
64
#include "app-layer-parser.h"
65
#include "app-layer.h"
66

67
#include "util-profiling.h"
68

69
/**
70
 * \internal
71
 * \brief Pseudo packet setup to finish a flow when needed.
72
 *
73
 * \param p         a dummy pseudo packet from packet pool.  Not all pseudo
74
 *                  packets need to force reassembly, in which case we just
75
 *                  set dummy ack/seq values.
76
 * \param direction Direction of the packet.  0 indicates toserver and 1
77
 *                  indicates toclient.
78
 * \param f         Pointer to the flow.
79
 * \param ssn       Pointer to the tcp session.
80
 * \retval          pseudo packet with everything set up
81
 */
82
static inline Packet *FlowPseudoPacketSetup(
83
        Packet *p, int direction, Flow *f, const TcpSession *ssn)
84
{
13,130✔
85
    const int orig_dir = direction;
13,130✔
86
    p->tenant_id = f->tenant_id;
13,130✔
87
    p->datalink = DatalinkGetGlobalType();
13,130✔
88
    p->proto = IPPROTO_TCP;
13,130✔
89
    FlowReference(&p->flow, f);
13,130✔
90
    p->flags |= PKT_STREAM_EST;
13,130✔
91
    p->flags |= PKT_HAS_FLOW;
13,130✔
92
    p->flags |= PKT_PSEUDO_STREAM_END;
13,130✔
93
    memcpy(&p->vlan_id[0], &f->vlan_id[0], sizeof(p->vlan_id));
13,130✔
94
    p->vlan_idx = f->vlan_idx;
13,130✔
95
    p->livedev = (struct LiveDevice_ *)f->livedev;
13,130✔
96

97
    if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
13,130✔
98
        DecodeSetNoPayloadInspectionFlag(p);
613✔
99
    }
613✔
100

101
    if (direction == 0)
13,130✔
102
        p->flowflags |= FLOW_PKT_TOSERVER;
6,754✔
103
    else
6,376✔
104
        p->flowflags |= FLOW_PKT_TOCLIENT;
6,376✔
105
    p->flowflags |= FLOW_PKT_ESTABLISHED;
13,130✔
106
    p->payload = NULL;
13,130✔
107
    p->payload_len = 0;
13,130✔
108

109
    /* apply reversed flow logic after setting direction to the packet */
110
    direction ^= ((f->flags & FLOW_DIR_REVERSED) != 0);
13,130✔
111

112
    if (FLOW_IS_IPV4(f)) {
13,130✔
113
        if (direction == 0) {
12,756✔
114
            FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, &p->src);
6,622✔
115
            FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, &p->dst);
6,622✔
116
            p->sp = f->sp;
6,622✔
117
            p->dp = f->dp;
6,622✔
118
        } else {
6,698✔
119
            FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->src, &p->dst);
6,134✔
120
            FLOW_COPY_IPV4_ADDR_TO_PACKET(&f->dst, &p->src);
6,134✔
121
            p->sp = f->dp;
6,134✔
122
            p->dp = f->sp;
6,134✔
123
        }
6,134✔
124

125
        /* Check if we have enough room in direct data. We need ipv4 hdr + tcp hdr.
126
         * Force an allocation if it is not the case.
127
         */
128
        if (GET_PKT_DIRECT_MAX_SIZE(p) <  40) {
12,756✔
UNCOV
129
            if (PacketCallocExtPkt(p, 40) == -1) {
×
130
                goto error;
×
131
            }
×
UNCOV
132
        }
×
133
        /* set the ip header */
134
        IPV4Hdr *ip4h = PacketSetIPV4(p, GET_PKT_DATA(p));
12,756✔
135
        /* version 4 and length 20 bytes for the tcp header */
136
        ip4h->ip_verhl = 0x45;
12,756✔
137
        ip4h->ip_tos = 0;
12,756✔
138
        ip4h->ip_len = htons(40);
12,756✔
139
        ip4h->ip_id = 0;
12,756✔
140
        ip4h->ip_off = 0;
12,756✔
141
        ip4h->ip_ttl = 64;
12,756✔
142
        ip4h->ip_proto = IPPROTO_TCP;
12,756✔
143
        //p->ip4h->ip_csum =
144
        if (direction == 0) {
12,756✔
145
            ip4h->s_ip_src.s_addr = f->src.addr_data32[0];
6,622✔
146
            ip4h->s_ip_dst.s_addr = f->dst.addr_data32[0];
6,622✔
147
        } else {
6,698✔
148
            ip4h->s_ip_src.s_addr = f->dst.addr_data32[0];
6,134✔
149
            ip4h->s_ip_dst.s_addr = f->src.addr_data32[0];
6,134✔
150
        }
6,134✔
151

152
        /* set the tcp header */
153
        PacketSetTCP(p, GET_PKT_DATA(p) + 20);
12,756✔
154

155
        SET_PKT_LEN(p, 40); /* ipv4 hdr + tcp hdr */
12,756✔
156

157
    } else if (FLOW_IS_IPV6(f)) {
12,756✔
158
        if (direction == 0) {
374✔
159
            FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, &p->src);
186✔
160
            FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, &p->dst);
186✔
161
            p->sp = f->sp;
186✔
162
            p->dp = f->dp;
186✔
163
        } else {
191✔
164
            FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->src, &p->dst);
188✔
165
            FLOW_COPY_IPV6_ADDR_TO_PACKET(&f->dst, &p->src);
188✔
166
            p->sp = f->dp;
188✔
167
            p->dp = f->sp;
188✔
168
        }
188✔
169

170
        /* Check if we have enough room in direct data. We need ipv6 hdr + tcp hdr.
171
         * Force an allocation if it is not the case.
172
         */
173
        if (GET_PKT_DIRECT_MAX_SIZE(p) <  60) {
374✔
174
            if (PacketCallocExtPkt(p, 60) == -1) {
×
175
                goto error;
×
176
            }
×
177
        }
×
178
        /* set the ip header */
179
        IPV6Hdr *ip6h = PacketSetIPV6(p, GET_PKT_DATA(p));
374✔
180
        /* version 6 */
181
        ip6h->s_ip6_vfc = 0x60;
374✔
182
        ip6h->s_ip6_flow = 0;
374✔
183
        ip6h->s_ip6_nxt = IPPROTO_TCP;
374✔
184
        ip6h->s_ip6_plen = htons(20);
374✔
185
        ip6h->s_ip6_hlim = 64;
374✔
186
        if (direction == 0) {
374✔
187
            ip6h->s_ip6_src[0] = f->src.addr_data32[0];
186✔
188
            ip6h->s_ip6_src[1] = f->src.addr_data32[1];
186✔
189
            ip6h->s_ip6_src[2] = f->src.addr_data32[2];
186✔
190
            ip6h->s_ip6_src[3] = f->src.addr_data32[3];
186✔
191
            ip6h->s_ip6_dst[0] = f->dst.addr_data32[0];
186✔
192
            ip6h->s_ip6_dst[1] = f->dst.addr_data32[1];
186✔
193
            ip6h->s_ip6_dst[2] = f->dst.addr_data32[2];
186✔
194
            ip6h->s_ip6_dst[3] = f->dst.addr_data32[3];
186✔
195
        } else {
191✔
196
            ip6h->s_ip6_src[0] = f->dst.addr_data32[0];
188✔
197
            ip6h->s_ip6_src[1] = f->dst.addr_data32[1];
188✔
198
            ip6h->s_ip6_src[2] = f->dst.addr_data32[2];
188✔
199
            ip6h->s_ip6_src[3] = f->dst.addr_data32[3];
188✔
200
            ip6h->s_ip6_dst[0] = f->src.addr_data32[0];
188✔
201
            ip6h->s_ip6_dst[1] = f->src.addr_data32[1];
188✔
202
            ip6h->s_ip6_dst[2] = f->src.addr_data32[2];
188✔
203
            ip6h->s_ip6_dst[3] = f->src.addr_data32[3];
188✔
204
        }
188✔
205

206
        /* set the tcp header */
207
        PacketSetTCP(p, GET_PKT_DATA(p) + 40);
374✔
208

209
        SET_PKT_LEN(p, 60); /* ipv6 hdr + tcp hdr */
374✔
210
    }
374✔
211

212
    p->l4.hdrs.tcph->th_offx2 = 0x50;
13,130✔
213
    p->l4.hdrs.tcph->th_flags = 0;
13,130✔
214
    p->l4.hdrs.tcph->th_win = 10;
13,130✔
215
    p->l4.hdrs.tcph->th_urp = 0;
13,130✔
216

217
    /* to server */
218
    if (orig_dir == 0) {
13,130✔
219
        p->l4.hdrs.tcph->th_sport = htons(f->sp);
6,754✔
220
        p->l4.hdrs.tcph->th_dport = htons(f->dp);
6,754✔
221

222
        p->l4.hdrs.tcph->th_seq = htonl(ssn->client.next_seq);
6,754✔
223
        p->l4.hdrs.tcph->th_ack = 0;
6,754✔
224

225
        /* to client */
226
    } else {
6,827✔
227
        p->l4.hdrs.tcph->th_sport = htons(f->dp);
6,376✔
228
        p->l4.hdrs.tcph->th_dport = htons(f->sp);
6,376✔
229

230
        p->l4.hdrs.tcph->th_seq = htonl(ssn->server.next_seq);
6,376✔
231
        p->l4.hdrs.tcph->th_ack = 0;
6,376✔
232
    }
6,376✔
233

234
    if (FLOW_IS_IPV4(f)) {
13,130✔
235
        IPV4Hdr *ip4h = p->l3.hdrs.ip4h;
12,756✔
236
        p->l4.hdrs.tcph->th_sum = TCPChecksum(ip4h->s_ip_addrs, (uint16_t *)p->l4.hdrs.tcph, 20, 0);
12,756✔
237
        /* calc ipv4 csum as we may log it and barnyard might reject
238
         * a wrong checksum */
239
        ip4h->ip_csum = IPV4Checksum((uint16_t *)ip4h, IPV4_GET_RAW_HLEN(ip4h), 0);
12,756✔
240
    } else if (FLOW_IS_IPV6(f)) {
12,756✔
241
        const IPV6Hdr *ip6h = PacketGetIPv6(p);
374✔
242
        p->l4.hdrs.tcph->th_sum =
374✔
243
                TCPChecksum(ip6h->s_ip6_addrs, (uint16_t *)p->l4.hdrs.tcph, 20, 0);
374✔
244
    }
374✔
245

246
    p->ts = TimeGet();
13,130✔
247

248
    if (direction == 0) {
13,130✔
249
        if (f->alparser && !STREAM_HAS_SEEN_DATA(&ssn->client)) {
6,808✔
250
            SCAppLayerParserStateSetFlag(f->alparser, APP_LAYER_PARSER_EOF_TS);
319✔
251
        }
319✔
252
    } else {
6,881✔
253
        if (f->alparser && !STREAM_HAS_SEEN_DATA(&ssn->server)) {
6,322✔
254
            SCAppLayerParserStateSetFlag(f->alparser, APP_LAYER_PARSER_EOF_TC);
874✔
255
        }
874✔
256
    }
6,322✔
257

258
    return p;
13,130✔
259

260
error:
×
261
    FlowDeReference(&p->flow);
×
262
    return NULL;
×
263
}
13,130✔
264

265
Packet *FlowPseudoPacketGet(int direction, Flow *f, const TcpSession *ssn)
266
{
13,130✔
267
    PacketPoolWait();
13,130✔
268
    Packet *p = PacketPoolGetPacket();
13,130✔
269
    if (p == NULL) {
13,130✔
270
        return NULL;
×
271
    }
×
272

273
    PACKET_PROFILING_START(p);
13,130✔
274

275
    return FlowPseudoPacketSetup(p, direction, f, ssn);
13,130✔
276
}
13,130✔
277

278
/**
279
 *  \brief Check if a flow needs forced reassembly, or any other processing
280
 *
281
 *  \param f *LOCKED* flow
282
 *
283
 *  \retval false no
284
 *  \retval true yes
285
 */
286
bool FlowNeedsReassembly(Flow *f)
287
{
11,064✔
288
    if (f == NULL || f->protoctx == NULL) {
11,064✔
289
        return false;
57✔
290
    }
57✔
291

292
    TcpSession *ssn = (TcpSession *)f->protoctx;
11,007✔
293
    uint8_t client = StreamNeedsReassembly(ssn, STREAM_TOSERVER);
11,007✔
294
    uint8_t server = StreamNeedsReassembly(ssn, STREAM_TOCLIENT);
11,007✔
295

296
    /* if state is not fully closed we assume that we haven't fully
297
     * inspected the app layer state yet */
298
    if (ssn->state >= TCP_ESTABLISHED && ssn->state != TCP_CLOSED)
11,007✔
299
    {
6,030✔
300
        client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
6,030✔
301
        server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
6,030✔
302
    }
6,030✔
303

304
    /* if app layer still needs some love, push through */
305
    if (f->alproto != ALPROTO_UNKNOWN && f->alstate != NULL) {
11,007✔
306
        const uint64_t total_txs = AppLayerParserGetTxCnt(f, f->alstate);
6,430✔
307

308
        if (AppLayerParserGetTransactionActive(f, f->alparser, STREAM_TOCLIENT) < total_txs)
6,430✔
309
        {
3,682✔
310
            server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
3,682✔
311
        }
3,682✔
312
        if (AppLayerParserGetTransactionActive(f, f->alparser, STREAM_TOSERVER) < total_txs)
6,430✔
313
        {
3,070✔
314
            client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
3,070✔
315
        }
3,070✔
316
    }
6,430✔
317

318
    /* if any frame is present we assume it still needs work */
319
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
11,007✔
320
    if (frames_container) {
11,007✔
321
        if (frames_container->toserver.cnt)
2,646✔
322
            client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
1,556✔
323
        if (frames_container->toclient.cnt)
2,646✔
324
            server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
1,411✔
325
    }
2,646✔
326

327
    /* nothing to do */
328
    if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NONE &&
11,007✔
329
        server == STREAM_HAS_UNPROCESSED_SEGMENTS_NONE) {
11,007✔
330
        return false;
2,535✔
331
    }
2,535✔
332

333
    f->ffr_ts = client;
8,472✔
334
    f->ffr_tc = server;
8,472✔
335
    return true;
8,472✔
336
}
11,007✔
337

338
/**
339
 * \internal
340
 * \brief Sends the flow to its respective thread's flow queue.
341
 *
342
 *        The function requires flow to be locked beforehand.
343
 *
344
 * Normally, the first thread_id value should be used. This is when the flow is
345
 * created on seeing the first packet to the server; when the flow's reversed
346
 * flag is set, choose the second thread_id (to client/source).
347
 *
348
 * \param f Pointer to the flow.
349
 */
350
void FlowSendToLocalThread(Flow *f)
351
{
1,376✔
352
    // Choose the thread_id based on whether the flow has been
353
    // reversed.
354
    int idx = f->flags & FLOW_DIR_REVERSED ? 1 : 0;
1,376✔
355
    TmThreadsInjectFlowById(f, (const int)f->thread_id[idx]);
1,376✔
356
}
1,376✔
357

358
/**
359
 * \internal
360
 * \brief Remove flows from the hash bucket as they have more work to be done in
361
 *        in the detection engine.
362
 *
363
 * When this function is called we're running in virtually dead engine,
364
 * so locking the flows is not strictly required. The reasons it is still
365
 * done are:
366
 * - code consistency
367
 * - silence complaining profilers
368
 * - allow us to aggressively check using debug validation assertions
369
 * - be robust in case of future changes
370
 * - locking overhead is negligible when no other thread fights us
371
 */
372
static inline void FlowRemoveHash(void)
373
{
1,418✔
374
    for (uint32_t idx = 0; idx < flow_config.hash_size; idx++) {
92,931,466✔
375
        FlowBucket *fb = &flow_hash[idx];
92,930,048✔
376
        FBLOCK_LOCK(fb);
92,930,048✔
377

378
        Flow *f = fb->head;
92,930,048✔
379
        Flow *prev_f = NULL;
92,930,048✔
380

381
        /* we need to loop through all the flows in the queue */
382
        while (f != NULL) {
92,934,412✔
383
            Flow *next_f = f->next;
4,364✔
384

385
            FLOWLOCK_WRLOCK(f);
4,364✔
386

387
            /* Get the tcp session for the flow */
388
            TcpSession *ssn = (TcpSession *)f->protoctx;
4,364✔
389
            /* \todo Also skip flows that shouldn't be inspected */
390
            if (ssn == NULL) {
4,364✔
391
                FLOWLOCK_UNLOCK(f);
1,682✔
392
                prev_f = f;
1,682✔
393
                f = next_f;
1,682✔
394
                continue;
1,682✔
395
            }
1,682✔
396

397
            /* in case of additional work, we pull the flow out of the
398
             * hash and xfer ownership to the injected packet(s) */
399
            if (FlowNeedsReassembly(f)) {
2,682✔
400
                RemoveFromHash(f, prev_f);
1,376✔
401
                f->flow_end_flags |= FLOW_END_FLAG_SHUTDOWN;
1,376✔
402
                FlowSendToLocalThread(f);
1,376✔
403
                FLOWLOCK_UNLOCK(f);
1,376✔
404
                f = next_f;
1,376✔
405
                continue;
1,376✔
406
            }
1,376✔
407

408
            FLOWLOCK_UNLOCK(f);
1,306✔
409

410
            /* next flow in the queue */
411
            prev_f = f;
1,306✔
412
            f = f->next;
1,306✔
413
        }
1,306✔
414
        FBLOCK_UNLOCK(fb);
92,930,048✔
415
    }
92,930,048✔
416
}
1,418✔
417

418
/**
419
 * \brief Clean up all the flows that have unprocessed segments and have
420
 *        some work to do in the detection engine.
421
 */
422
void FlowWorkToDoCleanup(void)
423
{
1,418✔
424
    /* Carry out cleanup of unattended flows */
425
    FlowRemoveHash();
1,418✔
426
}
1,418✔
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