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;
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 
93  g_flowrate_storage_id = SCFlowStorageRegister("flowrate", FlowRateStoreFree);
94 }
95 
97 {
98  return (g_flowrate_storage_id.id != -1);
99 }
100 
102 {
103  FlowRateStore *frs = NULL;
104  size_t total_memuse = 0;
105  size_t expected_memuse = (2 * flow_rate_config.interval.secs * sizeof(uint64_t)) + sizeof(*frs);
106 
107  if (!FLOW_CHECK_MEMCAP(expected_memuse)) {
108  return NULL;
109  }
110  frs = SCCalloc(1, sizeof(*frs));
111  if (unlikely(frs == NULL)) {
112  return NULL;
113  }
114 
115  total_memuse += sizeof(*frs);
116  for (int i = 0; i < 2; i++) {
117  frs->dir[i].size = (uint16_t)flow_rate_config.interval.secs;
118  frs->dir[i].buf = SCCalloc(frs->dir[i].size, sizeof(uint64_t));
119  if (unlikely(frs->dir[i].buf == NULL)) {
120  FlowRateStoreFree(frs);
121  return NULL;
122  }
123  frs->dir[i].start_ts = SCTIME_INITIALIZER;
124  frs->dir[i].last_ts = SCTIME_INITIALIZER;
125  total_memuse += (frs->dir[i].size * sizeof(uint64_t));
126  }
127  DEBUG_VALIDATE_BUG_ON(total_memuse != expected_memuse);
128  (void)SC_ATOMIC_ADD(flow_memuse, total_memuse);
129 
130  return frs;
131 }
132 
134 {
135  return g_flowrate_storage_id;
136 }
137 
138 static inline void FlowRateClearSumInRange(
139  FlowRateStore *frs, uint16_t start, uint16_t end, int direction)
140 {
141  for (uint16_t i = start; i <= end; i++) {
142  uint64_t byte_count_at_i = frs->dir[direction].buf[i];
143  frs->dir[direction].buf[i] = 0;
144  DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < byte_count_at_i);
145  frs->dir[direction].sum -= byte_count_at_i;
146  }
147 }
148 
149 static inline void FlowRateStoreUpdateCurrentRing(
150  FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, uint16_t idx, int direction)
151 {
152  if (idx > frs->dir[direction].last_idx + 1) {
153  /* Index is not the same as last or the next so, the ring must be flushed for the items
154  * in between and sum updated */
155  FlowRateClearSumInRange(frs, frs->dir[direction].last_idx + 1, idx, direction);
156  frs->dir[direction].buf[idx] += pkt_len;
157  /* Update the total sum */
158  frs->dir[direction].sum += pkt_len;
159  } else if (idx == frs->dir[direction].last_idx) {
160  /* Index matches the last updated index in the ring buffer */
161  /* Add to the existing open time interval */
162  frs->dir[direction].buf[idx] += pkt_len;
163  /* Update the total sum */
164  frs->dir[direction].sum += pkt_len;
165  } else {
166  /* Index is revisited after a full round of the buffer */
167  uint64_t prev_byte_count = frs->dir[direction].buf[idx];
168  /* Overwrite the buffer */
169  frs->dir[direction].buf[idx] = pkt_len;
170  DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < prev_byte_count);
171  /* Sum should get rid of previous count on the same index */
172  frs->dir[direction].sum += pkt_len - prev_byte_count;
173  if (idx != frs->dir[direction].last_idx + 1) {
174  /* Revisited index but not the next to last, so, reset start_ts */
175  frs->dir[direction].start_ts = p_ts;
176  }
177  }
178  frs->dir[direction].last_idx = idx;
179 }
180 
181 static inline void FlowRateStoreFlushRing(
182  FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
183 {
184  memset(frs->dir[direction].buf, 0, frs->dir[direction].size * sizeof(*frs->dir[direction].buf));
185  frs->dir[direction].last_idx = 0;
186  frs->dir[direction].start_ts = p_ts;
187  frs->dir[direction].buf[0] = pkt_len;
188  /* Overwrite the sum calculated so far */
189  frs->dir[direction].sum = pkt_len;
190 }
191 
192 void FlowRateStoreUpdate(FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction)
193 {
194  if (frs->dir[direction].last_ts.secs == 0) {
195  /* Should only happen when the ring is first used */
196  DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum > 0);
197  /* Initialize last_ts and start_ts with the first packet's timestamp */
198  frs->dir[direction].last_ts = p_ts;
199  frs->dir[direction].start_ts = p_ts;
200  }
201 
202  SCTime_t start_ts = frs->dir[direction].start_ts;
203  uint16_t idx = (p_ts.secs - start_ts.secs) % frs->dir[direction].size;
204  /* Update start_ts in case of initiating the revisit of buffer */
205  if ((frs->dir[direction].last_idx == frs->dir[direction].size - 1) &&
206  (frs->dir[direction].last_idx != idx)) {
207  start_ts = p_ts;
208  if (idx != 0) {
209  /* Update the sum */
210  FlowRateClearSumInRange(frs, 0, idx, direction);
211  /* Consider current packet a new start of the ring */
212  idx = 0;
213  }
214  }
215  /* If the packet has come in the last open interval of time */
216  if (p_ts.secs - start_ts.secs < frs->dir[direction].size) {
217  FlowRateStoreUpdateCurrentRing(frs, p_ts, pkt_len, idx, direction);
218  } else {
219  /* Packet arrived after one or more rounds of the entire buffer */
220  /* Flush the entire buffer */
221  FlowRateStoreFlushRing(frs, p_ts, pkt_len, direction);
222  }
223  /* In any case, update the last seen timestamp */
224  frs->dir[direction].last_ts = p_ts;
225 }
226 
227 bool FlowRateIsExceeding(FlowRateStore *frs, int direction)
228 {
229  return frs->dir[direction].sum >= flow_rate_config.bytes;
230 }
231 
232 #ifdef UNITTESTS
233 
234 /* Test to check update of the same buffer item */
235 static int FlowRateTest01(void)
236 {
237  SC_ATOMIC_SET(flow_config.memcap, 10000);
238  flow_rate_config.bytes = 100;
239  flow_rate_config.interval = (SCTime_t){ .secs = 10, .usecs = 0 };
241  FAIL_IF_NULL(frs);
242  for (int i = 0; i < 2; i++) {
243  FAIL_IF(frs->dir[i].size != 10);
244  FAIL_IF(frs->dir[i].sum != 0);
245  }
246  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
247  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
248  /* Total length of packet is 48 */
249  FAIL_IF(frs->dir[0].sum != 48);
250  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
251  FAIL_IF(frs->dir[0].buf[0] != 48);
252 
253  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
254  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
255  /* Total length of packet is 44 */
256  FAIL_IF(frs->dir[0].sum != 92);
257  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
258  FAIL_IF(frs->dir[0].buf[0] != 92);
259 
260  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
261  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
262  /* Total length of packet is 47 */
263  FAIL_IF(frs->dir[0].sum != 139);
264  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
265  FAIL_IF(frs->dir[0].buf[0] != 139);
266 
267  UTHFreePacket(p1);
268  UTHFreePacket(p2);
269  UTHFreePacket(p3);
270  FlowRateStoreFree(frs);
271  PASS;
272 }
273 
274 /* Test to check update of all buffer items */
275 static int FlowRateTest02(void)
276 {
277  SC_ATOMIC_SET(flow_config.memcap, 10000);
278  flow_rate_config.bytes = 200;
279  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
281  FAIL_IF_NULL(frs);
282  for (int i = 0; i < 2; i++) {
283  FAIL_IF(frs->dir[i].size != 4);
284  FAIL_IF(frs->dir[i].sum != 0);
285  }
286  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
287  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
288  /* Total length of packet is 48 */
289  FAIL_IF(frs->dir[0].sum != 48);
290  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
291  FAIL_IF(frs->dir[0].buf[0] != 48);
292 
293  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
294  p2->ts.secs = p1->ts.secs + 1;
295  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
296  /* Total length of packet is 44 */
297  FAIL_IF(frs->dir[0].sum != 92);
298  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
299  FAIL_IF(frs->dir[0].buf[1] != 44);
300 
301  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
302  p3->ts.secs = p1->ts.secs + 2;
303  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
304  /* Total length of packet is 47 */
305  FAIL_IF(frs->dir[0].sum != 139);
306  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
307  FAIL_IF(frs->dir[0].buf[2] != 47);
308 
309  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
310  p4->ts.secs = p1->ts.secs + 3;
311  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
312  /* Total length of packet is 46 */
313  FAIL_IF(frs->dir[0].sum != 185);
314  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
315  FAIL_IF(frs->dir[0].buf[3] != 46);
316 
317  UTHFreePacket(p1);
318  UTHFreePacket(p2);
319  UTHFreePacket(p3);
320  UTHFreePacket(p4);
321  FlowRateStoreFree(frs);
322  PASS;
323 }
324 
325 /* Test to check update of wrapping around ring buffer */
326 static int FlowRateTest03(void)
327 {
328  SC_ATOMIC_SET(flow_config.memcap, 10000);
329  flow_rate_config.bytes = 200;
330  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
332  FAIL_IF_NULL(frs);
333  for (int i = 0; i < 2; i++) {
334  FAIL_IF(frs->dir[i].size != 4);
335  FAIL_IF(frs->dir[i].sum != 0);
336  }
337  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
338  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
339  /* Total length of packet is 48 */
340  FAIL_IF(frs->dir[0].sum != 48);
341  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
342  FAIL_IF(frs->dir[0].buf[0] != 48);
343  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
344 
345  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
346  p2->ts.secs = p1->ts.secs + 1;
347  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
348  /* Total length of packet is 44 */
349  FAIL_IF(frs->dir[0].sum != 92);
350  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
351  FAIL_IF(frs->dir[0].buf[1] != 44);
352  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
353 
354  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
355  p3->ts.secs = p1->ts.secs + 2;
356  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
357  /* Total length of packet is 47 */
358  FAIL_IF(frs->dir[0].sum != 139);
359  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
360  FAIL_IF(frs->dir[0].buf[2] != 47);
361  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
362 
363  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
364  p4->ts.secs = p1->ts.secs + 3;
365  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
366  /* Total length of packet is 46 */
367  FAIL_IF(frs->dir[0].sum != 185);
368  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
369  FAIL_IF(frs->dir[0].buf[3] != 46);
370  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
371 
372  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
373  p5->ts.secs = p1->ts.secs + 4;
374  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
375  /* Total length of packet is 43 */
376  FAIL_IF(frs->dir[0].sum != 180);
377  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
378  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
379  FAIL_IF(frs->dir[0].buf[0] != 43);
380 
381  Packet *p6 = UTHBuildPacket((uint8_t *)"meerkat", 7, IPPROTO_TCP);
382  p6->ts.secs = p1->ts.secs + 5;
383  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
384  /* Total length of packet is 47 */
385  FAIL_IF(frs->dir[0].sum != 183);
386  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
387  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
388  FAIL_IF(frs->dir[0].buf[1] != 47);
389 
390  UTHFreePacket(p1);
391  UTHFreePacket(p2);
392  UTHFreePacket(p3);
393  UTHFreePacket(p4);
394  UTHFreePacket(p5);
395  UTHFreePacket(p6);
396  FlowRateStoreFree(frs);
397  PASS;
398 }
399 
400 /* Test to check update of buffer if new pkt comes out of the window, and that
401  * flushing clears the whole ring rather than only its first bytes. */
402 static int FlowRateTest04(void)
403 {
404  SC_ATOMIC_SET(flow_config.memcap, 10000);
405  flow_rate_config.bytes = 200;
406  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
408  FAIL_IF_NULL(frs);
409  for (int i = 0; i < 2; i++) {
410  FAIL_IF(frs->dir[i].size != 4);
411  FAIL_IF(frs->dir[i].sum != 0);
412  }
413  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
414  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
415  /* Total length of packet is 48 */
416  FAIL_IF(frs->dir[0].sum != 48);
417  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
418  FAIL_IF(frs->dir[0].buf[0] != 48);
419  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
420 
421  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
422  p2->ts.secs = p1->ts.secs + 1;
423  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
424 
425  /* Total length of packet is 44 */
426  FAIL_IF(frs->dir[0].sum != 92);
427  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
428  FAIL_IF(frs->dir[0].buf[1] != 44);
429  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
430 
431  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
432  p3->ts.secs = p1->ts.secs + 2;
433  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
434 
435  /* Total length of packet is 47 */
436  FAIL_IF(frs->dir[0].sum != 139);
437  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
438  FAIL_IF(frs->dir[0].buf[2] != 47);
439  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
440 
441  /* Silence of a full interval, so this packet is out of the window and
442  * flushes the ring */
443  Packet *p4 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
444  p4->ts.secs = p3->ts.secs + 4;
445  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
446 
447  /* Total length of packet is 43 */
448  FAIL_IF(frs->dir[0].sum != 43);
449  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
450  FAIL_IF(frs->dir[0].start_ts.secs != p4->ts.secs);
451  FAIL_IF(frs->dir[0].buf[0] != 43);
452 
453  /* Every other slot must be cleared too, not just the first bytes */
454  FAIL_IF(frs->dir[0].buf[1] != 0);
455  FAIL_IF(frs->dir[0].buf[2] != 0);
456  FAIL_IF(frs->dir[0].buf[3] != 0);
457 
458  Packet *p5 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
459  p5->ts.secs = p4->ts.secs + 2;
460  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
461 
462  /* Total length of packet is 46 */
463  FAIL_IF(frs->dir[0].sum != 89);
464  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
465  FAIL_IF(frs->dir[0].start_ts.secs != p4->ts.secs);
466  FAIL_IF(frs->dir[0].buf[0] != 43);
467  FAIL_IF(frs->dir[0].buf[1] != 0);
468  FAIL_IF(frs->dir[0].buf[2] != 46);
469 
470  UTHFreePacket(p1);
471  UTHFreePacket(p2);
472  UTHFreePacket(p3);
473  UTHFreePacket(p4);
474  UTHFreePacket(p5);
475  FlowRateStoreFree(frs);
476  PASS;
477 }
478 
479 /* Test to check update of wrapping around ring buffer when the packet
480  * out of the window but also does not fall on the first index of the ring */
481 static int FlowRateTest05(void)
482 {
483  SC_ATOMIC_SET(flow_config.memcap, 10000);
484  flow_rate_config.bytes = 200;
485  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
487  FAIL_IF_NULL(frs);
488  for (int i = 0; i < 2; i++) {
489  FAIL_IF(frs->dir[i].size != 4);
490  FAIL_IF(frs->dir[i].sum != 0);
491  }
492  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
493  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
494  /* Total length of packet is 48 */
495  FAIL_IF(frs->dir[0].sum != 48);
496  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
497  FAIL_IF(frs->dir[0].buf[0] != 48);
498  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
499 
500  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
501  p2->ts.secs = p1->ts.secs + 1;
502  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
503  /* Total length of packet is 44 */
504  FAIL_IF(frs->dir[0].sum != 92);
505  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
506  FAIL_IF(frs->dir[0].buf[1] != 44);
507  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
508 
509  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
510  p3->ts.secs = p1->ts.secs + 2;
511  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
512  /* Total length of packet is 47 */
513  FAIL_IF(frs->dir[0].sum != 139);
514  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
515  FAIL_IF(frs->dir[0].buf[2] != 47);
516  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
517 
518  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
519  p4->ts.secs = p1->ts.secs + 3;
520  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
521  /* Total length of packet is 46 */
522  FAIL_IF(frs->dir[0].sum != 185);
523  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
524  FAIL_IF(frs->dir[0].buf[3] != 46);
525  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
526 
527  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
528  p5->ts.secs = p1->ts.secs + 6;
529  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
530  /* Total length of packet is 43 */
531  FAIL_IF(frs->dir[0].sum != 89);
532  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
533  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
534  FAIL_IF(frs->dir[0].buf[0] != 43);
535 
536  UTHFreePacket(p1);
537  UTHFreePacket(p2);
538  UTHFreePacket(p3);
539  UTHFreePacket(p4);
540  UTHFreePacket(p5);
541  FlowRateStoreFree(frs);
542  PASS;
543 }
544 
545 /* Test to check sum when packet is within the window but is coming after a gap */
546 static int FlowRateTest06(void)
547 {
548  SC_ATOMIC_SET(flow_config.memcap, 10000);
549  flow_rate_config.bytes = 200;
550  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
552  FAIL_IF_NULL(frs);
553  for (int i = 0; i < 2; i++) {
554  FAIL_IF(frs->dir[i].size != 4);
555  FAIL_IF(frs->dir[i].sum != 0);
556  }
557  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
558  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
559  /* Total length of packet is 48 */
560  FAIL_IF(frs->dir[0].sum != 48);
561  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
562  FAIL_IF(frs->dir[0].buf[0] != 48);
563  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
564 
565  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
566  p2->ts.secs = p1->ts.secs + 1;
567  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
568  /* Total length of packet is 44 */
569  FAIL_IF(frs->dir[0].sum != 92);
570  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
571  FAIL_IF(frs->dir[0].buf[1] != 44);
572  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
573 
574  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
575  p3->ts.secs = p1->ts.secs + 2;
576  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
577  /* Total length of packet is 47 */
578  FAIL_IF(frs->dir[0].sum != 139);
579  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
580  FAIL_IF(frs->dir[0].buf[2] != 47);
581  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
582 
583  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
584  p4->ts.secs = p1->ts.secs + 3;
585  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
586  /* Total length of packet is 46 */
587  FAIL_IF(frs->dir[0].sum != 185);
588  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
589  FAIL_IF(frs->dir[0].buf[3] != 46);
590  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
591 
592  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
593  p5->ts.secs = p1->ts.secs + 4;
594  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
595  /* Total length of packet is 43 */
596  FAIL_IF(frs->dir[0].sum != 180);
597  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
598  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
599  FAIL_IF(frs->dir[0].buf[0] != 43);
600 
601  Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
602  p6->ts.secs = p1->ts.secs + 7;
603  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
604  /* Total length of packet is 48 */
605  FAIL_IF(frs->dir[0].sum != 91);
606  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
607  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
608  FAIL_IF(frs->dir[0].buf[0] != 43);
609  FAIL_IF(frs->dir[0].buf[1] != 0);
610  FAIL_IF(frs->dir[0].buf[2] != 0);
611  FAIL_IF(frs->dir[0].buf[3] != 48);
612 
613  UTHFreePacket(p1);
614  UTHFreePacket(p2);
615  UTHFreePacket(p3);
616  UTHFreePacket(p4);
617  UTHFreePacket(p5);
618  UTHFreePacket(p6);
619  FlowRateStoreFree(frs);
620  PASS;
621 }
622 
623 /* Test to check sum when two packets are back to back within the window but are coming after a gap
624  */
625 static int FlowRateTest07(void)
626 {
627  SC_ATOMIC_SET(flow_config.memcap, 10000);
628  flow_rate_config.bytes = 200;
629  flow_rate_config.interval = (SCTime_t){ .secs = 4, .usecs = 0 };
631  FAIL_IF_NULL(frs);
632  for (int i = 0; i < 2; i++) {
633  FAIL_IF(frs->dir[i].size != 4);
634  FAIL_IF(frs->dir[i].sum != 0);
635  }
636  Packet *p1 = UTHBuildPacket((uint8_t *)"blahblah", 8, IPPROTO_TCP);
637  FlowRateStoreUpdate(frs, p1->ts, GET_PKT_LEN(p1), TOSERVER);
638  /* Total length of packet is 48 */
639  FAIL_IF(frs->dir[0].sum != 48);
640  FAIL_IF(frs->dir[0].last_ts.secs != p1->ts.secs);
641  FAIL_IF(frs->dir[0].buf[0] != 48);
642  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
643 
644  Packet *p2 = UTHBuildPacket((uint8_t *)"DATA", 4, IPPROTO_TCP);
645  p2->ts.secs = p1->ts.secs + 1;
646  FlowRateStoreUpdate(frs, p2->ts, GET_PKT_LEN(p2), TOSERVER);
647  /* Total length of packet is 44 */
648  FAIL_IF(frs->dir[0].sum != 92);
649  FAIL_IF(frs->dir[0].last_ts.secs != p2->ts.secs);
650  FAIL_IF(frs->dir[0].buf[1] != 44);
651  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
652 
653  Packet *p3 = UTHBuildPacket((uint8_t *)"ABababa", 7, IPPROTO_TCP);
654  p3->ts.secs = p1->ts.secs + 2;
655  FlowRateStoreUpdate(frs, p3->ts, GET_PKT_LEN(p3), TOSERVER);
656  /* Total length of packet is 47 */
657  FAIL_IF(frs->dir[0].sum != 139);
658  FAIL_IF(frs->dir[0].last_ts.secs != p3->ts.secs);
659  FAIL_IF(frs->dir[0].buf[2] != 47);
660  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
661 
662  Packet *p4 = UTHBuildPacket((uint8_t *)"yoohoo", 6, IPPROTO_TCP);
663  p4->ts.secs = p1->ts.secs + 3;
664  FlowRateStoreUpdate(frs, p4->ts, GET_PKT_LEN(p4), TOSERVER);
665  /* Total length of packet is 46 */
666  FAIL_IF(frs->dir[0].sum != 185);
667  FAIL_IF(frs->dir[0].last_ts.secs != p4->ts.secs);
668  FAIL_IF(frs->dir[0].buf[3] != 46);
669  FAIL_IF(frs->dir[0].start_ts.secs != p1->ts.secs);
670 
671  Packet *p5 = UTHBuildPacket((uint8_t *)"nmn", 3, IPPROTO_TCP);
672  p5->ts.secs = p1->ts.secs + 5;
673  FlowRateStoreUpdate(frs, p5->ts, GET_PKT_LEN(p5), TOSERVER);
674  /* Total length of packet is 43 */
675  FAIL_IF(frs->dir[0].sum != 136);
676  FAIL_IF(frs->dir[0].last_ts.secs != p5->ts.secs);
677  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
678  FAIL_IF(frs->dir[0].buf[0] != 43);
679 
680  Packet *p6 = UTHBuildPacket((uint8_t *)"suricata", 8, IPPROTO_TCP);
681  p6->ts.secs = p1->ts.secs + 8;
682  FlowRateStoreUpdate(frs, p6->ts, GET_PKT_LEN(p6), TOSERVER);
683  /* Total length of packet is 48 */
684  FAIL_IF(frs->dir[0].sum != 91);
685  FAIL_IF(frs->dir[0].last_ts.secs != p6->ts.secs);
686  FAIL_IF(frs->dir[0].start_ts.secs != p5->ts.secs);
687  FAIL_IF(frs->dir[0].buf[0] != 43);
688  FAIL_IF(frs->dir[0].buf[1] != 0);
689  FAIL_IF(frs->dir[0].buf[2] != 0);
690  FAIL_IF(frs->dir[0].buf[3] != 48);
691 
692  UTHFreePacket(p1);
693  UTHFreePacket(p2);
694  UTHFreePacket(p3);
695  UTHFreePacket(p4);
696  UTHFreePacket(p5);
697  UTHFreePacket(p6);
698  FlowRateStoreFree(frs);
699  PASS;
700 }
701 
703 {
704  UtRegisterTest("FlowRateTest01", FlowRateTest01);
705  UtRegisterTest("FlowRateTest02", FlowRateTest02);
706  UtRegisterTest("FlowRateTest03", FlowRateTest03);
707  UtRegisterTest("FlowRateTest04", FlowRateTest04);
708  UtRegisterTest("FlowRateTest05", FlowRateTest05);
709  UtRegisterTest("FlowRateTest06", FlowRateTest06);
710  UtRegisterTest("FlowRateTest07", FlowRateTest07);
711 }
712 #endif
util-byte.h
FlowRateDirStore_::sum
uint64_t sum
Definition: util-flow-rate.h:36
FlowRateRegisterTests
void FlowRateRegisterTests(void)
Definition: util-flow-rate.c:702
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:296
SCFlowStorageId
Definition: flow-storage.h:33
FlowRateGetStorageID
SCFlowStorageId FlowRateGetStorageID(void)
Definition: util-flow-rate.c:133
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:243
SCConfNodeLookupChildValue
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:878
g_flowrate_storage_id
SCFlowStorageId g_flowrate_storage_id
Definition: util-flow-rate.c:37
util-unittest.h
util-unittest-helper.h
SCFlowStorageRegister
SCFlowStorageId SCFlowStorageRegister(const char *name, void(*Free)(void *))
Definition: flow-storage.c:59
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:192
util-flow-rate.h
TOSERVER
#define TOSERVER
Definition: flow.h:46
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
File_::end
uint64_t end
Definition: util-file.h:173
Packet_::ts
SCTime_t ts
Definition: decode.h:570
SCTime_t::secs
uint64_t secs
Definition: util-time.h:41
FlowRateRegisterFlowStorage
void FlowRateRegisterFlowStorage(void)
Definition: util-flow-rate.c:60
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:516
GET_PKT_LEN
#define GET_PKT_LEN(p)
Definition: decode.h:209
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:850
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:101
flow_config
FlowConfig flow_config
Definition: flow.c:93
FlowRateIsExceeding
bool FlowRateIsExceeding(FlowRateStore *frs, int direction)
Definition: util-flow-rate.c:227
FatalError
#define FatalError(...)
Definition: util-debug.h:517
File_::start
uint64_t start
Definition: util-file.h:172
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:184
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:472
FlowRateDirStore_::buf
uint64_t * buf
Definition: util-flow-rate.h:34
FlowRateStorageEnabled
bool FlowRateStorageEnabled(void)
Definition: util-flow-rate.c:96
SCFlowStorageId::id
int id
Definition: flow-storage.h:34
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:109
FlowRateStore_
Definition: util-flow-rate.h:47
util-storage.h
FlowRateConfig_
Definition: util-flow-rate.h:27