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

OISF / suricata / 22586342403

02 Mar 2026 04:53PM UTC coverage: 73.68% (-0.007%) from 73.687%
22586342403

Pull #14930

github

web-flow
Merge 1d6dc53fc into 90823fa90
Pull Request #14930: Sslproxy/v16

38328 of 77468 branches covered (49.48%)

Branch coverage included in aggregate %.

336 of 431 new or added lines in 14 files covered. (77.96%)

66 existing lines in 15 files now uncovered.

265724 of 335196 relevant lines covered (79.27%)

5207223.13 hits per line

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

84.56
/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
836✔
66

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

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

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

140
void FlowCleanupAppLayer(Flow *f)
141
{
35,557,049✔
142
    if (f == NULL || f->proto == 0)
35,557,049!
143
        return;
35,435,039✔
144

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

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

171
    return 0;
30,925✔
172
}
631,485✔
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
{
3,141✔
180
    f->flags |= FLOW_CHANGE_PROTO;
3,141✔
181
}
3,141✔
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
{
2,935✔
189
    f->flags &= ~FLOW_CHANGE_PROTO;
2,935✔
190
}
2,935✔
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
{
7,966,854✔
199
    if (f->flags & FLOW_CHANGE_PROTO) {
7,966,854✔
200
        return 1;
23,382✔
201
    }
23,382✔
202

203
    return 0;
7,943,472✔
204
}
7,966,854✔
205

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

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

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

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

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

250
    SWAP_VARS(uint32_t, f->probing_parser_toserver_alproto_masks,
4,045!
251
                   f->probing_parser_toclient_alproto_masks);
4,045✔
252

253
    FlowSwapFlags(f);
4,045✔
254
    FlowSwapFileFlags(f);
4,045✔
255

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

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

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

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

268
    SWAP_VARS(const void *, f->sgh_toclient, f->sgh_toserver);
4,045!
269

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

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

290
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
3,971,787✔
291
        if (!(CMP_PORT(p->sp,p->dp))) {
3,045,872✔
292
            /* update flags and counters */
293
            if (CMP_PORT(f->sp,p->sp)) {
3,021,455✔
294
                return TOSERVER ^ reverse;
1,553,409✔
295
            } else {
1,553,409✔
296
                return TOCLIENT ^ reverse;
1,468,046✔
297
            }
1,468,046✔
298
        } else {
3,021,455✔
299
            if (CMP_ADDR(&f->src,&p->src)) {
24,417✔
300
                return TOSERVER ^ reverse;
22,145✔
301
            } else {
22,145✔
302
                return TOCLIENT ^ reverse;
2,272✔
303
            }
2,272✔
304
        }
24,417✔
305
    } else if (p->proto == IPPROTO_ICMP || p->proto == IPPROTO_ICMPV6) {
3,046,502✔
306
        if (CMP_ADDR(&f->src,&p->src)) {
925,631✔
307
            return TOSERVER  ^ reverse;
902,152✔
308
        } else {
902,152✔
309
            return TOCLIENT ^ reverse;
23,479✔
310
        }
23,479✔
311
    }
925,631✔
312

313
    /* default to toserver */
314
    return TOSERVER;
284✔
315
}
3,971,787✔
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
{
125,938✔
327
    if (PacketIsICMPv4(p)) {
125,938✔
328
        if (ICMPV4_IS_ERROR_MSG(p->icmp_s.type)) {
1,025✔
329
            return 0;
22✔
330
        }
22✔
331
    }
1,025✔
332

333
    return 1;
125,916✔
334
}
125,938✔
335

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

346
static inline void FlowUpdateTtlTC(Flow *f, uint8_t ttl)
347
{
1,468,843✔
348
    if (f->min_ttl_toclient == 0) {
1,468,843✔
349
        f->min_ttl_toclient = ttl;
43,475✔
350
    } else {
1,425,368✔
351
        f->min_ttl_toclient = MIN(f->min_ttl_toclient, ttl);
1,425,368✔
352
    }
1,425,368✔
353
    f->max_ttl_toclient = MAX(f->max_ttl_toclient, ttl);
1,468,843✔
354
}
1,468,843✔
355

356
static inline void FlowUpdateFlowRate(
357
        ThreadVars *tv, DecodeThreadVars *dtv, Flow *f, const Packet *p, int dir)
358
{
3,945,289✔
359
    if (FlowRateStorageEnabled()) {
3,945,289✔
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))
264,028✔
362
            return;
131,708✔
363
        if ((dir == TOCLIENT) && (f->flags & FLOW_IS_ELEPHANT_TOCLIENT))
132,320✔
364
            return;
131,702✔
365
        FlowRateStore *frs = FlowGetStorageById(f, FlowRateGetStorageID());
618✔
366
        if (frs != NULL) {
618!
367
            FlowRateStoreUpdate(frs, p->ts, GET_PKT_LEN(p), dir);
618✔
368
            bool fr_exceeds = FlowRateIsExceeding(frs, dir);
618✔
369
            if (fr_exceeds) {
618✔
370
                SCLogDebug("Flow rate for flow %p exceeds the configured values, marking it as an "
4!
371
                           "elephant flow",
4✔
372
                        f);
4✔
373
                if (dir == TOSERVER) {
4✔
374
                    f->flags |= FLOW_IS_ELEPHANT_TOSERVER;
2✔
375
                    if (tv != NULL) {
2!
376
                        if ((f->flags & FLOW_IS_ELEPHANT_TOCLIENT) == 0) {
2!
377
                            StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant);
2✔
378
                        }
2✔
379
                        StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant_toserver);
2✔
380
                    }
2✔
381
                } else {
2✔
382
                    f->flags |= FLOW_IS_ELEPHANT_TOCLIENT;
2✔
383
                    if (tv != NULL) {
2!
384
                        if ((f->flags & FLOW_IS_ELEPHANT_TOSERVER) == 0) {
2!
385
                            StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant);
×
386
                        }
×
387
                        StatsCounterIncr(&tv->stats, dtv->counter_flow_elephant_toclient);
2✔
388
                    }
2✔
389
                }
2✔
390
            }
4✔
391
        }
618✔
392
    }
618✔
393
}
3,945,289✔
394

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

428
    const int pkt_dir = FlowGetPacketDirection(f, p);
3,945,178✔
429
#ifdef CAPTURE_OFFLOAD
473✔
430
    int state = f->flow_state;
473✔
431

432
    if (state != FLOW_STATE_CAPTURE_BYPASSED) {
473✔
433
#endif
473✔
434
        /* update the last seen timestamp of this flow */
435
        if (SCTIME_CMP_GT(p->ts, f->lastts)) {
3,945,178✔
436
            f->lastts = p->ts;
3,519,468✔
437
        }
3,519,468✔
438
#ifdef CAPTURE_OFFLOAD
473✔
439
    } else {
473✔
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
473✔
456
    /* update flags and counters */
457
    if (pkt_dir == TOSERVER) {
3,945,178✔
458
        f->todstpktcnt++;
2,476,659✔
459
        f->todstbytecnt += GET_PKT_LEN(p);
2,476,659✔
460
        FlowUpdateFlowRate(tv, dtv, f, p, TOSERVER);
2,476,659✔
461
        p->flowflags = FLOW_PKT_TOSERVER;
2,476,659✔
462
        if (!(f->flags & FLOW_TO_DST_SEEN)) {
2,476,659✔
463
            if (FlowUpdateSeenFlag(p)) {
79,954✔
464
                f->flags |= FLOW_TO_DST_SEEN;
79,951✔
465
                p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
79,951✔
466
                p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
79,951✔
467
            }
79,951✔
468
        }
79,954✔
469
        /* xfer proto detect ts flag to first packet in ts dir */
470
        if (f->flags & FLOW_PROTO_DETECT_TS_DONE) {
2,476,659✔
471
            f->flags &= ~FLOW_PROTO_DETECT_TS_DONE;
41,137✔
472
            p->flags |= PKT_PROTO_DETECT_TS_DONE;
41,137✔
473
        }
41,137✔
474
        FlowUpdateEthernet(tv, dtv, f, p, true);
2,476,659✔
475
        /* update flow's ttl fields if needed */
476
        if (PacketIsIPv4(p)) {
2,476,659✔
477
            const IPV4Hdr *ip4h = PacketGetIPv4(p);
1,562,721✔
478
            FlowUpdateTtlTS(f, IPV4_GET_RAW_IPTTL(ip4h));
1,562,721✔
479
        } else if (PacketIsIPv6(p)) {
1,653,170✔
480
            const IPV6Hdr *ip6h = PacketGetIPv6(p);
913,887✔
481
            FlowUpdateTtlTS(f, IPV6_GET_RAW_HLIM(ip6h));
913,887✔
482
        }
913,887✔
483
    } else {
2,476,659✔
484
        f->tosrcpktcnt++;
1,468,519✔
485
        f->tosrcbytecnt += GET_PKT_LEN(p);
1,468,519✔
486
        FlowUpdateFlowRate(tv, dtv, f, p, TOCLIENT);
1,468,519✔
487
        p->flowflags = FLOW_PKT_TOCLIENT;
1,468,519✔
488
        if (!(f->flags & FLOW_TO_SRC_SEEN)) {
1,468,519✔
489
            if (FlowUpdateSeenFlag(p)) {
45,985✔
490
                f->flags |= FLOW_TO_SRC_SEEN;
45,965✔
491
                p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
45,965✔
492
                p->pkt_hooks |= BIT_U16(SIGNATURE_HOOK_PKT_FLOW_START);
45,965✔
493
            }
45,965✔
494
        }
45,985✔
495
        /* xfer proto detect tc flag to first packet in tc dir */
496
        if (f->flags & FLOW_PROTO_DETECT_TC_DONE) {
1,468,519✔
497
            f->flags &= ~FLOW_PROTO_DETECT_TC_DONE;
34,396✔
498
            p->flags |= PKT_PROTO_DETECT_TC_DONE;
34,396✔
499
        }
34,396✔
500
        FlowUpdateEthernet(tv, dtv, f, p, false);
1,468,519✔
501
        /* update flow's ttl fields if needed */
502
        if (PacketIsIPv4(p)) {
1,468,519✔
503
            const IPV4Hdr *ip4h = PacketGetIPv4(p);
1,438,413✔
504
            FlowUpdateTtlTC(f, IPV4_GET_RAW_IPTTL(ip4h));
1,438,413✔
505
        } else if (PacketIsIPv6(p)) {
1,438,413✔
506
            const IPV6Hdr *ip6h = PacketGetIPv6(p);
30,430✔
507
            FlowUpdateTtlTC(f, IPV6_GET_RAW_HLIM(ip6h));
30,430✔
508
        }
30,430✔
509
    }
1,468,519✔
510
    if (f->thread_id[pkt_dir] == 0) {
3,945,178✔
511
        f->thread_id[pkt_dir] = (FlowThreadId)tv->id;
1,270,601✔
512
    }
1,270,601✔
513

514
    if (f->flow_state == FLOW_STATE_ESTABLISHED) {
3,945,178✔
515
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
2,797,763!
516
        p->flowflags |= FLOW_PKT_ESTABLISHED;
2,797,763✔
517

518
    } else if (f->proto == IPPROTO_TCP) {
2,797,850✔
519
        TcpSession *ssn = (TcpSession *)f->protoctx;
195,611✔
520
        if (ssn != NULL && ssn->state >= TCP_ESTABLISHED) {
195,611✔
521
            p->flowflags |= FLOW_PKT_ESTABLISHED;
59,770✔
522
        }
59,770✔
523
    } else if ((f->flags & (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) ==
1,003,555✔
524
            (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) {
951,804✔
525
        SCLogDebug("pkt %p FLOW_PKT_ESTABLISHED", p);
8,604!
526
        p->flowflags |= FLOW_PKT_ESTABLISHED;
8,604✔
527

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

537
    if (f->flags & FLOW_ACTION_DROP) {
3,945,178✔
538
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_FLOW_DROP);
5,467✔
539
    }
5,467✔
540

541
    if (f->flags & FLOW_NOPAYLOAD_INSPECTION) {
3,945,178✔
542
        SCLogDebug("setting FLOW_NOPAYLOAD_INSPECTION flag on flow %p", f);
186,561!
543
        DecodeSetNoPayloadInspectionFlag(p);
186,561✔
544
    }
186,561✔
545

546
    if ((f->flags & FLOW_IS_TRANSLATED) != 0) {
3,945,178✔
547
        const FlowTuple *ft = SCFlowGetTranslated(f);
52✔
548
        if (ft != NULL) {
52!
549
            if (FlowGetPacketDirection(f, p) == TOSERVER) {
52✔
550
                memcpy(&p->src.address, &ft->src.address, 16);
26✔
551
                memcpy(&p->dst.address, &ft->dst.address, 16);
26✔
552
                p->sp = ft->sp;
26✔
553
                p->dp = ft->dp;
26✔
554
            } else {
26✔
555
                memcpy(&p->src.address, &ft->dst.address, 16);
26✔
556
                memcpy(&p->dst.address, &ft->src.address, 16);
26✔
557
                p->sp = ft->dp;
26✔
558
                p->dp = ft->sp;
26✔
559
            }
26✔
560
        }
52✔
561
    }
52✔
562
    SCFlowRunUpdateCallbacks(tv, f, p);
3,945,178✔
563
}
3,945,178✔
564

565
/** \brief Entry point for packet flow handling
566
 *
567
 * This is called for every packet.
568
 *
569
 *  \param tv threadvars
570
 *  \param dtv decode thread vars (for flow output api thread data)
571
 *  \param p packet to handle flow for
572
 */
573
void FlowHandlePacket(ThreadVars *tv, FlowLookupStruct *fls, Packet *p)
574
{
3,975,724✔
575
    /* Get this packet's flow from the hash. FlowHandlePacket() will setup
576
     * a new flow if necessary. If we get NULL, we're out of flow memory.
577
     * The returned flow is locked. */
578
    Flow *f = FlowGetFlowFromHash(tv, fls, p, &p->flow);
3,975,724✔
579
    if (f != NULL) {
3,975,724✔
580
        /* set the flow in the packet */
581
        p->flags |= PKT_HAS_FLOW;
3,975,628✔
582
    }
3,975,628✔
583
}
3,975,724✔
584

585
/** \brief initialize the configuration
586
 *  \warning Not thread safe */
587
void FlowInitConfig(bool quiet)
588
{
3,723✔
589
    SCLogDebug("initializing flow engine...");
3,723!
590

591
    memset(&flow_config,  0, sizeof(flow_config));
3,723✔
592
    SC_ATOMIC_INIT(flow_flags);
3,723✔
593
    SC_ATOMIC_INIT(flow_memuse);
3,723✔
594
    SC_ATOMIC_INIT(flow_prune_idx);
3,723✔
595
    SC_ATOMIC_INIT(flow_config.memcap);
3,723✔
596
    FlowQueueInit(&flow_recycle_q);
3,723✔
597

598
    /* set defaults */
599
    flow_config.hash_rand   = (uint32_t)RandomGet();
3,723✔
600
    flow_config.hash_size   = FLOW_DEFAULT_HASHSIZE;
3,723✔
601
    flow_config.prealloc    = FLOW_DEFAULT_PREALLOC;
3,723✔
602
    SC_ATOMIC_SET(flow_config.memcap, FLOW_DEFAULT_MEMCAP);
3,723✔
603

604
    /* If we have specific config, overwrite the defaults with them,
605
     * otherwise, leave the default values */
606
    intmax_t val = 0;
3,723✔
607
    if (SCConfGetInt("flow.emergency-recovery", &val) == 1) {
3,723✔
608
        if (val <= 100 && val >= 1) {
2,887!
609
            flow_config.emergency_recovery = (uint8_t)val;
2,887✔
610
        } else {
2,887✔
611
            SCLogError("flow.emergency-recovery must be in the range of "
×
612
                       "1 and 100 (as percentage)");
×
613
            flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
×
614
        }
×
615
    } else {
3,054✔
616
        SCLogDebug("flow.emergency-recovery, using default value");
836!
617
        flow_config.emergency_recovery = FLOW_DEFAULT_EMERGENCY_RECOVERY;
836✔
618
    }
836✔
619

620
    /* Check if we have memcap and hash_size defined at config */
621
    const char *conf_val;
3,723✔
622
    uint32_t configval = 0;
3,723✔
623

624
    /** set config values for memcap, prealloc and hash_size */
625
    uint64_t flow_memcap_copy = 0;
3,723✔
626
    if ((SCConfGet("flow.memcap", &conf_val)) == 1) {
3,723✔
627
        if (conf_val == NULL) {
2,887!
628
            FatalError("Invalid value for flow.memcap: NULL");
×
629
        }
×
630

631
        if (ParseSizeStringU64(conf_val, &flow_memcap_copy) < 0) {
2,887!
632
            SCLogError("Error parsing flow.memcap "
×
633
                       "from conf file - %s.  Killing engine",
×
634
                    conf_val);
×
635
            exit(EXIT_FAILURE);
×
636
        } else {
2,887✔
637
            SC_ATOMIC_SET(flow_config.memcap, flow_memcap_copy);
2,887✔
638
        }
2,887✔
639
    }
2,887✔
640
    if ((SCConfGet("flow.hash-size", &conf_val)) == 1) {
3,723✔
641
        if (conf_val == NULL) {
2,887!
642
            FatalError("Invalid value for flow.hash-size: NULL");
×
643
        }
×
644

645
        if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) && configval != 0) {
2,887!
646
            flow_config.hash_size = configval;
2,887✔
647
        } else {
2,887✔
648
            FatalError("Invalid value for flow.hash-size. Must be a numeric value in the range "
×
649
                       "1-4294967295");
×
650
        }
×
651
    }
2,887✔
652
    if ((SCConfGet("flow.prealloc", &conf_val)) == 1) {
3,723✔
653
        if (conf_val == NULL) {
2,887!
654
            FatalError("Invalid value for flow.prealloc: NULL");
×
655
        }
×
656

657
        if (StringParseUint32(&configval, 10, strlen(conf_val),
2,887!
658
                                    conf_val) > 0) {
2,887✔
659
            flow_config.prealloc = configval;
2,887✔
660
        }
2,887✔
661
    }
2,887✔
662

663
    flow_config.memcap_policy = ExceptionPolicyParse("flow.memcap-policy", false);
3,723✔
664

665
    SCLogDebug("Flow config from suricata.yaml: memcap: %"PRIu64", hash-size: "
3,723!
666
               "%"PRIu32", prealloc: %"PRIu32, SC_ATOMIC_GET(flow_config.memcap),
3,723✔
667
               flow_config.hash_size, flow_config.prealloc);
3,723✔
668

669
    /* alloc hash memory */
670
    uint64_t hash_size = flow_config.hash_size * sizeof(FlowBucket);
3,723✔
671
    if (!(FLOW_CHECK_MEMCAP(hash_size))) {
3,723!
672
        SCLogError("allocating flow hash failed: "
×
673
                   "max flow memcap is smaller than projected hash size. "
×
674
                   "Memcap: %" PRIu64 ", Hash table size %" PRIu64 ". Calculate "
×
675
                   "total hash size by multiplying \"flow.hash-size\" with %" PRIuMAX ", "
×
676
                   "which is the hash bucket size.",
×
677
                SC_ATOMIC_GET(flow_config.memcap), hash_size, (uintmax_t)sizeof(FlowBucket));
×
678
        exit(EXIT_FAILURE);
×
679
    }
×
680
    flow_hash = SCMallocAligned(flow_config.hash_size * sizeof(FlowBucket), CLS);
3,723✔
681
    if (unlikely(flow_hash == NULL)) {
3,723!
682
        FatalError("Fatal error encountered in FlowInitConfig. Exiting...");
×
683
    }
×
684
    memset(flow_hash, 0, flow_config.hash_size * sizeof(FlowBucket));
3,723✔
685

686
    uint32_t i = 0;
3,723✔
687
    for (i = 0; i < flow_config.hash_size; i++) {
243,994,251✔
688
        FBLOCK_INIT(&flow_hash[i]);
243,990,528✔
689
        SC_ATOMIC_INIT(flow_hash[i].next_ts);
243,990,528✔
690
    }
243,990,528✔
691
    (void) SC_ATOMIC_ADD(flow_memuse, (flow_config.hash_size * sizeof(FlowBucket)));
3,723✔
692

693
    if (!quiet) {
3,723!
694
        SCLogConfig("allocated %"PRIu64" bytes of memory for the flow hash... "
×
695
                  "%" PRIu32 " buckets of size %" PRIuMAX "",
×
696
                  SC_ATOMIC_GET(flow_memuse), flow_config.hash_size,
×
697
                  (uintmax_t)sizeof(FlowBucket));
×
698
    }
×
699
    FlowSparePoolInit();
3,723✔
700
    if (!quiet) {
3,723!
701
        SCLogConfig("flow memory usage: %"PRIu64" bytes, maximum: %"PRIu64,
×
702
                SC_ATOMIC_GET(flow_memuse), SC_ATOMIC_GET(flow_config.memcap));
×
703
    }
×
704

705
    FlowInitFlowProto();
3,723✔
706

707
    uint32_t sz = sizeof(Flow) + FlowStorageSize();
3,723✔
708
    SCLogConfig("flow size %u, memcap allows for %" PRIu64 " flows. Per hash row in perfect "
3,723✔
709
                "conditions %" PRIu64,
3,723✔
710
            sz, flow_memcap_copy / sz, (flow_memcap_copy / sz) / flow_config.hash_size);
3,723✔
711
}
3,723✔
712

713
void FlowReset(void)
714
{
18,502✔
715
    // resets the flows (for reuse by fuzzing)
716
    for (uint32_t u = 0; u < flow_config.hash_size; u++) {
1,212,565,574!
717
        Flow *f = flow_hash[u].head;
1,212,547,072✔
718
        while (f) {
1,212,601,041!
719
            Flow *n = f->next;
53,969✔
720
            uint8_t proto_map = FlowGetProtoMapping(f->proto);
53,969✔
721
            FlowClearMemory(f, proto_map);
53,969✔
722
            FlowFree(f);
53,969✔
723
            f = n;
53,969✔
724
        }
53,969✔
725
        flow_hash[u].head = NULL;
1,212,547,072✔
726
    }
1,212,547,072✔
727
}
18,502✔
728

729
/** \brief shutdown the flow engine
730
 *  \warning Not thread safe */
731
void FlowShutdown(void)
732
{
3,541✔
733
    Flow *f;
3,541✔
734
    while ((f = FlowDequeue(&flow_recycle_q))) {
3,541!
735
        FlowFree(f);
×
736
    }
×
737

738
    /* clear and free the hash */
739
    if (flow_hash != NULL) {
3,541!
740
        /* clean up flow mutexes */
741
        for (uint32_t u = 0; u < flow_config.hash_size; u++) {
232,066,517✔
742
            f = flow_hash[u].head;
232,062,976✔
743
            while (f) {
232,062,977!
744
                Flow *n = f->next;
1✔
745
                uint8_t proto_map = FlowGetProtoMapping(f->proto);
1✔
746
                FlowClearMemory(f, proto_map);
1✔
747
                FlowFree(f);
1✔
748
                f = n;
1✔
749
            }
1✔
750
            f = flow_hash[u].evicted;
232,062,976✔
751
            while (f) {
232,062,976!
752
                Flow *n = f->next;
×
753
                uint8_t proto_map = FlowGetProtoMapping(f->proto);
×
754
                FlowClearMemory(f, proto_map);
×
755
                FlowFree(f);
×
756
                f = n;
×
757
            }
×
758

759
            FBLOCK_DESTROY(&flow_hash[u]);
232,062,976✔
760
        }
232,062,976✔
761
        SCFreeAligned(flow_hash);
3,541✔
762
        flow_hash = NULL;
3,541✔
763
    }
3,541✔
764
    (void) SC_ATOMIC_SUB(flow_memuse, flow_config.hash_size * sizeof(FlowBucket));
3,541✔
765
    FlowQueueDestroy(&flow_recycle_q);
3,541✔
766
    FlowSparePoolDestroy();
3,541✔
767
    DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(flow_memuse) != 0);
3,541✔
768
}
3,541✔
769

770
/**
771
 *  \brief  Function to set the default timeout, free function and flow state
772
 *          function for all supported flow_proto.
773
 */
774

775
void FlowInitFlowProto(void)
776
{
3,724✔
777
    FlowTimeoutsInit();
3,724✔
778

779
#define SET_DEFAULTS(p, n, e, c, b, ne, ee, ce, be)     \
3,724✔
780
    flow_timeouts_normal[(p)].new_timeout = (n);     \
14,896✔
781
    flow_timeouts_normal[(p)].est_timeout = (e);     \
14,896✔
782
    flow_timeouts_normal[(p)].closed_timeout = (c);  \
14,896✔
783
    flow_timeouts_normal[(p)].bypassed_timeout = (b); \
14,896✔
784
    flow_timeouts_emerg[(p)].new_timeout = (ne);     \
14,896✔
785
    flow_timeouts_emerg[(p)].est_timeout = (ee);     \
14,896✔
786
    flow_timeouts_emerg[(p)].closed_timeout = (ce); \
14,896✔
787
    flow_timeouts_emerg[(p)].bypassed_timeout = (be); \
14,896✔
788

3,724✔
789
    SET_DEFAULTS(FLOW_PROTO_DEFAULT,
3,724✔
790
                FLOW_DEFAULT_NEW_TIMEOUT, FLOW_DEFAULT_EST_TIMEOUT,
3,724✔
791
                    0, FLOW_DEFAULT_BYPASSED_TIMEOUT,
3,724✔
792
                FLOW_DEFAULT_EMERG_NEW_TIMEOUT, FLOW_DEFAULT_EMERG_EST_TIMEOUT,
3,724✔
793
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
3,724✔
794
    SET_DEFAULTS(FLOW_PROTO_TCP,
3,724✔
795
                FLOW_IPPROTO_TCP_NEW_TIMEOUT, FLOW_IPPROTO_TCP_EST_TIMEOUT,
3,724✔
796
                    FLOW_IPPROTO_TCP_CLOSED_TIMEOUT, FLOW_IPPROTO_TCP_BYPASSED_TIMEOUT,
3,724✔
797
                FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT,
3,724✔
798
                    FLOW_IPPROTO_TCP_EMERG_CLOSED_TIMEOUT, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
3,724✔
799
    SET_DEFAULTS(FLOW_PROTO_UDP,
3,724✔
800
                FLOW_IPPROTO_UDP_NEW_TIMEOUT, FLOW_IPPROTO_UDP_EST_TIMEOUT,
3,724✔
801
                    0, FLOW_IPPROTO_UDP_BYPASSED_TIMEOUT,
3,724✔
802
                FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT,
3,724✔
803
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
3,724✔
804
    SET_DEFAULTS(FLOW_PROTO_ICMP,
3,724✔
805
                FLOW_IPPROTO_ICMP_NEW_TIMEOUT, FLOW_IPPROTO_ICMP_EST_TIMEOUT,
3,724✔
806
                    0, FLOW_IPPROTO_ICMP_BYPASSED_TIMEOUT,
3,724✔
807
                FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT, FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT,
3,724✔
808
                    0, FLOW_DEFAULT_EMERG_BYPASSED_TIMEOUT);
3,724✔
809

810
    flow_freefuncs[FLOW_PROTO_DEFAULT].Freefunc = NULL;
3,724✔
811
    flow_freefuncs[FLOW_PROTO_TCP].Freefunc = NULL;
3,724✔
812
    flow_freefuncs[FLOW_PROTO_UDP].Freefunc = NULL;
3,724✔
813
    flow_freefuncs[FLOW_PROTO_ICMP].Freefunc = NULL;
3,724✔
814

815
    /* Let's see if we have custom timeouts defined from config */
816
    const char *new = NULL;
3,724✔
817
    const char *established = NULL;
3,724✔
818
    const char *closed = NULL;
3,724✔
819
    const char *bypassed = NULL;
3,724✔
820
    const char *emergency_new = NULL;
3,724✔
821
    const char *emergency_established = NULL;
3,724✔
822
    const char *emergency_closed = NULL;
3,724✔
823
    const char *emergency_bypassed = NULL;
3,724✔
824

825
    SCConfNode *flow_timeouts = SCConfGetNode("flow-timeouts");
3,724✔
826
    if (flow_timeouts != NULL) {
3,724✔
827
        SCConfNode *proto = NULL;
2,889✔
828
        uint32_t configval = 0;
2,889✔
829

830
        /* Defaults. */
831
        proto = SCConfNodeLookupChild(flow_timeouts, "default");
2,889✔
832
        if (proto != NULL) {
2,889✔
833
            new = SCConfNodeLookupChildValue(proto, "new");
2,887✔
834
            established = SCConfNodeLookupChildValue(proto, "established");
2,887✔
835
            closed = SCConfNodeLookupChildValue(proto, "closed");
2,887✔
836
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
2,887✔
837
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
2,887✔
838
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
2,887✔
839
            emergency_closed = SCConfNodeLookupChildValue(proto, "emergency-closed");
2,887✔
840
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
2,887✔
841

842
            if (new != NULL &&
2,887!
843
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
2,887!
844

845
                    flow_timeouts_normal[FLOW_PROTO_DEFAULT].new_timeout = configval;
2,887✔
846
            }
2,887✔
847
            if (established != NULL &&
2,887!
848
                StringParseUint32(&configval, 10, strlen(established),
2,887!
849
                                        established) > 0) {
2,887✔
850

851
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].est_timeout = configval;
2,887✔
852
            }
2,887✔
853
            if (closed != NULL &&
2,887!
854
                StringParseUint32(&configval, 10, strlen(closed),
2,887!
855
                                        closed) > 0) {
2,887✔
856

857
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].closed_timeout = configval;
2,887✔
858
            }
2,887✔
859
            if (bypassed != NULL &&
2,887✔
860
                    StringParseUint32(&configval, 10,
2,887!
861
                                            strlen(bypassed),
2,867✔
862
                                            bypassed) > 0) {
2,867✔
863

864
                flow_timeouts_normal[FLOW_PROTO_DEFAULT].bypassed_timeout = configval;
2,867✔
865
            }
2,867✔
866
            if (emergency_new != NULL &&
2,887!
867
                StringParseUint32(&configval, 10, strlen(emergency_new),
2,887!
868
                                        emergency_new) > 0) {
2,887✔
869

870
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].new_timeout = configval;
2,887✔
871
            }
2,887✔
872
            if (emergency_established != NULL &&
2,887!
873
                    StringParseUint32(&configval, 10,
2,887!
874
                                            strlen(emergency_established),
2,887✔
875
                                            emergency_established) > 0) {
2,887✔
876

877
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].est_timeout= configval;
2,887✔
878
            }
2,887✔
879
            if (emergency_closed != NULL &&
2,887!
880
                    StringParseUint32(&configval, 10,
2,887!
881
                                            strlen(emergency_closed),
2,887✔
882
                                            emergency_closed) > 0) {
2,887✔
883

884
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].closed_timeout = configval;
2,887✔
885
            }
2,887✔
886
            if (emergency_bypassed != NULL &&
2,887✔
887
                    StringParseUint32(&configval, 10,
2,887!
888
                                            strlen(emergency_bypassed),
2,867✔
889
                                            emergency_bypassed) > 0) {
2,867✔
890

891
                flow_timeouts_emerg[FLOW_PROTO_DEFAULT].bypassed_timeout = configval;
2,867✔
892
            }
2,867✔
893
        }
2,887✔
894

895
        /* TCP. */
896
        proto = SCConfNodeLookupChild(flow_timeouts, "tcp");
2,889✔
897
        if (proto != NULL) {
2,889!
898
            new = SCConfNodeLookupChildValue(proto, "new");
2,889✔
899
            established = SCConfNodeLookupChildValue(proto, "established");
2,889✔
900
            closed = SCConfNodeLookupChildValue(proto, "closed");
2,889✔
901
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
2,889✔
902
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
2,889✔
903
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
2,889✔
904
            emergency_closed = SCConfNodeLookupChildValue(proto, "emergency-closed");
2,889✔
905
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
2,889✔
906

907
            if (new != NULL &&
2,889!
908
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
2,889!
909

910
                flow_timeouts_normal[FLOW_PROTO_TCP].new_timeout = configval;
2,889✔
911
            }
2,889✔
912
            if (established != NULL &&
2,889!
913
                StringParseUint32(&configval, 10, strlen(established),
2,889!
914
                                        established) > 0) {
2,889✔
915

916
                flow_timeouts_normal[FLOW_PROTO_TCP].est_timeout = configval;
2,889✔
917
            }
2,889✔
918
            if (closed != NULL &&
2,889!
919
                StringParseUint32(&configval, 10, strlen(closed),
2,889!
920
                                        closed) > 0) {
2,889✔
921

922
                flow_timeouts_normal[FLOW_PROTO_TCP].closed_timeout = configval;
2,889✔
923
            }
2,889✔
924
            if (bypassed != NULL &&
2,889✔
925
                    StringParseUint32(&configval, 10,
2,889!
926
                                            strlen(bypassed),
2,867✔
927
                                            bypassed) > 0) {
2,867✔
928

929
                flow_timeouts_normal[FLOW_PROTO_TCP].bypassed_timeout = configval;
2,867✔
930
            }
2,867✔
931
            if (emergency_new != NULL &&
2,889✔
932
                StringParseUint32(&configval, 10, strlen(emergency_new),
2,889!
933
                                        emergency_new) > 0) {
2,887✔
934

935
                flow_timeouts_emerg[FLOW_PROTO_TCP].new_timeout = configval;
2,887✔
936
            }
2,887✔
937
            if (emergency_established != NULL &&
2,889✔
938
                StringParseUint32(&configval, 10,
2,889!
939
                                        strlen(emergency_established),
2,887✔
940
                                        emergency_established) > 0) {
2,887✔
941

942
                flow_timeouts_emerg[FLOW_PROTO_TCP].est_timeout = configval;
2,887✔
943
            }
2,887✔
944
            if (emergency_closed != NULL &&
2,889✔
945
                StringParseUint32(&configval, 10,
2,889!
946
                                        strlen(emergency_closed),
2,887✔
947
                                        emergency_closed) > 0) {
2,887✔
948

949
                flow_timeouts_emerg[FLOW_PROTO_TCP].closed_timeout = configval;
2,887✔
950
            }
2,887✔
951
            if (emergency_bypassed != NULL &&
2,889✔
952
                    StringParseUint32(&configval, 10,
2,889!
953
                                            strlen(emergency_bypassed),
2,867✔
954
                                            emergency_bypassed) > 0) {
2,867✔
955

956
                flow_timeouts_emerg[FLOW_PROTO_TCP].bypassed_timeout = configval;
2,867✔
957
            }
2,867✔
958
        }
2,889✔
959

960
        /* UDP. */
961
        proto = SCConfNodeLookupChild(flow_timeouts, "udp");
2,889✔
962
        if (proto != NULL) {
2,889✔
963
            new = SCConfNodeLookupChildValue(proto, "new");
2,887✔
964
            established = SCConfNodeLookupChildValue(proto, "established");
2,887✔
965
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
2,887✔
966
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
2,887✔
967
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
2,887✔
968
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
2,887✔
969

970
            if (new != NULL &&
2,887!
971
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
2,887!
972

973
                flow_timeouts_normal[FLOW_PROTO_UDP].new_timeout = configval;
2,887✔
974
            }
2,887✔
975
            if (established != NULL &&
2,887!
976
                StringParseUint32(&configval, 10, strlen(established),
2,887!
977
                                        established) > 0) {
2,887✔
978

979
                flow_timeouts_normal[FLOW_PROTO_UDP].est_timeout = configval;
2,887✔
980
            }
2,887✔
981
            if (bypassed != NULL &&
2,887✔
982
                    StringParseUint32(&configval, 10,
2,887!
983
                                            strlen(bypassed),
2,867✔
984
                                            bypassed) > 0) {
2,867✔
985

986
                flow_timeouts_normal[FLOW_PROTO_UDP].bypassed_timeout = configval;
2,867✔
987
            }
2,867✔
988
            if (emergency_new != NULL &&
2,887!
989
                StringParseUint32(&configval, 10, strlen(emergency_new),
2,887!
990
                                        emergency_new) > 0) {
2,887✔
991

992
                flow_timeouts_emerg[FLOW_PROTO_UDP].new_timeout = configval;
2,887✔
993
            }
2,887✔
994
            if (emergency_established != NULL &&
2,887!
995
                StringParseUint32(&configval, 10,
2,887!
996
                                        strlen(emergency_established),
2,887✔
997
                                        emergency_established) > 0) {
2,887✔
998

999
                flow_timeouts_emerg[FLOW_PROTO_UDP].est_timeout = configval;
2,887✔
1000
            }
2,887✔
1001
            if (emergency_bypassed != NULL &&
2,887✔
1002
                    StringParseUint32(&configval, 10,
2,887!
1003
                                            strlen(emergency_bypassed),
2,867✔
1004
                                            emergency_bypassed) > 0) {
2,867✔
1005

1006
                flow_timeouts_emerg[FLOW_PROTO_UDP].bypassed_timeout = configval;
2,867✔
1007
            }
2,867✔
1008
        }
2,887✔
1009

1010
        /* ICMP. */
1011
        proto = SCConfNodeLookupChild(flow_timeouts, "icmp");
2,889✔
1012
        if (proto != NULL) {
2,889✔
1013
            new = SCConfNodeLookupChildValue(proto, "new");
2,887✔
1014
            established = SCConfNodeLookupChildValue(proto, "established");
2,887✔
1015
            bypassed = SCConfNodeLookupChildValue(proto, "bypassed");
2,887✔
1016
            emergency_new = SCConfNodeLookupChildValue(proto, "emergency-new");
2,887✔
1017
            emergency_established = SCConfNodeLookupChildValue(proto, "emergency-established");
2,887✔
1018
            emergency_bypassed = SCConfNodeLookupChildValue(proto, "emergency-bypassed");
2,887✔
1019

1020
            if (new != NULL &&
2,887!
1021
                StringParseUint32(&configval, 10, strlen(new), new) > 0) {
2,887!
1022

1023
                flow_timeouts_normal[FLOW_PROTO_ICMP].new_timeout = configval;
2,887✔
1024
            }
2,887✔
1025
            if (established != NULL &&
2,887!
1026
                StringParseUint32(&configval, 10, strlen(established),
2,887!
1027
                                        established) > 0) {
2,887✔
1028

1029
                flow_timeouts_normal[FLOW_PROTO_ICMP].est_timeout = configval;
2,887✔
1030
            }
2,887✔
1031
            if (bypassed != NULL &&
2,887✔
1032
                    StringParseUint32(&configval, 10,
2,887!
1033
                                            strlen(bypassed),
2,867✔
1034
                                            bypassed) > 0) {
2,867✔
1035

1036
                flow_timeouts_normal[FLOW_PROTO_ICMP].bypassed_timeout = configval;
2,867✔
1037
            }
2,867✔
1038
            if (emergency_new != NULL &&
2,887!
1039
                StringParseUint32(&configval, 10, strlen(emergency_new),
2,887!
1040
                                        emergency_new) > 0) {
2,887✔
1041

1042
                flow_timeouts_emerg[FLOW_PROTO_ICMP].new_timeout = configval;
2,887✔
1043
            }
2,887✔
1044
            if (emergency_established != NULL &&
2,887!
1045
                StringParseUint32(&configval, 10,
2,887!
1046
                                        strlen(emergency_established),
2,887✔
1047
                                        emergency_established) > 0) {
2,887✔
1048

1049
                flow_timeouts_emerg[FLOW_PROTO_ICMP].est_timeout = configval;
2,887✔
1050
            }
2,887✔
1051
            if (emergency_bypassed != NULL &&
2,887✔
1052
                    StringParseUint32(&configval, 10,
2,887!
1053
                                            strlen(emergency_bypassed),
2,867✔
1054
                                            emergency_bypassed) > 0) {
2,867✔
1055

1056
                flow_timeouts_emerg[FLOW_PROTO_ICMP].bypassed_timeout = configval;
2,867✔
1057
            }
2,867✔
1058
        }
2,887✔
1059
    }
2,889✔
1060

1061
    /* validate and if needed update emergency timeout values */
1062
    for (uint8_t i = 0; i < FLOW_PROTO_MAX; i++) {
18,620✔
1063
        const FlowProtoTimeout *n = &flow_timeouts_normal[i];
14,896✔
1064
        FlowProtoTimeout *e = &flow_timeouts_emerg[i];
14,896✔
1065

1066
        if (e->est_timeout > n->est_timeout) {
14,896!
1067
            SCLogWarning("emergency timeout value %u for \'established\' "
×
1068
                         "must be below regular value %u",
×
1069
                    e->est_timeout, n->est_timeout);
×
1070
            e->est_timeout = n->est_timeout / 10;
×
1071
        }
×
1072

1073
        if (e->new_timeout > n->new_timeout) {
14,896!
1074
            SCLogWarning("emergency timeout value %u for \'new\' must be "
×
1075
                         "below regular value %u",
×
1076
                    e->new_timeout, n->new_timeout);
×
1077
            e->new_timeout = n->new_timeout / 10;
×
1078
        }
×
1079

1080
        if (e->closed_timeout > n->closed_timeout) {
14,896!
1081
            SCLogWarning("emergency timeout value %u for \'closed\' must "
×
1082
                         "be below regular value %u",
×
1083
                    e->closed_timeout, n->closed_timeout);
×
1084
            e->closed_timeout = n->closed_timeout / 10;
×
1085
        }
×
1086

1087
        if (e->bypassed_timeout > n->bypassed_timeout) {
14,896!
1088
            SCLogWarning("emergency timeout value %u for \'bypassed\' "
×
1089
                         "must be below regular value %u",
×
1090
                    e->bypassed_timeout, n->bypassed_timeout);
×
1091
            e->bypassed_timeout = n->bypassed_timeout / 10;
×
1092
        }
×
1093
    }
14,896✔
1094

1095
    for (uint8_t i = 0; i < FLOW_PROTO_MAX; i++) {
18,620✔
1096
        FlowProtoTimeout *n = &flow_timeouts_normal[i];
14,896✔
1097
        FlowProtoTimeout *e = &flow_timeouts_emerg[i];
14,896✔
1098
        FlowProtoTimeout *d = &flow_timeouts_delta[i];
14,896✔
1099

1100
        if (e->est_timeout > n->est_timeout) {
14,896!
1101
            SCLogWarning("emergency timeout value for \'established\' must be below normal value");
×
1102
            e->est_timeout = n->est_timeout / 10;
×
1103
        }
×
1104
        d->est_timeout = n->est_timeout - e->est_timeout;
14,896✔
1105

1106
        if (e->new_timeout > n->new_timeout) {
14,896!
1107
            SCLogWarning("emergency timeout value for \'new\' must be below normal value");
×
1108
            e->new_timeout = n->new_timeout / 10;
×
1109
        }
×
1110
        d->new_timeout = n->new_timeout - e->new_timeout;
14,896✔
1111

1112
        if (e->closed_timeout > n->closed_timeout) {
14,896!
1113
            SCLogWarning("emergency timeout value for \'closed\' must be below normal value");
×
1114
            e->closed_timeout = n->closed_timeout / 10;
×
1115
        }
×
1116
        d->closed_timeout = n->closed_timeout - e->closed_timeout;
14,896✔
1117

1118
        if (e->bypassed_timeout > n->bypassed_timeout) {
14,896!
1119
            SCLogWarning("emergency timeout value for \'bypassed\' must be below normal value");
×
1120
            e->bypassed_timeout = n->bypassed_timeout / 10;
×
1121
        }
×
1122
        d->bypassed_timeout = n->bypassed_timeout - e->bypassed_timeout;
14,896✔
1123

1124
        SCLogDebug("deltas: new: -%u est: -%u closed: -%u bypassed: -%u",
14,896!
1125
                d->new_timeout, d->est_timeout, d->closed_timeout, d->bypassed_timeout);
14,896✔
1126
    }
14,896✔
1127
}
3,724✔
1128

1129
/**
1130
 *  \brief  Function clear the flow memory before queueing it to spare flow
1131
 *          queue.
1132
 *
1133
 *  \param  f           pointer to the flow needed to be cleared.
1134
 *  \param  proto_map   mapped value of the protocol to FLOW_PROTO's.
1135
 */
1136

1137
int FlowClearMemory(Flow* f, uint8_t proto_map)
1138
{
78,405✔
1139
    SCEnter();
78,405✔
1140

1141
    if (unlikely(f->flags & FLOW_HAS_EXPECTATION)) {
78,405✔
1142
        AppLayerExpectationClean(f);
169✔
1143
    }
169✔
1144

1145
    /* call the protocol specific free function if we have one */
1146
    if (flow_freefuncs[proto_map].Freefunc != NULL) {
78,405✔
1147
        flow_freefuncs[proto_map].Freefunc(f->protoctx);
51,881✔
1148
    }
51,881✔
1149

1150
    FlowFreeStorage(f);
78,405✔
1151

1152
    FLOW_RECYCLE(f);
78,405!
1153

1154
    SCReturnInt(1);
78,405✔
1155
}
78,405✔
1156

1157
/**
1158
 *  \brief  Function to set the function to get protocol specific flow state.
1159
 *
1160
 *  \param   proto  protocol of which function is needed to be set.
1161
 *  \param   Free   Function pointer which will be called to free the protocol
1162
 *                  specific memory.
1163
 */
1164

1165
int FlowSetProtoFreeFunc (uint8_t proto, void (*Free)(void *))
1166
{
4,297✔
1167
    uint8_t proto_map;
4,297✔
1168
    proto_map = FlowGetProtoMapping(proto);
4,297✔
1169

1170
    flow_freefuncs[proto_map].Freefunc = Free;
4,297✔
1171
    return 1;
4,297✔
1172
}
4,297✔
1173

1174
/**
1175
 *  \brief get 'disruption' flags: GAP/DEPTH/PASS
1176
 *  \param f locked flow
1177
 *  \param flags existing flags to be amended
1178
 *  \retval flags original flags + disrupt flags (if any)
1179
 *  \TODO handle UDP
1180
 */
1181
uint8_t FlowGetDisruptionFlags(const Flow *f, uint8_t flags)
1182
{
6,814,574✔
1183
    if (f->proto != IPPROTO_TCP) {
6,814,574✔
1184
        return flags;
433,515✔
1185
    }
433,515✔
1186
    if (f->protoctx == NULL) {
6,381,059✔
1187
        return flags;
65✔
1188
    }
65✔
1189

1190
    uint8_t newflags = flags;
6,380,994✔
1191
    TcpSession *ssn = f->protoctx;
6,380,994✔
1192
    TcpStream *stream = flags & STREAM_TOSERVER ? &ssn->client : &ssn->server;
6,380,994✔
1193

1194
    if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) {
6,380,994✔
1195
        newflags |= STREAM_DEPTH;
23,662✔
1196
    }
23,662✔
1197
    /* todo: handle pass case (also for UDP!) */
1198

1199
    return newflags;
6,380,994✔
1200
}
6,381,059✔
1201

1202
void FlowUpdateState(Flow *f, const enum FlowState s)
1203
{
207,068✔
1204
    if (s != f->flow_state) {
207,068✔
1205
        /* set the state */
1206
        // Explicit cast from the enum type to the compact version
1207
        f->flow_state = (FlowStateType)s;
68,503✔
1208

1209
        /* update timeout policy and value */
1210
        const uint32_t timeout_policy = FlowGetTimeoutPolicy(f);
68,503✔
1211
        if (timeout_policy != f->timeout_policy) {
68,503✔
1212
            f->timeout_policy = timeout_policy;
68,483✔
1213
        }
68,483✔
1214
    }
68,503✔
1215
#ifdef UNITTESTS
30,129✔
1216
    if (f->fb != NULL) {
30,129✔
1217
#endif
30,100✔
1218
        /* and reset the flow bucket next_ts value so that the flow manager
1219
         * has to revisit this row */
1220
        SC_ATOMIC_SET(f->fb->next_ts, 0);
207,039✔
1221
#ifdef UNITTESTS
30,100✔
1222
    }
30,100✔
1223
#endif
30,129✔
1224
}
207,068✔
1225

1226
static FlowStorageId g_flow_translate_id = { .id = -1 };
1227

1228
static void FlowTranslationFree(void *ptr)
1229
{
2✔
1230
    SCFree(ptr);
2✔
1231
}
2✔
1232

1233
void SCFlowEnableTranslation(const char *name)
1234
{
2,196✔
1235
    g_flow_translate_id =
2,196✔
1236
            FlowStorageRegister("translation", sizeof(void *), NULL, FlowTranslationFree);
2,196✔
1237
    SCLogNotice("translation tracking %s by %s",
2,196!
1238
            g_flow_translate_id.id != -1 ? "enabled" : "failed to enable", name);
2,196✔
1239
}
2,196✔
1240

1241
const FlowTuple *SCFlowGetTranslated(const Flow *f)
1242
{
18,544✔
1243
    if (g_flow_translate_id.id == -1)
18,544!
NEW
1244
        return NULL;
×
1245
    if (!(f->flags & FLOW_IS_TRANSLATED))
18,544✔
1246
        return NULL;
18,486✔
1247
    const FlowTuple *ft = FlowGetStorageById(f, g_flow_translate_id);
58✔
1248
    return ft;
58✔
1249
}
18,544✔
1250

1251
/**
1252
 * \retval 1 ok
1253
 * \retval 0 already set up as translated
1254
 * \retval -1 error
1255
 */
1256
int SCFlowSetTranslated(
1257
        Flow *f, uint8_t proto, uint32_t src_ip, uint16_t sp, uint32_t dest_ip, uint16_t dp)
1258
{
2✔
1259
    /* flow storage not registered */
1260
    if (g_flow_translate_id.id == -1)
2!
NEW
1261
        return -1;
×
1262
    if (f->flags & FLOW_IS_TRANSLATED)
2!
NEW
1263
        return 0;
×
1264
    FlowTuple *ft = SCCalloc(1, sizeof(*ft));
2✔
1265
    if (ft) {
2!
1266
        ft->proto = f->proto;
2✔
1267
        ft->sp = sp;
2✔
1268
        ft->dp = dp;
2✔
1269
        memcpy(&ft->src, &src_ip, sizeof(src_ip));
2✔
1270
        memcpy(&ft->dst, &dest_ip, sizeof(dest_ip));
2✔
1271
        if (FlowSetStorageById(f, g_flow_translate_id, (void *)ft) != 0) {
2!
NEW
1272
            return -1;
×
NEW
1273
        }
×
1274
        f->flags |= FLOW_IS_TRANSLATED;
2✔
1275
        return 1;
2✔
1276
    }
2✔
NEW
1277
    return -1;
×
1278
}
2✔
1279

1280
/**
1281
 * \brief Get flow last time as individual values.
1282
 *
1283
 * Instead of returning a pointer to the timeval copy the timeval
1284
 * parts into output pointers to make it simpler to call from Rust
1285
 * over FFI using only basic data types.
1286
 */
1287
void SCFlowGetLastTimeAsParts(const Flow *flow, uint64_t *secs, uint64_t *usecs)
1288
{
325,855✔
1289
    *secs = (uint64_t)SCTIME_SECS(flow->lastts);
325,855✔
1290
    *usecs = (uint64_t)SCTIME_USECS(flow->lastts);
325,855✔
1291
}
325,855✔
1292

1293
/**
1294
 * \brief Get flow source port.
1295
 *
1296
 * A function to get the flow sport useful when the caller only has an
1297
 * opaque pointer to the flow structure.
1298
 */
1299
uint16_t SCFlowGetSourcePort(const Flow *flow)
1300
{
48✔
1301
    return flow->sp;
48✔
1302
}
48✔
1303

1304
/**
1305
 * \brief Get flow destination port.
1306
 *
1307
 * A function to get the flow dport useful when the caller only has an
1308
 * opaque pointer to the flow structure.
1309
 */
1310

1311
uint16_t SCFlowGetDestinationPort(const Flow *flow)
1312
{
48✔
1313
    return flow->dp;
48✔
1314
}
48✔
1315

1316
/**
1317
 * \brief Get flow flags.
1318
 *
1319
 * A function to get the flow flags useful when the caller only has an
1320
 * opaque pointer to the flow structure.
1321
 */
1322
uint64_t SCFlowGetFlags(const Flow *flow)
1323
{
48✔
1324
    return flow->flags;
48✔
1325
}
48✔
1326
/************************************Unittests*******************************/
1327

1328
#ifdef UNITTESTS
1329
#include "threads.h"
1330

1331
/**
1332
 *  \test   Test the setting of the per protocol timeouts.
1333
 *
1334
 *  \retval On success it returns 1 and on failure 0.
1335
 */
1336

1337
static int FlowTest01 (void)
1338
{
1✔
1339
    uint8_t proto_map;
1✔
1340

1341
    FlowInitFlowProto();
1✔
1342
    proto_map = FlowGetProtoMapping(IPPROTO_TCP);
1✔
1343
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_TCP_NEW_TIMEOUT);
1✔
1344
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_TCP_EST_TIMEOUT);
1✔
1345
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_TCP_EMERG_NEW_TIMEOUT);
1✔
1346
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_TCP_EMERG_EST_TIMEOUT);
1✔
1347

1348
    proto_map = FlowGetProtoMapping(IPPROTO_UDP);
1✔
1349
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_UDP_NEW_TIMEOUT);
1✔
1350
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_UDP_EST_TIMEOUT);
1✔
1351
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_UDP_EMERG_NEW_TIMEOUT);
1✔
1352
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_UDP_EMERG_EST_TIMEOUT);
1✔
1353

1354
    proto_map = FlowGetProtoMapping(IPPROTO_ICMP);
1✔
1355
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_IPPROTO_ICMP_NEW_TIMEOUT);
1✔
1356
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_IPPROTO_ICMP_EST_TIMEOUT);
1✔
1357
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_IPPROTO_ICMP_EMERG_NEW_TIMEOUT);
1✔
1358
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_IPPROTO_ICMP_EMERG_EST_TIMEOUT);
1✔
1359

1360
    proto_map = FlowGetProtoMapping(IPPROTO_DCCP);
1✔
1361
    FAIL_IF(flow_timeouts_normal[proto_map].new_timeout != FLOW_DEFAULT_NEW_TIMEOUT);
1✔
1362
    FAIL_IF(flow_timeouts_normal[proto_map].est_timeout != FLOW_DEFAULT_EST_TIMEOUT);
1✔
1363
    FAIL_IF(flow_timeouts_emerg[proto_map].new_timeout != FLOW_DEFAULT_EMERG_NEW_TIMEOUT);
1✔
1364
    FAIL_IF(flow_timeouts_emerg[proto_map].est_timeout != FLOW_DEFAULT_EMERG_EST_TIMEOUT);
1✔
1365

1366
    PASS;
1✔
1367
}
1✔
1368

1369
/*Test function for the unit test FlowTest02*/
1370

1371
static void test(void *f) {}
1372

1373
/**
1374
 *  \test   Test the setting of the per protocol free function to free the
1375
 *          protocol specific memory.
1376
 *
1377
 *  \retval On success it returns 1 and on failure 0.
1378
 */
1379

1380
static int FlowTest02 (void)
1381
{
1✔
1382
    FlowSetProtoFreeFunc(IPPROTO_DCCP, test);
1✔
1383
    FlowSetProtoFreeFunc(IPPROTO_TCP, test);
1✔
1384
    FlowSetProtoFreeFunc(IPPROTO_UDP, test);
1✔
1385
    FlowSetProtoFreeFunc(IPPROTO_ICMP, test);
1✔
1386

1387
    FAIL_IF(flow_freefuncs[FLOW_PROTO_DEFAULT].Freefunc != test);
1✔
1388
    FAIL_IF(flow_freefuncs[FLOW_PROTO_TCP].Freefunc != test);
1✔
1389
    FAIL_IF(flow_freefuncs[FLOW_PROTO_UDP].Freefunc != test);
1✔
1390
    FAIL_IF(flow_freefuncs[FLOW_PROTO_ICMP].Freefunc != test);
1✔
1391

1392
    PASS;
1✔
1393
}
1✔
1394

1395
/**
1396
 *  \test   Test flow allocations when it reach memcap
1397
 *
1398
 *
1399
 *  \retval On success it returns 1 and on failure 0.
1400
 */
1401

1402
static int FlowTest07 (void)
1403
{
1✔
1404
    int result = 0;
1✔
1405
    FlowInitConfig(FLOW_QUIET);
1✔
1406
    FlowConfig backup;
1✔
1407
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1✔
1408

1409
    uint32_t ini = 0;
1✔
1410
    uint32_t end = FlowSpareGetPoolSize();
1✔
1411
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1✔
1412
    flow_config.prealloc = 100;
1✔
1413

1414
    /* Let's get the flow spare pool empty */
1415
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1416

1417
    /* And now let's try to reach the memcap val */
1418
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1✔
1419
        ini = end + 1;
1420
        end = end + 2;
1421
        UTHBuildPacketOfFlows(ini, end, 0);
1422
    }
1423

1424
    /* should time out normal */
1425
    TimeSetIncrementTime(2000);
1✔
1426
    ini = end + 1;
1✔
1427
    end = end + 2;
1✔
1428
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1429

1430
    /* This means that the engine entered emerg mode: should happen as easy
1431
     * with flow mgr activated */
1432
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1✔
1433
        result = 1;
1✔
1434

1435
    FlowShutdown();
1✔
1436
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1✔
1437

1438
    return result;
1✔
1439
}
1✔
1440

1441
/**
1442
 *  \test   Test flow allocations when it reach memcap
1443
 *
1444
 *
1445
 *  \retval On success it returns 1 and on failure 0.
1446
 */
1447

1448
static int FlowTest08 (void)
1449
{
1✔
1450
    int result = 0;
1✔
1451

1452
    FlowInitConfig(FLOW_QUIET);
1✔
1453
    FlowConfig backup;
1✔
1454
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1✔
1455

1456
    uint32_t ini = 0;
1✔
1457
    uint32_t end = FlowSpareGetPoolSize();
1✔
1458
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1✔
1459
    flow_config.prealloc = 100;
1✔
1460

1461
    /* Let's get the flow spare pool empty */
1462
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1463

1464
    /* And now let's try to reach the memcap val */
1465
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1✔
1466
        ini = end + 1;
1467
        end = end + 2;
1468
        UTHBuildPacketOfFlows(ini, end, 0);
1469
    }
1470

1471
    /* By default we use 30  for timing out new flows. This means
1472
     * that the Emergency mode should be set */
1473
    TimeSetIncrementTime(20);
1✔
1474
    ini = end + 1;
1✔
1475
    end = end + 2;
1✔
1476
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1477

1478
    /* This means that the engine released 5 flows by emergency timeout */
1479
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1✔
1480
        result = 1;
1✔
1481

1482
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1✔
1483
    FlowShutdown();
1✔
1484

1485
    return result;
1✔
1486
}
1✔
1487

1488
/**
1489
 *  \test   Test flow allocations when it reach memcap
1490
 *
1491
 *
1492
 *  \retval On success it returns 1 and on failure 0.
1493
 */
1494

1495
static int FlowTest09 (void)
1496
{
1✔
1497
    int result = 0;
1✔
1498

1499
    FlowInitConfig(FLOW_QUIET);
1✔
1500
    FlowConfig backup;
1✔
1501
    memcpy(&backup, &flow_config, sizeof(FlowConfig));
1✔
1502

1503
    uint32_t ini = 0;
1✔
1504
    uint32_t end = FlowSpareGetPoolSize();
1✔
1505
    SC_ATOMIC_SET(flow_config.memcap, 10000);
1✔
1506
    flow_config.prealloc = 100;
1✔
1507

1508
    /* Let's get the flow spare pool empty */
1509
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1510

1511
    /* And now let's try to reach the memcap val */
1512
    while (FLOW_CHECK_MEMCAP(sizeof(Flow))) {
1✔
1513
        ini = end + 1;
1514
        end = end + 2;
1515
        UTHBuildPacketOfFlows(ini, end, 0);
1516
    }
1517

1518
    /* No timeout will work */
1519
    TimeSetIncrementTime(5);
1✔
1520
    ini = end + 1;
1✔
1521
    end = end + 2;
1✔
1522
    UTHBuildPacketOfFlows(ini, end, 0);
1✔
1523

1524
    /* engine in emerg mode */
1525
    if (SC_ATOMIC_GET(flow_flags) & FLOW_EMERGENCY)
1✔
1526
        result = 1;
1✔
1527

1528
    memcpy(&flow_config, &backup, sizeof(FlowConfig));
1✔
1529
    FlowShutdown();
1✔
1530

1531
    return result;
1✔
1532
}
1✔
1533

1534
#endif /* UNITTESTS */
1535

1536
/**
1537
 *  \brief   Function to register the Flow Unitests.
1538
 */
1539
void FlowRegisterTests (void)
1540
{
1✔
1541
#ifdef UNITTESTS
1✔
1542
    UtRegisterTest("FlowTest01 -- Protocol Specific Timeouts", FlowTest01);
1✔
1543
    UtRegisterTest("FlowTest02 -- Setting Protocol Specific Free Function",
1✔
1544
                   FlowTest02);
1✔
1545
    UtRegisterTest("FlowTest07 -- Test flow Allocations when it reach memcap",
1✔
1546
                   FlowTest07);
1✔
1547
    UtRegisterTest("FlowTest08 -- Test flow Allocations when it reach memcap",
1✔
1548
                   FlowTest08);
1✔
1549
    UtRegisterTest("FlowTest09 -- Test flow Allocations when it reach memcap",
1✔
1550
                   FlowTest09);
1✔
1551

1552
    RegisterFlowStorageTests();
1✔
1553
#endif /* UNITTESTS */
1✔
1554
}
1✔
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