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