]> git.ipfire.org Git - thirdparty/snort3.git/blame - src/flow/test/flow_cache_test.cc
Pull request #4152: flow: Add tenant ID to FlowKey
[thirdparty/snort3.git] / src / flow / test / flow_cache_test.cc
CommitLineData
92968633 1//--------------------------------------------------------------------------
5ad5de33 2// Copyright (C) 2019-2023 Cisco and/or its affiliates. All rights reserved.
d126c051
MS
3//
4// This program is free software; you can redistribute it and/or modify it
5// under the terms of the GNU General Public License Version 2 as published
6// by the Free Software Foundation. You may not use, modify or distribute
7// this program under any other version of the GNU General Public License.
8//
9// This program is distributed in the hope that it will be useful, but
10// WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License along
15// with this program; if not, write to the Free Software Foundation, Inc.,
16// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17//--------------------------------------------------------------------------
18
c62211ad 19// flow_cache_test.cc author davis mcpherson <davmcphe@cisco.com>
d126c051
MS
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include <daq_common.h>
26
27#include "flow/flow_control.h"
28
29#include "detection/detection_engine.h"
03d0b89c
RC
30#include "flow/expect_cache.h"
31#include "flow/flow_cache.h"
32#include "flow/ha.h"
33#include "flow/session.h"
4758ecbe 34#include "main/analyzer.h"
d126c051 35#include "managers/inspector_manager.h"
d126c051 36#include "packet_io/active.h"
d126c051 37#include "protocols/icmp4.h"
d126c051
MS
38#include "protocols/tcp.h"
39#include "protocols/udp.h"
40#include "protocols/vlan.h"
d126c051 41#include "utils/util.h"
1a1e8822 42#include "trace/trace_api.h"
d126c051
MS
43
44#include <CppUTest/CommandLineTestRunner.h>
45#include <CppUTest/TestHarness.h>
46
4758ecbe
RD
47#include "flow_stubs.h"
48
d126c051
MS
49using namespace snort;
50
51THREAD_LOCAL bool Active::s_suspend = false;
ad6965d1 52THREAD_LOCAL Active::ActiveSuspendReason Active::s_suspend_reason = Active::ASP_NONE;
d126c051 53
96295240 54THREAD_LOCAL const Trace* stream_trace = nullptr;
d126c051 55
d68edfa0 56void Active::drop_packet(snort::Packet const*, bool) { }
9255327b 57void Active::set_drop_reason(char const*) { }
c62211ad 58ExpectCache::ExpectCache(uint32_t) { }
23206d51 59ExpectCache::~ExpectCache() = default;
c62211ad 60bool ExpectCache::check(Packet*, Flow*) { return true; }
23206d51 61void DetectionEngine::disable_all(Packet*) { }
c62211ad 62Flow* HighAvailabilityManager::import(Packet&, FlowKey&) { return nullptr; }
6d75bcdd 63bool HighAvailabilityManager::in_standby(Flow*) { return false; }
c62211ad 64SfIpRet SfIp::set(void const*, int) { return SFIP_SUCCESS; }
23206d51
RD
65const SnortConfig* SnortConfig::get_conf() { return nullptr; }
66uint8_t TraceApi::get_constraints_generation() { return 0; }
67void TraceApi::filter(const Packet&) {}
8b00ac66 68void ThreadConfig::preemptive_kick() {}
d126c051
MS
69
70namespace snort
71{
4758ecbe
RD
72Flow::~Flow() = default;
73void Flow::init(PktType) { }
74void Flow::flush(bool) { }
75void Flow::reset(bool) { }
76void Flow::free_flow_data() { }
77void Flow::set_client_initiate(Packet*) { }
78void Flow::set_direction(Packet*) { }
79void Flow::set_mpls_layer_per_dir(Packet*) { }
79faa2fe 80
d126c051 81time_t packet_time() { return 0; }
23206d51
RD
82
83void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list) {}
d126c051 84
d126c051
MS
85namespace ip
86{
87uint32_t IpApi::id() const { return 0; }
88}
89}
90
c62211ad 91int ExpectCache::add_flow(const Packet*, PktType, IpProtocol, const SfIp*, uint16_t,
1a626bd5 92 const SfIp*, uint16_t, char, FlowData*, SnortProtocolId, bool, bool, bool, bool)
3127a0e9
MA
93{
94 return 1;
d126c051
MS
95}
96
97TEST_GROUP(flow_prune) { };
98
99// No flows in the flow cache, pruning should not happen
100TEST(flow_prune, empty_cache_prune_flows)
101{
102 FlowCacheConfig fcg;
103 fcg.max_flows = 3;
104 FlowCache *cache = new FlowCache(fcg);
105
106 CHECK(cache->get_count() == 0);
107 CHECK(cache->delete_flows(1) == 0);
108 CHECK(cache->get_count() == 0);
109 delete cache;
110}
111
112// Do not delete blocked flow
113TEST(flow_prune, blocked_flow_prune_flows)
3127a0e9 114{
d126c051
MS
115 FlowCacheConfig fcg;
116 fcg.max_flows = 2;
117 FlowCache *cache = new FlowCache(fcg);
118
d126c051
MS
119 int first_port = 1;
120 int second_port = 2;
121
122 // Add two flows in the flow cache
123 FlowKey flow_key;
124 memset(&flow_key, 0, sizeof(FlowKey));
125 flow_key.pkt_type = PktType::TCP;
3127a0e9 126
d126c051 127 flow_key.port_l = first_port;
c62211ad 128 cache->allocate(&flow_key);
d126c051
MS
129
130 flow_key.port_l = second_port;
c62211ad 131 Flow* flow = cache->allocate(&flow_key);
d126c051
MS
132
133 CHECK(cache->get_count() == fcg.max_flows);
134
135 // block the second flow
c62211ad 136 flow->block();
d126c051
MS
137
138 // Access the first flow
139 // This will move it to the MRU
140 flow_key.port_l = first_port;
141 CHECK(cache->find(&flow_key) != nullptr);
142
3127a0e9 143 // Prune one flow. This should delete the MRU flow, since
d126c051
MS
144 // LRU flow is blocked
145 CHECK(cache->delete_flows(1) == 1);
146
147 // Blocked Flow should still be there
148 flow_key.port_l = second_port;
149 CHECK(cache->find(&flow_key) != nullptr);
150 cache->purge();
151 CHECK(cache->get_flows_allocated() == 0);
152 delete cache;
153}
154
155// Add 3 flows in flow cache and delete one
156TEST(flow_prune, prune_flows)
157{
158 FlowCacheConfig fcg;
159 fcg.max_flows = 3;
160 FlowCache *cache = new FlowCache(fcg);
161 int port = 1;
162
c62211ad 163 for ( unsigned i = 0; i < fcg.max_flows; i++ )
d126c051
MS
164 {
165 FlowKey flow_key;
166 flow_key.port_l = port++;
167 flow_key.pkt_type = PktType::TCP;
c62211ad 168 cache->allocate(&flow_key);
d126c051
MS
169 }
170
171 CHECK(cache->get_count() == fcg.max_flows);
172 CHECK(cache->delete_flows(1) == 1);
173 CHECK(cache->get_count() == fcg.max_flows-1);
174 cache->purge();
175 CHECK(cache->get_flows_allocated() == 0);
176 delete cache;
177}
178
179
180// Add 3 flows in flow cache, delete all
181TEST(flow_prune, prune_all_flows)
182{
183 FlowCacheConfig fcg;
184 fcg.max_flows = 3;
185 FlowCache *cache = new FlowCache(fcg);
186 int port = 1;
187
c62211ad 188 for ( unsigned i = 0; i < fcg.max_flows; i++ )
d126c051
MS
189 {
190 FlowKey flow_key;
191 flow_key.port_l = port++;
192 flow_key.pkt_type = PktType::TCP;
c62211ad 193 cache->allocate(&flow_key);
d126c051
MS
194 }
195
196 CHECK(cache->get_count() == fcg.max_flows);
197 CHECK(cache->delete_flows(3) == 3);
198 CHECK(cache->get_count() == 0);
199 cache->purge();
200 CHECK(cache->get_flows_allocated() == 0);
201 delete cache;
202}
203
204// Add 3 flows, all blocked, in flow cache, delete all
205TEST(flow_prune, prune_all_blocked_flows)
206{
207 FlowCacheConfig fcg;
208 fcg.max_flows = 3;
209 FlowCache *cache = new FlowCache(fcg);
210 int port = 1;
211
c62211ad 212 for ( unsigned i = 0; i < fcg.max_flows; i++ )
d126c051
MS
213 {
214 FlowKey flow_key;
215 flow_key.port_l = port++;
216 flow_key.pkt_type = PktType::TCP;
c62211ad
MA
217 Flow* flow = cache->allocate(&flow_key);
218 flow->block();
d126c051
MS
219 }
220
221 CHECK(cache->get_count() == fcg.max_flows);
222 CHECK(cache->delete_flows(3) == 3);
223 CHECK(cache->get_count() == 0);
224
225 cache->purge();
226 CHECK(cache->get_flows_allocated() == 0);
227 delete cache;
228}
229
6d75bcdd
RS
230
231// prune base on the proto type of the flow
232TEST(flow_prune, prune_proto)
233{
234 FlowCacheConfig fcg;
235 fcg.max_flows = 5;
236 fcg.prune_flows = 3;
23206d51 237
6d75bcdd
RS
238 for(uint8_t i = to_utype(PktType::NONE); i <= to_utype(PktType::MAX); i++)
239 fcg.proto[i].nominal_timeout = 5;
23206d51 240
6d75bcdd
RS
241 FlowCache *cache = new FlowCache(fcg);
242 int port = 1;
243
244 for ( unsigned i = 0; i < 2; i++ )
245 {
246 FlowKey flow_key;
247 flow_key.port_l = port++;
248 flow_key.pkt_type = PktType::UDP;
249 cache->allocate(&flow_key);
250 }
251
252 CHECK (cache->get_count() == 2);
253
254 //pruning should not happen for all other proto except UDP
255 for(uint8_t i = 0; i < to_utype(PktType::MAX) - 1; i++)
256 {
257 if (i == to_utype(PktType::UDP))
258 continue;
259 CHECK(cache->prune_one(PruneReason::NONE, true, i) == false);
260 }
23206d51 261
6d75bcdd
RS
262 //pruning should happen for UDP
263 CHECK(cache->prune_one(PruneReason::NONE, true, to_utype(PktType::UDP)) == true);
264
265 FlowKey flow_key2;
266 flow_key2.port_l = port++;
267 flow_key2.pkt_type = PktType::ICMP;
268 cache->allocate(&flow_key2);
269
270 CHECK (cache->get_count() == 2);
271
272 //target flow is ICMP
273 CHECK(cache->prune_multiple(PruneReason::NONE, true) == 1);
274
275 //adding UDP flow it will become LRU
276 for ( unsigned i = 0; i < 2; i++ )
277 {
278 FlowKey flow_key;
279 flow_key.port_l = port++;
280 flow_key.pkt_type = PktType::UDP;
281 Flow* flow = cache->allocate(&flow_key);
282 flow->last_data_seen = 2+i;
283 }
284
285 //adding TCP flow it will become MRU and put UDP flow to LRU
286 for ( unsigned i = 0; i < 3; i++ )
287 {
288 FlowKey flow_key;
289 flow_key.port_l = port++;
290 flow_key.pkt_type = PktType::TCP;
291 Flow* flow = cache->allocate(&flow_key);
292 flow->last_data_seen = 4+i; //this will force to timeout later than UDP
293 }
294
295 //timeout should happen for 2 UDP and 1 TCP flow
296 CHECK( 3 == cache->timeout(5,9));
297
298 //target flow UDP flow and it will fail because no UDP flow is present
299 CHECK(cache->prune_one(PruneReason::NONE, true, to_utype(PktType::UDP)) == false);
300
301 cache->purge();
302 CHECK(cache->get_flows_allocated() == 0);
303 delete cache;
304}
305
41e1076d
RS
306TEST(flow_prune, prune_counts)
307{
308 PruneStats stats;
309
310 // Simulate a few prunes for different reasons and protocol types
311 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
312 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::TCP);
313 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::UDP);
314 stats.update(PruneReason::MEMCAP, PktType::ICMP);
315 stats.update(PruneReason::MEMCAP, PktType::USER);
316
317 // Check the total prunes
318 CHECK_EQUAL(5, stats.get_total());
319
320 // Check individual protocol prunes
321 CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
322 CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::TCP));
323 CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::UDP));
324 CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::ICMP));
325 CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::USER));
326
327 // Check prunes for a specific protocol across all reasons
328 CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::IP));
329 CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::TCP));
330 CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::UDP));
331 CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::ICMP));
332 CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::USER));
333
334 // Reset the counts
335 stats = PruneStats();
336
337 // Ensure that the counts have been reset
338 CHECK_EQUAL(0, stats.get_total());
339 CHECK_EQUAL(0, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
340 CHECK_EQUAL(0, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::TCP));
341
342 // Update the same protocol and reason multiple times
343 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
344 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
345 stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
346
347 CHECK_EQUAL(3, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
348}
349
d126c051
MS
350int main(int argc, char** argv)
351{
352 return CommandLineTestRunner::RunAllTests(argc, argv);
3127a0e9 353}