00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 // Copyright (C) 2013-2013 Sourcefire, Inc.
00004 //
00005 // This program is free software; you can redistribute it and/or modify it
00006 // under the terms of the GNU General Public License Version 2 as published
00007 // by the Free Software Foundation. You may not use, modify or distribute
00008 // this program under any other version of the GNU General Public License.
00009 //
00010 // This program is distributed in the hope that it will be useful, but
00011 // WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program; if not, write to the Free Software Foundation, Inc.,
00017 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 //--------------------------------------------------------------------------
00019 // flow_cache.cc author Russ Combs <rucombs@cisco.com>
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024
00025 #include "flow/flow_cache.h"
00026
00027 #include "flow/ha.h"
00028 #include "hash/zhash.h"
00029 #include "helpers/flag_context.h"
00030 #include "ips_options/ips_flowbits.h"
00031 #include "main/snort_debug.h"
00032 #include "packet_io/active.h"
00033 #include "time/packet_time.h"
00034 #include "utils/stats.h"
00035
00036
00037 #include "flow_key.h"
00038
00039 #define SESSION_CACHE_FLAG_PURGING 0x01
00040
00041
00042
00043
00044 #include <stdlib.h>
00045 #include <iostream>
00046
00047
00048 #include <cppconn/driver.h>
00049 #include <cppconn/exception.h>
00050 #include <cppconn/resultset.h>
00051 #include <cppconn/statement.h>
00052 #include <cppconn/prepared_statement.h>
00053
00054 //#include "main/sql_stuff.h"
00055
00056
00057
00058 //#include <stdio.h>
00059
00060
00061 using namespace std;
00062
00063
00064
00065 //-------------------------------------------------------------------------
00066 // FlowCache stuff
00067 //-------------------------------------------------------------------------
00068
00069 FlowCache::FlowCache (const FlowConfig& cfg) : config(cfg)
00070 {
00071 hash_table = new ZHash(config.max_sessions, sizeof(FlowKey));
00072 hash_table->set_keyops(FlowKey::hash, FlowKey::compare);
00073
00074 uni_head = new Flow;
00075 uni_tail = new Flow;
00076
00077 uni_head->next = uni_tail;
00078 uni_tail->prev = uni_head;
00079
00080 uni_count = 0;
00081 flags = 0x0;
00082
00083 assert(prune_stats.get_total() == 0);
00084
00085
00086 }
00087
00088 FlowCache::~FlowCache ()
00089 {
00090 while ( Flow* flow = (Flow*)hash_table->pop() )
00091 flow->term();
00092
00093 delete uni_head;
00094 delete uni_tail;
00095
00096 delete hash_table;
00097 }
00098
00099 void FlowCache::push(Flow* flow)
00100 {
00101 void* key = hash_table->push(flow);
00102 flow->key = (FlowKey*)key;
00103 //new_flow(flow);
00104 }
00105
00106 unsigned FlowCache::get_count()
00107 {
00108 return hash_table ? hash_table->get_count() : 0;
00109 }
00110
00111 Flow* FlowCache::find(const FlowKey* key)
00112 {
00113 Flow* flow = (Flow*)hash_table->find(key);
00114
00115 if ( flow )
00116 {
00117 time_t t = packet_time();
00118
00119 if ( flow->last_data_seen < t )
00120 flow->last_data_seen = t;
00121 }
00122
00123 return flow;
00124 }
00125
00126 // always prepend
00127 void FlowCache::link_uni(Flow* flow)
00128 {
00129 flow->next = uni_head->next;
00130 flow->prev = uni_head;
00131
00132 uni_head->next->prev = flow;
00133 uni_head->next = flow;
00134
00135 ++uni_count;
00136 }
00137
00138 // but remove from any point
00139 void FlowCache::unlink_uni(Flow* flow)
00140 {
00141 if ( !flow->next )
00142 return;
00143
00144 --uni_count;
00145
00146 flow->next->prev = flow->prev;
00147 flow->prev->next = flow->next;
00148
00149 flow->next = flow->prev = nullptr;
00150 }
00151
00152 Flow* FlowCache::get(const FlowKey* key)
00153 {
00154 time_t timestamp = packet_time();
00155 Flow* flow = (Flow*)hash_table->get(key);
00156
00157 if ( !flow )
00158 {
00159 if ( !prune_stale(timestamp, nullptr) )
00160 {
00161 if ( !prune_unis() )
00162 prune_excess(nullptr);
00163 }
00164
00165 flow = (Flow*)hash_table->get(key);
00166
00167 assert(flow);
00168 flow->reset();
00169 link_uni(flow);
00170
00171 }
00172
00173
00174
00175 flow->last_data_seen = timestamp;
00176 //new_flow(flow);
00177
00178 return flow;
00179 }
00180
00181 int FlowCache::release(Flow* flow, PruneReason reason, bool do_cleanup)
00182 {
00183 flow->reset(do_cleanup);
00184 prune_stats.update(reason);
00185 return remove(flow);
00186 }
00187
00188 int FlowCache::remove(Flow* flow)
00189 {
00190 if ( flow->next )
00191 unlink_uni(flow);
00192
00193 return hash_table->remove(flow->key);
00194 }
00195
00196 unsigned FlowCache::prune_stale(uint32_t thetime, const Flow* save_me)
00197 {
00198 ActiveSuspendContext act_susp;
00199
00200 unsigned pruned = 0;
00201 auto flow = static_cast<Flow*>(hash_table->first());
00202
00203 while ( flow and pruned <= cleanup_flows )
00204 {
00205 #if 0
00206 // FIXIT-H this loops forever if 1 flow in cache
00207 if (flow == save_me)
00208 {
00209 break;
00210 if ( hash_table->get_count() == 1 )
00211 break;
00212
00213 hash_table->touch();
00214 }
00215 #else
00216 // Reached the current flow. This *should* be the newest flow
00217 if ( flow == save_me )
00218 {
00219 // assert( flow->last_data_seen + config.pruning_timeout >= thetime );
00220 // bool rv = hash_table->touch(); assert( !rv );
00221 break;
00222 }
00223 #endif
00224 if ( flow->is_offloaded() )
00225 break;
00226
00227 if ( flow->last_data_seen + config.pruning_timeout >= thetime )
00228 break;
00229
00230 DebugMessage(DEBUG_STREAM, "pruning stale flow\n");
00231 flow->ssn_state.session_flags |= SSNFLAG_TIMEDOUT;
00232 release(flow, PruneReason::IDLE);
00233 ++pruned;
00234
00235 flow = static_cast<Flow*>(hash_table->first());
00236 }
00237
00238 return pruned;
00239 }
00240
00241 unsigned FlowCache::prune_unis()
00242 {
00243 ActiveSuspendContext act_susp;
00244
00245 // we may have many or few unis; need to find reasonable ratio
00246 // FIXIT-M max_uni should be based on typical ratios seen in perfmon
00247 const unsigned max_uni = (config.max_sessions >> 2) + 1;
00248
00249 Flow* curr = uni_tail->prev;
00250 unsigned pruned = 0;
00251
00252 while ( (uni_count > max_uni) && curr && (pruned < cleanup_flows) )
00253 {
00254 Flow* flow = curr;
00255 curr = curr->prev;
00256
00257 if ( flow->was_blocked() )
00258 continue;
00259
00260 release(flow, PruneReason::UNI);
00261 ++pruned;
00262 }
00263
00264 return pruned;
00265 }
00266
00267 unsigned FlowCache::prune_excess(const Flow* save_me)
00268 {
00269 ActiveSuspendContext act_susp;
00270
00271 auto max_cap = config.max_sessions - cleanup_flows;
00272 assert(max_cap > 0);
00273
00274 unsigned pruned = 0;
00275 unsigned blocks = 0;
00276
00277 while ( hash_table->get_count() > max_cap and hash_table->get_count() > blocks )
00278 {
00279 auto flow = static_cast<Flow*>(hash_table->first());
00280 assert(flow); // holds true because hash_table->get_count() > 0
00281
00282 if ( (save_me and flow == save_me) or flow->was_blocked() or
00283 flow->is_offloaded() )
00284 {
00285 // check for non-null save_me above to silence analyzer
00286 // "called C++ object pointer is null" here
00287 if ( flow->was_blocked() )
00288 ++blocks;
00289
00290 // FIXIT-M we should update last_data_seen upon touch to ensure
00291 // the hash_table LRU list remains sorted by time
00292 if ( !hash_table->touch() )
00293 break;
00294 }
00295
00296 else
00297 {
00298 flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
00299 release(flow, PruneReason::EXCESS);
00300 ++pruned;
00301 }
00302 }
00303
00304 return pruned;
00305 }
00306
00307 bool FlowCache::prune_one(PruneReason reason, bool do_cleanup)
00308 {
00309
00310 // so we don't prune the current flow (assume current == MRU)
00311 if ( hash_table->get_count() <= 1 )
00312 return false;
00313
00314 auto flow = static_cast<Flow*>(hash_table->first());
00315 assert(flow);
00316
00317 flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
00318 release(flow, reason, do_cleanup);
00319
00320 #ifdef DEBUG_MSGS
00321 const char* s =
00322 (reason == PruneReason::MEMCAP) ? "memcap" :
00323 (reason == PruneReason::PREEMPTIVE) ? "preemptive" :
00324 "other";
00325
00326 DebugFormat(DEBUG_MEMORY, "prune one for reason %s\n", s);
00327 #endif
00328
00329 return true;
00330 }
00331
00332 unsigned FlowCache::timeout(unsigned num_flows, time_t thetime)
00333 {
00334 // FIXIT-H should Active be suspended here too?
00335 unsigned retired = 0;
00336
00337 auto flow = static_cast<Flow*>(hash_table->current());
00338
00339 if ( !flow )
00340 flow = static_cast<Flow*>(hash_table->first());
00341
00342 while ( flow and (retired < num_flows) )
00343 {
00344 if ( flow->last_data_seen + config.nominal_timeout > thetime )
00345 break;
00346
00347 if ( HighAvailabilityManager::in_standby(flow) or
00348 flow->is_offloaded() )
00349 {
00350 flow = static_cast<Flow*>(hash_table->next());
00351 continue;
00352 }
00353
00354 DebugMessage(DEBUG_STREAM, "retiring stale flow\n");
00355 flow->ssn_state.session_flags |= SSNFLAG_TIMEDOUT;
00356 release(flow, PruneReason::IDLE);
00357
00358 ++retired;
00359
00360 flow = static_cast<Flow*>(hash_table->current());
00361 }
00362
00363 return retired;
00364 }
00365
00366 // Remove all flows from the hash table.
00367 unsigned FlowCache::purge()
00368 {
00369 ActiveSuspendContext act_susp;
00370 FlagContext<decltype(flags)>(flags, SESSION_CACHE_FLAG_PURGING);
00371
00372 unsigned retired = 0;
00373
00374 while ( auto flow = static_cast<Flow*>(hash_table->first()) )
00375 {
00376 release(flow, PruneReason::NONE);
00377 ++retired;
00378 }
00379
00380 return retired;
00381 }
00382
END OF CODE