detection_util.h
detect_trace.h
ips_context.h
+ ips_context_chain.h
ips_context_data.h
regex_offload.h
rule_option_types.h
fp_utils.cc
fp_utils.h
ips_context.cc
+ ips_context_chain.cc
ips_context_data.cc
pattern_match_data.h
pcrm.cc
using namespace snort;
+static THREAD_LOCAL uint64_t global_context_num = 0;
+
//--------------------------------------------------------------------------
// context switcher methods
//--------------------------------------------------------------------------
-ContextSwitcher::ContextSwitcher(unsigned max) :
- hold(max+1, nullptr) // use 1-based index / skip hold[0]
-{
-}
-
ContextSwitcher::~ContextSwitcher()
{
abort();
- for ( auto* p : idle )
+ for ( auto* p : contexts )
delete p;
}
void ContextSwitcher::push(IpsContext* c)
{
- c->set_slot(idle.size() + 1);
+ assert(c->state == IpsContext::IDLE);
idle.emplace_back(c);
+ contexts.emplace_back(c);
}
-IpsContext* ContextSwitcher::pop()
+void ContextSwitcher::start()
{
- if ( idle.empty() )
- return nullptr;
+ assert(busy.empty());
+ assert(!idle.empty());
IpsContext* c = idle.back();
+ assert(c->state == IpsContext::IDLE);
assert(!c->has_callbacks());
- idle.pop_back();
- return c;
-}
+
+ c->context_num = ++global_context_num;
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::start %" PRIu64 " (i=%zu, b=%zu)\n",
+ get_packet_number(), c->context_num, idle.size(), busy.size());
-void ContextSwitcher::start()
-{
- assert(busy.empty());
- assert(!idle.empty());
- trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::start %u (i=%zu, b=%zu)\n",
- get_packet_number(), idle.back()->get_slot(), idle.size(), busy.size());
- assert(!idle.back()->has_callbacks());
- busy.emplace_back(idle.back());
idle.pop_back();
- IpsContext* c = busy.back();
c->packet->active = c->packet->active_inst;
c->packet->active->reset();
c->packet->action = &c->packet->action_inst;
+ c->state = IpsContext::BUSY;
+
+ busy.emplace_back(c);
}
void ContextSwitcher::stop()
{
assert(busy.size() == 1);
- trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::stop %u (i=%zu, b=%zu)\n",
- get_packet_number(), busy.back()->get_slot(), idle.size(), busy.size());
IpsContext* c = busy.back();
+ assert(c->state == IpsContext::BUSY);
+
assert(!c->has_callbacks());
+ assert(!c->dependencies());
+
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::stop %" PRIu64 " (i=%zu, b=%zu)\n",
+ get_packet_number(), c->context_num, idle.size(), busy.size());
+
+ busy.pop_back();
+
c->clear_context_data();
+ c->packet->active = nullptr;
+ c->packet->action = nullptr;
+ c->state = IpsContext::IDLE;
+
idle.emplace_back(c);
- busy.back()->packet->active = nullptr;
- busy.back()->packet->action = nullptr;
- busy.pop_back();
}
void ContextSwitcher::abort()
{
trace_logf(detection, TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " cs::abort (i=%zu, b=%zu)\n",
get_packet_number(), idle.size(), busy.size());
- for ( unsigned i = 0; i < hold.capacity(); ++i )
+
+ busy.clear();
+
+ for ( IpsContext* c : contexts )
{
- if ( hold[i] )
+ switch ( c->state )
{
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::abort hold",
- hold[i]->packet_number);
-
- idle.emplace_back(hold[i]);
- hold[i] = nullptr;
- idle.back()->clear_callbacks();
- idle.back()->clear_context_data();
+ case IpsContext::IDLE:
+ continue;
+ case IpsContext::BUSY:
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::abort busy",
+ c->packet_number);
+ break;
+ case IpsContext::SUSPENDED:
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::abort suspended",
+ c->packet_number);
+ break;
}
- }
- while ( !busy.empty() )
- {
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::abort busy",
- busy[0]->packet_number);
- idle.emplace_back(busy.back());
- busy.pop_back();
- idle.back()->clear_callbacks();
- idle.back()->clear_context_data();
+ if ( c->packet->flow )
+ c->packet->flow->context_chain.abort();
+
+ c->abort();
+ c->state = IpsContext::IDLE;
+ c->clear_callbacks();
+ c->clear_context_data();
+ idle.emplace_back(c);
}
+ non_flow_chain.abort();
}
IpsContext* ContextSwitcher::interrupt()
{
assert(!idle.empty());
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::interrupt %u (i=%zu, b=%zu)\n",
- idle.back()->packet_number, idle.back()->get_slot(), idle.size(), busy.size());
-
assert(!idle.back()->has_callbacks());
- busy.emplace_back(idle.back());
+
+ IpsContext* c = idle.back();
+ assert(c->state == IpsContext::IDLE);
+
+ c->context_num = ++global_context_num;
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::interrupt %" PRIu64 " (i=%zu, b=%zu)\n",
+ busy.empty() ? get_packet_number() : busy.back()->packet_number,
+ busy.empty() ? 0 : busy.back()->context_num, idle.size(), busy.size());
+
idle.pop_back();
- return busy.back();
+
+ c->state = IpsContext::BUSY;
+ busy.emplace_back(c);
+ return c;
}
IpsContext* ContextSwitcher::complete()
{
assert(!busy.empty());
+
IpsContext* c = busy.back();
+ assert(c->state == IpsContext::BUSY);
+ assert(!c->dependencies());
+ assert(!c->has_callbacks());
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::complete %u (i=%zu, b=%zu)\n",
- c->packet_number, busy.back()->get_slot(), idle.size(), busy.size());
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::complete %" PRIu64 " (i=%zu, b=%zu)\n",
+ c->packet_number, c->context_num, idle.size(), busy.size());
- assert(!c->has_callbacks());
+ busy.pop_back();
c->clear_context_data();
-
+ c->state = IpsContext::IDLE;
idle.emplace_back(c);
- busy.pop_back();
- return busy.empty() ? nullptr : busy.back();
+
+ if ( busy.empty() )
+ return nullptr;
+
+ return busy.back();
}
-unsigned ContextSwitcher::suspend()
+void ContextSwitcher::suspend()
{
assert(!busy.empty());
- IpsContext* c = busy.back();
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::suspend %u (i=%zu, b=%zu)\n",
- c->packet_number, busy.back()->get_slot(), idle.size(), busy.size());
+ IpsContext* c = busy.back();
+ assert(c->state == IpsContext::BUSY);
+
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::suspend %" PRIu64 " (i=%zu, b=%zu, wh=%zu)\n",
+ c->packet_number, c->context_num, idle.size(), busy.size(), contexts.size() - idle.size() - busy.size());
+ c->state = IpsContext::SUSPENDED;
busy.pop_back();
- unsigned slot = c->get_slot();
- assert(!hold[slot]);
- hold[slot] = c;
- return slot;
+
+ if ( c->packet->flow )
+ c->packet->flow->context_chain.push_back(c);
+ else
+ non_flow_chain.push_back(c);
}
-void ContextSwitcher::resume(unsigned slot)
+void ContextSwitcher::resume(IpsContext* c)
{
- assert(slot <= hold.capacity());
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::resume %u (i=%zu, b=%zu)\n",
- hold[slot]->packet_number, slot, idle.size(), busy.size());
- busy.emplace_back(hold[slot]);
- hold[slot] = nullptr;
+ assert(c->state == IpsContext::SUSPENDED);
+
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " cs::resume %" PRIu64 " (i=%zu)\n",
+ c->packet_number, c->context_num, idle.size());
+
+ IpsContextChain& chain = c->packet->flow ? c->packet->flow->context_chain : non_flow_chain;
+ assert(c == chain.front());
+ assert(!c->dependencies());
+ chain.pop();
+
+ c->state = IpsContext::BUSY;
+ busy.emplace_back(c);
}
IpsContext* ContextSwitcher::get_context() const
return busy.back();
}
-IpsContext* ContextSwitcher::get_context(unsigned slot) const
-{
- assert(slot <= hold.capacity());
- IpsContext* c = hold[slot];
- assert(c);
- return c;
-}
-
IpsContext* ContextSwitcher::get_next() const
{
assert(!idle.empty());
}
IpsContextData* ContextSwitcher::get_context_data(unsigned id) const
-{
- return get_context()->get_context_data(id);
-}
+{ return get_context()->get_context_data(id); }
void ContextSwitcher::set_context_data(unsigned id, IpsContextData* cd) const
-{
- get_context()->set_context_data(id, cd);
-}
+{ get_context()->set_context_data(id, cd); }
unsigned ContextSwitcher::idle_count() const
{ return idle.size(); }
unsigned ContextSwitcher::busy_count() const
{ return busy.size(); }
-unsigned ContextSwitcher::hold_count() const
-{
- unsigned c = 0;
-
- for ( auto* p : hold )
- if ( p ) c++;
-
- return c;
-}
-
-bool ContextSwitcher::on_hold(Flow* f)
-{
- for ( unsigned i = 0; i < hold.capacity(); ++i )
- {
- IpsContext* c = hold[i];
- if ( c and c->packet and c->packet->flow == f )
- return true;
- }
- return false;
-}
-
//--------------------------------------------------------------------------
// unit tests
//--------------------------------------------------------------------------
ContextData(int) { }
};
-TEST_CASE("ContextSwitcher normal", "[ContextSwitcher]")
+TEST_CASE("ContextSwitcher single wire", "[ContextSwitcher]")
{
- const unsigned max = 3;
- auto mgr = ContextSwitcher(max);
- auto id = IpsContextData::get_ips_id();
- CHECK(!mgr.pop());
+ const unsigned max = 10;
+ ContextSwitcher mgr;
for ( unsigned i = 0; i < max; ++i )
- mgr.push(new IpsContext(id+1));
-
- SECTION("workflow")
+ mgr.push(new IpsContext);
+
+ IpsContext *c1, *c2, *c3, *c4, *c5, *c6, *c7, *c8, *c9;
+
+ /*
+ __1__
+ / \
+ _2_ _3_
+ / | \ / | \
+ *4*5 6 7 8 9
+
+ 6 2 7 8 9 3 1
+ */
+
+ mgr.start();
+
+ c1 = mgr.get_context();
+ CHECK(c1->state == IpsContext::BUSY);
+ c2 = mgr.interrupt();
+ CHECK(c2->state == IpsContext::BUSY);
+ c4 = mgr.interrupt();
+ CHECK(c4->state == IpsContext::BUSY);
+
+ mgr.complete();
+ CHECK(c4->state == IpsContext::IDLE);
+
+ c5 = mgr.interrupt();
+ CHECK(c5->state == IpsContext::BUSY);
+ mgr.complete();
+ CHECK(c5->state == IpsContext::IDLE);
+
+ c6 = mgr.interrupt();
+ CHECK(c6->state == IpsContext::BUSY);
+ c6->packet->set_offloaded();
+ mgr.suspend();
+ CHECK(c6->state == IpsContext::SUSPENDED);
+ CHECK(mgr.non_flow_chain.front() == c6);
+
+ mgr.suspend();
+ CHECK(c6->next() == c2);
+ CHECK(c2->state == IpsContext::SUSPENDED);
+ CHECK(mgr.non_flow_chain.front() == c6);
+
+ c3 = mgr.interrupt();
+ CHECK(c3->state == IpsContext::BUSY);
+ c7 = mgr.interrupt();
+ CHECK(c7->state == IpsContext::BUSY);
+ mgr.suspend();
+ CHECK(c2->next() == c7);
+ CHECK(c7->state == IpsContext::SUSPENDED);
+
+ c8 = mgr.interrupt();
+ CHECK(c8->state == IpsContext::BUSY);
+ mgr.suspend();
+ CHECK(c7->next() == c8);
+ CHECK(c8->state == IpsContext::SUSPENDED);
+
+ c9 = mgr.interrupt();
+ CHECK(c9->state == IpsContext::BUSY);
+ mgr.suspend();
+ CHECK(c8->next() == c9);
+ CHECK(c9->state == IpsContext::SUSPENDED);
+
+ mgr.suspend();
+ CHECK(c9->next() == c3);
+ CHECK(c3->state == IpsContext::SUSPENDED);
+
+ mgr.suspend();
+ CHECK(c3->next() == c1);
+ CHECK(c1->state == IpsContext::SUSPENDED);
+
+ std::vector<IpsContext*> expected = { c6, c2, c7, c8, c9, c3, c1 };
+
+ for ( auto& e : expected )
{
- CHECK(mgr.idle_count() == max);
-
- mgr.start();
- CHECK(mgr.idle_count() == max-1);
- CHECK(mgr.busy_count() == 1);
-
- IpsContextData* a = new ContextData(id);
- mgr.set_context_data(1, a);
- mgr.interrupt();
- CHECK(mgr.idle_count() == max-2);
- CHECK((mgr.busy_count() == 2));
+ mgr.resume(e);
+ CHECK(mgr.get_context() == e);
+ CHECK(e->state == IpsContext::BUSY);
- unsigned u = mgr.suspend();
- CHECK(mgr.idle_count() == max-2);
- CHECK(mgr.busy_count() == 1);
- CHECK(mgr.hold_count() == 1);
+ if ( e == c1 )
+ mgr.stop();
+ else
+ mgr.complete();
- mgr.resume(u);
- CHECK(mgr.idle_count() == max-2);
- CHECK((mgr.busy_count() == 2));
- CHECK(mgr.hold_count() == 0);
-
- mgr.complete();
- CHECK(mgr.idle_count() == max-1);
- CHECK(mgr.busy_count() == 1);
+ CHECK(e->state == IpsContext::IDLE);
+ }
+}
- IpsContextData* b = mgr.get_context_data(1);
- CHECK(a == b);
+TEST_CASE("ContextSwitcher multi wire", "[ContextSwitcher]")
+{
+ const unsigned max = 3;
+ ContextSwitcher mgr;
- mgr.stop();
- CHECK(mgr.idle_count() == max);
- }
+ IpsContext *c1, *c2, *c3;
for ( unsigned i = 0; i < max; ++i )
{
- IpsContext* p = mgr.pop();
- CHECK(p);
- delete p;
+ IpsContext* c = new IpsContext;
+ c->packet->flow = new Flow;
+ mgr.push(c);
}
- CHECK(!mgr.pop());
+
+ mgr.start();
+ c1 = mgr.get_context();
+ mgr.suspend();
+ CHECK(mgr.busy_count() == 0);
+
+ mgr.start();
+ c2 = mgr.get_context();
+ mgr.suspend();
+ CHECK(mgr.busy_count() == 0);
+
+ mgr.start();
+ c3 = mgr.get_context();
+ mgr.suspend();
+ CHECK(mgr.busy_count() == 0);
+
+ // middle
+ CHECK(c2->state == IpsContext::SUSPENDED);
+ mgr.resume(c2);
+ CHECK(c2->state == IpsContext::BUSY);
+ CHECK(mgr.get_context() == c2);
+ CHECK(mgr.busy_count() == 1);
+
+ mgr.stop();
+ CHECK(c2->state == IpsContext::IDLE);
+ CHECK(mgr.busy_count() == 0);
+
+ // end
+ CHECK(c3->state == IpsContext::SUSPENDED);
+ mgr.resume(c3);
+ CHECK(c3->state == IpsContext::BUSY);
+ CHECK(mgr.get_context() == c3);
+ CHECK(mgr.busy_count() == 1);
+
+ mgr.stop();
+ CHECK(c3->state == IpsContext::IDLE);
+ CHECK(mgr.busy_count() == 0);
+
+ // only
+ CHECK(c1->state == IpsContext::SUSPENDED);
+ mgr.resume(c1);
+ CHECK(c1->state == IpsContext::BUSY);
+ CHECK(mgr.busy_count() == 1);
+
+ mgr.stop();
+ CHECK(c1->state == IpsContext::IDLE);
+ CHECK(mgr.busy_count() == 0);
+
+ delete c1->packet->flow;
+ delete c2->packet->flow;
+ delete c3->packet->flow;
}
TEST_CASE("ContextSwitcher abort", "[ContextSwitcher]")
{
const unsigned max = 3;
- auto mgr = ContextSwitcher(max);
+ ContextSwitcher mgr;
auto id = IpsContextData::get_ips_id();
- CHECK(!mgr.pop());
for ( unsigned i = 0; i < max; ++i )
mgr.push(new IpsContext(id+1));
- SECTION("cleanup")
- {
- mgr.start();
- IpsContextData* a = new ContextData(id);
- mgr.set_context_data(1, a);
- mgr.interrupt();
- mgr.interrupt();
- CHECK(mgr.idle_count() == max-3);
-
- mgr.suspend();
- CHECK((mgr.busy_count() == 2));
- CHECK(mgr.hold_count() == 1);
-
- mgr.abort();
- CHECK(mgr.idle_count() == max);
- }
+ mgr.start();
+ IpsContextData* a = new ContextData(id);
+ mgr.set_context_data(1, a);
+ mgr.interrupt();
+ mgr.interrupt();
+ CHECK(mgr.idle_count() == max-3);
+
+ mgr.suspend();
+ CHECK((mgr.busy_count() == 2));
+
+ mgr.abort();
+ CHECK(mgr.idle_count() == max);
+ CHECK(!mgr.busy_count());
}
#endif
#define CONTEXT_SWITCHER_H
// ContextSwitcher maintains a set of contexts, only one of which can be
-// active at any time. the normal workflow is:
+// active at any time. the normal workflow is:
//
-// 1. start and stop are called at the beginning and end of each packet
-// callback which activates and releases one context from among those
+// 1. start and stop are called at the beginning and end of each wire
+// packet which activates and releases one context from among those
// available.
//
// 2. during processing interrupt and complete should be called to start
-// and finish processing of a generated pseudo packet. it is possible to
-// interrupt pseudo packets.
+// and finish processing of a generated pseudo packet. it is possible to
+// interrupt pseudo packets. complete may return without doing anything if
+// dependent contexts were suspended.
//
-// 3. suspend may be called to place the current context on hold and
-// activate the prior. multiple contexts may be placed on hold.
+// 3. suspend may be called to pause the current context and activate the
+// prior. multiple contexts may be suspended.
//
-// 4. there is no ordering of idle contexts. busy contexts are in strict
-// LIFO order. contexts on hold can be resumed in any order.
+// 4. there is no ordering of idle contexts. busy contexts are in strict LIFO
+// order. context dependency chains are maintained in depth-first order by Flow.
#include <vector>
+#include "detection/ips_context_chain.h"
+#include "utils/primed_allocator.h"
+
namespace snort
{
class Flow;
class IpsContextData;
}
+// FIXIT-H add the hold to catch offloads that don't return
class ContextSwitcher
{
public:
- ContextSwitcher(unsigned max);
~ContextSwitcher();
void push(snort::IpsContext*);
- snort::IpsContext* pop();
void start();
void stop();
snort::IpsContext* interrupt();
snort::IpsContext* complete();
- unsigned suspend();
- void resume(unsigned suspended);
+ void suspend();
+ void resume(snort::IpsContext*);
snort::IpsContext* get_context() const;
- snort::IpsContext* get_context(unsigned) const;
snort::IpsContext* get_next() const;
snort::IpsContextData* get_context_data(unsigned id) const;
unsigned idle_count() const;
unsigned busy_count() const;
- unsigned hold_count() const;
-
- bool can_hold() const
- { return idle_count() > 5; } // FIXIT-RC define appropriate const
- bool on_hold(snort::Flow*);
+public:
+ snort::IpsContextChain non_flow_chain;
private:
std::vector<snort::IpsContext*> idle;
std::vector<snort::IpsContext*> busy;
- std::vector<snort::IpsContext*> hold;
+ std::vector<snort::IpsContext*> contexts;
};
#endif
#include "regex_offload.h"
static THREAD_LOCAL RegexOffload* offloader = nullptr;
-static THREAD_LOCAL uint64_t context_num = 0;
using namespace snort;
void DetectionEngine::reset()
{
IpsContext* c = Snort::get_switcher()->get_context();
- c->context_num = ++context_num;
c->alt_data.len = 0; // FIXIT-H need context::reset()
}
static THREAD_LOCAL Active shutdown_active;
static THREAD_LOCAL IpsAction* shutdown_action = nullptr;
+ wait_for_context();
IpsContext* c = Snort::get_switcher()->get_next();
- if ( parent )
+ if ( parent ) // FIXIT-L parent can probably be determined by busy queue
{
c->snapshot_flow(parent->flow);
c->packet_number = parent->context->packet_number;
// this also handles block pending state
Stream::check_flow_closed(p);
- if ( inspected and (!p->flow or !p->flow->is_offloaded()) )
+ if ( inspected and !p->context->next() )
InspectorManager::clear(p);
clear_events(p);
void DetectionEngine::finish_packet(Packet* p, bool flow_deletion)
{
+ ContextSwitcher* sw = Snort::get_switcher();
+
log_events(p);
clear_events(p);
p->release_helpers();
// clean up any failed rebuilds
- const IpsContext* c = Snort::get_switcher()->get_next();
- c->packet->release_helpers();
-
- ContextSwitcher* sw = Snort::get_switcher();
+ if ( sw->idle_count() )
+ {
+ const IpsContext* c = sw->get_next();
+ c->packet->release_helpers();
+ }
- if ( flow_deletion or sw->busy_count() > 1 )
+ if ( flow_deletion or p->is_rebuilt() )
sw->complete();
+
+ // FIXIT-H enable for daqng
+#if 0
+ if ( !p->is_rebuilt() )
+ {
+ sw->stop();
+ queue for daq msg finalize
+ }
+#endif
}
uint8_t* DetectionEngine::get_buffer(unsigned& max)
void DetectionEngine::set_detects(Packet* p, IpsContext::ActiveRules ar)
{ p->context->active_rules = ar; }
-bool DetectionEngine::offloaded(Packet* p)
-{ return p->flow and p->flow->is_offloaded(); }
-
void DetectionEngine::set_check_tags(bool enable)
{ Snort::get_switcher()->get_context()->check_tags = enable; }
// offload / onload
//--------------------------------------------------------------------------
+void DetectionEngine::do_offload(Packet* p)
+{
+ ContextSwitcher* sw = Snort::get_switcher();
+
+ assert(p == p->context->packet);
+ assert(p->context == sw->get_context());
+
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::offload %" PRIu64 " (r=%d)\n",
+ p->context->packet_number, p->context->context_num, offloader->count());
+
+ sw->suspend();
+
+ p->context->conf = SnortConfig::get_conf();
+ p->set_offloaded();
+
+ offloader->put(p);
+ pc.offloads++;
+}
+
+bool DetectionEngine::offload(Packet* p)
+{
+ ContextSwitcher* sw = Snort::get_switcher();
+
+ bool depends_on_suspended = p->flow ? p->flow->context_chain.front() : sw->non_flow_chain.front();
+ bool can_offload = offloader->available();
+ bool should_offload = p->dsize >= SnortConfig::get_conf()->offload_limit;
+
+ if ( can_offload and should_offload )
+ {
+ do_offload(p);
+ return true;
+ }
+
+ if ( depends_on_suspended )
+ {
+ fp_partial(p);
+ sw->suspend();
+ return true;
+ }
+
+ assert(p->flow ? !p->flow->is_suspended() : true);
+ fp_full(p);
+ return false;
+}
+
void DetectionEngine::idle()
{
if (offloader)
void DetectionEngine::onload(Flow* flow)
{
- while ( flow->is_offloaded() )
+ while ( flow->is_suspended() )
{
- const struct timespec blip = { 0, 1 };
trace_logf(detection,
TRACE_DETECTION_ENGINE, "(wire) %" PRIu64 " de::sleep\n", get_packet_number());
- nanosleep(&blip, nullptr);
+ resume_ready_suspends(flow->context_chain); // FIXIT-M makes onload reentrant-safe
onload();
}
- assert(!Snort::get_switcher()->on_hold(flow));
assert(!offloader->on_hold(flow));
}
void DetectionEngine::onload()
{
- unsigned id;
-
- if ( !offloader->get(id) )
- return;
-
- ContextSwitcher* sw = Snort::get_switcher();
- IpsContext* c = sw->get_context(id);
- assert(c);
-
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::onload %u (r=%d)\n",
- c->packet_number, id, offloader->count());
+ for( Packet* p; offloader->count() and offloader->get(p); )
+ {
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::onload %" PRIu64 " (r=%d)\n",
+ p->context->packet_number, p->context->context_num, offloader->count());
+
+ p->clear_offloaded();
+
+ IpsContextChain& chain = p->flow ? p->flow->context_chain : Snort::get_switcher()->non_flow_chain;
+
+ resume_ready_suspends(chain);
+ }
+}
- Packet* p = c->packet;
- p->flow->clear_offloaded();
+void DetectionEngine::resume_ready_suspends(IpsContextChain& chain)
+{
+ while ( chain.front() and !chain.front()->packet->is_offloaded() )
+ resume(chain.front()->packet);
+}
- sw->resume(id);
+void DetectionEngine::resume(Packet* p)
+{
+ trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::resume %" PRIu64 " (r=%d)\n",
+ p->context->packet_number, p->context->context_num, offloader->count());
- fp_onload(p);
+ Snort::get_switcher()->resume(p->context);
+
+ fp_complete(p);
finish_inspect_with_latency(p); // FIXIT-L should latency be evaluated here?
finish_inspect(p, true);
finish_packet(p);
}
-bool DetectionEngine::offload(Packet* p)
+void DetectionEngine::wait_for_context()
{
ContextSwitcher* sw = Snort::get_switcher();
- if ( p->dsize < SnortConfig::get_conf()->offload_limit or
- !sw->can_hold() or
- !offloader->available() )
+ if ( !sw->idle_count() )
{
- fp_local(p);
- return false;
+ pc.context_stalls++;
+ do
+ {
+ onload();
+ } while ( !sw->idle_count() );
}
- assert(p == p->context->packet);
- assert(p->context == sw->get_context());
-
- unsigned id = sw->suspend();
-
- trace_logf(detection, TRACE_DETECTION_ENGINE, "%" PRIu64 " de::offload %u (r=%d)\n",
- p->context->packet_number, id, offloader->count());
-
- p->flow->set_offloaded();
- p->context->conf = SnortConfig::get_conf();
-
- offloader->put(id, p);
- pc.offloads++;
-
- return true;
}
//--------------------------------------------------------------------------
if ( offload_ok and p->flow )
return offload(p);
- fp_local(p);
+ fp_full(p);
break;
default:
SF_EVENTQ* pq = p->context->equeue;
pc.log_limit += sfeventq_reset(pq);
}
-
struct Packet;
class Flow;
class IpsContext;
+class IpsContextChain;
class IpsContextData;
class SO_PUBLIC DetectionEngine
static Packet* set_next_packet(Packet* parent = nullptr);
static uint8_t* get_next_buffer(unsigned& max);
- static bool offloaded(Packet*);
static bool offload(Packet*);
static void onload(Flow*);
+ static void onload();
static void idle();
static void set_encode_packet(Packet*);
private:
static struct SF_EVENTQ* get_event_queue();
+ static void do_offload(snort::Packet*);
static void offload_thread(IpsContext*);
- static void onload();
+ static void resume(snort::Packet*);
+ static void resume_ready_suspends(IpsContextChain&);
static int log_events(Packet*);
static void clear_events(Packet*);
static void finish_inspect_with_latency(Packet*);
static void finish_inspect(Packet*, bool inspected);
static void finish_packet(Packet*, bool flow_deletion = false);
+ static void wait_for_context();
private:
IpsContext* context;
// need n >= 4 for src+dst+gen+svc
static const unsigned max = 32;
+ MpseStash()
+ { enable = false; }
+
void init()
{
if ( enable )
return 0;
}
-void fp_local(Packet* p)
+void fp_full(Packet* p)
{
IpsContext* c = p->context;
MpseStash* stash = c->stash;
stash->enable_process();
stash->init();
init_match_info(c->otnx);
-
- // FIXIT-L set up a dependency chain between contexts and "pause" here
- if ( p->flow )
- DetectionEngine::onload(p->flow);
-
fpEvalPacket(p);
fpFinalSelectEvent(c->otnx, p);
}
-void fp_offload(Packet* p)
+void fp_partial(Packet* p)
{
IpsContext* c = p->context;
MpseStash* stash = c->stash;
fpEvalPacket(p);
}
-void fp_onload(Packet* p)
+void fp_complete(Packet* p)
{
IpsContext* c = p->context;
MpseStash* stash = c->stash;
void fp_set_context(snort::IpsContext&);
void fp_clear_context(snort::IpsContext&);
-void fp_local(snort::Packet*);
-void fp_offload(snort::Packet*);
-void fp_onload(snort::Packet*);
+void fp_full(snort::Packet*);
+void fp_partial(snort::Packet*);
+void fp_complete(snort::Packet*);
#endif
#include "ips_context.h"
#include <cassert>
+
#include "detection/detection_engine.h"
+#include "detection/fp_detect.h"
#include "detection/ips_context_data.h"
#include "events/event_queue.h"
#include "events/sfeventq.h"
#include "main/snort_config.h"
-#include "fp_detect.h"
-
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
#endif
IpsContext::IpsContext(unsigned size) :
data(size ? size : max_ips_id, nullptr)
{
+ depends_on = nullptr;
+ next_to_process = nullptr;
+
+ state = IDLE;
+
packet = new Packet(false);
encode_packet = nullptr;
c.post_detection();
CHECK( post_val == nullptr );
}
+
+TEST_CASE("IpsContext Link", "[IpsContext]")
+{
+ IpsContext c0, c1, c2;
+
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == nullptr);
+
+ c0.link(&c1);
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == &c1);
+ CHECK(c1.dependencies() == &c0);
+ CHECK(c1.next() == nullptr);
+
+ c1.link(&c2);
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == &c1);
+ CHECK(c1.dependencies() == &c0);
+ CHECK(c1.next() == &c2);
+ CHECK(c2.dependencies() == &c1);
+ CHECK(c2.next() == nullptr);
+}
+
+TEST_CASE("IpsContext Unlink", "[IpsContext]")
+{
+ IpsContext c0, c1, c2;
+ c0.link(&c1);
+ c1.link(&c2);
+
+ c0.unlink();
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == nullptr);
+ CHECK(c1.dependencies() == nullptr);
+ CHECK(c1.next() == &c2);
+ CHECK(c2.dependencies() == &c1);
+ CHECK(c2.next() == nullptr);
+
+ c1.unlink();
+ CHECK(c1.dependencies() == nullptr);
+ CHECK(c1.next() == nullptr);
+ CHECK(c2.dependencies() == nullptr);
+ CHECK(c2.next() == nullptr);
+
+ c2.unlink();
+ CHECK(c2.dependencies() == nullptr);
+ CHECK(c2.next() == nullptr);
+}
+
+TEST_CASE("IpsContext Abort, [IpsContext]")
+{
+ IpsContext c0, c1, c2, c3;
+ Flow flow;
+
+ c0.link(&c1);
+ c1.link(&c2);
+ c2.link(&c3);
+
+ // mid list
+ // c0 <- c1 <- c2 <- c3
+ // c0 <- c2 <- c3
+ c1.abort();
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == &c2);
+ CHECK(c1.dependencies() == nullptr);
+ CHECK(c1.next() == nullptr);
+ CHECK(c2.dependencies() == &c0);
+ CHECK(c2.next() == &c3);
+ CHECK(c3.dependencies() == &c2);
+ CHECK(c3.next() == nullptr);
+
+ // front of list
+ // c0 <- c2 <- c3
+ // c2 <- c3
+ c0.abort();
+ CHECK(c0.dependencies() == nullptr);
+ CHECK(c0.next() == nullptr);
+ CHECK(c2.dependencies() == nullptr);
+ CHECK(c2.next() == &c3);
+ CHECK(c3.dependencies() == &c2);
+ CHECK(c3.next() == nullptr);
+
+ // back of list
+ // c2 <- c3
+ // c2
+ c3.abort();
+ CHECK(c2.dependencies() == nullptr);
+ CHECK(c2.next() == nullptr);
+ CHECK(c3.dependencies() == nullptr);
+ CHECK(c3.next() == nullptr);
+
+ // only
+ c2.abort();
+ CHECK(c2.dependencies() == nullptr);
+ CHECK(c2.next() == nullptr);
+}
#endif
{
public:
using Callback = void(*)(IpsContext*);
+ enum State { IDLE, BUSY, SUSPENDED };
IpsContext(unsigned size = 0); // defaults to max id
~IpsContext();
IpsContextData* get_context_data(unsigned id) const;
void clear_context_data();
- void set_slot(unsigned s)
- { slot = s; }
-
- unsigned get_slot()
- { return slot; }
-
void snapshot_flow(Flow*);
uint32_t get_session_flags()
void post_detection();
+ void link(IpsContext* next)
+ {
+ assert(!next->depends_on);
+ assert(!next->next_to_process);
+ assert(!next_to_process);
+
+ next->depends_on = this;
+ next_to_process = next;
+ }
+
+ void unlink()
+ {
+ assert(!depends_on);
+ if ( next_to_process )
+ {
+ assert(next_to_process->depends_on == this);
+ next_to_process->depends_on = nullptr;
+ }
+ next_to_process = nullptr;
+ }
+
+ IpsContext* dependencies() const
+ { return depends_on; }
+
+ IpsContext* next() const
+ { return next_to_process; }
+
+ void abort()
+ {
+ if ( next_to_process )
+ next_to_process->depends_on = depends_on;
+
+ if ( depends_on )
+ depends_on->next_to_process = next_to_process;
+
+ depends_on = next_to_process = nullptr;
+ }
+
public:
std::vector<Replacement> rpl;
uint64_t context_num;
uint64_t packet_number;
ActiveRules active_rules;
+ State state;
bool check_tags;
static const unsigned buf_size = Codec::PKT_MAX;
FlowSnapshot flow;
std::vector<IpsContextData*> data;
std::vector<Callback> post_callbacks;
-
- unsigned slot;
+ IpsContext* depends_on;
+ IpsContext* next_to_process;
};
}
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 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.
+//--------------------------------------------------------------------------
+
+// ips_context_chain.cc author Carter Waxman <cwamxan@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "detection/ips_context_chain.h"
+
+#include "detection/ips_context.h"
+
+using namespace snort;
+
+void IpsContextChain::pop()
+{
+ assert(_front);
+
+ IpsContext* new_front = _front->next();
+ _front->unlink();
+ _front = new_front;
+ if ( !_front )
+ _back = nullptr;
+}
+
+void IpsContextChain::push_back(IpsContext* new_back)
+{
+ if ( _back )
+ {
+ _back->link(new_back);
+ _back = new_back;
+ }
+ else
+ {
+ assert(!_front);
+ _front = _back = new_back;
+ }
+}
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+
+TEST_CASE("IpsContextChain push_back", "[IpsContextChain]")
+{
+ IpsContextChain chain;
+ IpsContext a, b, c;
+
+ CHECK(chain.front() == nullptr);
+ CHECK(chain.back() == nullptr);
+
+ chain.push_back(&a);
+ CHECK(chain.front() == &a);
+ CHECK(chain.back() == &a);
+
+ chain.push_back(&b);
+ CHECK(chain.front() == &a);
+ CHECK(chain.back() == &b);
+
+ chain.push_back(&c);
+ CHECK(chain.front() == &a);
+ CHECK(chain.back() == &c);
+
+ CHECK(a.next() == &b);
+ CHECK(b.next() == &c);
+ CHECK(c.next() == nullptr);
+}
+
+TEST_CASE("IpsContextChain pop", "[IpsContextChain]")
+{
+ IpsContextChain chain;
+ IpsContext a, b, c;
+
+ chain.push_back(&a);
+ chain.push_back(&b);
+ chain.push_back(&c);
+
+ chain.pop();
+ CHECK(chain.front() == &b);
+ CHECK(chain.back() == &c);
+
+ chain.pop();
+ CHECK(chain.front() == &c);
+ CHECK(chain.back() == &c);
+
+ chain.pop();
+ CHECK(chain.front() == nullptr);
+ CHECK(chain.back() == nullptr);
+}
+
+TEST_CASE("IpsContextChain abort", "[IpsContextChain]")
+{
+ IpsContextChain chain;
+ IpsContext a, b;
+
+ chain.push_back(&a);
+ chain.push_back(&b);
+ chain.abort();
+
+ CHECK(!chain.front());
+ CHECK(!chain.back());
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2018-2018 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.
+//--------------------------------------------------------------------------
+
+// ips_context_chain.h author Carter Waxman <cwaxman@cisco.com>
+
+#ifndef IPS_CONTEXT_CHAIN_H
+#define IPS_CONTEXT_CHAIN_H
+
+// IpsContextChain provides an interface for maintaining dependencies between
+// IpsContexts. This class is provided to handle all linking and ensure only
+// the tips of dependency chains are able to be processed, enforcing strict
+// processing order.
+
+namespace snort
+{
+class IpsContext;
+class IpsContextChain
+{
+public:
+ void abort()
+ { _front = _back = nullptr; }
+
+ IpsContext* front() const
+ { return _front; }
+
+ IpsContext* back() const
+ { return _back; }
+
+ void pop();
+ void push_back(IpsContext*);
+
+private:
+ IpsContext* _front = nullptr;
+ IpsContext* _back = nullptr;
+};
+}
+
+#endif
+
std::atomic<bool> offload { false };
- unsigned id = 0;
bool go = true;
};
}
assert(req->packet);
+ assert(req->packet->is_offloaded());
snort::SnortConfig::set_conf(req->packet->context->conf); // FIXIT-H reload issue
- fp_offload(req->packet);
+ fp_partial(req->packet);
req->offload = false;
}
RuleLatency::tterm();
}
-void RegexOffload::put(unsigned id, snort::Packet* p)
+void RegexOffload::put(snort::Packet* p)
{
assert(p);
assert(!idle.empty());
std::unique_lock<std::mutex> lock(req->mutex);
- req->id = id;
req->packet = p;
req->offload = true;
req->cond.notify_one();
}
-bool RegexOffload::get(unsigned& id)
+bool RegexOffload::get(snort::Packet*& p)
{
assert(!busy.empty());
- // FIXIT-H onload flows in any order
- RegexRequest* req = busy.front();
+ for ( auto i = busy.begin(); i != busy.end(); i++ )
+ {
+ RegexRequest* req = *i;
- if ( req->offload )
- return false;
+ if ( req->offload )
+ continue;
- id = req->id;
- req->packet = nullptr;
+ p = req->packet;
+ req->packet = nullptr;
- busy.pop_front(); // FIXIT-H use splice to move instead
- idle.emplace_back(req);
+ busy.erase(i);
+ idle.emplace_back(req);
- return true;
+ return true;
+ }
+
+ p = nullptr;
+ return false;
}
bool RegexOffload::on_hold(snort::Flow* f)
unsigned count()
{ return busy.size(); }
- void put(unsigned id, snort::Packet*);
- bool get(unsigned& id);
+ void put(snort::Packet*);
+ bool get(snort::Packet*&);
bool on_hold(snort::Flow*);
}
mpls_client.length = 0;
mpls_server.length = 0;
- offloads_pending = 0;
}
void Flow::term()
service = new_service;
DataBus::publish(FLOW_SERVICE_CHANGE_EVENT, pkt);
}
-
// state. Inspector state is stored in FlowData, and Flow manages a list
// of FlowData items.
+#include "detection/ips_context_chain.h"
#include "framework/data_bus.h"
#include "framework/decode_data.h"
#include "framework/inspector.h"
namespace snort
{
struct FlowKey;
+class IpsContext;
struct Packet;
typedef void (* StreamAppDataFree)(void*);
bool is_inspection_disabled() const
{ return disable_inspect; }
- bool is_offloaded() const
- { return offloads_pending; }
-
- void set_offloaded()
- {
- assert(offloads_pending < 0xFF);
-
- offloads_pending++;
- }
-
- void clear_offloaded()
- {
- assert(offloads_pending);
-
- offloads_pending--;
- }
+ bool is_suspended() const
+ { return context_chain.front(); }
public: // FIXIT-M privatize if possible
// fields are organized by initialization and size to minimize
Layer mpls_client, mpls_server;
// everything from here down is zeroed
+ IpsContextChain context_chain;
FlowData* flow_data;
Inspector* clouseau; // service identifier
Inspector* gadget; // service handler
uint8_t response_count;
- uint8_t offloads_pending;
bool disable_inspect;
private:
break;
}
#endif
- if ( flow->is_offloaded() )
+ if ( flow->is_suspended() )
break;
if ( flow->last_data_seen + config.pruning_timeout >= thetime )
assert(flow); // holds true because hash_table->get_count() > 0
if ( (save_me and flow == save_me) or flow->was_blocked() or
- (flow->is_offloaded() and ignore_offloads) )
+ (flow->is_suspended() and ignore_offloads) )
{
// check for non-null save_me above to silence analyzer
// "called C++ object pointer is null" here
break;
if ( HighAvailabilityManager::in_standby(flow) or
- flow->is_offloaded() )
+ flow->is_suspended() )
{
flow = static_cast<Flow*>(hash_table->next());
continue;
virtual void set_opt(int) { }
virtual int print_info() { return 0; }
- virtual int get_pattern_count() { return 0; }
+ virtual int get_pattern_count() const { return 0; }
const char* get_method() { return method.c_str(); }
void set_verbose(bool b = true) { verbose = b; }
// using dummy values until further integration
const unsigned max_contexts = 20;
- s_switcher = new ContextSwitcher(max_contexts);
+ s_switcher = new ContextSwitcher;
for ( unsigned i = 0; i < max_contexts; ++i )
s_switcher->push(new IpsContext);
{
flow = nullptr;
packet_flags = 0;
+ ts_packet_flags = 0;
xtradata_mask = 0;
proto_bits = 0;
alt_dsize = 0;
#define PKT_FILE_EVENT_SET 0x00400000
#define PKT_IGNORE 0x00800000 /* this packet should be ignored, based on port */
-#define PKT_UNUSED_FLAGS 0xfe000000
+#define PKT_UNUSED_FLAGS 0xff000000
+
+#define PKT_TS_OFFLOADED 0x01
// 0x40000000 are available
#define PKT_PDU_FULL (PKT_PDU_HEAD | PKT_PDU_TAIL)
uint32_t user_network_policy_id;
uint8_t vlan_idx;
+ uint8_t ts_packet_flags; // FIXIT-M packet flags should always be thread safe
// IP_MAXPACKET is the minimum allowable max_dsize
// there is no requirement that all data fit into an IP datagram
bool is_rebuilt() const
{ return (packet_flags & (PKT_REBUILT_STREAM|PKT_REBUILT_FRAG)) != 0; }
+ bool is_offloaded() const
+ { return (ts_packet_flags & PKT_TS_OFFLOADED) != 0; }
+
+ void set_offloaded()
+ { ts_packet_flags |= PKT_TS_OFFLOADED; }
+
+ void clear_offloaded()
+ { ts_packet_flags &= (~PKT_TS_OFFLOADED); }
+
bool is_detection_enabled(bool to_server);
bool test_session_flags(uint32_t);
int print_info() override
{ return acsmPrintDetailInfo2(obj); }
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return acsmPatternCount2(obj); }
};
return 0;
}
- int get_pattern_count() override
+ int get_pattern_count() const override
{
return bnfaPatternCount(obj);
}
int print_info() override
{ return acsmPrintDetailInfo2(obj); }
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return acsmPatternCount2(obj); }
};
int print_info() override
{ return acsmPrintDetailInfo2(obj); }
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return acsmPatternCount2(obj); }
};
int print_info() override
{ return acsmPrintDetailInfo2(obj); }
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return acsmPatternCount2(obj); }
};
int print_info() override
{ return acsmPrintDetailInfo(obj); }
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return acsmPatternCount(obj); }
};
int _search(const uint8_t*, int, MpseMatch, void*, int*) override;
- int get_pattern_count() override
+ int get_pattern_count() const override
{ return pvector.size(); }
int match(unsigned id, unsigned long long to);
// this will get called on each onload
// eventually all onloads will occur and delete will be called
- if ( not flow->is_offloaded() )
+ if ( not flow->is_suspended() )
flow_con->delete_flow(flow, PruneReason::NONE);
p->flow = nullptr;
cpp_macros.h
endian.h
kmap.h
+ primed_allocator.h
safec.h
segment_mem.h
sflsq.h
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2018 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2005-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+// primed_allocator.h author Carter Waxman <cwaxman@cisco.com>
+
+#ifndef PRIMED_ALLOCATOR_H
+#define PRIMED_ALLOCATOR_H
+
+// An STL list allocator that pools deallocs on a freelist but only frees memory at destruction.
+// Use this for containers where the maximum size is somehow capped but where storage may not reach
+// that cap. This prevents unnecessary memory allocation but still provides the speed benefit of
+// not repeatedly allocating memory. Additionally, the free list may be shared among multiple
+// STL lists of the same type.
+
+namespace snort
+{
+template<typename T>
+class PrimedAllocator
+{
+public:
+ struct Node
+ {
+ Node* next;
+ };
+
+ struct State
+ {
+ Node* free_list = nullptr;
+ unsigned ref_count = 0;
+
+ ~State()
+ {
+ while ( free_list )
+ {
+ Node* next = free_list->next;
+ delete free_list;
+ free_list = next;
+ }
+ }
+ };
+
+ using size_type = std::size_t;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using value_type = T;
+
+ template<typename U> struct rebind { typedef PrimedAllocator<U> other; };
+
+ PrimedAllocator() noexcept
+ { state = new State; }
+
+ PrimedAllocator(const PrimedAllocator& other) noexcept
+ {
+ state = other.state;
+ state->ref_count++;
+ }
+
+ ~PrimedAllocator()
+ {
+ state->ref_count--;
+
+ if ( !state->ref_count )
+ delete state;
+ }
+
+ void set_state(State* state)
+ {
+ this->state->ref_count--;
+
+ if ( !this->state->ref_count )
+ delete state;
+
+ this->state = state;
+ state->ref_count++;
+ }
+
+ State* get_state() const
+ { return state; }
+
+ pointer allocate(size_type, const_pointer = 0)
+ {
+ if ( state->free_list )
+ {
+ T* ret = reinterpret_cast<T*>(state->free_list);
+ state->free_list = state->free_list->next;
+ return ret;
+ }
+ return reinterpret_cast<T*>(operator new(sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_type) noexcept
+ {
+ Node* node = reinterpret_cast<Node*>(p);
+ node->next = state->free_list;
+ state->free_list = node;
+ }
+
+private:
+ State* state;
+};
+}
+
+#endif
{ CountType::SUM, "log_limit", "events queued but not logged" },
{ CountType::SUM, "event_limit", "events filtered" },
{ CountType::SUM, "alert_limit", "events previously triggered on same PDU" },
+ { CountType::SUM, "context_stalls", "times processing stalled to wait for an available context" },
{ CountType::END, nullptr, nullptr }
};
PegCount log_limit;
PegCount event_limit;
PegCount alert_limit;
+ PegCount context_stalls;
};
struct ProcessCount