1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
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
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <functional>
24
25 #include "detection/ips_context.h"
26 #include "flow/expect_cache.h"
27 #include "flow/flow_control.h"
28 #include "flow/prune_stats.h"
29 #include "framework/data_bus.h"
30 #include "log/messages.h"
31 #include "main/snort_config.h"
32 #include "main/snort_types.h"
33 #include "managers/inspector_manager.h"
34 #include "profiler/profiler_defs.h"
35 #include "protocols/packet.h"
36 #include "protocols/tcp.h"
37 #include "stream/flush_bucket.h"
38 #include "stream/tcp/tcp_stream_tracker.h"
39
40 #include "stream_ha.h"
41 #include "stream_module.h"
42
43 using namespace snort;
44
45 //-------------------------------------------------------------------------
46 // stats
47 //-------------------------------------------------------------------------
48
49 THREAD_LOCAL ProfileStats s5PerfStats;
50 THREAD_LOCAL FlowControl* flow_con = nullptr;
51
52 static BaseStats g_stats;
53 THREAD_LOCAL BaseStats stream_base_stats;
54
55 // FIXIT-L dependency on stats define in another file
56 const PegInfo base_pegs[] =
57 {
58 { CountType::SUM, "flows", "total sessions" },
59 { CountType::SUM, "total_prunes", "total sessions pruned" },
60 { CountType::SUM, "idle_prunes", " sessions pruned due to timeout" },
61 { CountType::SUM, "excess_prunes", "sessions pruned due to excess" },
62 { CountType::SUM, "uni_prunes", "uni sessions pruned" },
63 { CountType::SUM, "preemptive_prunes", "sessions pruned during preemptive pruning (deprecated)" },
64 { CountType::SUM, "memcap_prunes", "sessions pruned due to memcap" },
65 { CountType::SUM, "ha_prunes", "sessions pruned by high availability sync" },
66 { CountType::SUM, "stale_prunes", "sessions pruned due to stale connection" },
67 { CountType::SUM, "expected_flows", "total expected flows created within snort" },
68 { CountType::SUM, "expected_realized", "number of expected flows realized" },
69 { CountType::SUM, "expected_pruned", "number of expected flows pruned" },
70 { CountType::SUM, "expected_overflows", "number of expected cache overflows" },
71 { CountType::SUM, "reload_tuning_idle", "number of times stream resource tuner called while idle" },
72 { CountType::SUM, "reload_tuning_packets", "number of times stream resource tuner called while processing packets" },
73 { CountType::SUM, "reload_total_adds", "number of flows added by config reloads" },
74 { CountType::SUM, "reload_total_deletes", "number of flows deleted by config reloads" },
75 { CountType::SUM, "reload_freelist_deletes", "number of flows deleted from the free list by config reloads" },
76 { CountType::SUM, "reload_allowed_deletes", "number of allowed flows deleted by config reloads" },
77 { CountType::SUM, "reload_blocked_deletes", "number of blocked flows deleted by config reloads" },
78 { CountType::SUM, "reload_offloaded_deletes", "number of offloaded flows deleted by config reloads" },
79 { CountType::END, nullptr, nullptr }
80 };
81
82 // FIXIT-L dependency on stats define in another file
base_prep()83 void base_prep()
84 {
85 if ( !flow_con )
86 return;
87
88 stream_base_stats.flows = flow_con->get_flows();
89 stream_base_stats.prunes = flow_con->get_total_prunes();
90 stream_base_stats.timeout_prunes = flow_con->get_prunes(PruneReason::IDLE);
91 stream_base_stats.excess_prunes = flow_con->get_prunes(PruneReason::EXCESS);
92 stream_base_stats.uni_prunes = flow_con->get_prunes(PruneReason::UNI);
93 stream_base_stats.preemptive_prunes = flow_con->get_prunes(PruneReason::MEMCAP);
94 stream_base_stats.memcap_prunes = flow_con->get_prunes(PruneReason::MEMCAP);
95 stream_base_stats.ha_prunes = flow_con->get_prunes(PruneReason::HA);
96 stream_base_stats.stale_prunes = flow_con->get_prunes(PruneReason::STALE);
97 stream_base_stats.reload_freelist_flow_deletes = flow_con->get_deletes(FlowDeleteState::FREELIST);
98 stream_base_stats.reload_allowed_flow_deletes = flow_con->get_deletes(FlowDeleteState::ALLOWED);
99 stream_base_stats.reload_offloaded_flow_deletes= flow_con->get_deletes(FlowDeleteState::OFFLOADED);
100 stream_base_stats.reload_blocked_flow_deletes= flow_con->get_deletes(FlowDeleteState::BLOCKED);
101 ExpectCache* exp_cache = flow_con->get_exp_cache();
102
103 if ( exp_cache )
104 {
105 stream_base_stats.expected_flows = exp_cache->get_expects();
106 stream_base_stats.expected_realized = exp_cache->get_realized();
107 stream_base_stats.expected_pruned = exp_cache->get_prunes();
108 stream_base_stats.expected_overflows = exp_cache->get_overflows();
109 }
110 }
111
base_sum()112 void base_sum()
113 {
114 sum_stats((PegCount*)&g_stats, (PegCount*)&stream_base_stats,
115 array_size(base_pegs) - 1);
116 base_reset(false);
117 }
118
base_stats()119 void base_stats()
120 {
121 show_stats((PegCount*)&g_stats, base_pegs, array_size(base_pegs) - 1, MOD_NAME);
122 }
123
base_reset(bool reset_all)124 void base_reset(bool reset_all)
125 {
126 if ( flow_con )
127 flow_con->clear_counts();
128
129 memset(&stream_base_stats, 0, sizeof(stream_base_stats));
130
131 if ( reset_all )
132 {
133 if ( flow_con )
134 {
135 ExpectCache* exp_cache = flow_con->get_exp_cache();
136 if ( exp_cache )
137 exp_cache->reset_stats();
138 }
139 memset(&g_stats, 0, sizeof(g_stats));
140 }
141 }
142
143 //-------------------------------------------------------------------------
144 // runtime support
145 //-------------------------------------------------------------------------
146
is_eligible(Packet * p)147 static inline bool is_eligible(Packet* p)
148 {
149 #ifdef NDEBUG
150 UNUSED(p);
151 #endif
152 assert(!(p->ptrs.decode_flags & DECODE_ERR_CKSUM_IP));
153 assert(!(p->packet_flags & PKT_REBUILT_STREAM));
154 assert(p->ptrs.ip_api.is_valid());
155
156 return true;
157 }
158
159
160 //-------------------------------------------------------------------------
161 // inspector stuff
162 //-------------------------------------------------------------------------
163
164 class StreamBase : public Inspector
165 {
166 public:
167 StreamBase(const StreamModuleConfig*);
168 void show(const SnortConfig*) const override;
169
170 void tear_down(SnortConfig*) override;
171
172 void tinit() override;
173 void tterm() override;
174
175 void eval(Packet*) override;
176
177 public:
178 StreamModuleConfig config;
179 };
180
StreamBase(const StreamModuleConfig * c)181 StreamBase::StreamBase(const StreamModuleConfig* c)
182 { config = *c; }
183
tear_down(SnortConfig * sc)184 void StreamBase::tear_down(SnortConfig* sc)
185 { sc->register_reload_resource_tuner(new StreamUnloadReloadResourceManager); }
186
tinit()187 void StreamBase::tinit()
188 {
189 assert(!flow_con && config.flow_cache_cfg.max_flows);
190
191 // this is temp added to suppress the compiler error only
192 flow_con = new FlowControl(config.flow_cache_cfg);
193 InspectSsnFunc f;
194
195 StreamHAManager::tinit();
196
197 if ( (f = InspectorManager::get_session(PROTO_BIT__IP)) )
198 flow_con->init_proto(PktType::IP, f);
199
200 if ( (f = InspectorManager::get_session(PROTO_BIT__ICMP)) )
201 flow_con->init_proto(PktType::ICMP, f);
202
203 if ( (f = InspectorManager::get_session(PROTO_BIT__TCP)) )
204 flow_con->init_proto(PktType::TCP, f);
205
206 if ( (f = InspectorManager::get_session(PROTO_BIT__UDP)) )
207 flow_con->init_proto(PktType::UDP, f);
208
209 if ( (f = InspectorManager::get_session(PROTO_BIT__PDU)) )
210 flow_con->init_proto(PktType::PDU, f);
211
212 if ( (f = InspectorManager::get_session(PROTO_BIT__FILE)) )
213 flow_con->init_proto(PktType::FILE, f);
214
215 if ( config.flow_cache_cfg.max_flows > 0 )
216 flow_con->init_exp(config.flow_cache_cfg.max_flows);
217
218 TcpStreamTracker::set_held_packet_timeout(config.held_packet_timeout);
219
220 #ifdef REG_TEST
221 FlushBucket::set(config.footprint);
222 #else
223 FlushBucket::set();
224 #endif
225 }
226
tterm()227 void StreamBase::tterm()
228 {
229 StreamHAManager::tterm();
230 FlushBucket::clear();
231 base_prep();
232 delete flow_con;
233 flow_con = nullptr;
234 }
235
show(const SnortConfig * sc) const236 void StreamBase::show(const SnortConfig* sc) const
237 {
238 if ( sc )
239 ConfigLogger::log_flag("ip_frags_only", sc->ip_frags_only());
240 config.show();
241 }
242
eval(Packet * p)243 void StreamBase::eval(Packet* p)
244 {
245 Profile profile(s5PerfStats);
246
247 if ( !is_eligible(p) )
248 return;
249
250 switch ( p->type() )
251 {
252 case PktType::NONE:
253 break;
254
255 case PktType::IP:
256 if ( p->has_ip() and ((p->ptrs.decode_flags & DECODE_FRAG) or
257 !p->context->conf->ip_frags_only()) )
258 {
259 bool new_flow = false;
260 flow_con->process(PktType::IP, p, &new_flow);
261 if ( new_flow )
262 DataBus::publish(STREAM_IP_NEW_FLOW_EVENT, p);
263 }
264 break;
265
266 case PktType::TCP:
267 if ( p->ptrs.tcph )
268 flow_con->process(PktType::TCP, p);
269 break;
270
271 case PktType::UDP:
272 if ( p->ptrs.decode_flags & DECODE_FRAG )
273 flow_con->process(PktType::IP, p);
274
275 if ( p->ptrs.udph )
276 {
277 bool new_flow = false;
278 flow_con->process(PktType::UDP, p, &new_flow);
279 if ( new_flow )
280 DataBus::publish(STREAM_UDP_NEW_FLOW_EVENT, p);
281 }
282 break;
283
284 case PktType::ICMP:
285 if ( p->ptrs.icmph )
286 {
287 bool new_flow = false;
288 if ( !flow_con->process(PktType::ICMP, p, &new_flow) )
289 flow_con->process(PktType::IP, p, &new_flow);
290 if ( new_flow )
291 DataBus::publish(STREAM_ICMP_NEW_FLOW_EVENT, p);
292 }
293 break;
294
295 case PktType::PDU:
296 flow_con->process(PktType::PDU, p);
297 break;
298
299 case PktType::FILE:
300 flow_con->process(PktType::FILE, p);
301 break;
302
303 case PktType::MAX:
304 break;
305 }
306 }
307
308 //-------------------------------------------------------------------------
309 // api stuff
310 //-------------------------------------------------------------------------
311
mod_ctor()312 static Module* mod_ctor()
313 { return new StreamModule; }
314
mod_dtor(Module * m)315 static void mod_dtor(Module* m)
316 { delete m; }
317
base_ctor(Module * m)318 static Inspector* base_ctor(Module* m)
319 {
320 StreamModule* mod = (StreamModule*)m;
321 return new StreamBase(mod->get_data());
322 }
323
base_dtor(Inspector * p)324 static void base_dtor(Inspector* p)
325 {
326 delete p;
327 }
328
base_tinit()329 static void base_tinit()
330 {
331 TcpStreamTracker::thread_init();
332 }
333
base_tterm()334 static void base_tterm()
335 {
336 StreamHAManager::tterm();
337 FlushBucket::clear();
338 TcpStreamTracker::thread_term();
339 }
340
341 static const InspectApi base_api =
342 {
343 {
344 PT_INSPECTOR,
345 sizeof(InspectApi),
346 INSAPI_VERSION,
347 0,
348 API_RESERVED,
349 API_OPTIONS,
350 MOD_NAME,
351 MOD_HELP,
352 mod_ctor,
353 mod_dtor
354 },
355 IT_STREAM,
356 PROTO_BIT__ANY_SSN,
357 nullptr, // buffers
358 nullptr, // service
359 nullptr, // init
360 nullptr, // term
361 base_tinit,
362 base_tterm,
363 base_ctor,
364 base_dtor,
365 nullptr, // ssn
366 nullptr // reset
367 };
368
369 const BaseApi* nin_stream_base = &base_api.base;
370