]>
Commit | Line | Data |
---|---|---|
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 |
49 | using namespace snort; |
50 | ||
51 | THREAD_LOCAL bool Active::s_suspend = false; | |
ad6965d1 | 52 | THREAD_LOCAL Active::ActiveSuspendReason Active::s_suspend_reason = Active::ASP_NONE; |
d126c051 | 53 | |
96295240 | 54 | THREAD_LOCAL const Trace* stream_trace = nullptr; |
d126c051 | 55 | |
d68edfa0 | 56 | void Active::drop_packet(snort::Packet const*, bool) { } |
9255327b | 57 | void Active::set_drop_reason(char const*) { } |
c62211ad | 58 | ExpectCache::ExpectCache(uint32_t) { } |
23206d51 | 59 | ExpectCache::~ExpectCache() = default; |
c62211ad | 60 | bool ExpectCache::check(Packet*, Flow*) { return true; } |
23206d51 | 61 | void DetectionEngine::disable_all(Packet*) { } |
c62211ad | 62 | Flow* HighAvailabilityManager::import(Packet&, FlowKey&) { return nullptr; } |
6d75bcdd | 63 | bool HighAvailabilityManager::in_standby(Flow*) { return false; } |
c62211ad | 64 | SfIpRet SfIp::set(void const*, int) { return SFIP_SUCCESS; } |
23206d51 RD |
65 | const SnortConfig* SnortConfig::get_conf() { return nullptr; } |
66 | uint8_t TraceApi::get_constraints_generation() { return 0; } | |
67 | void TraceApi::filter(const Packet&) {} | |
8b00ac66 | 68 | void ThreadConfig::preemptive_kick() {} |
d126c051 MS |
69 | |
70 | namespace snort | |
71 | { | |
4758ecbe RD |
72 | Flow::~Flow() = default; |
73 | void Flow::init(PktType) { } | |
74 | void Flow::flush(bool) { } | |
75 | void Flow::reset(bool) { } | |
76 | void Flow::free_flow_data() { } | |
77 | void Flow::set_client_initiate(Packet*) { } | |
78 | void Flow::set_direction(Packet*) { } | |
79 | void Flow::set_mpls_layer_per_dir(Packet*) { } | |
79faa2fe | 80 | |
d126c051 | 81 | time_t packet_time() { return 0; } |
23206d51 RD |
82 | |
83 | void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list) {} | |
d126c051 | 84 | |
d126c051 MS |
85 | namespace ip |
86 | { | |
87 | uint32_t IpApi::id() const { return 0; } | |
88 | } | |
89 | } | |
90 | ||
c62211ad | 91 | int 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 | ||
97 | TEST_GROUP(flow_prune) { }; | |
98 | ||
99 | // No flows in the flow cache, pruning should not happen | |
100 | TEST(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 | |
113 | TEST(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 | |
156 | TEST(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 | |
181 | TEST(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 | |
205 | TEST(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 | |
232 | TEST(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 |
306 | TEST(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 |
350 | int main(int argc, char** argv) |
351 | { | |
352 | return CommandLineTestRunner::RunAllTests(argc, argv); | |
3127a0e9 | 353 | } |