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"
35 #include "util-unittest-helper.h"
36 
38 
40 
41 static 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  }
86  SCTime_t interval = SCTIME_INITIALIZER;
87  uint16_t secs = 0;
88  if ((StringParseUint16(&secs, 10, 0, val) < 0) || (secs == 0)) {
89  FatalError("Invalid value for flow tracking interval");
90  }
91  flow_rate_config.interval = SCTIME_ADD_SECS(interval, secs);
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  }
124  frs->dir[i].start_ts = SCTIME_INITIALIZER;
125  frs->dir[i].last_ts = SCTIME_INITIALIZER;
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 
135 {
136  return g_flowrate_storage_id;
137 }
138 
139 static 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 
150 static 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 
182 static 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 
193 void 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 
228 bool 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 */
239 static int FlowRateTest01(void)
240 {
241  SC_ATOMIC_SET(flow_config.memcap, 10000);
242  flow_rate_config.bytes = 100;
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);
251  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
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);
258  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
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);
265  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
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  UTHFreePacket(p1);
272  UTHFreePacket(p2);
273  UTHFreePacket(p3);
274  FlowRateStoreFree(frs);
275  PASS;
276 }
277 
278 /* Test to check update of all buffer items */
279 static int FlowRateTest02(void)
280 {
281  SC_ATOMIC_SET(flow_config.memcap, 10000);
282  flow_rate_config.bytes = 200;
283  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
285  FAIL_IF_NULL(frs);
286  for (int i = 0; i < 2; i++) {
287  FAIL_IF(frs->dir[i].size != 4);
288  FAIL_IF(frs->dir[i].sum != 0);
289  }
290  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
291  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
292  /* Total length of packet is 48 */
293  FAIL_IF(frs->dir[0].sum != 48);
294  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
295  FAIL_IF(frs->dir[0].buf[0] != 48);
296 
297  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
298  p2->ts.secs = p1->ts.secs + 1;
299  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
300  /* Total length of packet is 44 */
301  FAIL_IF(frs->dir[0].sum != 92);
302  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
303  FAIL_IF(frs->dir[0].buf[1] != 44);
304 
305  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
306  p3->ts.secs = p1->ts.secs + 2;
307  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
308  /* Total length of packet is 47 */
309  FAIL_IF(frs->dir[0].sum != 139);
310  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
311  FAIL_IF(frs->dir[0].buf[2] != 47);
312 
313  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
314  p4->ts.secs = p1->ts.secs + 3;
315  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
316  /* Total length of packet is 46 */
317  FAIL_IF(frs->dir[0].sum != 185);
318  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
319  FAIL_IF(frs->dir[0].buf[3] != 46);
320 
321  UTHFreePacket(p1);
322  UTHFreePacket(p2);
323  UTHFreePacket(p3);
324  UTHFreePacket(p4);
325  FlowRateStoreFree(frs);
326  PASS;
327 }
328 
329 /* Test to check update of wrapping around ring buffer */
330 static int FlowRateTest03(void)
331 {
332  SC_ATOMIC_SET(flow_config.memcap, 10000);
333  flow_rate_config.bytes = 200;
334  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
336  FAIL_IF_NULL(frs);
337  for (int i = 0; i < 2; i++) {
338  FAIL_IF(frs->dir[i].size != 4);
339  FAIL_IF(frs->dir[i].sum != 0);
340  }
341  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
342  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
343  /* Total length of packet is 48 */
344  FAIL_IF(frs->dir[0].sum != 48);
345  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
346  FAIL_IF(frs->dir[0].buf[0] != 48);
347  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
348 
349  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
350  p2->ts.secs = p1->ts.secs + 1;
351  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
352  /* Total length of packet is 44 */
353  FAIL_IF(frs->dir[0].sum != 92);
354  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
355  FAIL_IF(frs->dir[0].buf[1] != 44);
356  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
357 
358  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
359  p3->ts.secs = p1->ts.secs + 2;
360  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
361  /* Total length of packet is 47 */
362  FAIL_IF(frs->dir[0].sum != 139);
363  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
364  FAIL_IF(frs->dir[0].buf[2] != 47);
365  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
366 
367  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
368  p4->ts.secs = p1->ts.secs + 3;
369  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
370  /* Total length of packet is 46 */
371  FAIL_IF(frs->dir[0].sum != 185);
372  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
373  FAIL_IF(frs->dir[0].buf[3] != 46);
374  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
375 
376  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
377  p5->ts.secs = p1->ts.secs + 4;
378  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
379  /* Total length of packet is 43 */
380  FAIL_IF(frs->dir[0].sum != 180);
381  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
382  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
383  FAIL_IF(frs->dir[0].buf[0] != 43);
384 
385  Packet *p6 = UTHBuildPacket((uint8_t *)"meerkat", 7, IPPROTO_TCP);
386  p6->ts.secs = p1->ts.secs + 5;
387  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
388  /* Total length of packet is 47 */
389  FAIL_IF(frs->dir[0].sum != 183);
390  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
391  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
392  FAIL_IF(frs->dir[0].buf[1] != 47);
393 
394  UTHFreePacket(p1);
395  UTHFreePacket(p2);
396  UTHFreePacket(p3);
397  UTHFreePacket(p4);
398  UTHFreePacket(p5);
399  UTHFreePacket(p6);
400  FlowRateStoreFree(frs);
401  PASS;
402 }
403 
404 /* Test to check update of buffer if new pkt comes out of the window */
405 static int FlowRateTest04(void)
406 {
407  SC_ATOMIC_SET(flow_config.memcap, 10000);
408  flow_rate_config.bytes = 200;
409  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
411  FAIL_IF_NULL(frs);
412  for (int i = 0; i < 2; i++) {
413  FAIL_IF(frs->dir[i].size != 4);
414  FAIL_IF(frs->dir[i].sum != 0);
415  }
416  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
417  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
418  /* Total length of packet is 48 */
419  FAIL_IF(frs->dir[0].sum != 48);
420  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
421  FAIL_IF(frs->dir[0].buf[0] != 48);
422  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
423 
424  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
425  p2->ts.secs = p1->ts.secs + 60;
426  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
427  /* Total length of packet is 44 */
428  FAIL_IF(frs->dir[0].sum != 44);
429  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
430  FAIL_IF(frs->dir[0].buf[0] != 44);
431  FAIL_IF(frs->dir[0].start_ts.secs != p2->ts.secs);
432 
433  UTHFreePacket(p1);
434  UTHFreePacket(p2);
435  FlowRateStoreFree(frs);
436  PASS;
437 }
438 
439 /* Test to check update of wrapping around ring buffer when the packet
440  * out of the window but also does not fall on the first index of the ring */
441 static int FlowRateTest05(void)
442 {
443  SC_ATOMIC_SET(flow_config.memcap, 10000);
444  flow_rate_config.bytes = 200;
445  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
447  FAIL_IF_NULL(frs);
448  for (int i = 0; i < 2; i++) {
449  FAIL_IF(frs->dir[i].size != 4);
450  FAIL_IF(frs->dir[i].sum != 0);
451  }
452  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
453  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
454  /* Total length of packet is 48 */
455  FAIL_IF(frs->dir[0].sum != 48);
456  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
457  FAIL_IF(frs->dir[0].buf[0] != 48);
458  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
459 
460  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
461  p2->ts.secs = p1->ts.secs + 1;
462  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
463  /* Total length of packet is 44 */
464  FAIL_IF(frs->dir[0].sum != 92);
465  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
466  FAIL_IF(frs->dir[0].buf[1] != 44);
467  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
468 
469  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
470  p3->ts.secs = p1->ts.secs + 2;
471  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
472  /* Total length of packet is 47 */
473  FAIL_IF(frs->dir[0].sum != 139);
474  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
475  FAIL_IF(frs->dir[0].buf[2] != 47);
476  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
477 
478  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
479  p4->ts.secs = p1->ts.secs + 3;
480  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
481  /* Total length of packet is 46 */
482  FAIL_IF(frs->dir[0].sum != 185);
483  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
484  FAIL_IF(frs->dir[0].buf[3] != 46);
485  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
486 
487  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
488  p5->ts.secs = p1->ts.secs + 6;
489  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
490  /* Total length of packet is 43 */
491  FAIL_IF(frs->dir[0].sum != 89);
492  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
493  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
494  FAIL_IF(frs->dir[0].buf[0] != 43);
495 
496  UTHFreePacket(p1);
497  UTHFreePacket(p2);
498  UTHFreePacket(p3);
499  UTHFreePacket(p4);
500  UTHFreePacket(p5);
501  FlowRateStoreFree(frs);
502  PASS;
503 }
504 
505 /* Test to check sum when packet is within the window but is coming after a gap */
506 static int FlowRateTest06(void)
507 {
508  SC_ATOMIC_SET(flow_config.memcap, 10000);
509  flow_rate_config.bytes = 200;
510  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
512  FAIL_IF_NULL(frs);
513  for (int i = 0; i < 2; i++) {
514  FAIL_IF(frs->dir[i].size != 4);
515  FAIL_IF(frs->dir[i].sum != 0);
516  }
517  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
518  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
519  /* Total length of packet is 48 */
520  FAIL_IF(frs->dir[0].sum != 48);
521  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
522  FAIL_IF(frs->dir[0].buf[0] != 48);
523  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
524 
525  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
526  p2->ts.secs = p1->ts.secs + 1;
527  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
528  /* Total length of packet is 44 */
529  FAIL_IF(frs->dir[0].sum != 92);
530  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
531  FAIL_IF(frs->dir[0].buf[1] != 44);
532  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
533 
534  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
535  p3->ts.secs = p1->ts.secs + 2;
536  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
537  /* Total length of packet is 47 */
538  FAIL_IF(frs->dir[0].sum != 139);
539  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
540  FAIL_IF(frs->dir[0].buf[2] != 47);
541  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
542 
543  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
544  p4->ts.secs = p1->ts.secs + 3;
545  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
546  /* Total length of packet is 46 */
547  FAIL_IF(frs->dir[0].sum != 185);
548  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
549  FAIL_IF(frs->dir[0].buf[3] != 46);
550  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
551 
552  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
553  p5->ts.secs = p1->ts.secs + 4;
554  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
555  /* Total length of packet is 43 */
556  FAIL_IF(frs->dir[0].sum != 180);
557  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
558  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
559  FAIL_IF(frs->dir[0].buf[0] != 43);
560 
561  Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
562  p6->ts.secs = p1->ts.secs + 7;
563  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
564  /* Total length of packet is 48 */
565  FAIL_IF(frs->dir[0].sum != 91);
566  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
567  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
568  FAIL_IF(frs->dir[0].buf[0] != 43);
569  FAIL_IF(frs->dir[0].buf[1] != 0);
570  FAIL_IF(frs->dir[0].buf[2] != 0);
571  FAIL_IF(frs->dir[0].buf[3] != 48);
572 
573  UTHFreePacket(p1);
574  UTHFreePacket(p2);
575  UTHFreePacket(p3);
576  UTHFreePacket(p4);
577  UTHFreePacket(p5);
578  UTHFreePacket(p6);
579  FlowRateStoreFree(frs);
580  PASS;
581 }
582 
583 /* Test to check sum when two packets are back to back within the window but are coming after a gap
584  */
585 static int FlowRateTest07(void)
586 {
587  SC_ATOMIC_SET(flow_config.memcap, 10000);
588  flow_rate_config.bytes = 200;
589  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
591  FAIL_IF_NULL(frs);
592  for (int i = 0; i < 2; i++) {
593  FAIL_IF(frs->dir[i].size != 4);
594  FAIL_IF(frs->dir[i].sum != 0);
595  }
596  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
597  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
598  /* Total length of packet is 48 */
599  FAIL_IF(frs->dir[0].sum != 48);
600  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
601  FAIL_IF(frs->dir[0].buf[0] != 48);
602  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
603 
604  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
605  p2->ts.secs = p1->ts.secs + 1;
606  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
607  /* Total length of packet is 44 */
608  FAIL_IF(frs->dir[0].sum != 92);
609  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
610  FAIL_IF(frs->dir[0].buf[1] != 44);
611  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
612 
613  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
614  p3->ts.secs = p1->ts.secs + 2;
615  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
616  /* Total length of packet is 47 */
617  FAIL_IF(frs->dir[0].sum != 139);
618  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
619  FAIL_IF(frs->dir[0].buf[2] != 47);
620  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
621 
622  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
623  p4->ts.secs = p1->ts.secs + 3;
624  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
625  /* Total length of packet is 46 */
626  FAIL_IF(frs->dir[0].sum != 185);
627  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
628  FAIL_IF(frs->dir[0].buf[3] != 46);
629  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
630 
631  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
632  p5->ts.secs = p1->ts.secs + 5;
633  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
634  /* Total length of packet is 43 */
635  FAIL_IF(frs->dir[0].sum != 136);
636  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
637  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
638  FAIL_IF(frs->dir[0].buf[0] != 43);
639 
640  Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
641  p6->ts.secs = p1->ts.secs + 8;
642  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
643  /* Total length of packet is 48 */
644  FAIL_IF(frs->dir[0].sum != 91);
645  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
646  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
647  FAIL_IF(frs->dir[0].buf[0] != 43);
648  FAIL_IF(frs->dir[0].buf[1] != 0);
649  FAIL_IF(frs->dir[0].buf[2] != 0);
650  FAIL_IF(frs->dir[0].buf[3] != 48);
651 
652  UTHFreePacket(p1);
653  UTHFreePacket(p2);
654  UTHFreePacket(p3);
655  UTHFreePacket(p4);
656  UTHFreePacket(p5);
657  UTHFreePacket(p6);
658  FlowRateStoreFree(frs);
659  PASS;
660 }
661 
663 {
664  UtRegisterTest("FlowRateTest01", FlowRateTest01);
665  UtRegisterTest("FlowRateTest02", FlowRateTest02);
666  UtRegisterTest("FlowRateTest03", FlowRateTest03);
667  UtRegisterTest("FlowRateTest04", FlowRateTest04);
668  UtRegisterTest("FlowRateTest05", FlowRateTest05);
669  UtRegisterTest("FlowRateTest06", FlowRateTest06);
670  UtRegisterTest("FlowRateTest07", FlowRateTest07);
671 }
672 #endif
FlowStorageId
Definition: flow-storage.h:31
g_flowrate_storage_id
FlowStorageId g_flowrate_storage_id
Definition: util-flow-rate.c:37
util-byte.h
FlowRateDirStore_::sum
uint64_t sum
Definition: util-flow-rate.h:36
FlowRateRegisterTests
void FlowRateRegisterTests(void)
Definition: util-flow-rate.c:662
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
flow-util.h
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
FlowRateDirStore_::start_ts
SCTime_t start_ts
Definition: util-flow-rate.h:42
FlowRateConfig_::interval
SCTime_t interval
Definition: util-flow-rate.h:29
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:191
flow-private.h
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
StringParseUint16
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:343
FlowRateGetStorageID
FlowStorageId FlowRateGetStorageID(void)
Definition: util-flow-rate.c:134
UTHBuildPacket
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.
Definition: util-unittest-helper.c:365
SCConfNodeLookupChildValue
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:824
util-unittest.h
util-unittest-helper.h
FLOW_CHECK_MEMCAP
#define FLOW_CHECK_MEMCAP(size)
check if a memory alloc would fit in the memcap
Definition: flow-util.h:134
FlowRateStore_::dir
FlowRateDirStore dir[2]
Definition: util-flow-rate.h:48
FlowRateStoreUpdate
void FlowRateStoreUpdate(FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
Definition: util-flow-rate.c:193
util-flow-rate.h
TOSERVER
#define TOSERVER
Definition: flow.h:45
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
File_::end
uint64_t end
Definition: util-file.h:134
Packet_::ts
SCTime_t ts
Definition: decode.h:555
SCTime_t::secs
uint64_t secs
Definition: util-time.h:41
FlowRateRegisterFlowStorage
void FlowRateRegisterFlowStorage(void)
Definition: util-flow-rate.c:60
FlowStorageRegister
FlowStorageId FlowStorageRegister(const char *name, const unsigned int size, void *(*Alloc)(unsigned int), void(*Free)(void *))
Definition: flow-storage.c:66
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
Packet_
Definition: decode.h:501
GET_PKT_LEN
#define GET_PKT_LEN(p)
Definition: decode.h:208
flow_rate_config
FlowRateConfig flow_rate_config
Definition: util-flow-rate.c:39
conf.h
SCTime_t
Definition: util-time.h:40
SCConfNodeLookupChild
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:796
flow-storage.h
FlowRateDirStore_::last_idx
uint16_t last_idx
Definition: util-flow-rate.h:38
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
suricata-common.h
FlowRateStoreInit
FlowRateStore * FlowRateStoreInit(void)
Definition: util-flow-rate.c:102
flow_config
FlowConfig flow_config
Definition: flow.c:94
FlowRateIsExceeding
bool FlowRateIsExceeding(FlowRateStore *frs, int direction)
Definition: util-flow-rate.c:228
FatalError
#define FatalError(...)
Definition: util-debug.h:514
File_::start
uint64_t start
Definition: util-file.h:133
FlowRateConfig_::bytes
uint64_t bytes
Definition: util-flow-rate.h:28
SCConfGetNode
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition: conf.c:181
SCFree
#define SCFree(p)
Definition: util-mem.h:61
UTHFreePacket
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.
Definition: util-unittest-helper.c:473
FlowStorageId::id
int id
Definition: flow-storage.h:32
FlowRateDirStore_::buf
uint64_t * buf
Definition: util-flow-rate.h:34
FlowRateStorageEnabled
bool FlowRateStorageEnabled(void)
Definition: util-flow-rate.c:97
util-misc.h
SCTIME_INITIALIZER
#define SCTIME_INITIALIZER
Definition: util-time.h:51
SCTIME_ADD_SECS
#define SCTIME_ADD_SECS(ts, s)
Definition: util-time.h:64
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
FlowRateDirStore_::size
uint16_t size
Definition: util-flow-rate.h:40
SCConfNode_
Definition: conf.h:37
FlowRateDirStore_::last_ts
SCTime_t last_ts
Definition: util-flow-rate.h:44
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
FlowRateStore_
Definition: util-flow-rate.h:47
util-storage.h
FlowRateConfig_
Definition: util-flow-rate.h:27