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

OISF / suricata / 23374838686

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

Pull #15075

github

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

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

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

51.77
/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
2✔
66

67
//#define FLOW_DEFAULT_HASHSIZE    262144
68
#define FLOW_DEFAULT_HASHSIZE    65536
2✔
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
2✔
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)
UNCOV
124
{
×
UNCOV
125
    uint64_t memcapcopy = SC_ATOMIC_GET(flow_config.memcap);
×
UNCOV
126
    return memcapcopy;
×
UNCOV
127
}
×
128

129
uint64_t FlowGetMemuse(void)
UNCOV
130
{
×
UNCOV
131
    uint64_t memusecopy = SC_ATOMIC_GET(flow_memuse);
×
UNCOV
132
    return memusecopy;
×
UNCOV
133
}
×
134

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

140
void FlowCleanupAppLayer(Flow *f)
141
{
130,303✔
142
    if (f == NULL || f->proto == 0)
130,303✔
143
        return;
53,953✔
144

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

150
/** \brief Set flag to indicate that flow has alerts
151
 *
152
 * \param f flow
153
 */
154
void FlowSetHasAlertsFlag(Flow *f)
155
{
10,046✔
156
    f->flags |= FLOW_HAS_ALERTS;
10,046✔
157
}
10,046✔
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
{
76,841✔
167
    if (f->flags & FLOW_HAS_ALERTS) {
76,841✔
168
        return 1;
58,798✔
169
    }
58,798✔
170

171
    return 0;
18,043✔
172
}
76,841✔
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
{
1,475✔
180
    f->flags |= FLOW_CHANGE_PROTO;
1,475✔
181
}
1,475✔
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
{
1,284✔
189
    f->flags &= ~FLOW_CHANGE_PROTO;
1,284✔
190
}
1,284✔
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
{
3,732,446✔
199
    if (f->flags & FLOW_CHANGE_PROTO) {
3,732,446✔
200
        return 1;
12,480✔
201
    }
12,480✔
202

203
    return 0;
3,719,966✔
204
}
3,732,446✔
205

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

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

216
    SWAP_FLAGS(f->flags, FLOW_PROTO_DETECT_TS_DONE, FLOW_PROTO_DETECT_TC_DONE);
3,999✔
217
}
3,999✔
218

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

228
static inline void TcpStreamFlowSwap(Flow *f)
229
{
3,839✔
230
    TcpSession *ssn = f->protoctx;
3,839✔
231
    SWAP_VARS(TcpStream, ssn->server, ssn->client);
3,839✔
232
    if (ssn->data_first_seen_dir & STREAM_TOSERVER) {
3,839✔
233
        ssn->data_first_seen_dir = STREAM_TOCLIENT;
1,431✔
234
    } else if (ssn->data_first_seen_dir & STREAM_TOCLIENT) {
2,408✔
235
        ssn->data_first_seen_dir = STREAM_TOSERVER;
54✔
236
    }
54✔
237
}
3,839✔
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
{
3,999✔
248
    f->flags |= FLOW_DIR_REVERSED;
3,999✔
249

250
    SWAP_VARS(uint32_t, f->probing_parser_toserver_alproto_masks,
3,999✔
251
                   f->probing_parser_toclient_alproto_masks);
3,999✔
252

253
    FlowSwapFlags(f);
3,999✔
254
    FlowSwapFileFlags(f);
3,999✔
255

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

258
    if (f->proto == IPPROTO_TCP) {
3,999✔
259
        TcpStreamFlowSwap(f);
3,839✔
260
    }
3,839✔
261

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

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

268
    SWAP_VARS(const void *, f->sgh_toclient, f->sgh_toserver);
3,999✔
269

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

273
    if (MacSetFlowStorageEnabled()) {
3,999✔
UNCOV
274
        MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
×
UNCOV
275
        if (ms != NULL) {
×
UNCOV
276
            MacSetSwap(ms);
×
UNCOV
277
        }
×
UNCOV
278
    }
×
279
}
3,999✔
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,286,430✔
288
    const int reverse = (f->flags & FLOW_DIR_REVERSED) != 0;
1,286,430✔
289

290
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
1,286,430✔
291
        if (!(CMP_PORT(p->sp,p->dp))) {
1,231,534✔
292
            /* update flags and counters */
293
            if (CMP_PORT(f->sp,p->sp)) {
1,215,535✔
294
                return TOSERVER ^ reverse;
634,550✔
295
            } else {
634,550✔
296
                return TOCLIENT ^ reverse;
580,985✔
297
            }
580,985✔
298
        } else {
1,215,535✔
299
            if (CMP_ADDR(&f->src,&p->src)) {
15,999✔
300
                return TOSERVER ^ reverse;
14,346✔
301
            } else {
14,346✔
302
                return TOCLIENT ^ reverse;
1,653✔
303
            }
1,653✔
304
        }
15,999✔
305
    } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) {
1,231,534✔
306
        if (CMP_ADDR(&f->src,&p->src)) {
54,775✔
307
            return TOSERVER  ^ reverse;
51,062✔
308
        } else {
51,062✔
309
            return TOCLIENT ^ reverse;
3,713✔
310
        }
3,713✔
311
    }
54,775✔
312

313
    /* default to toserver */
314
    return TOSERVER;
121✔
315
}
1,286,430✔
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
{
99,311✔
327
    if (PacketIsICMPv4(p)) {
99,311✔
328
        if (ICMPV4_IS_ERROR_MSG(p->icmp_s.type)) {
637✔
329
            return 0;
10✔
330
        }
10✔
331
    }
637✔
332

333
    return 1;
99,301✔
334
}
99,311✔
335

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

346
static inline void FlowUpdateTtlTC(Flow *f, uint8_t ttl)
347
{
561,186✔
348
    if (f->min_ttl_toclient == 0) {
561,186✔
349
        f->min_ttl_toclient = ttl;
32,063✔
350
    } else {
529,123✔
351
        f->min_ttl_toclient = MIN(f->min_ttl_toclient, ttl);
529,123✔
352
    }
529,123✔
353
    f->max_ttl_toclient = MAX(f->max_ttl_toclient, ttl);
561,186✔
354
}
561,186✔
355

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

395
static inline void FlowUpdateEthernet(
396
        ThreadVars *tv, DecodeThreadVars *dtv, Flow *f, const Packet *p, bool toserver)
397
{
1,260,570✔
398
    if (PacketIsEthernet(p) && MacSetFlowStorageEnabled()) {
1,260,570✔
UNCOV
399
        const EthernetHdr *ethh = PacketGetEthernet(p);
×
UNCOV
400
        MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
×
UNCOV
401
        if (ms != NULL) {
×
UNCOV
402
            if (toserver) {
×
UNCOV
403
                MacSetAddWithCtr(ms, ethh->eth_src, ethh->eth_dst, tv,
×
UNCOV
404
                                 dtv->counter_max_mac_addrs_src,
×
UNCOV
405
                                 dtv->counter_max_mac_addrs_dst);
×
UNCOV
406
            } else {
×
UNCOV
407
                MacSetAddWithCtr(ms, ethh->eth_dst, ethh->eth_src, tv,
×
UNCOV
408
                                 dtv->counter_max_mac_addrs_dst,
×
UNCOV
409
                                 dtv->counter_max_mac_addrs_src);
×
UNCOV
410
            }
×
UNCOV
411
        }
×
UNCOV
412
    }
×
413
}
1,260,570✔
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,260,570✔
426
    SCLogDebug("packet %" PRIu64 " -- flow %p", PcapPacketCntGet(p), f);
1,260,570✔
427

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

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

514
    if (f->flow_state == FLOW_STATE_ESTABLISHED) {
1,260,570✔
515
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
1,006,502✔
516
        p->flowflags |= FLOW_PKT_ESTABLISHED;
1,006,502✔
517

518
    } else if (f->proto == IPPROTO_TCP) {
1,006,502✔
519
        TcpSession *ssn = (TcpSession *)f->protoctx;
152,846✔
520
        if (ssn != NULL && ssn->state >= TCP_ESTABLISHED) {
152,846✔
521
            p->flowflags |= FLOW_PKT_ESTABLISHED;
51,797✔
522
        }
51,797✔
523
    } else if ((f->flags & (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) ==
152,846✔
524
            (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) {
101,222✔
525
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
6,371✔
526
        p->flowflags |= FLOW_PKT_ESTABLISHED;
6,371✔
527

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

537
    if (f->flags & FLOW_ACTION_DROP) {
1,260,570✔
538
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FLOW_DROP);
2,879✔
539
    }
2,879✔
540

541
    if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
1,260,570✔
542
        SCLogDebug("setting FLOW_NOPAYLOAD_INSPECTION flag on flow %p", f);
37,041✔
543
        DecodeSetNoPayloadInspectionFlag(p);
37,041✔
544
    }
37,041✔
545

546
    SCFlowRunUpdateCallbacks(tv, f, p);
1,260,570✔
547
}
1,260,570✔
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,260,625✔
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,260,625✔
563
    if (f != NULL) {
1,260,625✔
564
        /* set the flow in the packet */
565
        p->flags |= PKT_HAS_FLOW;
1,260,570✔
566
    }
1,260,570✔
567
}
1,260,625✔
568

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

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

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

588
    /* If we have specific config, overwrite the defaults with them,
589
     * otherwise, leave the default values */
590
    intmax_t val = 0;
2✔
591
    if (SCConfGetInt("flow.emergency-recovery", &val) == 1) {
2✔
UNCOV
592
        if (val <= 100 && val >= 1) {
×
UNCOV
593
            flow_config.emergency_recovery = (uint8_t)val;
×
UNCOV
594
        } else {
×
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 {
2✔
600
        SCLogDebug("flow.emergency-recovery, using default value");
2✔
601
        flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
2✔
602
    }
2✔
603

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

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

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

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

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

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

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

653
    /* alloc hash memory */
654
    uint64_t hash_size = flow_config.hash_size * sizeof(FlowBucket);
2✔
655
    if (!(FLOW_CHECK_MEMCAP(hash_size))) {
2✔
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);
2✔
665
    if (unlikely(flow_hash == NULL)) {
2✔
666
        FatalError("Fatal error encountered in FlowInitConfig. Exiting...");
×
667
    }
×
668
    memset(flow_hash, 0, flow_config.hash_size * sizeof(FlowBucket));
2✔
669

670
    uint32_t i = 0;
2✔
671
    for (i = 0; i < flow_config.hash_size; i++) {
131,074✔
672
        FBLOCK_INIT(&flow_hash[i]);
131,072✔
673
        SC_ATOMIC_INIT(flow_hash[i].next_ts);
131,072✔
674
    }
131,072✔
675
    (void) SC_ATOMIC_ADD(flow_memuse, (flow_config.hash_size * sizeof(FlowBucket)));
2✔
676

677
    if (!quiet) {
2✔
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();
2✔
684
    if (!quiet) {
2✔
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();
2✔
690

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

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

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

722
    /* clear and free the hash */
UNCOV
723
    if (flow_hash != NULL) {
×
724
        /* clean up flow mutexes */
UNCOV
725
        for (uint32_t u = 0; u < flow_config.hash_size; u++) {
×
UNCOV
726
            f = flow_hash[u].head;
×
UNCOV
727
            while (f) {
×
UNCOV
728
                Flow *n = f->next;
×
UNCOV
729
                uint8_t proto_map = FlowGetProtoMapping(f->proto);
×
UNCOV
730
                FlowClearMemory(f, proto_map);
×
UNCOV
731
                FlowFree(f);
×
UNCOV
732
                f = n;
×
UNCOV
733
            }
×
UNCOV
734
            f = flow_hash[u].evicted;
×
UNCOV
735
            while (f) {
×
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

UNCOV
743
            FBLOCK_DESTROY(&flow_hash[u]);
×
UNCOV
744
        }
×
UNCOV
745
        SCFreeAligned(flow_hash);
×
UNCOV
746
        flow_hash = NULL;
×
UNCOV
747
    }
×
UNCOV
748
    (void) SC_ATOMIC_SUB(flow_memuse, flow_config.hash_size * sizeof(FlowBucket));
×
UNCOV
749
    FlowQueueDestroy(&flow_recycle_q);
×
UNCOV
750
    FlowSparePoolDestroy();
×
UNCOV
751
    DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(flow_memuse) != 0);
×
UNCOV
752
}
×
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
{
2✔
761
    FlowTimeoutsInit();
2✔
762

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

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

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

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

809
    SCConfNode *flow_timeouts = SCConfGetNode("flow-timeouts");
2✔
810
    if (flow_timeouts != NULL) {
2✔
UNCOV
811
        SCConfNode *proto = NULL;
×
UNCOV
812
        uint32_t configval = 0;
×
813

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

UNCOV
826
            if (new != NULL &&
×
UNCOV
827
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
×
828

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

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

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

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

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

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

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

UNCOV
875
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].bypassed_timeout = configval;
×
UNCOV
876
            }
×
UNCOV
877
        }
×
878

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

UNCOV
891
            if (new != NULL &&
×
UNCOV
892
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
×
893

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

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

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

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

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

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

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

UNCOV
940
                flow_timeouts_emerg[FLOW_PROTO_TCP].bypassed_timeout = configval;
×
UNCOV
941
            }
×
UNCOV
942
        }
×
943

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

UNCOV
954
            if (new != NULL &&
×
UNCOV
955
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
×
956

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

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

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

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

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

UNCOV
990
                flow_timeouts_emerg[FLOW_PROTO_UDP].bypassed_timeout = configval;
×
UNCOV
991
            }
×
UNCOV
992
        }
×
993

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

UNCOV
1004
            if (new != NULL &&
×
UNCOV
1005
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
×
1006

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

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

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

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

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

UNCOV
1040
                flow_timeouts_emerg[FLOW_PROTO_ICMP].bypassed_timeout = configval;
×
UNCOV
1041
            }
×
UNCOV
1042
        }
×
UNCOV
1043
    }
×
1044

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

1050
        if (e->est_timeout > n->est_timeout) {
8✔
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) {
8✔
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) {
8✔
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) {
8✔
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
    }
8✔
1078

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

1084
        if (e->est_timeout > n->est_timeout) {
8✔
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;
8✔
1089

1090
        if (e->new_timeout > n->new_timeout) {
8✔
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;
8✔
1095

1096
        if (e->closed_timeout > n->closed_timeout) {
8✔
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;
8✔
1101

1102
        if (e->bypassed_timeout > n->bypassed_timeout) {
8✔
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;
8✔
1107

1108
        SCLogDebug("deltas: new: -%u est: -%u closed: -%u bypassed: -%u",
8✔
1109
                d->new_timeout, d->est_timeout, d->closed_timeout, d->bypassed_timeout);
8✔
1110
    }
8✔
1111
}
2✔
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
{
63,336✔
1123
    SCEnter();
63,336✔
1124

1125
    if (unlikely(f->flags & FLOW_HAS_EXPECTATION)) {
63,336✔
1126
        AppLayerExpectationClean(f);
127✔
1127
    }
127✔
1128

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

1134
    FlowFreeStorage(f);
63,336✔
1135

1136
    FLOW_RECYCLE(f);
63,336✔
1137

1138
    SCReturnInt(1);
63,336✔
1139
}
63,336✔
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
{
2✔
1151
    uint8_t proto_map;
2✔
1152
    proto_map = FlowGetProtoMapping(proto);
2✔
1153

1154
    flow_freefuncs[proto_map].Freefunc = Free;
2✔
1155
    return 1;
2✔
1156
}
2✔
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
{
4,068,214✔
1167
    if (f->proto != IPPROTO_TCP) {
4,068,214✔
1168
        return flags;
248,646✔
1169
    }
248,646✔
1170
    if (f->protoctx == NULL) {
3,819,568✔
1171
        return flags;
5✔
1172
    }
5✔
1173

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

1178
    if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
3,819,563✔
1179
        newflags |= STREAM_DEPTH;
2,035✔
1180
    }
2,035✔
1181
    /* todo: handle pass case (also for UDP!) */
1182

1183
    return newflags;
3,819,563✔
1184
}
3,819,568✔
1185

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

1193
        /* update timeout policy and value */
1194
        const uint32_t timeout_policy = FlowGetTimeoutPolicy(f);
50,968✔
1195
        if (timeout_policy != f->timeout_policy) {
50,968✔
1196
            f->timeout_policy = timeout_policy;
50,968✔
1197
        }
50,968✔
1198
    }
50,968✔
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);
131,864✔
1205
#ifdef UNITTESTS
1206
    }
1207
#endif
1208
}
131,864✔
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
{
290,062✔
1219
    *secs = (uint64_t)SCTIME_SECS(flow->lastts);
290,062✔
1220
    *usecs = (uint64_t)SCTIME_USECS(flow->lastts);
290,062✔
1221
}
290,062✔
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
{
46✔
1231
    return flow->sp;
46✔
1232
}
46✔
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
{
46✔
1243
    return flow->dp;
46✔
1244
}
46✔
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
{
46✔
1254
    return flow->flags;
46✔
1255
}
46✔
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)
UNCOV
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 */
UNCOV
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