suricata
util-flow-rate.c
Go to the documentation of this file.
1/* Copyright (C) 2025 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 Shivani Bhardwaj <shivani@oisf.net>
22 *
23 */
24
25#include "suricata-common.h"
26#include "flow-storage.h"
27#include "flow-util.h"
28#include "flow-private.h"
29#include "util-storage.h"
30#include "conf.h"
31#include "util-misc.h"
32#include "util-byte.h"
33#include "util-flow-rate.h"
34#include "util-unittest.h"
36
38
40
41static void FlowRateStoreFree(void *ptr)
42{
43 FlowRateStore *frs = (FlowRateStore *)ptr;
44 size_t total_free = 0;
45 if (frs == NULL)
46 return;
47
48 for (int i = 0; i < 2; i++) {
49 if (frs->dir[i].buf != NULL) {
50 SCFree(frs->dir[i].buf);
51 total_free += (frs->dir[i].size * sizeof(uint64_t));
52 }
53 }
54
55 SCFree(frs);
56 total_free += sizeof(*frs);
57 (void)SC_ATOMIC_SUB(flow_memuse, total_free);
58}
59
61{
62 SCConfNode *root = SCConfGetNode("flow");
63 if (root == NULL)
64 return;
65
66 bool track_flow = false;
67 track_flow = SCConfNodeLookupChild(root, "rate-tracking") != NULL ? true : false;
68 if (!track_flow)
69 return;
70
71 SCConfNode *node = SCConfGetNode("flow.rate-tracking");
72 const char *val = SCConfNodeLookupChildValue(node, "bytes");
73 if (val == NULL) {
74 FatalError("No value for flow tracking bytes");
75 }
76 uint64_t bytes = 0;
77 if (ParseSizeStringU64(val, &bytes) < 0) {
78 FatalError("Invalid value for flow tracking bytes");
79 }
80 flow_rate_config.bytes = bytes;
81
82 val = SCConfNodeLookupChildValue(node, "interval");
83 if (val == NULL) {
84 FatalError("No value for flow tracking interval");
85 }
87 uint16_t secs = 0;
88 if ((StringParseUint16(&secs, 10, 0, val) < 0) || (secs == 0)) {
89 FatalError("Invalid value for flow tracking interval");
90 }
92
94 FlowStorageRegister("flowrate", sizeof(void *), NULL, FlowRateStoreFree);
95}
96
98{
99 return (g_flowrate_storage_id.id != -1);
100}
101
103{
104 FlowRateStore *frs = NULL;
105 size_t total_memuse = 0;
106 size_t expected_memuse = (2 * flow_rate_config.interval.secs * sizeof(uint64_t)) + sizeof(*frs);
107
108 if (!FLOW_CHECK_MEMCAP(expected_memuse)) {
109 return NULL;
110 }
111 frs = SCCalloc(1, sizeof(*frs));
112 if (unlikely(frs == NULL)) {
113 return NULL;
114 }
115
116 total_memuse += sizeof(*frs);
117 for (int i = 0; i < 2; i++) {
118 frs->dir[i].size = (uint16_t)flow_rate_config.interval.secs;
119 frs->dir[i].buf = SCCalloc(frs->dir[i].size, sizeof(uint64_t));
120 if (unlikely(frs->dir[i].buf == NULL)) {
121 FlowRateStoreFree(frs);
122 return NULL;
123 }
126 total_memuse += (frs->dir[i].size * sizeof(uint64_t));
127 }
128 DEBUG_VALIDATE_BUG_ON(total_memuse != expected_memuse);
129 (void)SC_ATOMIC_ADD(flow_memuse, total_memuse);
130
131 return frs;
132}
133
138
139static inline void FlowRateClearSumInRange(
140 FlowRateStore *frs, uint16_t start, uint16_t end, int direction)
141{
142 for (uint16_t i = start; i <= end; i++) {
143 uint64_t byte_count_at_i = frs->dir[direction].buf[i];
144 frs->dir[direction].buf[i] = 0;
145 DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < byte_count_at_i);
146 frs->dir[direction].sum -= byte_count_at_i;
147 }
148}
149
150static inline void FlowRateStoreUpdateCurrentRing(
151 FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, uint16_t idx, int direction)
152{
153 if (idx > frs->dir[direction].last_idx + 1) {
154 /* Index is not the same as last or the next so, the ring must be flushed for the items
155 * in between and sum updated */
156 FlowRateClearSumInRange(frs, frs->dir[direction].last_idx + 1, idx, direction);
157 frs->dir[direction].buf[idx] += pkt_len;
158 /* Update the total sum */
159 frs->dir[direction].sum += pkt_len;
160 } else if (idx == frs->dir[direction].last_idx) {
161 /* Index matches the last updated index in the ring buffer */
162 /* Add to the existing open time interval */
163 frs->dir[direction].buf[idx] += pkt_len;
164 /* Update the total sum */
165 frs->dir[direction].sum += pkt_len;
166 } else {
167 /* Index is revisited after a full round of the buffer */
168 uint64_t prev_byte_count = frs->dir[direction].buf[idx];
169 /* Overwrite the buffer */
170 frs->dir[direction].buf[idx] = pkt_len;
171 DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < prev_byte_count);
172 /* Sum should get rid of previous count on the same index */
173 frs->dir[direction].sum += pkt_len - prev_byte_count;
174 if (idx != frs->dir[direction].last_idx + 1) {
175 /* Revisited index but not the next to last, so, reset start_ts */
176 frs->dir[direction].start_ts = p_ts;
177 }
178 }
179 frs->dir[direction].last_idx = idx;
180}
181
182static inline void FlowRateStoreFlushRing(
183 FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
184{
185 memset(frs->dir[direction].buf, 0, frs->dir[direction].size);
186 frs->dir[direction].last_idx = 0;
187 frs->dir[direction].start_ts = p_ts;
188 frs->dir[direction].buf[0] = pkt_len;
189 /* Overwrite the sum calculated so far */
190 frs->dir[direction].sum = pkt_len;
191}
192
193void FlowRateStoreUpdate(FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
194{
195 if (frs->dir[direction].last_ts.secs == 0) {
196 /* Should only happen when the ring is first used */
197 DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum > 0);
198 /* Initialize last_ts and start_ts with the first packet's timestamp */
199 frs->dir[direction].last_ts = p_ts;
200 frs->dir[direction].start_ts = p_ts;
201 }
202
203 SCTime_t start_ts = frs->dir[direction].start_ts;
204 uint16_t idx = (p_ts.secs - start_ts.secs) % frs->dir[direction].size;
205 /* Update start_ts in case of initiating the revisit of buffer */
206 if ((frs->dir[direction].last_idx == frs->dir[direction].size - 1) &&
207 (frs->dir[direction].last_idx != idx)) {
208 start_ts = p_ts;
209 if (idx != 0) {
210 /* Update the sum */
211 FlowRateClearSumInRange(frs, 0, idx, direction);
212 /* Consider current packet a new start of the ring */
213 idx = 0;
214 }
215 }
216 /* If the packet has come in the last open interval of time */
217 if (p_ts.secs - start_ts.secs < frs->dir[direction].size) {
218 FlowRateStoreUpdateCurrentRing(frs, p_ts, pkt_len, idx, direction);
219 } else {
220 /* Packet arrived after one or more rounds of the entire buffer */
221 /* Flush the entire buffer */
222 FlowRateStoreFlushRing(frs, p_ts, pkt_len, direction);
223 }
224 /* In any case, update the last seen timestamp */
225 frs->dir[direction].last_ts = p_ts;
226}
227
228bool FlowRateIsExceeding(FlowRateStore *frs, int direction)
229{
230 if (frs->dir[direction].sum >= flow_rate_config.bytes) {
231 return true;
232 }
233 return false;
234}
235
236#ifdef UNITTESTS
237
238/* Test to check update of the same buffer item */
239static int FlowRateTest01(void)
240{
241 SC_ATOMIC_SET(flow_config.memcap, 10000);
243 flow_rate_config.interval = (SCTime_t){ .secs = 10, .usecs = 0 };
245 FAIL_IF_NULL(frs);
246 for (int i = 0; i < 2; i++) {
247 FAIL_IF(frs->dir[i].size != 10);
248 FAIL_IF(frs->dir[i].sum != 0);
249 }
250 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
252 /* Total length of packet is 48 */
253 FAIL_IF(frs->dir[0].sum != 48);
254 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
255 FAIL_IF(frs->dir[0].buf[0] != 48);
256
257 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
259 /* Total length of packet is 44 */
260 FAIL_IF(frs->dir[0].sum != 92);
261 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
262 FAIL_IF(frs->dir[0].buf[0] != 92);
263
264 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
266 /* Total length of packet is 47 */
267 FAIL_IF(frs->dir[0].sum != 139);
268 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
269 FAIL_IF(frs->dir[0].buf[0] != 139);
270
271 FlowRateStoreFree(frs);
272 PASS;
273}
274
275/* Test to check update of all buffer items */
276static int FlowRateTest02(void)
277{
278 SC_ATOMIC_SET(flow_config.memcap, 10000);
280 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
282 FAIL_IF_NULL(frs);
283 for (int i = 0; i < 2; i++) {
284 FAIL_IF(frs->dir[i].size != 4);
285 FAIL_IF(frs->dir[i].sum != 0);
286 }
287 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
289 /* Total length of packet is 48 */
290 FAIL_IF(frs->dir[0].sum != 48);
291 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
292 FAIL_IF(frs->dir[0].buf[0] != 48);
293
294 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
295 p2->ts.secs = p1->ts.secs + 1;
297 /* Total length of packet is 44 */
298 FAIL_IF(frs->dir[0].sum != 92);
299 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
300 FAIL_IF(frs->dir[0].buf[1] != 44);
301
302 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
303 p3->ts.secs = p1->ts.secs + 2;
305 /* Total length of packet is 47 */
306 FAIL_IF(frs->dir[0].sum != 139);
307 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
308 FAIL_IF(frs->dir[0].buf[2] != 47);
309
310 Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
311 p4->ts.secs = p1->ts.secs + 3;
313 /* Total length of packet is 46 */
314 FAIL_IF(frs->dir[0].sum != 185);
315 FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
316 FAIL_IF(frs->dir[0].buf[3] != 46);
317
318 FlowRateStoreFree(frs);
319 PASS;
320}
321
322/* Test to check update of wrapping around ring buffer */
323static int FlowRateTest03(void)
324{
325 SC_ATOMIC_SET(flow_config.memcap, 10000);
327 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
329 FAIL_IF_NULL(frs);
330 for (int i = 0; i < 2; i++) {
331 FAIL_IF(frs->dir[i].size != 4);
332 FAIL_IF(frs->dir[i].sum != 0);
333 }
334 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
336 /* Total length of packet is 48 */
337 FAIL_IF(frs->dir[0].sum != 48);
338 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
339 FAIL_IF(frs->dir[0].buf[0] != 48);
340 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
341
342 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
343 p2->ts.secs = p1->ts.secs + 1;
345 /* Total length of packet is 44 */
346 FAIL_IF(frs->dir[0].sum != 92);
347 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
348 FAIL_IF(frs->dir[0].buf[1] != 44);
349 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
350
351 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
352 p3->ts.secs = p1->ts.secs + 2;
354 /* Total length of packet is 47 */
355 FAIL_IF(frs->dir[0].sum != 139);
356 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
357 FAIL_IF(frs->dir[0].buf[2] != 47);
358 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
359
360 Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
361 p4->ts.secs = p1->ts.secs + 3;
363 /* Total length of packet is 46 */
364 FAIL_IF(frs->dir[0].sum != 185);
365 FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
366 FAIL_IF(frs->dir[0].buf[3] != 46);
367 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
368
369 Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
370 p5->ts.secs = p1->ts.secs + 4;
372 /* Total length of packet is 43 */
373 FAIL_IF(frs->dir[0].sum != 180);
374 FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
375 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
376 FAIL_IF(frs->dir[0].buf[0] != 43);
377
378 Packet *p6 = UTHBuildPacket((uint8_t *)"meerkat", 7, IPPROTO_TCP);
379 p6->ts.secs = p1->ts.secs + 5;
381 /* Total length of packet is 47 */
382 FAIL_IF(frs->dir[0].sum != 183);
383 FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
384 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
385 FAIL_IF(frs->dir[0].buf[1] != 47);
386
387 FlowRateStoreFree(frs);
388 PASS;
389}
390
391/* Test to check update of buffer if new pkt comes out of the window */
392static int FlowRateTest04(void)
393{
394 SC_ATOMIC_SET(flow_config.memcap, 10000);
396 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
398 FAIL_IF_NULL(frs);
399 for (int i = 0; i < 2; i++) {
400 FAIL_IF(frs->dir[i].size != 4);
401 FAIL_IF(frs->dir[i].sum != 0);
402 }
403 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
405 /* Total length of packet is 48 */
406 FAIL_IF(frs->dir[0].sum != 48);
407 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
408 FAIL_IF(frs->dir[0].buf[0] != 48);
409 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
410
411 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
412 p2->ts.secs = p1->ts.secs + 60;
414 /* Total length of packet is 44 */
415 FAIL_IF(frs->dir[0].sum != 44);
416 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
417 FAIL_IF(frs->dir[0].buf[0] != 44);
418 FAIL_IF(frs->dir[0].start_ts.secs != p2->ts.secs);
419
420 FlowRateStoreFree(frs);
421 PASS;
422}
423
424/* Test to check update of wrapping around ring buffer when the packet
425 * out of the window but also does not fall on the first index of the ring */
426static int FlowRateTest05(void)
427{
428 SC_ATOMIC_SET(flow_config.memcap, 10000);
430 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
432 FAIL_IF_NULL(frs);
433 for (int i = 0; i < 2; i++) {
434 FAIL_IF(frs->dir[i].size != 4);
435 FAIL_IF(frs->dir[i].sum != 0);
436 }
437 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
439 /* Total length of packet is 48 */
440 FAIL_IF(frs->dir[0].sum != 48);
441 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
442 FAIL_IF(frs->dir[0].buf[0] != 48);
443 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
444
445 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
446 p2->ts.secs = p1->ts.secs + 1;
448 /* Total length of packet is 44 */
449 FAIL_IF(frs->dir[0].sum != 92);
450 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
451 FAIL_IF(frs->dir[0].buf[1] != 44);
452 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
453
454 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
455 p3->ts.secs = p1->ts.secs + 2;
457 /* Total length of packet is 47 */
458 FAIL_IF(frs->dir[0].sum != 139);
459 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
460 FAIL_IF(frs->dir[0].buf[2] != 47);
461 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
462
463 Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
464 p4->ts.secs = p1->ts.secs + 3;
466 /* Total length of packet is 46 */
467 FAIL_IF(frs->dir[0].sum != 185);
468 FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
469 FAIL_IF(frs->dir[0].buf[3] != 46);
470 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
471
472 Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
473 p5->ts.secs = p1->ts.secs + 6;
475 /* Total length of packet is 43 */
476 FAIL_IF(frs->dir[0].sum != 89);
477 FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
478 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
479 FAIL_IF(frs->dir[0].buf[0] != 43);
480
481 FlowRateStoreFree(frs);
482 PASS;
483}
484
485/* Test to check sum when packet is within the window but is coming after a gap */
486static int FlowRateTest06(void)
487{
488 SC_ATOMIC_SET(flow_config.memcap, 10000);
490 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
492 FAIL_IF_NULL(frs);
493 for (int i = 0; i < 2; i++) {
494 FAIL_IF(frs->dir[i].size != 4);
495 FAIL_IF(frs->dir[i].sum != 0);
496 }
497 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
499 /* Total length of packet is 48 */
500 FAIL_IF(frs->dir[0].sum != 48);
501 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
502 FAIL_IF(frs->dir[0].buf[0] != 48);
503 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
504
505 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
506 p2->ts.secs = p1->ts.secs + 1;
508 /* Total length of packet is 44 */
509 FAIL_IF(frs->dir[0].sum != 92);
510 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
511 FAIL_IF(frs->dir[0].buf[1] != 44);
512 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
513
514 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
515 p3->ts.secs = p1->ts.secs + 2;
517 /* Total length of packet is 47 */
518 FAIL_IF(frs->dir[0].sum != 139);
519 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
520 FAIL_IF(frs->dir[0].buf[2] != 47);
521 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
522
523 Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
524 p4->ts.secs = p1->ts.secs + 3;
526 /* Total length of packet is 46 */
527 FAIL_IF(frs->dir[0].sum != 185);
528 FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
529 FAIL_IF(frs->dir[0].buf[3] != 46);
530 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
531
532 Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
533 p5->ts.secs = p1->ts.secs + 4;
535 /* Total length of packet is 43 */
536 FAIL_IF(frs->dir[0].sum != 180);
537 FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
538 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
539 FAIL_IF(frs->dir[0].buf[0] != 43);
540
541 Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
542 p6->ts.secs = p1->ts.secs + 7;
544 /* Total length of packet is 48 */
545 FAIL_IF(frs->dir[0].sum != 91);
546 FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
547 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
548 FAIL_IF(frs->dir[0].buf[0] != 43);
549 FAIL_IF(frs->dir[0].buf[1] != 0);
550 FAIL_IF(frs->dir[0].buf[2] != 0);
551 FAIL_IF(frs->dir[0].buf[3] != 48);
552
553 FlowRateStoreFree(frs);
554 PASS;
555}
556
557/* Test to check sum when two packets are back to back within the window but are coming after a gap
558 */
559static int FlowRateTest07(void)
560{
561 SC_ATOMIC_SET(flow_config.memcap, 10000);
563 flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
565 FAIL_IF_NULL(frs);
566 for (int i = 0; i < 2; i++) {
567 FAIL_IF(frs->dir[i].size != 4);
568 FAIL_IF(frs->dir[i].sum != 0);
569 }
570 Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
572 /* Total length of packet is 48 */
573 FAIL_IF(frs->dir[0].sum != 48);
574 FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
575 FAIL_IF(frs->dir[0].buf[0] != 48);
576 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
577
578 Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
579 p2->ts.secs = p1->ts.secs + 1;
581 /* Total length of packet is 44 */
582 FAIL_IF(frs->dir[0].sum != 92);
583 FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
584 FAIL_IF(frs->dir[0].buf[1] != 44);
585 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
586
587 Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
588 p3->ts.secs = p1->ts.secs + 2;
590 /* Total length of packet is 47 */
591 FAIL_IF(frs->dir[0].sum != 139);
592 FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
593 FAIL_IF(frs->dir[0].buf[2] != 47);
594 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
595
596 Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
597 p4->ts.secs = p1->ts.secs + 3;
599 /* Total length of packet is 46 */
600 FAIL_IF(frs->dir[0].sum != 185);
601 FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
602 FAIL_IF(frs->dir[0].buf[3] != 46);
603 FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
604
605 Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
606 p5->ts.secs = p1->ts.secs + 5;
608 /* Total length of packet is 43 */
609 FAIL_IF(frs->dir[0].sum != 136);
610 FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
611 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
612 FAIL_IF(frs->dir[0].buf[0] != 43);
613
614 Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
615 p6->ts.secs = p1->ts.secs + 8;
617 /* Total length of packet is 48 */
618 FAIL_IF(frs->dir[0].sum != 91);
619 FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
620 FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
621 FAIL_IF(frs->dir[0].buf[0] != 43);
622 FAIL_IF(frs->dir[0].buf[1] != 0);
623 FAIL_IF(frs->dir[0].buf[2] != 0);
624 FAIL_IF(frs->dir[0].buf[3] != 48);
625
626 FlowRateStoreFree(frs);
627 PASS;
628}
629
631{
632 UtRegisterTest("FlowRateTest01", FlowRateTest01);
633 UtRegisterTest("FlowRateTest02", FlowRateTest02);
634 UtRegisterTest("FlowRateTest03", FlowRateTest03);
635 UtRegisterTest("FlowRateTest04", FlowRateTest04);
636 UtRegisterTest("FlowRateTest05", FlowRateTest05);
637 UtRegisterTest("FlowRateTest06", FlowRateTest06);
638 UtRegisterTest("FlowRateTest07", FlowRateTest07);
639}
640#endif
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition conf.c:796
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
#define GET_PKT_LEN(p)
Definition decode.h:208
FlowConfig flow_config
Definition flow.c:93
FlowStorageId FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
#define FLOW_CHECK_MEMCAP(size)
check if a memory alloc would fit in the memcap
Definition flow-util.h:134
#define TOSERVER
Definition flow.h:45
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
FlowRateDirStore dir[2]
SCTime_t ts
Definition decode.h:555
uint64_t secs
Definition util-time.h:41
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition util-byte.c:337
#define FatalError(...)
Definition util-debug.h:510
FlowStorageId FlowRateGetStorageID(void)
FlowStorageId g_flowrate_storage_id
bool FlowRateIsExceeding(FlowRateStore *frs, int direction)
void FlowRateRegisterTests(void)
FlowRateStore * FlowRateStoreInit(void)
bool FlowRateStorageEnabled(void)
FlowRateConfig flow_rate_config
void FlowRateRegisterFlowStorage(void)
void FlowRateStoreUpdate(FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition util-misc.c:190
#define unlikely(expr)
#define SCTIME_INITIALIZER
Definition util-time.h:51
#define SCTIME_ADD_SECS(ts, s)
Definition util-time.h:64
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
#define DEBUG_VALIDATE_BUG_ON(exp)