/src/suricata/src/util-flow-rate.c
Line | Count | Source |
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 | | |
37 | | FlowStorageId g_flowrate_storage_id = { .id = -1 }; |
38 | | |
39 | | FlowRateConfig flow_rate_config; |
40 | | |
41 | | static void FlowRateStoreFree(void *ptr) |
42 | 0 | { |
43 | 0 | FlowRateStore *frs = (FlowRateStore *)ptr; |
44 | 0 | size_t total_free = 0; |
45 | 0 | if (frs == NULL) |
46 | 0 | return; |
47 | | |
48 | 0 | for (int i = 0; i < 2; i++) { |
49 | 0 | if (frs->dir[i].buf != NULL) { |
50 | 0 | SCFree(frs->dir[i].buf); |
51 | 0 | total_free += (frs->dir[i].size * sizeof(uint64_t)); |
52 | 0 | } |
53 | 0 | } |
54 | |
|
55 | 0 | SCFree(frs); |
56 | 0 | total_free += sizeof(*frs); |
57 | 0 | (void)SC_ATOMIC_SUB(flow_memuse, total_free); |
58 | 0 | } |
59 | | |
60 | | void FlowRateRegisterFlowStorage(void) |
61 | 38 | { |
62 | 38 | SCConfNode *root = SCConfGetNode("flow"); |
63 | 38 | if (root == NULL) |
64 | 38 | return; |
65 | | |
66 | 38 | bool track_flow = false; |
67 | 0 | track_flow = SCConfNodeLookupChild(root, "rate-tracking") != NULL ? true : false; |
68 | 0 | if (!track_flow) |
69 | 0 | return; |
70 | | |
71 | 0 | SCConfNode *node = SCConfGetNode("flow.rate-tracking"); |
72 | 0 | const char *val = SCConfNodeLookupChildValue(node, "bytes"); |
73 | 0 | if (val == NULL) { |
74 | 0 | FatalError("No value for flow tracking bytes"); |
75 | 0 | } |
76 | 0 | uint64_t bytes = 0; |
77 | 0 | if (ParseSizeStringU64(val, &bytes) < 0) { |
78 | 0 | FatalError("Invalid value for flow tracking bytes"); |
79 | 0 | } |
80 | 0 | flow_rate_config.bytes = bytes; |
81 | |
|
82 | 0 | val = SCConfNodeLookupChildValue(node, "interval"); |
83 | 0 | if (val == NULL) { |
84 | 0 | FatalError("No value for flow tracking interval"); |
85 | 0 | } |
86 | 0 | SCTime_t interval = SCTIME_INITIALIZER; |
87 | 0 | uint16_t secs = 0; |
88 | 0 | if ((StringParseUint16(&secs, 10, 0, val) < 0) || (secs == 0)) { |
89 | 0 | FatalError("Invalid value for flow tracking interval"); |
90 | 0 | } |
91 | 0 | flow_rate_config.interval = SCTIME_ADD_SECS(interval, secs); |
92 | |
|
93 | 0 | g_flowrate_storage_id = |
94 | 0 | FlowStorageRegister("flowrate", sizeof(void *), NULL, FlowRateStoreFree); |
95 | 0 | } |
96 | | |
97 | | bool FlowRateStorageEnabled(void) |
98 | 8.72M | { |
99 | 8.72M | return (g_flowrate_storage_id.id != -1); |
100 | 8.72M | } |
101 | | |
102 | | FlowRateStore *FlowRateStoreInit(void) |
103 | 0 | { |
104 | 0 | FlowRateStore *frs = NULL; |
105 | 0 | size_t total_memuse = 0; |
106 | 0 | size_t expected_memuse = (2 * flow_rate_config.interval.secs * sizeof(uint64_t)) + sizeof(*frs); |
107 | |
|
108 | 0 | if (!FLOW_CHECK_MEMCAP(expected_memuse)) { |
109 | 0 | return NULL; |
110 | 0 | } |
111 | 0 | frs = SCCalloc(1, sizeof(*frs)); |
112 | 0 | if (unlikely(frs == NULL)) { |
113 | 0 | return NULL; |
114 | 0 | } |
115 | | |
116 | 0 | total_memuse += sizeof(*frs); |
117 | 0 | for (int i = 0; i < 2; i++) { |
118 | 0 | frs->dir[i].size = (uint16_t)flow_rate_config.interval.secs; |
119 | 0 | frs->dir[i].buf = SCCalloc(frs->dir[i].size, sizeof(uint64_t)); |
120 | 0 | if (unlikely(frs->dir[i].buf == NULL)) { |
121 | 0 | FlowRateStoreFree(frs); |
122 | 0 | return NULL; |
123 | 0 | } |
124 | 0 | frs->dir[i].start_ts = SCTIME_INITIALIZER; |
125 | 0 | frs->dir[i].last_ts = SCTIME_INITIALIZER; |
126 | 0 | total_memuse += (frs->dir[i].size * sizeof(uint64_t)); |
127 | 0 | } |
128 | 0 | DEBUG_VALIDATE_BUG_ON(total_memuse != expected_memuse); |
129 | 0 | (void)SC_ATOMIC_ADD(flow_memuse, total_memuse); |
130 | |
|
131 | 0 | return frs; |
132 | 0 | } |
133 | | |
134 | | FlowStorageId FlowRateGetStorageID(void) |
135 | 0 | { |
136 | 0 | return g_flowrate_storage_id; |
137 | 0 | } |
138 | | |
139 | | static inline void FlowRateClearSumInRange( |
140 | | FlowRateStore *frs, uint16_t start, uint16_t end, int direction) |
141 | 0 | { |
142 | 0 | for (uint16_t i = start; i <= end; i++) { |
143 | 0 | uint64_t byte_count_at_i = frs->dir[direction].buf[i]; |
144 | 0 | frs->dir[direction].buf[i] = 0; |
145 | 0 | DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < byte_count_at_i); |
146 | 0 | frs->dir[direction].sum -= byte_count_at_i; |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | static inline void FlowRateStoreUpdateCurrentRing( |
151 | | FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, uint16_t idx, int direction) |
152 | 0 | { |
153 | 0 | 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 | 0 | FlowRateClearSumInRange(frs, frs->dir[direction].last_idx + 1, idx, direction); |
157 | 0 | frs->dir[direction].buf[idx] += pkt_len; |
158 | | /* Update the total sum */ |
159 | 0 | frs->dir[direction].sum += pkt_len; |
160 | 0 | } 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 | 0 | frs->dir[direction].buf[idx] += pkt_len; |
164 | | /* Update the total sum */ |
165 | 0 | frs->dir[direction].sum += pkt_len; |
166 | 0 | } else { |
167 | | /* Index is revisited after a full round of the buffer */ |
168 | 0 | uint64_t prev_byte_count = frs->dir[direction].buf[idx]; |
169 | | /* Overwrite the buffer */ |
170 | 0 | frs->dir[direction].buf[idx] = pkt_len; |
171 | 0 | DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum < prev_byte_count); |
172 | | /* Sum should get rid of previous count on the same index */ |
173 | 0 | frs->dir[direction].sum += pkt_len - prev_byte_count; |
174 | 0 | if (idx != frs->dir[direction].last_idx + 1) { |
175 | | /* Revisited index but not the next to last, so, reset start_ts */ |
176 | 0 | frs->dir[direction].start_ts = p_ts; |
177 | 0 | } |
178 | 0 | } |
179 | 0 | frs->dir[direction].last_idx = idx; |
180 | 0 | } |
181 | | |
182 | | static inline void FlowRateStoreFlushRing( |
183 | | FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction) |
184 | 0 | { |
185 | 0 | memset(frs->dir[direction].buf, 0, frs->dir[direction].size); |
186 | 0 | frs->dir[direction].last_idx = 0; |
187 | 0 | frs->dir[direction].start_ts = p_ts; |
188 | 0 | frs->dir[direction].buf[0] = pkt_len; |
189 | | /* Overwrite the sum calculated so far */ |
190 | 0 | frs->dir[direction].sum = pkt_len; |
191 | 0 | } |
192 | | |
193 | | void FlowRateStoreUpdate(FlowRateStore *frs, SCTime_t p_ts, uint32_t pkt_len, int direction) |
194 | 0 | { |
195 | 0 | if (frs->dir[direction].last_ts.secs == 0) { |
196 | | /* Should only happen when the ring is first used */ |
197 | 0 | DEBUG_VALIDATE_BUG_ON(frs->dir[direction].sum > 0); |
198 | | /* Initialize last_ts and start_ts with the first packet's timestamp */ |
199 | 0 | frs->dir[direction].last_ts = p_ts; |
200 | 0 | frs->dir[direction].start_ts = p_ts; |
201 | 0 | } |
202 | | |
203 | 0 | SCTime_t start_ts = frs->dir[direction].start_ts; |
204 | 0 | 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 | 0 | if ((frs->dir[direction].last_idx == frs->dir[direction].size - 1) && |
207 | 0 | (frs->dir[direction].last_idx != idx)) { |
208 | 0 | start_ts = p_ts; |
209 | 0 | if (idx != 0) { |
210 | | /* Update the sum */ |
211 | 0 | FlowRateClearSumInRange(frs, 0, idx, direction); |
212 | | /* Consider current packet a new start of the ring */ |
213 | 0 | idx = 0; |
214 | 0 | } |
215 | 0 | } |
216 | | /* If the packet has come in the last open interval of time */ |
217 | 0 | if (p_ts.secs - start_ts.secs < frs->dir[direction].size) { |
218 | 0 | FlowRateStoreUpdateCurrentRing(frs, p_ts, pkt_len, idx, direction); |
219 | 0 | } else { |
220 | | /* Packet arrived after one or more rounds of the entire buffer */ |
221 | | /* Flush the entire buffer */ |
222 | 0 | FlowRateStoreFlushRing(frs, p_ts, pkt_len, direction); |
223 | 0 | } |
224 | | /* In any case, update the last seen timestamp */ |
225 | 0 | frs->dir[direction].last_ts = p_ts; |
226 | 0 | } |
227 | | |
228 | | bool FlowRateIsExceeding(FlowRateStore *frs, int direction) |
229 | 0 | { |
230 | 0 | if (frs->dir[direction].sum >= flow_rate_config.bytes) { |
231 | 0 | return true; |
232 | 0 | } |
233 | 0 | return false; |
234 | 0 | } |
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 }; |
244 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
284 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
335 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
410 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
446 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
511 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 }; |
590 | | FlowRateStore *frs = FlowRateStoreInit(); |
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 | | |
662 | | void FlowRateRegisterTests(void) |
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 |