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