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

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

3416852.41 hits per line

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

73.88
/src/flow.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 Victor Julien <victor@inliniac.net>
22
 *
23
 *  Flow implementation.
24
 */
25

26
#include "suricata-common.h"
27
#include "suricata.h"
28

29
#include "action-globals.h"
30
#include "packet.h"
31
#include "decode.h"
32
#include "conf.h"
33
#include "threadvars.h"
34

35
#include "util-random.h"
36
#include "util-time.h"
37

38
#include "flow.h"
39
#include "flow-bindgen.h"
40
#include "flow-queue.h"
41
#include "flow-hash.h"
42
#include "flow-util.h"
43
#include "flow-private.h"
44
#include "flow-manager.h"
45
#include "flow-storage.h"
46
#include "flow-bypass.h"
47
#include "flow-spare-pool.h"
48
#include "flow-callbacks.h"
49

50
#include "stream-tcp-private.h"
51

52
#include "util-unittest.h"
53
#include "util-unittest-helper.h"
54
#include "util-byte.h"
55
#include "util-misc.h"
56
#include "util-macset.h"
57
#include "util-flow-rate.h"
58

59
#include "util-debug.h"
60

61
#include "rust.h"
62
#include "app-layer-parser.h"
63
#include "app-layer-expectation.h"
64

65
#define FLOW_DEFAULT_EMERGENCY_RECOVERY 30
6✔
66

67
//#define FLOW_DEFAULT_HASHSIZE    262144
68
#define FLOW_DEFAULT_HASHSIZE    65536
1,397✔
69
//#define FLOW_DEFAULT_MEMCAP      128 * 1024 * 1024 /* 128 MB */
70
#define FLOW_DEFAULT_MEMCAP      (32 * 1024 * 1024) /* 32 MB */
71

72
#define FLOW_DEFAULT_PREALLOC    10000
1,397✔
73

74
SC_ATOMIC_DECLARE(FlowProtoTimeoutPtr, flow_timeouts);
75

76
/** atomic int that is used when freeing a flow from the hash. In this
77
 *  case we walk the hash to find a flow to free. This var records where
78
 *  we left off in the hash. Without this only the top rows of the hash
79
 *  are freed. This isn't just about fairness. Under severe pressure, the
80
 *  hash rows on top would be all freed and the time to find a flow to
81
 *  free increased with every run. */
82
SC_ATOMIC_DECLARE(unsigned int, flow_prune_idx);
83

84
/** atomic flags */
85
SC_ATOMIC_DECLARE(unsigned int, flow_flags);
86

87
/** FlowProto specific timeouts and free/state functions */
88

89
FlowProtoTimeout flow_timeouts_normal[FLOW_PROTO_MAX];
90
FlowProtoTimeout flow_timeouts_emerg[FLOW_PROTO_MAX];
91
FlowProtoTimeout flow_timeouts_delta[FLOW_PROTO_MAX];
92
FlowProtoFreeFunc flow_freefuncs[FLOW_PROTO_MAX];
93

94
FlowConfig flow_config;
95

96
/** flow memuse counter (atomic), for enforcing memcap limit */
97
SC_ATOMIC_DECLARE(uint64_t, flow_memuse);
98

99
void FlowRegisterTests(void);
100
void FlowInitFlowProto(void);
101
int FlowSetProtoFreeFunc(uint8_t, void (*Free)(void *));
102

103
/**
104
 *  \brief Update memcap value
105
 *
106
 *  \param size new memcap value
107
 */
108
int FlowSetMemcap(uint64_t size)
109
{
×
110
    if ((uint64_t)SC_ATOMIC_GET(flow_memuse) < size) {
×
111
        SC_ATOMIC_SET(flow_config.memcap, size);
×
112
        return 1;
×
113
    }
×
114

115
    return 0;
×
116
}
×
117

118
/**
119
 *  \brief Return memcap value
120
 *
121
 *  \retval memcap value
122
 */
123
uint64_t FlowGetMemcap(void)
124
{
3,331✔
125
    uint64_t memcapcopy = SC_ATOMIC_GET(flow_config.memcap);
3,331✔
126
    return memcapcopy;
3,331✔
127
}
3,331✔
128

129
uint64_t FlowGetMemuse(void)
130
{
4,770✔
131
    uint64_t memusecopy = SC_ATOMIC_GET(flow_memuse);
4,770✔
132
    return memusecopy;
4,770✔
133
}
4,770✔
134

135
enum ExceptionPolicy FlowGetMemcapExceptionPolicy(void)
136
{
7,010✔
137
    return flow_config.memcap_policy;
7,010✔
138
}
7,010✔
139

140
void FlowCleanupAppLayer(Flow *f)
141
{
13,975,839✔
142
    if (f == NULL || f->proto == 0)
13,975,839✔
143
        return;
13,971,200✔
144

145
    AppLayerParserStateCleanup(f, f->alstate, f->alparser);
4,639✔
146
    f->alstate = NULL;
4,639✔
147
    f->alparser = NULL;
4,639✔
148
}
4,639✔
149

150
/** \brief Set flag to indicate that flow has alerts
151
 *
152
 * \param f flow
153
 */
154
void FlowSetHasAlertsFlag(Flow *f)
155
{
148✔
156
    f->flags |= FLOW_HAS_ALERTS;
148✔
157
}
148✔
158

159
/** \brief Check if flow has alerts
160
 *
161
 * \param f flow
162
 * \retval 1 has alerts
163
 * \retval 0 has not alerts
164
 */
165
int FlowHasAlerts(const Flow *f)
166
{
4,862✔
167
    if (f->flags & FLOW_HAS_ALERTS) {
4,862✔
168
        return 1;
308✔
169
    }
308✔
170

171
    return 0;
4,554✔
172
}
4,862✔
173

174
/** \brief Set flag to indicate to change proto for the flow
175
 *
176
 * \param f flow
177
 */
178
void FlowSetChangeProtoFlag(Flow *f)
179
{
143✔
180
    f->flags |= FLOW_CHANGE_PROTO;
143✔
181
}
143✔
182

183
/** \brief Unset flag to indicate to change proto for the flow
184
 *
185
 * \param f flow
186
 */
187
void FlowUnsetChangeProtoFlag(Flow *f)
188
{
141✔
189
    f->flags &= ~FLOW_CHANGE_PROTO;
141✔
190
}
141✔
191

192
/** \brief Check if change proto flag is set for flow
193
 * \param f flow
194
 * \retval 1 change proto flag is set
195
 * \retval 0 change proto flag is not set
196
 */
197
int FlowChangeProto(Flow *f)
198
{
1,445,098✔
199
    if (f->flags & FLOW_CHANGE_PROTO) {
1,445,098✔
200
        return 1;
960✔
201
    }
960✔
202

203
    return 0;
1,444,138✔
204
}
1,445,098✔
205

206
static inline void FlowSwapFlags(Flow *f)
207
{
×
208
    SWAP_FLAGS(f->flags, FLOW_TO_SRC_SEEN, FLOW_TO_DST_SEEN);
×
209
    SWAP_FLAGS(f->flags, FLOW_SGH_TOSERVER, FLOW_SGH_TOCLIENT);
×
210

211
    SWAP_FLAGS(f->flags, FLOW_TOSERVER_DROP_LOGGED, FLOW_TOCLIENT_DROP_LOGGED);
×
212
    SWAP_FLAGS(f->flags, FLOW_TS_PM_ALPROTO_DETECT_DONE, FLOW_TC_PM_ALPROTO_DETECT_DONE);
×
213
    SWAP_FLAGS(f->flags, FLOW_TS_PP_ALPROTO_DETECT_DONE, FLOW_TC_PP_ALPROTO_DETECT_DONE);
×
214
    SWAP_FLAGS(f->flags, FLOW_TS_PE_ALPROTO_DETECT_DONE, FLOW_TC_PE_ALPROTO_DETECT_DONE);
×
215

216
    SWAP_FLAGS(f->flags, FLOW_PROTO_DETECT_TS_DONE, FLOW_PROTO_DETECT_TC_DONE);
×
217
}
×
218

219
static inline void FlowSwapFileFlags(Flow *f)
220
{
×
221
    SWAP_FLAGS(f->file_flags, FLOWFILE_NO_MAGIC_TS, FLOWFILE_NO_MAGIC_TC);
×
222
    SWAP_FLAGS(f->file_flags, FLOWFILE_NO_STORE_TS, FLOWFILE_NO_STORE_TC);
×
223
    SWAP_FLAGS(f->file_flags, FLOWFILE_NO_MD5_TS, FLOWFILE_NO_MD5_TC);
×
224
    SWAP_FLAGS(f->file_flags, FLOWFILE_NO_SHA1_TS, FLOWFILE_NO_SHA1_TC);
×
225
    SWAP_FLAGS(f->file_flags, FLOWFILE_NO_SHA256_TS, FLOWFILE_NO_SHA256_TC);
×
226
}
×
227

228
static inline void TcpStreamFlowSwap(Flow *f)
229
{
×
230
    TcpSession *ssn = f->protoctx;
×
231
    SWAP_VARS(TcpStream, ssn->server, ssn->client);
×
232
    if (ssn->data_first_seen_dir & STREAM_TOSERVER) {
×
233
        ssn->data_first_seen_dir = STREAM_TOCLIENT;
×
234
    } else if (ssn->data_first_seen_dir & STREAM_TOCLIENT) {
×
235
        ssn->data_first_seen_dir = STREAM_TOSERVER;
×
236
    }
×
237
}
×
238

239
/** \brief swap the flow's direction
240
 *  \note leaves the 'header' untouched. Interpret that based
241
 *        on FLOW_DIR_REVERSED flag.
242
 *  \warning: only valid before applayer parsing started. This
243
 *            function doesn't swap anything in Flow::alparser,
244
 *            Flow::alstate
245
 */
246
void FlowSwap(Flow *f)
247
{
×
248
    f->flags |= FLOW_DIR_REVERSED;
×
249

250
    SWAP_VARS(uint32_t, f->probing_parser_toserver_alproto_masks,
×
251
                   f->probing_parser_toclient_alproto_masks);
×
252

253
    FlowSwapFlags(f);
×
254
    FlowSwapFileFlags(f);
×
255

256
    SWAP_VARS(FlowThreadId, f->thread_id[0], f->thread_id[1]);
×
257

258
    if (f->proto == IPPROTO_TCP) {
×
259
        TcpStreamFlowSwap(f);
×
260
    }
×
261

262
    SWAP_VARS(AppProto, f->alproto_ts, f->alproto_tc);
×
263
    SWAP_VARS(uint8_t, f->min_ttl_toserver, f->max_ttl_toserver);
×
264
    SWAP_VARS(uint8_t, f->min_ttl_toclient, f->max_ttl_toclient);
×
265

266
    /* not touching Flow::alparser and Flow::alstate */
267

268
    SWAP_VARS(const void *, f->sgh_toclient, f->sgh_toserver);
×
269

270
    SWAP_VARS(uint32_t, f->todstpktcnt, f->tosrcpktcnt);
×
271
    SWAP_VARS(uint64_t, f->todstbytecnt, f->tosrcbytecnt);
×
272

273
    if (MacSetFlowStorageEnabled()) {
×
274
        MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
×
275
        if (ms != NULL) {
×
276
            MacSetSwap(ms);
×
277
        }
×
278
    }
×
279
}
×
280

281
/**
282
 *  \brief determine the direction of the packet compared to the flow
283
 *  \retval 0 to_server
284
 *  \retval 1 to_client
285
 */
286
int FlowGetPacketDirection(const Flow *f, const Packet *p)
287
{
1,099,825✔
288
    const int reverse = (f->flags & FLOW_DIR_REVERSED) != 0;
1,099,825✔
289

290
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
1,099,825✔
291
        if (!(CMP_PORT(p->sp,p->dp))) {
665,136✔
292
            /* update flags and counters */
293
            if (CMP_PORT(f->sp,p->sp)) {
662,852✔
294
                return TOSERVER ^ reverse;
335,664✔
295
            } else {
335,664✔
296
                return TOCLIENT ^ reverse;
327,188✔
297
            }
327,188✔
298
        } else {
662,852✔
299
            if (CMP_ADDR(&f->src,&p->src)) {
2,284✔
300
                return TOSERVER ^ reverse;
2,168✔
301
            } else {
2,168✔
302
                return TOCLIENT ^ reverse;
116✔
303
            }
116✔
304
        }
2,284✔
305
    } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) {
665,136✔
306
        if (CMP_ADDR(&f->src,&p->src)) {
434,722✔
307
            return TOSERVER  ^ reverse;
425,198✔
308
        } else {
425,198✔
309
            return TOCLIENT ^ reverse;
9,524✔
310
        }
9,524✔
311
    }
434,722✔
312

313
    /* default to toserver */
314
    return TOSERVER;
2,147,483,647✔
315
}
1,099,825✔
316

317
/**
318
 *  \brief Check to update "seen" flags
319
 *
320
 *  \param p packet
321
 *
322
 *  \retval 1 true
323
 *  \retval 0 false
324
 */
325
static inline int FlowUpdateSeenFlag(const Packet *p)
326
{
8,007✔
327
    if (PacketIsICMPv4(p)) {
8,007✔
328
        if (ICMPV4_IS_ERROR_MSG(p->icmp_s.type)) {
120✔
329
            return 0;
6✔
330
        }
6✔
331
    }
120✔
332

333
    return 1;
8,001✔
334
}
8,007✔
335

336
static inline void FlowUpdateTtlTS(Flow *f, uint8_t ttl)
337
{
762,894✔
338
    if (f->min_ttl_toserver == 0) {
762,894✔
339
        f->min_ttl_toserver = ttl;
7✔
340
    } else {
762,887✔
341
        f->min_ttl_toserver = MIN(f->min_ttl_toserver, ttl);
762,887✔
342
    }
762,887✔
343
    f->max_ttl_toserver = MAX(f->max_ttl_toserver, ttl);
762,894✔
344
}
762,894✔
345

346
static inline void FlowUpdateTtlTC(Flow *f, uint8_t ttl)
347
{
336,863✔
348
    if (f->min_ttl_toclient == 0) {
336,863✔
349
        f->min_ttl_toclient = ttl;
3,452✔
350
    } else {
333,411✔
351
        f->min_ttl_toclient = MIN(f->min_ttl_toclient, ttl);
333,411✔
352
    }
333,411✔
353
    f->max_ttl_toclient = MAX(f->max_ttl_toclient, ttl);
336,863✔
354
}
336,863✔
355

356
static inline void FlowUpdateFlowRate(
357
        ThreadVars *tv, DecodeThreadVars *dtv, Flow *f, const Packet *p, int dir)
358
{
1,099,698✔
359
    if (FlowRateStorageEnabled()) {
1,099,698✔
360
        /* No need to update the struct if flow is already marked as elephant flow */
361
        if ((dir == TOSERVER) && (f->flags & FLOW_IS_ELEPHANT_TOSERVER))
×
362
            return;
×
363
        if ((dir == TOCLIENT) && (f->flags & FLOW_IS_ELEPHANT_TOCLIENT))
×
364
            return;
×
365
        FlowRateStore *frs = FlowGetStorageById(f, FlowRateGetStorageID());
×
366
        if (frs != NULL) {
×
367
            FlowRateStoreUpdate(frs, p->ts, GET_PKT_LEN(p), dir);
×
368
            bool fr_exceeds = FlowRateIsExceeding(frs, dir);
×
369
            if (fr_exceeds) {
×
370
                SCLogDebug("Flow rate for flow %p exceeds the configured values, marking it as an "
×
371
                           "elephant flow",
×
372
                        f);
×
373
                if (dir == TOSERVER) {
×
374
                    f->flags |= FLOW_IS_ELEPHANT_TOSERVER;
×
375
                    if (tv != NULL) {
×
376
                        if ((f->flags & FLOW_IS_ELEPHANT_TOCLIENT) == 0) {
×
377
                            StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant);
×
378
                        }
×
379
                        StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant_toserver);
×
380
                    }
×
381
                } else {
×
382
                    f->flags |= FLOW_IS_ELEPHANT_TOCLIENT;
×
383
                    if (tv != NULL) {
×
384
                        if ((f->flags & FLOW_IS_ELEPHANT_TOSERVER) == 0) {
×
385
                            StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant);
×
386
                        }
×
387
                        StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant_toclient);
×
388
                    }
×
389
                }
×
390
            }
×
391
        }
×
392
    }
×
393
}
1,099,698✔
394

395
static inline void FlowUpdateEthernet(
396
        ThreadVars *tv, DecodeThreadVars *dtv, Flow *f, const Packet *p, bool toserver)
397
{
1,099,649✔
398
    if (PacketIsEthernet(p) && MacSetFlowStorageEnabled()) {
1,099,649✔
399
        const EthernetHdr *ethh = PacketGetEthernet(p);
×
400
        MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
×
401
        if (ms != NULL) {
×
402
            if (toserver) {
×
403
                MacSetAddWithCtr(ms, ethh->eth_src, ethh->eth_dst, tv,
×
404
                                 dtv->counter_max_mac_addrs_src,
×
405
                                 dtv->counter_max_mac_addrs_dst);
×
406
            } else {
×
407
                MacSetAddWithCtr(ms, ethh->eth_dst, ethh->eth_src, tv,
×
408
                                 dtv->counter_max_mac_addrs_dst,
×
409
                                 dtv->counter_max_mac_addrs_src);
×
410
            }
×
411
        }
×
412
    }
×
413
}
1,099,649✔
414

415
/** \brief Update Packet and Flow
416
 *
417
 *  Updates packet and flow based on the new packet.
418
 *
419
 *  \param f locked flow
420
 *  \param p packet
421
 *
422
 *  \note overwrites p::flowflags
423
 */
424
void FlowHandlePacketUpdate(Flow *f, Packet *p, ThreadVars *tv, DecodeThreadVars *dtv)
425
{
1,099,657✔
426
    SCLogDebug("packet %" PRIu64 " -- flow %p", PcapPacketCntGet(p), f);
1,099,657✔
427

428
    const int pkt_dir = FlowGetPacketDirection(f, p);
1,099,657✔
429
#ifdef CAPTURE_OFFLOAD
471✔
430
    int state = f->flow_state;
471✔
431

432
    if (state != FLOW_STATE_CAPTURE_BYPASSED) {
471✔
433
#endif
468✔
434
        /* update the last seen timestamp of this flow */
435
        if (SCTIME_CMP_GT(p->ts, f->lastts)) {
1,099,655✔
436
            f->lastts = p->ts;
1,067,797✔
437
        }
1,067,797✔
438
#ifdef CAPTURE_OFFLOAD
468✔
439
    } else {
468✔
440
        FlowProtoTimeoutPtr flow_timeouts = SC_ATOMIC_GET(flow_timeouts);
3✔
441
        /* still seeing packet, we downgrade to local bypass */
442
        if (SCTIME_SECS(p->ts) - SCTIME_SECS(f->lastts) >
3✔
443
                flow_timeouts[f->protomap].bypassed_timeout / 2) {
3✔
444
            SCLogDebug("Downgrading flow to local bypass");
445
            f->lastts = p->ts;
446
            FlowUpdateState(f, FLOW_STATE_LOCAL_BYPASSED);
447
        } else {
3✔
448
            /* In IPS mode the packet could come from the other interface so it would
449
             * need to be bypassed */
450
            if (EngineModeIsIPS()) {
3✔
451
                BypassedFlowUpdate(f, p);
452
            }
453
        }
3✔
454
    }
3✔
455
#endif
471✔
456
    /* update flags and counters */
457
    if (pkt_dir == TOSERVER) {
1,099,657✔
458
        f->todstpktcnt++;
762,904✔
459
        f->todstbytecnt += GET_PKT_LEN(p);
762,904✔
460
        FlowUpdateFlowRate(tv, dtv, f, p, TOSERVER);
762,904✔
461
        p->flowflags = FLOW_PKT_TOSERVER;
762,904✔
462
        if (!(f->flags & FLOW_TO_DST_SEEN)) {
762,904✔
463
            if (FlowUpdateSeenFlag(p)) {
4,552✔
464
                f->flags |= FLOW_TO_DST_SEEN;
4,546✔
465
                p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
4,546✔
466
                p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
4,546✔
467
            }
4,546✔
468
        }
4,552✔
469
        /* xfer proto detect ts flag to first packet in ts dir */
470
        if (f->flags & FLOW_PROTO_DETECT_TS_DONE) {
762,904✔
471
            f->flags &= ~FLOW_PROTO_DETECT_TS_DONE;
2,999✔
472
            p->flags |= PKT_PROTO_DETECT_TS_DONE;
2,999✔
473
        }
2,999✔
474
        FlowUpdateEthernet(tv, dtv, f, p, true);
762,904✔
475
        /* update flow's ttl fields if needed */
476
        if (PacketIsIPv4(p)) {
762,904✔
477
            const IPV4Hdr *ip4h = PacketGetIPv4(p);
336,258✔
478
            FlowUpdateTtlTS(f, IPV4_GET_RAW_IPTTL(ip4h));
336,258✔
479
        } else if (PacketIsIPv6(p)) {
426,691✔
480
            const IPV6Hdr *ip6h = PacketGetIPv6(p);
426,633✔
481
            FlowUpdateTtlTS(f, IPV6_GET_RAW_HLIM(ip6h));
426,633✔
482
        }
426,633✔
483
    } else {
762,904✔
484
        f->tosrcpktcnt++;
336,753✔
485
        f->tosrcbytecnt += GET_PKT_LEN(p);
336,753✔
486
        FlowUpdateFlowRate(tv, dtv, f, p, TOCLIENT);
336,753✔
487
        p->flowflags = FLOW_PKT_TOCLIENT;
336,753✔
488
        if (!(f->flags & FLOW_TO_SRC_SEEN)) {
336,753✔
489
            if (FlowUpdateSeenFlag(p)) {
3,457✔
490
                f->flags |= FLOW_TO_SRC_SEEN;
3,451✔
491
                p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
3,451✔
492
                p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
3,451✔
493
            }
3,451✔
494
        }
3,457✔
495
        /* xfer proto detect tc flag to first packet in tc dir */
496
        if (f->flags & FLOW_PROTO_DETECT_TC_DONE) {
336,753✔
497
            f->flags &= ~FLOW_PROTO_DETECT_TC_DONE;
2,915✔
498
            p->flags |= PKT_PROTO_DETECT_TC_DONE;
2,915✔
499
        }
2,915✔
500
        FlowUpdateEthernet(tv, dtv, f, p, false);
336,753✔
501
        /* update flow's ttl fields if needed */
502
        if (PacketIsIPv4(p)) {
336,753✔
503
            const IPV4Hdr *ip4h = PacketGetIPv4(p);
325,675✔
504
            FlowUpdateTtlTC(f, IPV4_GET_RAW_IPTTL(ip4h));
325,675✔
505
        } else if (PacketIsIPv6(p)) {
325,675✔
506
            const IPV6Hdr *ip6h = PacketGetIPv6(p);
11,180✔
507
            FlowUpdateTtlTC(f, IPV6_GET_RAW_HLIM(ip6h));
11,180✔
508
        }
11,180✔
509
    }
336,753✔
510
    if (f->thread_id[pkt_dir] == 0) {
1,099,657✔
511
        f->thread_id[pkt_dir] = (FlowThreadId)tv->id;
3,454✔
512
    }
3,454✔
513

514
    if (f->flow_state == FLOW_STATE_ESTABLISHED) {
1,099,657✔
515
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
665,020✔
516
        p->flowflags |= FLOW_PKT_ESTABLISHED;
665,020✔
517

518
    } else if (f->proto == IPPROTO_TCP) {
665,107✔
519
        TcpSession *ssn = (TcpSession *)f->protoctx;
12,649✔
520
        if (ssn != NULL && ssn->state >= TCP_ESTABLISHED) {
12,649✔
521
            p->flowflags |= FLOW_PKT_ESTABLISHED;
2,000✔
522
        }
2,000✔
523
    } else if ((f->flags & (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) ==
421,988✔
524
            (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) {
421,988✔
525
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
618✔
526
        p->flowflags |= FLOW_PKT_ESTABLISHED;
618✔
527

528
        if (
618✔
529
#ifdef CAPTURE_OFFLOAD
530
                (f->flow_state != FLOW_STATE_CAPTURE_BYPASSED) &&
531
#endif
532
                (f->flow_state != FLOW_STATE_LOCAL_BYPASSED)) {
618✔
533
            FlowUpdateState(f, FLOW_STATE_ESTABLISHED);
618✔
534
        }
618✔
535
    }
618✔
536

537
    if (f->flags & FLOW_ACTION_DROP) {
1,099,657✔
538
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FLOW_DROP);
×
539
    }
×
540

541
    if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
1,099,657✔
542
        SCLogDebug("setting FLOW_NOPAYLOAD_INSPECTION flag on flow %p", f);
20,946✔
543
        DecodeSetNoPayloadInspectionFlag(p);
20,946✔
544
    }
20,946✔
545

546
    SCFlowRunUpdateCallbacks(tv, f, p);
1,099,657✔
547
}
1,099,657✔
548

549
/** \brief Entry point for packet flow handling
550
 *
551
 * This is called for every packet.
552
 *
553
 *  \param tv threadvars
554
 *  \param dtv decode thread vars (for flow output api thread data)
555
 *  \param p packet to handle flow for
556
 */
557
void FlowHandlePacket(ThreadVars *tv, FlowLookupStruct *fls, Packet *p)
558
{
1,099,786✔
559
    /* Get this packet's flow from the hash. FlowHandlePacket() will setup
560
     * a new flow if necessary. If we get NULL, we're out of flow memory.
561
     * The returned flow is locked. */
562
    Flow *f = FlowGetFlowFromHash(tv, fls, p, &p->flow);
1,099,786✔
563
    if (f != NULL) {
1,099,792✔
564
        /* set the flow in the packet */
565
        p->flags |= PKT_HAS_FLOW;
1,099,791✔
566
    }
1,099,791✔
567
}
1,099,786✔
568

569
/** \brief initialize the configuration
570
 *  \warning Not thread safe */
571
void FlowInitConfig(bool quiet)
572
{
1,397✔
573
    SCLogDebug("initializing flow engine...");
1,397✔
574

575
    memset(&flow_config,  0, sizeof(flow_config));
1,397✔
576
    SC_ATOMIC_INIT(flow_flags);
1,397✔
577
    SC_ATOMIC_INIT(flow_memuse);
1,397✔
578
    SC_ATOMIC_INIT(flow_prune_idx);
1,397✔
579
    SC_ATOMIC_INIT(flow_config.memcap);
1,397✔
580
    FlowQueueInit(&flow_recycle_q);
1,397✔
581

582
    /* set defaults */
583
    flow_config.hash_rand   = (uint32_t)RandomGet();
1,397✔
584
    flow_config.hash_size   = FLOW_DEFAULT_HASHSIZE;
1,397✔
585
    flow_config.prealloc    = FLOW_DEFAULT_PREALLOC;
1,397✔
586
    SC_ATOMIC_SET(flow_config.memcap, FLOW_DEFAULT_MEMCAP);
1,397✔
587

588
    /* If we have specific config, overwrite the defaults with them,
589
     * otherwise, leave the default values */
590
    intmax_t val = 0;
1,397✔
591
    if (SCConfGetInt("flow.emergency-recovery", &val) == 1) {
1,397✔
592
        if (val <= 100 && val >= 1) {
1,391✔
593
            flow_config.emergency_recovery = (uint8_t)val;
1,391✔
594
        } else {
1,391✔
595
            SCLogError("flow.emergency-recovery must be in the range of "
×
596
                       "1 and 100 (as percentage)");
×
597
            flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
×
598
        }
×
599
    } else {
1,397✔
600
        SCLogDebug("flow.emergency-recovery, using default value");
6✔
601
        flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
6✔
602
    }
6✔
603

604
    /* Check if we have memcap and hash_size defined at config */
605
    const char *conf_val;
1,397✔
606
    uint32_t configval = 0;
1,397✔
607

608
    /** set config values for memcap, prealloc and hash_size */
609
    uint64_t flow_memcap_copy = 0;
1,397✔
610
    if ((SCConfGet("flow.memcap", &conf_val)) == 1) {
1,397✔
611
        if (conf_val == NULL) {
1,391✔
612
            FatalError("Invalid value for flow.memcap: NULL");
×
613
        }
×
614

615
        if (ParseSizeStringU64(conf_val, &flow_memcap_copy) < 0) {
1,391✔
616
            SCLogError("Error parsing flow.memcap "
×
617
                       "from conf file - %s.  Killing engine",
×
618
                    conf_val);
×
619
            exit(EXIT_FAILURE);
×
620
        } else {
1,391✔
621
            SC_ATOMIC_SET(flow_config.memcap, flow_memcap_copy);
1,391✔
622
        }
1,391✔
623
    }
1,391✔
624
    if ((SCConfGet("flow.hash-size", &conf_val)) == 1) {
1,397✔
625
        if (conf_val == NULL) {
1,391✔
626
            FatalError("Invalid value for flow.hash-size: NULL");
×
627
        }
×
628

629
        if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) && configval != 0) {
1,391✔
630
            flow_config.hash_size = configval;
1,391✔
631
        } else {
1,391✔
632
            FatalError("Invalid value for flow.hash-size. Must be a numeric value in the range "
×
633
                       "1-4294967295");
×
634
        }
×
635
    }
1,391✔
636
    if ((SCConfGet("flow.prealloc", &conf_val)) == 1) {
1,397✔
637
        if (conf_val == NULL) {
1,391✔
638
            FatalError("Invalid value for flow.prealloc: NULL");
×
639
        }
×
640

641
        if (StringParseUint32(&configval, 10, strlen(conf_val),
1,391✔
642
                                    conf_val) > 0) {
1,391✔
643
            flow_config.prealloc = configval;
1,391✔
644
        }
1,391✔
645
    }
1,391✔
646

647
    flow_config.memcap_policy = ExceptionPolicyParse("flow.memcap-policy", false);
1,397✔
648

649
    SCLogDebug("Flow config from suricata.yaml: memcap: %"PRIu64", hash-size: "
1,397✔
650
               "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(flow_config.memcap),
1,397✔
651
               flow_config.hash_size, flow_config.prealloc);
1,397✔
652

653
    /* alloc hash memory */
654
    uint64_t hash_size = flow_config.hash_size * sizeof(FlowBucket);
1,397✔
655
    if (!(FLOW_CHECK_MEMCAP(hash_size))) {
1,397✔
656
        SCLogError("allocating flow hash failed: "
×
657
                   "max flow memcap is smaller than projected hash size. "
×
658
                   "Memcap: %" PRIu64 ", Hash table size %" PRIu64 ". Calculate "
×
659
                   "total hash size by multiplying \"flow.hash-size\" with %" PRIuMAX ", "
×
660
                   "which is the hash bucket size.",
×
661
                SC_ATOMIC_GET(flow_config.memcap), hash_size, (uintmax_t)sizeof(FlowBucket));
×
662
        exit(EXIT_FAILURE);
×
663
    }
×
664
    flow_hash = SCMallocAligned(flow_config.hash_size * sizeof(FlowBucket), CLS);
1,397✔
665
    if (unlikely(flow_hash == NULL)) {
1,397✔
666
        FatalError("Fatal error encountered in FlowInitConfig. Exiting...");
×
667
    }
×
668
    memset(flow_hash, 0, flow_config.hash_size * sizeof(FlowBucket));
1,397✔
669

670
    uint32_t i = 0;
1,397✔
671
    for (i = 0; i < flow_config.hash_size; i++) {
91,555,189✔
672
        FBLOCK_INIT(&flow_hash[i]);
91,553,792✔
673
        SC_ATOMIC_INIT(flow_hash[i].next_ts);
91,553,792✔
674
    }
91,553,792✔
675
    (void) SC_ATOMIC_ADD(flow_memuse, (flow_config.hash_size * sizeof(FlowBucket)));
1,397✔
676

677
    if (!quiet) {
1,397✔
678
        SCLogConfig("allocated %"PRIu64" bytes of memory for the flow hash... "
×
679
                  "%" PRIu32 " buckets of size %" PRIuMAX "",
×
680
                  SC_ATOMIC_GET(flow_memuse), flow_config.hash_size,
×
681
                  (uintmax_t)sizeof(FlowBucket));
×
682
    }
×
683
    FlowSparePoolInit();
1,397✔
684
    if (!quiet) {
1,397✔
685
        SCLogConfig("flow memory usage: %"PRIu64" bytes, maximum: %"PRIu64,
×
686
                SC_ATOMIC_GET(flow_memuse), SC_ATOMIC_GET(flow_config.memcap));
×
687
    }
×
688

689
    FlowInitFlowProto();
1,397✔
690

691
    uint32_t sz = sizeof(Flow) + FlowStorageSize();
1,397✔
692
    SCLogConfig("flow size %u, memcap allows for %" PRIu64 " flows. Per hash row in perfect "
1,397✔
693
                "conditions %" PRIu64,
1,397✔
694
            sz, flow_memcap_copy / sz, (flow_memcap_copy / sz) / flow_config.hash_size);
1,397✔
695
}
1,397✔
696

697
void FlowReset(void)
698
{
×
699
    // resets the flows (for reuse by fuzzing)
700
    for (uint32_t u = 0; u < flow_config.hash_size; u++) {
×
701
        Flow *f = flow_hash[u].head;
×
702
        while (f) {
×
703
            Flow *n = f->next;
×
704
            uint8_t proto_map = FlowGetProtoMapping(f->proto);
×
705
            FlowClearMemory(f, proto_map);
×
706
            FlowFree(f);
×
707
            f = n;
×
708
        }
×
709
        flow_hash[u].head = NULL;
×
710
    }
×
711
}
×
712

713
/** \brief shutdown the flow engine
714
 *  \warning Not thread safe */
715
void FlowShutdown(void)
716
{
1,397✔
717
    Flow *f;
1,397✔
718
    while ((f = FlowDequeue(&flow_recycle_q))) {
1,397✔
719
        FlowFree(f);
×
720
    }
×
721

722
    /* clear and free the hash */
723
    if (flow_hash != NULL) {
1,397✔
724
        /* clean up flow mutexes */
725
        for (uint32_t u = 0; u < flow_config.hash_size; u++) {
91,555,189✔
726
            f = flow_hash[u].head;
91,553,792✔
727
            while (f) {
91,553,792✔
728
                Flow *n = f->next;
×
729
                uint8_t proto_map = FlowGetProtoMapping(f->proto);
×
730
                FlowClearMemory(f, proto_map);
×
731
                FlowFree(f);
×
732
                f = n;
×
733
            }
×
734
            f = flow_hash[u].evicted;
91,553,792✔
735
            while (f) {
91,553,792✔
736
                Flow *n = f->next;
×
737
                uint8_t proto_map = FlowGetProtoMapping(f->proto);
×
738
                FlowClearMemory(f, proto_map);
×
739
                FlowFree(f);
×
740
                f = n;
×
741
            }
×
742

743
            FBLOCK_DESTROY(&flow_hash[u]);
91,553,792✔
744
        }
91,553,792✔
745
        SCFreeAligned(flow_hash);
1,397✔
746
        flow_hash = NULL;
1,397✔
747
    }
1,397✔
748
    (void) SC_ATOMIC_SUB(flow_memuse, flow_config.hash_size * sizeof(FlowBucket));
1,397✔
749
    FlowQueueDestroy(&flow_recycle_q);
1,397✔
750
    FlowSparePoolDestroy();
1,397✔
751
    DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(flow_memuse) != 0);
1,397✔
752
}
1,397✔
753

754
/**
755
 *  \brief  Function to set the default timeout, free function and flow state
756
 *          function for all supported flow_proto.
757
 */
758

759
void FlowInitFlowProto(void)
760
{
1,397✔
761
    FlowTimeoutsInit();
1,397✔
762

763
#define SET_DEFAULTS(p, n, e, c, b, ne, ee, ce, be)     \
1,397✔
764
    flow_timeouts_normal[(p)].new_timeout = (n);     \
5,588✔
765
    flow_timeouts_normal[(p)].est_timeout = (e);     \
5,588✔
766
    flow_timeouts_normal[(p)].closed_timeout = (c);  \
5,588✔
767
    flow_timeouts_normal[(p)].bypassed_timeout = (b); \
5,588✔
768
    flow_timeouts_emerg[(p)].new_timeout = (ne);     \
5,588✔
769
    flow_timeouts_emerg[(p)].est_timeout = (ee);     \
5,588✔
770
    flow_timeouts_emerg[(p)].closed_timeout = (ce); \
5,588✔
771
    flow_timeouts_emerg[(p)].bypassed_timeout = (be); \
5,588✔
772

1,397✔
773
    SET_DEFAULTS(FLOW_PROTO_DEFAULT,
1,397✔
774
                FLOW_DEFAULT_NEW_TIMEOUT, FLOW_DEFAULT_EST_TIMEOUT,
1,397✔
775
                    0, FLOW_DEFAULT_BYPASSED_TIMEOUT,
1,397✔
776
                FLOW_DEFAULT_EMERG_NEW_TIMEOUT, FLOW_DEFAULT_EMERG_EST_TIMEOUT,
1,397✔
777
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
1,397✔
778
    SET_DEFAULTS(FLOW_PROTO_TCP,
1,397✔
779
                FLOW_IPPROTO_TCP_NEW_TIMEOUT, FLOW_IPPROTO_TCP_EST_TIMEOUT,
1,397✔
780
                    FLOW_IPPROTO_TCP_CLOSED_TIMEOUT, FLOW_IPPROTO_TCP_BYPASSED_TIMEOUT,
1,397✔
781
                FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT,
1,397✔
782
                    FLOW_IPPROTO_TCP_EMERG_CLOSED_TIMEOUT, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
1,397✔
783
    SET_DEFAULTS(FLOW_PROTO_UDP,
1,397✔
784
                FLOW_IPPROTO_UDP_NEW_TIMEOUT, FLOW_IPPROTO_UDP_EST_TIMEOUT,
1,397✔
785
                    0, FLOW_IPPROTO_UDP_BYPASSED_TIMEOUT,
1,397✔
786
                FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT,
1,397✔
787
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
1,397✔
788
    SET_DEFAULTS(FLOW_PROTO_ICMP,
1,397✔
789
                FLOW_IPPROTO_ICMP_NEW_TIMEOUT, FLOW_IPPROTO_ICMP_EST_TIMEOUT,
1,397✔
790
                    0, FLOW_IPPROTO_ICMP_BYPASSED_TIMEOUT,
1,397✔
791
                FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT,
1,397✔
792
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
1,397✔
793

794
    flow_freefuncs[FLOW_PROTO_DEFAULT].Freefunc = NULL;
1,397✔
795
    flow_freefuncs[FLOW_PROTO_TCP].Freefunc = NULL;
1,397✔
796
    flow_freefuncs[FLOW_PROTO_UDP].Freefunc = NULL;
1,397✔
797
    flow_freefuncs[FLOW_PROTO_ICMP].Freefunc = NULL;
1,397✔
798

799
    /* Let's see if we have custom timeouts defined from config */
800
    const char *new = NULL;
1,397✔
801
    const char *established = NULL;
1,397✔
802
    const char *closed = NULL;
1,397✔
803
    const char *bypassed = NULL;
1,397✔
804
    const char *emergency_new = NULL;
1,397✔
805
    const char *emergency_established = NULL;
1,397✔
806
    const char *emergency_closed = NULL;
1,397✔
807
    const char *emergency_bypassed = NULL;
1,397✔
808

809
    SCConfNode *flow_timeouts = SCConfGetNode("flow-timeouts");
1,397✔
810
    if (flow_timeouts != NULL) {
1,397✔
811
        SCConfNode *proto = NULL;
1,391✔
812
        uint32_t configval = 0;
1,391✔
813

814
        /* Defaults. */
815
        proto = SCConfNodeLookupChild(flow_timeouts, "default");
1,391✔
816
        if (proto != NULL) {
1,391✔
817
            new = SCConfNodeLookupChildValue(proto, "new");
1,391✔
818
            established = SCConfNodeLookupChildValue(proto, "established");
1,391✔
819
            closed = SCConfNodeLookupChildValue(proto, "closed");
1,391✔
820
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
1,391✔
821
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
1,391✔
822
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
1,391✔
823
            emergency_closed = SCConfNodeLookupChildValue(proto, "emergency-closed");
1,391✔
824
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
1,391✔
825

826
            if (new != NULL &&
1,391✔
827
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
1,391✔
828

829
                    flow_timeouts_normal[FLOW_PROTO_DEFAULT].new_timeout = configval;
1,391✔
830
            }
1,391✔
831
            if (established != NULL &&
1,391✔
832
                StringParseUint32(&configval, 10, strlen(established),
1,391✔
833
                                        established) > 0) {
1,391✔
834

835
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].est_timeout = configval;
1,391✔
836
            }
1,391✔
837
            if (closed != NULL &&
1,391✔
838
                StringParseUint32(&configval, 10, strlen(closed),
1,391✔
839
                                        closed) > 0) {
1,391✔
840

841
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].closed_timeout = configval;
1,391✔
842
            }
1,391✔
843
            if (bypassed != NULL &&
1,391✔
844
                    StringParseUint32(&configval, 10,
1,391✔
845
                                            strlen(bypassed),
1,391✔
846
                                            bypassed) > 0) {
1,391✔
847

848
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].bypassed_timeout = configval;
1,391✔
849
            }
1,391✔
850
            if (emergency_new != NULL &&
1,391✔
851
                StringParseUint32(&configval, 10, strlen(emergency_new),
1,391✔
852
                                        emergency_new) > 0) {
1,391✔
853

854
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].new_timeout = configval;
1,391✔
855
            }
1,391✔
856
            if (emergency_established != NULL &&
1,391✔
857
                    StringParseUint32(&configval, 10,
1,391✔
858
                                            strlen(emergency_established),
1,391✔
859
                                            emergency_established) > 0) {
1,391✔
860

861
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].est_timeout= configval;
1,391✔
862
            }
1,391✔
863
            if (emergency_closed != NULL &&
1,391✔
864
                    StringParseUint32(&configval, 10,
1,391✔
865
                                            strlen(emergency_closed),
1,391✔
866
                                            emergency_closed) > 0) {
1,391✔
867

868
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].closed_timeout = configval;
1,391✔
869
            }
1,391✔
870
            if (emergency_bypassed != NULL &&
1,391✔
871
                    StringParseUint32(&configval, 10,
1,391✔
872
                                            strlen(emergency_bypassed),
1,391✔
873
                                            emergency_bypassed) > 0) {
1,391✔
874

875
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].bypassed_timeout = configval;
1,391✔
876
            }
1,391✔
877
        }
1,391✔
878

879
        /* TCP. */
880
        proto = SCConfNodeLookupChild(flow_timeouts, "tcp");
1,391✔
881
        if (proto != NULL) {
1,391✔
882
            new = SCConfNodeLookupChildValue(proto, "new");
1,391✔
883
            established = SCConfNodeLookupChildValue(proto, "established");
1,391✔
884
            closed = SCConfNodeLookupChildValue(proto, "closed");
1,391✔
885
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
1,391✔
886
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
1,391✔
887
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
1,391✔
888
            emergency_closed = SCConfNodeLookupChildValue(proto, "emergency-closed");
1,391✔
889
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
1,391✔
890

891
            if (new != NULL &&
1,391✔
892
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
1,391✔
893

894
                flow_timeouts_normal[FLOW_PROTO_TCP].new_timeout = configval;
1,391✔
895
            }
1,391✔
896
            if (established != NULL &&
1,391✔
897
                StringParseUint32(&configval, 10, strlen(established),
1,391✔
898
                                        established) > 0) {
1,391✔
899

900
                flow_timeouts_normal[FLOW_PROTO_TCP].est_timeout = configval;
1,391✔
901
            }
1,391✔
902
            if (closed != NULL &&
1,391✔
903
                StringParseUint32(&configval, 10, strlen(closed),
1,391✔
904
                                        closed) > 0) {
1,391✔
905

906
                flow_timeouts_normal[FLOW_PROTO_TCP].closed_timeout = configval;
1,391✔
907
            }
1,391✔
908
            if (bypassed != NULL &&
1,391✔
909
                    StringParseUint32(&configval, 10,
1,391✔
910
                                            strlen(bypassed),
1,391✔
911
                                            bypassed) > 0) {
1,391✔
912

913
                flow_timeouts_normal[FLOW_PROTO_TCP].bypassed_timeout = configval;
1,391✔
914
            }
1,391✔
915
            if (emergency_new != NULL &&
1,391✔
916
                StringParseUint32(&configval, 10, strlen(emergency_new),
1,391✔
917
                                        emergency_new) > 0) {
1,391✔
918

919
                flow_timeouts_emerg[FLOW_PROTO_TCP].new_timeout = configval;
1,391✔
920
            }
1,391✔
921
            if (emergency_established != NULL &&
1,391✔
922
                StringParseUint32(&configval, 10,
1,391✔
923
                                        strlen(emergency_established),
1,391✔
924
                                        emergency_established) > 0) {
1,391✔
925

926
                flow_timeouts_emerg[FLOW_PROTO_TCP].est_timeout = configval;
1,391✔
927
            }
1,391✔
928
            if (emergency_closed != NULL &&
1,391✔
929
                StringParseUint32(&configval, 10,
1,391✔
930
                                        strlen(emergency_closed),
1,391✔
931
                                        emergency_closed) > 0) {
1,391✔
932

933
                flow_timeouts_emerg[FLOW_PROTO_TCP].closed_timeout = configval;
1,391✔
934
            }
1,391✔
935
            if (emergency_bypassed != NULL &&
1,391✔
936
                    StringParseUint32(&configval, 10,
1,391✔
937
                                            strlen(emergency_bypassed),
1,391✔
938
                                            emergency_bypassed) > 0) {
1,391✔
939

940
                flow_timeouts_emerg[FLOW_PROTO_TCP].bypassed_timeout = configval;
1,391✔
941
            }
1,391✔
942
        }
1,391✔
943

944
        /* UDP. */
945
        proto = SCConfNodeLookupChild(flow_timeouts, "udp");
1,391✔
946
        if (proto != NULL) {
1,391✔
947
            new = SCConfNodeLookupChildValue(proto, "new");
1,391✔
948
            established = SCConfNodeLookupChildValue(proto, "established");
1,391✔
949
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
1,391✔
950
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
1,391✔
951
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
1,391✔
952
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
1,391✔
953

954
            if (new != NULL &&
1,391✔
955
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
1,391✔
956

957
                flow_timeouts_normal[FLOW_PROTO_UDP].new_timeout = configval;
1,391✔
958
            }
1,391✔
959
            if (established != NULL &&
1,391✔
960
                StringParseUint32(&configval, 10, strlen(established),
1,391✔
961
                                        established) > 0) {
1,391✔
962

963
                flow_timeouts_normal[FLOW_PROTO_UDP].est_timeout = configval;
1,391✔
964
            }
1,391✔
965
            if (bypassed != NULL &&
1,391✔
966
                    StringParseUint32(&configval, 10,
1,391✔
967
                                            strlen(bypassed),
1,391✔
968
                                            bypassed) > 0) {
1,391✔
969

970
                flow_timeouts_normal[FLOW_PROTO_UDP].bypassed_timeout = configval;
1,391✔
971
            }
1,391✔
972
            if (emergency_new != NULL &&
1,391✔
973
                StringParseUint32(&configval, 10, strlen(emergency_new),
1,391✔
974
                                        emergency_new) > 0) {
1,391✔
975

976
                flow_timeouts_emerg[FLOW_PROTO_UDP].new_timeout = configval;
1,391✔
977
            }
1,391✔
978
            if (emergency_established != NULL &&
1,391✔
979
                StringParseUint32(&configval, 10,
1,391✔
980
                                        strlen(emergency_established),
1,391✔
981
                                        emergency_established) > 0) {
1,391✔
982

983
                flow_timeouts_emerg[FLOW_PROTO_UDP].est_timeout = configval;
1,391✔
984
            }
1,391✔
985
            if (emergency_bypassed != NULL &&
1,391✔
986
                    StringParseUint32(&configval, 10,
1,391✔
987
                                            strlen(emergency_bypassed),
1,391✔
988
                                            emergency_bypassed) > 0) {
1,391✔
989

990
                flow_timeouts_emerg[FLOW_PROTO_UDP].bypassed_timeout = configval;
1,391✔
991
            }
1,391✔
992
        }
1,391✔
993

994
        /* ICMP. */
995
        proto = SCConfNodeLookupChild(flow_timeouts, "icmp");
1,391✔
996
        if (proto != NULL) {
1,391✔
997
            new = SCConfNodeLookupChildValue(proto, "new");
1,391✔
998
            established = SCConfNodeLookupChildValue(proto, "established");
1,391✔
999
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
1,391✔
1000
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
1,391✔
1001
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
1,391✔
1002
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
1,391✔
1003

1004
            if (new != NULL &&
1,391✔
1005
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
1,391✔
1006

1007
                flow_timeouts_normal[FLOW_PROTO_ICMP].new_timeout = configval;
1,391✔
1008
            }
1,391✔
1009
            if (established != NULL &&
1,391✔
1010
                StringParseUint32(&configval, 10, strlen(established),
1,391✔
1011
                                        established) > 0) {
1,391✔
1012

1013
                flow_timeouts_normal[FLOW_PROTO_ICMP].est_timeout = configval;
1,391✔
1014
            }
1,391✔
1015
            if (bypassed != NULL &&
1,391✔
1016
                    StringParseUint32(&configval, 10,
1,391✔
1017
                                            strlen(bypassed),
1,391✔
1018
                                            bypassed) > 0) {
1,391✔
1019

1020
                flow_timeouts_normal[FLOW_PROTO_ICMP].bypassed_timeout = configval;
1,391✔
1021
            }
1,391✔
1022
            if (emergency_new != NULL &&
1,391✔
1023
                StringParseUint32(&configval, 10, strlen(emergency_new),
1,391✔
1024
                                        emergency_new) > 0) {
1,391✔
1025

1026
                flow_timeouts_emerg[FLOW_PROTO_ICMP].new_timeout = configval;
1,391✔
1027
            }
1,391✔
1028
            if (emergency_established != NULL &&
1,391✔
1029
                StringParseUint32(&configval, 10,
1,391✔
1030
                                        strlen(emergency_established),
1,391✔
1031
                                        emergency_established) > 0) {
1,391✔
1032

1033
                flow_timeouts_emerg[FLOW_PROTO_ICMP].est_timeout = configval;
1,391✔
1034
            }
1,391✔
1035
            if (emergency_bypassed != NULL &&
1,391✔
1036
                    StringParseUint32(&configval, 10,
1,391✔
1037
                                            strlen(emergency_bypassed),
1,391✔
1038
                                            emergency_bypassed) > 0) {
1,391✔
1039

1040
                flow_timeouts_emerg[FLOW_PROTO_ICMP].bypassed_timeout = configval;
1,391✔
1041
            }
1,391✔
1042
        }
1,391✔
1043
    }
1,391✔
1044

1045
    /* validate and if needed update emergency timeout values */
1046
    for (uint8_t i = 0; i < FLOW_PROTO_MAX; i++) {
6,985✔
1047
        const FlowProtoTimeout *n = &flow_timeouts_normal[i];
5,588✔
1048
        FlowProtoTimeout *e = &flow_timeouts_emerg[i];
5,588✔
1049

1050
        if (e->est_timeout > n->est_timeout) {
5,588✔
1051
            SCLogWarning("emergency timeout value %u for \'established\' "
×
1052
                         "must be below regular value %u",
×
1053
                    e->est_timeout, n->est_timeout);
×
1054
            e->est_timeout = n->est_timeout / 10;
×
1055
        }
×
1056

1057
        if (e->new_timeout > n->new_timeout) {
5,588✔
1058
            SCLogWarning("emergency timeout value %u for \'new\' must be "
×
1059
                         "below regular value %u",
×
1060
                    e->new_timeout, n->new_timeout);
×
1061
            e->new_timeout = n->new_timeout / 10;
×
1062
        }
×
1063

1064
        if (e->closed_timeout > n->closed_timeout) {
5,588✔
1065
            SCLogWarning("emergency timeout value %u for \'closed\' must "
×
1066
                         "be below regular value %u",
×
1067
                    e->closed_timeout, n->closed_timeout);
×
1068
            e->closed_timeout = n->closed_timeout / 10;
×
1069
        }
×
1070

1071
        if (e->bypassed_timeout > n->bypassed_timeout) {
5,588✔
1072
            SCLogWarning("emergency timeout value %u for \'bypassed\' "
×
1073
                         "must be below regular value %u",
×
1074
                    e->bypassed_timeout, n->bypassed_timeout);
×
1075
            e->bypassed_timeout = n->bypassed_timeout / 10;
×
1076
        }
×
1077
    }
5,588✔
1078

1079
    for (uint8_t i = 0; i < FLOW_PROTO_MAX; i++) {
6,985✔
1080
        FlowProtoTimeout *n = &flow_timeouts_normal[i];
5,588✔
1081
        FlowProtoTimeout *e = &flow_timeouts_emerg[i];
5,588✔
1082
        FlowProtoTimeout *d = &flow_timeouts_delta[i];
5,588✔
1083

1084
        if (e->est_timeout > n->est_timeout) {
5,588✔
1085
            SCLogWarning("emergency timeout value for \'established\' must be below normal value");
×
1086
            e->est_timeout = n->est_timeout / 10;
×
1087
        }
×
1088
        d->est_timeout = n->est_timeout - e->est_timeout;
5,588✔
1089

1090
        if (e->new_timeout > n->new_timeout) {
5,588✔
1091
            SCLogWarning("emergency timeout value for \'new\' must be below normal value");
×
1092
            e->new_timeout = n->new_timeout / 10;
×
1093
        }
×
1094
        d->new_timeout = n->new_timeout - e->new_timeout;
5,588✔
1095

1096
        if (e->closed_timeout > n->closed_timeout) {
5,588✔
1097
            SCLogWarning("emergency timeout value for \'closed\' must be below normal value");
×
1098
            e->closed_timeout = n->closed_timeout / 10;
×
1099
        }
×
1100
        d->closed_timeout = n->closed_timeout - e->closed_timeout;
5,588✔
1101

1102
        if (e->bypassed_timeout > n->bypassed_timeout) {
5,588✔
1103
            SCLogWarning("emergency timeout value for \'bypassed\' must be below normal value");
×
1104
            e->bypassed_timeout = n->bypassed_timeout / 10;
×
1105
        }
×
1106
        d->bypassed_timeout = n->bypassed_timeout - e->bypassed_timeout;
5,588✔
1107

1108
        SCLogDebug("deltas: new: -%u est: -%u closed: -%u bypassed: -%u",
5,588✔
1109
                d->new_timeout, d->est_timeout, d->closed_timeout, d->bypassed_timeout);
5,588✔
1110
    }
5,588✔
1111
}
1,397✔
1112

1113
/**
1114
 *  \brief  Function clear the flow memory before queueing it to spare flow
1115
 *          queue.
1116
 *
1117
 *  \param  f           pointer to the flow needed to be cleared.
1118
 *  \param  proto_map   mapped value of the protocol to FLOW_PROTO's.
1119
 */
1120

1121
int FlowClearMemory(Flow* f, uint8_t proto_map)
1122
{
4,554✔
1123
    SCEnter();
4,554✔
1124

1125
    if (unlikely(f->flags & FLOW_HAS_EXPECTATION)) {
4,554✔
1126
        AppLayerExpectationClean(f);
13✔
1127
    }
13✔
1128

1129
    /* call the protocol specific free function if we have one */
1130
    if (flow_freefuncs[proto_map].Freefunc != NULL) {
4,554✔
1131
        flow_freefuncs[proto_map].Freefunc(f->protoctx);
3,003✔
1132
    }
3,003✔
1133

1134
    FlowFreeStorage(f);
4,554✔
1135

1136
    FLOW_RECYCLE(f);
4,554✔
1137

1138
    SCReturnInt(1);
4,554✔
1139
}
4,554✔
1140

1141
/**
1142
 *  \brief  Function to set the function to get protocol specific flow state.
1143
 *
1144
 *  \param   proto  protocol of which function is needed to be set.
1145
 *  \param   Free   Function pointer which will be called to free the protocol
1146
 *                  specific memory.
1147
 */
1148

1149
int FlowSetProtoFreeFunc (uint8_t proto, void (*Free)(void *))
1150
{
1,397✔
1151
    uint8_t proto_map;
1,397✔
1152
    proto_map = FlowGetProtoMapping(proto);
1,397✔
1153

1154
    flow_freefuncs[proto_map].Freefunc = Free;
1,397✔
1155
    return 1;
1,397✔
1156
}
1,397✔
1157

1158
/**
1159
 *  \brief get 'disruption' flags: GAP/DEPTH/PASS
1160
 *  \param f locked flow
1161
 *  \param flags existing flags to be amended
1162
 *  \retval flags original flags + disrupt flags (if any)
1163
 *  \TODO handle UDP
1164
 */
1165
uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags)
1166
{
996,110✔
1167
    if (f->proto != IPPROTO_TCP) {
996,110✔
1168
        return flags;
73,507✔
1169
    }
73,507✔
1170
    if (f->protoctx == NULL) {
922,603✔
1171
        return flags;
×
1172
    }
×
1173

1174
    uint8_t newflags = flags;
922,603✔
1175
    TcpSession *ssn = f->protoctx;
922,603✔
1176
    TcpStream *stream = flags & STREAM_TOSERVER ? &ssn->client : &ssn->server;
922,603✔
1177

1178
    if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
922,603✔
1179
        newflags |= STREAM_DEPTH;
8,413✔
1180
    }
8,413✔
1181
    /* todo: handle pass case (also for UDP!) */
1182

1183
    return newflags;
922,603✔
1184
}
922,603✔
1185

1186
void FlowUpdateState(Flow *f, const enum FlowState s)
1187
{
13,963✔
1188
    if (s != f->flow_state) {
13,963✔
1189
        /* set the state */
1190
        // Explicit cast from the enum type to the compact version
1191
        f->flow_state = (FlowStateType)s;
5,237✔
1192

1193
        /* update timeout policy and value */
1194
        const uint32_t timeout_policy = FlowGetTimeoutPolicy(f);
5,237✔
1195
        if (timeout_policy != f->timeout_policy) {
5,237✔
1196
            f->timeout_policy = timeout_policy;
5,227✔
1197
        }
5,227✔
1198
    }
5,237✔
1199
#ifdef UNITTESTS
1200
    if (f->fb != NULL) {
1201
#endif
1202
        /* and reset the flow bucket next_ts value so that the flow manager
1203
         * has to revisit this row */
1204
        SC_ATOMIC_SET(f->fb->next_ts, 0);
13,963✔
1205
#ifdef UNITTESTS
1206
    }
1207
#endif
1208
}
13,963✔
1209

1210
/**
1211
 * \brief Get flow last time as individual values.
1212
 *
1213
 * Instead of returning a pointer to the timeval copy the timeval
1214
 * parts into output pointers to make it simpler to call from Rust
1215
 * over FFI using only basic data types.
1216
 */
1217
void SCFlowGetLastTimeAsParts(const Flow *flow, uint64_t *secs, uint64_t *usecs)
1218
{
13,581✔
1219
    *secs = (uint64_t)SCTIME_SECS(flow->lastts);
13,581✔
1220
    *usecs = (uint64_t)SCTIME_USECS(flow->lastts);
13,581✔
1221
}
13,581✔
1222

1223
/**
1224
 * \brief Get flow source port.
1225
 *
1226
 * A function to get the flow sport useful when the caller only has an
1227
 * opaque pointer to the flow structure.
1228
 */
1229
uint16_t SCFlowGetSourcePort(const Flow *flow)
1230
{
×
1231
    return flow->sp;
×
1232
}
×
1233

1234
/**
1235
 * \brief Get flow destination port.
1236
 *
1237
 * A function to get the flow dport useful when the caller only has an
1238
 * opaque pointer to the flow structure.
1239
 */
1240

1241
uint16_t SCFlowGetDestinationPort(const Flow *flow)
1242
{
×
1243
    return flow->dp;
×
1244
}
×
1245

1246
/**
1247
 * \brief Get flow flags.
1248
 *
1249
 * A function to get the flow flags useful when the caller only has an
1250
 * opaque pointer to the flow structure.
1251
 */
1252
uint64_t SCFlowGetFlags(const Flow *flow)
1253
{
×
1254
    return flow->flags;
×
1255
}
×
1256
/************************************Unittests*******************************/
1257

1258
#ifdef UNITTESTS
1259
#include "threads.h"
1260

1261
/**
1262
 *  \test   Test the setting of the per protocol timeouts.
1263
 *
1264
 *  \retval On success it returns 1 and on failure 0.
1265
 */
1266

1267
static int FlowTest01 (void)
1268
{
1269
    uint8_t proto_map;
1270

1271
    FlowInitFlowProto();
1272
    proto_map = FlowGetProtoMapping(IPPROTO_TCP);
1273
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_TCP_NEW_TIMEOUT);
1274
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_TCP_EST_TIMEOUT);
1275
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT);
1276
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT);
1277

1278
    proto_map = FlowGetProtoMapping(IPPROTO_UDP);
1279
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_UDP_NEW_TIMEOUT);
1280
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_UDP_EST_TIMEOUT);
1281
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT);
1282
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT);
1283

1284
    proto_map = FlowGetProtoMapping(IPPROTO_ICMP);
1285
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_ICMP_NEW_TIMEOUT);
1286
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_ICMP_EST_TIMEOUT);
1287
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT);
1288
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT);
1289

1290
    proto_map = FlowGetProtoMapping(IPPROTO_DCCP);
1291
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_DEFAULT_NEW_TIMEOUT);
1292
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_DEFAULT_EST_TIMEOUT);
1293
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_DEFAULT_EMERG_NEW_TIMEOUT);
1294
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_DEFAULT_EMERG_EST_TIMEOUT);
1295

1296
    PASS;
1297
}
1298

1299
/*Test function for the unit test FlowTest02*/
1300

1301
static void test(void *f) {}
1302

1303
/**
1304
 *  \test   Test the setting of the per protocol free function to free the
1305
 *          protocol specific memory.
1306
 *
1307
 *  \retval On success it returns 1 and on failure 0.
1308
 */
1309

1310
static int FlowTest02 (void)
1311
{
1312
    FlowSetProtoFreeFunc(IPPROTO_DCCP, test);
1313
    FlowSetProtoFreeFunc(IPPROTO_TCP, test);
1314
    FlowSetProtoFreeFunc(IPPROTO_UDP, test);
1315
    FlowSetProtoFreeFunc(IPPROTO_ICMP, test);
1316

1317
    FAIL_IF(flow_freefuncs[FLOW_PROTO_DEFAULT].Freefunc != test);
1318
    FAIL_IF(flow_freefuncs[FLOW_PROTO_TCP].Freefunc != test);
1319
    FAIL_IF(flow_freefuncs[FLOW_PROTO_UDP].Freefunc != test);
1320
    FAIL_IF(flow_freefuncs[FLOW_PROTO_ICMP].Freefunc != test);
1321

1322
    PASS;
1323
}
1324

1325
/**
1326
 *  \test   Test flow allocations when it reach memcap
1327
 *
1328
 *
1329
 *  \retval On success it returns 1 and on failure 0.
1330
 */
1331

1332
static int FlowTest07 (void)
1333
{
1334
    int result = 0;
1335
    FlowInitConfig(FLOW_QUIET);
1336
    FlowConfig backup;
1337
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1338

1339
    uint32_t ini = 0;
1340
    uint32_t end = FlowSpareGetPoolSize();
1341
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1342
    flow_config.prealloc = 100;
1343

1344
    /* Let's get the flow spare pool empty */
1345
    UTHBuildPacketOfFlows(ini, end, 0);
1346

1347
    /* And now let's try to reach the memcap val */
1348
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1349
        ini = end + 1;
1350
        end = end + 2;
1351
        UTHBuildPacketOfFlows(ini, end, 0);
1352
    }
1353

1354
    /* should time out normal */
1355
    TimeSetIncrementTime(2000);
1356
    ini = end + 1;
1357
    end = end + 2;
1358
    UTHBuildPacketOfFlows(ini, end, 0);
1359

1360
    /* This means that the engine entered emerg mode: should happen as easy
1361
     * with flow mgr activated */
1362
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1363
        result = 1;
1364

1365
    FlowShutdown();
1366
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1367

1368
    return result;
1369
}
1370

1371
/**
1372
 *  \test   Test flow allocations when it reach memcap
1373
 *
1374
 *
1375
 *  \retval On success it returns 1 and on failure 0.
1376
 */
1377

1378
static int FlowTest08 (void)
1379
{
1380
    int result = 0;
1381

1382
    FlowInitConfig(FLOW_QUIET);
1383
    FlowConfig backup;
1384
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1385

1386
    uint32_t ini = 0;
1387
    uint32_t end = FlowSpareGetPoolSize();
1388
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1389
    flow_config.prealloc = 100;
1390

1391
    /* Let's get the flow spare pool empty */
1392
    UTHBuildPacketOfFlows(ini, end, 0);
1393

1394
    /* And now let's try to reach the memcap val */
1395
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1396
        ini = end + 1;
1397
        end = end + 2;
1398
        UTHBuildPacketOfFlows(ini, end, 0);
1399
    }
1400

1401
    /* By default we use 30  for timing out new flows. This means
1402
     * that the Emergency mode should be set */
1403
    TimeSetIncrementTime(20);
1404
    ini = end + 1;
1405
    end = end + 2;
1406
    UTHBuildPacketOfFlows(ini, end, 0);
1407

1408
    /* This means that the engine released 5 flows by emergency timeout */
1409
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1410
        result = 1;
1411

1412
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1413
    FlowShutdown();
1414

1415
    return result;
1416
}
1417

1418
/**
1419
 *  \test   Test flow allocations when it reach memcap
1420
 *
1421
 *
1422
 *  \retval On success it returns 1 and on failure 0.
1423
 */
1424

1425
static int FlowTest09 (void)
1426
{
1427
    int result = 0;
1428

1429
    FlowInitConfig(FLOW_QUIET);
1430
    FlowConfig backup;
1431
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1432

1433
    uint32_t ini = 0;
1434
    uint32_t end = FlowSpareGetPoolSize();
1435
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1436
    flow_config.prealloc = 100;
1437

1438
    /* Let's get the flow spare pool empty */
1439
    UTHBuildPacketOfFlows(ini, end, 0);
1440

1441
    /* And now let's try to reach the memcap val */
1442
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1443
        ini = end + 1;
1444
        end = end + 2;
1445
        UTHBuildPacketOfFlows(ini, end, 0);
1446
    }
1447

1448
    /* No timeout will work */
1449
    TimeSetIncrementTime(5);
1450
    ini = end + 1;
1451
    end = end + 2;
1452
    UTHBuildPacketOfFlows(ini, end, 0);
1453

1454
    /* engine in emerg mode */
1455
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1456
        result = 1;
1457

1458
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1459
    FlowShutdown();
1460

1461
    return result;
1462
}
1463

1464
#endif /* UNITTESTS */
1465

1466
/**
1467
 *  \brief   Function to register the Flow Unitests.
1468
 */
1469
void FlowRegisterTests (void)
1470
{
×
1471
#ifdef UNITTESTS
1472
    UtRegisterTest("FlowTest01 -- Protocol Specific Timeouts", FlowTest01);
1473
    UtRegisterTest("FlowTest02 -- Setting Protocol Specific Free Function",
1474
                   FlowTest02);
1475
    UtRegisterTest("FlowTest07 -- Test flow Allocations when it reach memcap",
1476
                   FlowTest07);
1477
    UtRegisterTest("FlowTest08 -- Test flow Allocations when it reach memcap",
1478
                   FlowTest08);
1479
    UtRegisterTest("FlowTest09 -- Test flow Allocations when it reach memcap",
1480
                   FlowTest09);
1481

1482
    RegisterFlowStorageTests();
1483
#endif /* UNITTESTS */
1484
}
×
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