#include "config.h"
#endif
-#include "packet_io/active.h"
-#include "time/packet_time.h"
-#include "ips_options/ips_flowbits.h"
#include "hash/zhash.h"
+#include "helpers/flag_context.h"
+#include "ips_options/ips_flowbits.h"
#include "main/snort_debug.h"
+#include "packet_io/active.h"
+#include "time/packet_time.h"
#define SESSION_CACHE_FLAG_PURGING 0x01
+uint64_t PruneStats::get_total() const
+{
+ uint64_t total = 0;
+ for ( reason_t i = 0;
+ i < static_cast<reason_t>(PruneReason::MAX); ++i )
+ {
+ total += prunes[i];
+ }
+
+ return total;
+}
+
//-------------------------------------------------------------------------
// FlowCache stuff
//-------------------------------------------------------------------------
else
cleanup_flows = cleanup_count;
+ if ( cleanup_flows >= cfg.max_sessions )
+ cleanup_flows = cfg.max_sessions - 1;
+
if ( !cleanup_flows )
cleanup_flows = 1;
uni_head->next = uni_tail;
uni_tail->prev = uni_head;
- prunes = uni_count = 0;
+ uni_count = 0;
flags = 0x0;
+
+ assert(prune_stats.get_total() == 0);
}
FlowCache::~FlowCache ()
flow->last_data_seen = t;
}
- last = flow;
return flow;
}
if ( !prune_stale(timestamp, nullptr) )
{
if ( !prune_unis() )
- prune_excess(false, nullptr);
+ prune_excess(nullptr);
}
flow = (Flow*)hash_table->get(key);
}
flow->last_data_seen = timestamp;
- last = flow;
return flow;
}
-int FlowCache::release(Flow* flow, const char*)
+int FlowCache::release(Flow* flow, PruneReason reason)
{
flow->reset();
+ prune_stats.update(reason);
return remove(flow);
}
uint32_t FlowCache::prune_stale(uint32_t thetime, const Flow* save_me)
{
- Flow* flow;
- uint32_t pruned = 0;
- Active::suspend();
+ ActiveSuspendContext act_susp;
- /* Pruning, look for flows that have time'd out */
- flow = (Flow*)hash_table->first();
+ uint32_t pruned = 0;
+ auto flow = static_cast<Flow*>(hash_table->first());
- while ( flow )
+ while ( flow and pruned <= cleanup_flows )
{
+#if 0
// FIXIT-L this loops forever if 1 flow in cache
if (flow == save_me)
{
+ break;
if ( hash_table->get_count() == 1 )
break;
hash_table->touch();
}
-
- else if ((flow->last_data_seen + config.pruning_timeout) < thetime)
+#else
+ // Reached the current flow. This *should* be the newest flow
+ if ( flow == save_me )
{
- DebugMessage(DEBUG_STREAM, "pruning stale flow\n");
- flow->ssn_state.session_flags |= SSNFLAG_TIMEDOUT;
- release(flow, "stale/timeout");
- pruned++;
+ // assert( flow->last_data_seen + config.pruning_timeout >= thetime );
+ // bool rv = hash_table->touch(); assert( !rv );
+ break;
}
- else
+#endif
+ if ( flow->last_data_seen + config.pruning_timeout >= thetime )
break;
- if (pruned > cleanup_flows)
- break;
+ DebugMessage(DEBUG_STREAM, "pruning stale flow\n");
+ flow->ssn_state.session_flags |= SSNFLAG_TIMEDOUT;
+ release(flow, PruneReason::TIMEOUT);
+ ++pruned;
- flow = (Flow*)hash_table->first();
+ flow = static_cast<Flow*>(hash_table->first());
}
- prunes += pruned;
- Active::resume();
return pruned;
}
uint32_t FlowCache::prune_unis()
{
+ ActiveSuspendContext act_susp;
+
// we may have many or few unis; need to find reasonable ratio
// FIXIT-L max_uni should be based on typical ratios seen in perfmon
const uint32_t max_uni = (config.max_sessions >> 2) + 1;
Flow* curr = uni_tail->prev;
uint32_t pruned = 0;
- Active::suspend();
while ( (uni_count > max_uni) && curr && (pruned < cleanup_flows) )
{
if ( flow->was_blocked() )
continue;
- release(flow, "unidirectional");
+ release(flow, PruneReason::UNI);
++pruned;
}
- prunes += pruned;
- Active::resume();
+
return pruned;
}
-uint32_t FlowCache::prune_excess(bool memCheck, const Flow* save_me)
+uint32_t FlowCache::prune_excess(const Flow* save_me)
{
- /* Free up 'n' flows at a time until we get under the
- * memcap or free enough flows to be able to create
- * new ones.
- */
- const uint32_t max_cap = config.max_sessions - cleanup_flows;
+ ActiveSuspendContext act_susp;
+
+ auto max_cap = config.max_sessions - cleanup_flows;
+ assert(max_cap > 0);
+
uint32_t pruned = 0;
- Active::suspend();
+ uint32_t blocks = 0;
- while (
- (hash_table->get_count() > 1) &&
- ((!memCheck && ((hash_table->get_count() > max_cap) || !pruned)) ||
- (memCheck && memcap.at_max()) ))
+ while ( hash_table->get_count() > max_cap and hash_table->get_count() > blocks )
{
- unsigned int blocks = 0;
- Flow* flow = (Flow*)hash_table->first();
+ auto flow = static_cast<Flow*>(hash_table->first());
+ assert(flow); // holds true because hash_table->get_count() > 0
- for (unsigned i=0; i<cleanup_flows &&
- (hash_table->get_count() > blocks); i++)
+ if ( flow == save_me or flow->was_blocked() )
{
- if ( (flow != save_me) && (!memCheck || !flow->was_blocked()) )
- {
- flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
- release(flow, memCheck ? "memcap/check" : "memcap/stale");
- pruned++;
- }
- else
- {
- if ( flow && flow->was_blocked() )
- blocks++;
-
- if ( !hash_table->touch() )
- break; // this flow is the only one left
-
- i--; /* Didn't clean this one */
- }
- flow = (Flow*)hash_table->first();
+ if ( flow->was_blocked() )
+ ++blocks;
+
+ // FIXIT-M J we should update last_data_seen upon touch to ensure
+ // the hash_table LRU list remains sorted by time
+ if ( !hash_table->touch() )
+ break;
}
- /* Nothing (or the one we're working with) in table, couldn't kill it */
- if (!memCheck && (pruned == 0))
- break;
+ else
+ {
+ flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
+ release(flow, PruneReason::EXCESS);
+ ++pruned;
+ }
}
- prunes += pruned;
- Active::resume();
+
return pruned;
}
-void FlowCache::timeout(uint32_t flowCount, time_t cur_time)
+bool FlowCache::prune_one(PruneReason reason)
{
- uint32_t flowRetiredCount = 0, flowExaminedCount = 0;
- uint32_t flowMax = flowCount * 2;
+ // so we don't prune the current flow (assume current == MRU)
+ if ( hash_table->get_count() <= 1 )
+ return false;
+
+ auto flow = static_cast<Flow*>(hash_table->first());
+ assert(flow);
- Flow* flow = (Flow*)hash_table->current();
+ flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
+ release(flow, reason);
+
+ return true;
+}
+
+void FlowCache::timeout(uint32_t num_flows, time_t thetime)
+{
+ uint32_t retired = 0;
+
+ auto flow = static_cast<Flow*>(hash_table->current());
if ( !flow )
- flow = (Flow*)hash_table->first();
+ flow = static_cast<Flow*>(hash_table->first());
- while ( flow && flowRetiredCount < flowCount && flowExaminedCount < flowMax )
+ while ( flow and retired < num_flows )
{
- if ((time_t)(flow->last_data_seen + config.nominal_timeout) > cur_time)
+ if ( flow->last_data_seen + config.nominal_timeout > thetime )
break;
- flowExaminedCount++;
-
DebugMessage(DEBUG_STREAM, "retiring stale flow\n");
flow->ssn_state.session_flags |= SSNFLAG_TIMEDOUT;
- release(flow, "stale/timeout");
+ release(flow, PruneReason::TIMEOUT);
- flowRetiredCount++;
- flow = (Flow*)hash_table->current();
+ ++retired;
+
+ flow = static_cast<Flow*>(hash_table->current());
}
}
-/* Remove all flows from the hash table. */
+// Remove all flows from the hash table.
int FlowCache::purge()
{
- int retCount = 0;
+ ActiveSuspendContext act_susp;
+ FlagContext<decltype(flags)>(flags, SESSION_CACHE_FLAG_PURGING);
- Active::suspend();
- flags |= SESSION_CACHE_FLAG_PURGING;
- Flow* flow = (Flow*)hash_table->first();
+ uint32_t retired = 0;
- while ( flow )
+ while ( auto flow = static_cast<Flow*>(hash_table->first()) )
{
flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
- release(flow, "purge whole cache");
- retCount++;
- flow = (Flow*)hash_table->first();
+ release(flow, PruneReason::PURGE);
+ ++retired;
}
- flags &= ~SESSION_CACHE_FLAG_PURGING;
- Active::resume();
-
- return retCount;
+ return retired;
}
// there is a FlowCache instance for each protocol.
// Flows are stored in a ZHash instance by FlowKey.
+#include <ctime>
+#include <type_traits>
+
#include "flow/flow_config.h"
-#include "flow/flow_key.h"
#include "flow/memcap.h"
-#include "stream/stream.h"
+
+class Flow;
+struct FlowKey;
+
+// FIXIT-L J we can probably fiddle with these breakdowns
+enum class PruneReason : uint8_t
+{
+ PURGE = 0,
+ TIMEOUT,
+ EXCESS,
+ UNI,
+ HA_SYNC,
+ CLOSED,
+ USER,
+ MAX
+};
+
+struct PruneStats
+{
+ using reason_t = std::underlying_type<PruneReason>::type;
+
+ uint32_t prunes[static_cast<reason_t>(PruneReason::MAX)] { };
+
+ uint64_t get_total() const;
+ void update(PruneReason reason)
+ { ++prunes[static_cast<reason_t>(reason)]; }
+};
class FlowCache
{
Flow* find(const FlowKey*);
Flow* get(const FlowKey*);
- int release(Flow*, const char* reason);
+ int release(Flow*, PruneReason = PruneReason::USER);
uint32_t prune_unis();
uint32_t prune_stale(uint32_t thetime, const Flow* save_me);
- uint32_t prune_excess(bool memCheck, const Flow* save_me);
- uint32_t prune_excess() { return prune_excess(false, last); }
- void timeout(uint32_t flowCount, time_t cur_time);
+ uint32_t prune_excess(const Flow* save_me);
+ bool prune_one(PruneReason);
+ void timeout(uint32_t num_flows, time_t cur_time);
int purge();
int get_count();
- uint32_t get_max_flows() { return config.max_sessions; }
- uint32_t get_prunes() { return prunes; }
- void reset_prunes() { prunes = 0; }
+ uint32_t get_max_flows() const
+ { return config.max_sessions; }
+
+ uint64_t get_prunes() const
+ { return prune_stats.get_total(); }
+
+ void reset_prunes()
+ { prune_stats = PruneStats(); }
void unlink_uni(Flow*);
private:
const FlowConfig& config;
uint32_t cleanup_flows;
- uint32_t prunes;
uint32_t uni_count;
uint32_t flags;
class ZHash* hash_table;
Flow* uni_head, * uni_tail;
- const Flow* last = nullptr;
+ PruneStats prune_stats;
};
#endif
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-#include "flow/flow_control.h"
+#include "flow_control.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include <assert.h>
-#include <arpa/inet.h>
+#include <cassert>
-#include "flow/flow_cache.h"
+#include "detection/detect.h"
#include "flow/expect_cache.h"
+#include "flow/flow_cache.h"
#include "flow/session.h"
-#include "packet_io/active.h"
-#include "packet_io/sfdaq.h"
-#include "utils/stats.h"
-#include "protocols/layer.h"
-#include "protocols/vlan.h"
#include "managers/inspector_manager.h"
-#include "sfip/sf_ip.h"
-#include "protocols/tcp.h"
-#include "protocols/udp.h"
+#include "packet_io/active.h"
#include "protocols/icmp4.h"
#include "protocols/icmp6.h"
-#include "detection/detect.h"
+#include "protocols/tcp.h"
+#include "protocols/udp.h"
+#include "protocols/vlan.h"
+#include "sfip/sf_ip.h"
FlowControl::FlowControl()
{
get_ip = get_icmp = nullptr;
get_tcp = get_udp = nullptr;
get_user = get_file = nullptr;
+
+ last_pkt_type = PktType::NONE;
}
FlowControl::~FlowControl()
Flow* flow = cache->find(key);
if ( flow )
- cache->release(flow, "ha sync");
+ cache->release(flow, PruneReason::HA_SYNC);
}
-void FlowControl::delete_flow(Flow* flow, const char* why)
+void FlowControl::delete_flow(Flow* flow, PruneReason reason)
{
FlowCache* cache = get_cache(flow->protocol);
if ( cache )
- cache->release(flow, why);
+ cache->release(flow, reason);
}
void FlowControl::purge_flows (PktType proto)
if (!cache->prune_stale(p->pkth->ts.tv_sec, (Flow*)p->flow))
{
// if no luck, try the memcap
- cache->prune_excess(true, (Flow*)p->flow);
+ cache->prune_excess((Flow*)p->flow);
}
}
-void FlowControl::prune_flows(PktType proto)
-{
- auto cache = get_cache(proto);
- if ( !cache )
- return;
-
- cache->prune_excess();
-}
-
-void FlowControl::prune_flows()
+// hole for memory manager/prune handler
+bool FlowControl::prune_one(PruneReason reason)
{
- prune_flows(PktType::IP);
- prune_flows(PktType::ICMP);
- prune_flows(PktType::TCP);
- prune_flows(PktType::UDP);
- prune_flows(PktType::PDU);
- prune_flows(PktType::FILE);
+ auto cache = get_cache(last_pkt_type);
+ return cache ? cache->prune_one(reason) : false;
}
void FlowControl::timeout_flows(uint32_t flowCount, time_t cur_time)
// this is where all the flow caches are managed and where all flows are
// processed. flows are pruned as needed to process new flows.
-#include "flow/flow.h"
+#include <cstdint>
+
#include "flow/flow_config.h"
+#include "framework/decode_data.h"
+#include "framework/inspector.h"
#include "utils/stats.h"
+class Flow;
+class FlowData;
+struct FlowKey;
+struct Packet;
+struct sfip_t;
+
+enum class PruneReason : uint8_t;
+
class FlowControl
{
public:
void init_exp(uint32_t max);
void delete_flow(const FlowKey*);
- void delete_flow(Flow*, const char* why);
+ void delete_flow(Flow*, PruneReason);
void purge_flows(PktType);
void prune_flows(PktType, Packet*);
- void prune_flows(PktType);
- void prune_flows();
+ bool prune_one(PruneReason);
void timeout_flows(uint32_t flowCount, time_t cur_time);
char expected_flow(Flow*, Packet*);
InspectSsnFunc get_file;
class ExpectCache* exp_cache;
+ PktType last_pkt_type;
};
#endif
// Captures decode information from Codecs.
+#include <cstdint>
#include <type_traits>
#include "protocols/mpls.h"
ring.h
ring_logic.h
swapper.h
+ flag_context.h
)
target_link_libraries(helpers
process.h \
ring.h \
ring_logic.h \
-swapper.h
-
+swapper.h \
+flag_context.h
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// flag_context.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef FLAG_CONTEXT_H
+#define FLAG_CONTEXT_H
+
+template<typename T>
+struct FlagContext
+{
+ FlagContext(T& dst, T flags) : dst(dst), flags(flags)
+ { dst |= flags; }
+
+ ~FlagContext()
+ { dst &= ~flags; }
+
+ T& dst;
+ T flags;
+};
+
+#endif
assert(requested <= cap);
const auto required = cap - requested;
+ // FIXIT-H J call handler repeatedly until unsuccessful or memory
+ // falls below 'required'
if ( trk.used() > required )
handler();
#include "prune_handler.h"
-#include <cassert>
-
-#include "stream/stream.h"
+#include "flow/flow_cache.h"
#include "flow/flow_control.h"
+#include "stream/stream.h"
namespace memory
{
{
// assert(flow_con);
if ( flow_con )
- flow_con->prune_flows();
+ flow_con->prune_one(PruneReason::USER);
}
} // namespace memory
static bool s_enabled;
};
+struct ActiveSuspendContext
+{
+ ActiveSuspendContext() { Active::suspend(); }
+ ~ActiveSuspendContext() { Active::resume(); }
+};
+
#endif
#ifndef PROTOCOLS_MPLS_H
#define PROTOCOLS_MPLS_H
+#include <cstdint>
+
namespace mpls
{
struct MplsHdr
};
} // namespace mpls
+// FIXIT-L J constexpr != const, they are orthogonal keywords
constexpr int MPLS_PAYLOADTYPE_ETHERNET = 1;
constexpr int MPLS_PAYLOADTYPE_IPV4 = 2;
constexpr int MPLS_PAYLOADTYPE_IPV6 = 3;
if (flow->session_state & STREAM_STATE_CLOSED)
{
assert(flow_con);
- flow_con->delete_flow(flow, "closed");
- p->flow = NULL;
+ flow_con->delete_flow(flow, PruneReason::CLOSED);
+ p->flow = nullptr;
}
}