From: Michael Altizer (mialtize) Date: Wed, 19 Dec 2018 18:48:38 +0000 (-0500) Subject: Merge pull request #1465 in SNORT/snort3 from ~CWAXMAN/snort3:offload_context_deps... X-Git-Tag: 3.0.0-251~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=728d36adb1b54733cefdc5ba30db85e871b5c66b;p=thirdparty%2Fsnort3.git Merge pull request #1465 in SNORT/snort3 from ~CWAXMAN/snort3:offload_context_deps to master Squashed commit of the following: commit e8c4313927cb73dbca52471a461c129c18460ac2 Author: Carter Waxman Date: Fri Dec 7 07:29:50 2018 -0500 Mpse: fixed build warning about constness of get_pattern_count commit 4d3aa71ec4fc7130073aad4ed143407b7c656f6a Author: Carter Waxman Date: Thu Dec 6 16:29:02 2018 -0500 Flow: is_offloaded is now is_suspended commit c8fddc2d61843ad1ff41e1ea721dea890007ff0b Author: Carter Waxman Date: Thu Dec 6 09:12:27 2018 -0500 IpsContext: removed useless SUSPENDED_OFFLOAD state commit ec47d206b928baa3f9c97364980cf99a06f804c1 Author: Carter Waxman Date: Wed Dec 5 15:07:33 2018 -0500 Packet: fixed thread safety in onload flag checks commit 8e6969234e2f1ba4d62522938342c1909b21c810 Author: Carter Waxman Date: Wed Dec 5 15:30:11 2018 -0500 RegexOffload: onload whatever is ready commit 59618c74d8a449128c82d4bddb0b2399ea77630b Author: Carter Waxman Date: Thu Dec 6 16:52:37 2018 -0500 DetectionEngine: make onload safe for reentrance commit fe186cabc5d0632af8c0555bed88f33529f7fa45 Author: Carter Waxman Date: Thu Dec 6 10:52:25 2018 -0500 DetectionEngine: stall when out of contexts commit 3250b20edbe279daa5d22c50381fbe6fbeaaefc0 Author: Carter Waxman Date: Wed Dec 5 16:52:59 2018 -0500 fp_detect: suspend instead of onload if fp_local can't occur yet commit dbee8b9c10be69e771d0206944f485cab2077cb7 Author: Carter Waxman Date: Thu Nov 29 13:26:14 2018 -0500 detection: allow suspension of entire chains of contexts --- diff --git a/src/detection/CMakeLists.txt b/src/detection/CMakeLists.txt index 02d3f2629..4620d7e84 100644 --- a/src/detection/CMakeLists.txt +++ b/src/detection/CMakeLists.txt @@ -6,6 +6,7 @@ set (DETECTION_INCLUDES detection_util.h detect_trace.h ips_context.h + ips_context_chain.h ips_context_data.h regex_offload.h rule_option_types.h @@ -33,6 +34,7 @@ add_library (detection OBJECT fp_utils.cc fp_utils.h ips_context.cc + ips_context_chain.cc ips_context_data.cc pattern_match_data.h pcrm.cc diff --git a/src/detection/context_switcher.cc b/src/detection/context_switcher.cc index 385a8a006..f58e22b91 100644 --- a/src/detection/context_switcher.cc +++ b/src/detection/context_switcher.cc @@ -41,150 +41,184 @@ 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 @@ -195,14 +229,6 @@ 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()); @@ -210,14 +236,10 @@ IpsContext* ContextSwitcher::get_next() const } 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(); } @@ -225,27 +247,6 @@ unsigned ContextSwitcher::idle_count() const 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 //-------------------------------------------------------------------------- @@ -257,85 +258,187 @@ public: 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 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 diff --git a/src/detection/context_switcher.h b/src/detection/context_switcher.h index 2ab04c738..fe4d90fb8 100644 --- a/src/detection/context_switcher.h +++ b/src/detection/context_switcher.h @@ -22,24 +22,28 @@ #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 +#include "detection/ips_context_chain.h" +#include "utils/primed_allocator.h" + namespace snort { class Flow; @@ -47,14 +51,13 @@ class IpsContext; 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(); @@ -63,11 +66,10 @@ public: 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; @@ -75,17 +77,14 @@ public: 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 idle; std::vector busy; - std::vector hold; + std::vector contexts; }; #endif diff --git a/src/detection/detection_engine.cc b/src/detection/detection_engine.cc index 6bc1e34ca..2b3bc1149 100644 --- a/src/detection/detection_engine.cc +++ b/src/detection/detection_engine.cc @@ -54,7 +54,6 @@ #include "regex_offload.h" static THREAD_LOCAL RegexOffload* offloader = nullptr; -static THREAD_LOCAL uint64_t context_num = 0; using namespace snort; @@ -87,7 +86,6 @@ DetectionEngine::~DetectionEngine() 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() } @@ -114,8 +112,9 @@ Packet* DetectionEngine::set_next_packet(Packet* parent) 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; @@ -182,7 +181,7 @@ void DetectionEngine::finish_inspect(Packet* p, bool inspected) // 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); @@ -190,18 +189,30 @@ void DetectionEngine::finish_inspect(Packet* p, bool inspected) 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) @@ -302,9 +313,6 @@ IpsContext::ActiveRules DetectionEngine::get_detects(Packet* p) 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; } @@ -315,6 +323,51 @@ bool DetectionEngine::get_check_tags() // 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) @@ -337,70 +390,63 @@ void DetectionEngine::idle() 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; } //-------------------------------------------------------------------------- @@ -435,7 +481,7 @@ bool DetectionEngine::detect(Packet* p, bool offload_ok) if ( offload_ok and p->flow ) return offload(p); - fp_local(p); + fp_full(p); break; default: @@ -574,4 +620,3 @@ void DetectionEngine::clear_events(Packet* p) SF_EVENTQ* pq = p->context->equeue; pc.log_limit += sfeventq_reset(pq); } - diff --git a/src/detection/detection_engine.h b/src/detection/detection_engine.h index 55de5bb38..4f926694e 100644 --- a/src/detection/detection_engine.h +++ b/src/detection/detection_engine.h @@ -38,6 +38,7 @@ namespace snort struct Packet; class Flow; class IpsContext; +class IpsContextChain; class IpsContextData; class SO_PUBLIC DetectionEngine @@ -58,10 +59,10 @@ public: 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*); @@ -102,14 +103,17 @@ public: 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; diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index fb34ff450..083d68b44 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -730,6 +730,9 @@ public: // need n >= 4 for src+dst+gen+svc static const unsigned max = 32; + MpseStash() + { enable = false; } + void init() { if ( enable ) @@ -1277,23 +1280,18 @@ static int fpEvalPacket(Packet* p) 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; @@ -1304,7 +1302,7 @@ void fp_offload(Packet* p) fpEvalPacket(p); } -void fp_onload(Packet* p) +void fp_complete(Packet* p) { IpsContext* c = p->context; MpseStash* stash = c->stash; diff --git a/src/detection/fp_detect.h b/src/detection/fp_detect.h index ff9d00dd0..e0af16168 100644 --- a/src/detection/fp_detect.h +++ b/src/detection/fp_detect.h @@ -102,9 +102,9 @@ int fpAddMatch(OtnxMatchData*, int pLen, const OptTreeNode*); 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 diff --git a/src/detection/ips_context.cc b/src/detection/ips_context.cc index e22495780..d8daeee63 100644 --- a/src/detection/ips_context.cc +++ b/src/detection/ips_context.cc @@ -25,14 +25,14 @@ #include "ips_context.h" #include + #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 @@ -46,6 +46,11 @@ using namespace snort; 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; @@ -191,5 +196,100 @@ TEST_CASE("IpsContext post detection", "[IpsContext]") 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 diff --git a/src/detection/ips_context.h b/src/detection/ips_context.h index 49f4006f4..2136f92c4 100644 --- a/src/detection/ips_context.h +++ b/src/detection/ips_context.h @@ -57,6 +57,7 @@ class SO_PUBLIC IpsContext { public: using Callback = void(*)(IpsContext*); + enum State { IDLE, BUSY, SUSPENDED }; IpsContext(unsigned size = 0); // defaults to max id ~IpsContext(); @@ -68,12 +69,6 @@ public: 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() @@ -96,6 +91,44 @@ public: 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 rpl; @@ -115,6 +148,7 @@ public: uint64_t context_num; uint64_t packet_number; ActiveRules active_rules; + State state; bool check_tags; static const unsigned buf_size = Codec::PKT_MAX; @@ -126,8 +160,8 @@ private: FlowSnapshot flow; std::vector data; std::vector post_callbacks; - - unsigned slot; + IpsContext* depends_on; + IpsContext* next_to_process; }; } #endif diff --git a/src/detection/ips_context_chain.cc b/src/detection/ips_context_chain.cc new file mode 100644 index 000000000..c372dbf44 --- /dev/null +++ b/src/detection/ips_context_chain.cc @@ -0,0 +1,120 @@ +//-------------------------------------------------------------------------- +// 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 + +#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 + diff --git a/src/detection/ips_context_chain.h b/src/detection/ips_context_chain.h new file mode 100644 index 000000000..4a9e103ed --- /dev/null +++ b/src/detection/ips_context_chain.h @@ -0,0 +1,54 @@ +//-------------------------------------------------------------------------- +// 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 + +#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 + diff --git a/src/detection/regex_offload.cc b/src/detection/regex_offload.cc index 571b1c7ad..0571e6038 100644 --- a/src/detection/regex_offload.cc +++ b/src/detection/regex_offload.cc @@ -49,7 +49,6 @@ struct RegexRequest std::atomic offload { false }; - unsigned id = 0; bool go = true; }; @@ -108,9 +107,10 @@ void RegexOffload::worker(RegexRequest* req) } 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; } @@ -124,7 +124,7 @@ void RegexOffload::tterm() RuleLatency::tterm(); } -void RegexOffload::put(unsigned id, snort::Packet* p) +void RegexOffload::put(snort::Packet* p) { assert(p); assert(!idle.empty()); @@ -136,30 +136,34 @@ void RegexOffload::put(unsigned id, snort::Packet* p) std::unique_lock 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) diff --git a/src/detection/regex_offload.h b/src/detection/regex_offload.h index 198071569..b42b6ef2a 100644 --- a/src/detection/regex_offload.h +++ b/src/detection/regex_offload.h @@ -55,8 +55,8 @@ public: 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*); diff --git a/src/flow/flow.cc b/src/flow/flow.cc index cd8bc9120..46e2e481b 100644 --- a/src/flow/flow.cc +++ b/src/flow/flow.cc @@ -72,7 +72,6 @@ void Flow::init(PktType type) } mpls_client.length = 0; mpls_server.length = 0; - offloads_pending = 0; } void Flow::term() @@ -478,4 +477,3 @@ void Flow::set_service(Packet* pkt, const char* new_service) service = new_service; DataBus::publish(FLOW_SERVICE_CHANGE_EVENT, pkt); } - diff --git a/src/flow/flow.h b/src/flow/flow.h index 6b90c3d56..c189ae121 100644 --- a/src/flow/flow.h +++ b/src/flow/flow.h @@ -27,6 +27,7 @@ // 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" @@ -98,6 +99,7 @@ class Session; namespace snort { struct FlowKey; +class IpsContext; struct Packet; typedef void (* StreamAppDataFree)(void*); @@ -285,22 +287,8 @@ public: 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 @@ -324,6 +312,7 @@ public: // FIXIT-M privatize if possible 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 @@ -354,7 +343,6 @@ public: // FIXIT-M privatize if possible uint8_t response_count; - uint8_t offloads_pending; bool disable_inspect; private: diff --git a/src/flow/flow_cache.cc b/src/flow/flow_cache.cc index a3ec40223..db7d4405c 100644 --- a/src/flow/flow_cache.cc +++ b/src/flow/flow_cache.cc @@ -190,7 +190,7 @@ unsigned FlowCache::prune_stale(uint32_t thetime, const Flow* save_me) break; } #endif - if ( flow->is_offloaded() ) + if ( flow->is_suspended() ) break; if ( flow->last_data_seen + config.pruning_timeout >= thetime ) @@ -252,7 +252,7 @@ unsigned FlowCache::prune_excess(const Flow* save_me) 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 @@ -308,7 +308,7 @@ unsigned FlowCache::timeout(unsigned num_flows, time_t thetime) break; if ( HighAvailabilityManager::in_standby(flow) or - flow->is_offloaded() ) + flow->is_suspended() ) { flow = static_cast(hash_table->next()); continue; diff --git a/src/framework/mpse.h b/src/framework/mpse.h index 46b03caf9..f1e7d150f 100644 --- a/src/framework/mpse.h +++ b/src/framework/mpse.h @@ -71,7 +71,7 @@ public: 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; } diff --git a/src/main/snort.cc b/src/main/snort.cc index c02133d22..36ece2304 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -804,7 +804,7 @@ void Snort::thread_init_unprivileged() // 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); diff --git a/src/protocols/packet.cc b/src/protocols/packet.cc index 164c0c7b2..38c9fdd9d 100644 --- a/src/protocols/packet.cc +++ b/src/protocols/packet.cc @@ -73,6 +73,7 @@ void Packet::reset() { flow = nullptr; packet_flags = 0; + ts_packet_flags = 0; xtradata_mask = 0; proto_bits = 0; alt_dsize = 0; diff --git a/src/protocols/packet.h b/src/protocols/packet.h index db38f028f..10edbfed8 100644 --- a/src/protocols/packet.h +++ b/src/protocols/packet.h @@ -76,7 +76,9 @@ class Obfuscator; #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) @@ -153,6 +155,7 @@ struct SO_PUBLIC Packet 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 @@ -268,6 +271,15 @@ struct SO_PUBLIC Packet 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); diff --git a/src/search_engines/ac_banded.cc b/src/search_engines/ac_banded.cc index 8e73a539b..265c4b00d 100644 --- a/src/search_engines/ac_banded.cc +++ b/src/search_engines/ac_banded.cc @@ -78,7 +78,7 @@ public: int print_info() override { return acsmPrintDetailInfo2(obj); } - int get_pattern_count() override + int get_pattern_count() const override { return acsmPatternCount2(obj); } }; diff --git a/src/search_engines/ac_bnfa.cc b/src/search_engines/ac_bnfa.cc index b21a27956..6c93156f5 100644 --- a/src/search_engines/ac_bnfa.cc +++ b/src/search_engines/ac_bnfa.cc @@ -94,7 +94,7 @@ public: return 0; } - int get_pattern_count() override + int get_pattern_count() const override { return bnfaPatternCount(obj); } diff --git a/src/search_engines/ac_full.cc b/src/search_engines/ac_full.cc index 7a024bf3a..cd0338250 100644 --- a/src/search_engines/ac_full.cc +++ b/src/search_engines/ac_full.cc @@ -85,7 +85,7 @@ public: int print_info() override { return acsmPrintDetailInfo2(obj); } - int get_pattern_count() override + int get_pattern_count() const override { return acsmPatternCount2(obj); } }; diff --git a/src/search_engines/ac_sparse.cc b/src/search_engines/ac_sparse.cc index a67fb6cb9..cfcd64f94 100644 --- a/src/search_engines/ac_sparse.cc +++ b/src/search_engines/ac_sparse.cc @@ -72,7 +72,7 @@ public: int print_info() override { return acsmPrintDetailInfo2(obj); } - int get_pattern_count() override + int get_pattern_count() const override { return acsmPatternCount2(obj); } }; diff --git a/src/search_engines/ac_sparse_bands.cc b/src/search_engines/ac_sparse_bands.cc index b86997f9d..cbe8930df 100644 --- a/src/search_engines/ac_sparse_bands.cc +++ b/src/search_engines/ac_sparse_bands.cc @@ -72,7 +72,7 @@ public: int print_info() override { return acsmPrintDetailInfo2(obj); } - int get_pattern_count() override + int get_pattern_count() const override { return acsmPatternCount2(obj); } }; diff --git a/src/search_engines/ac_std.cc b/src/search_engines/ac_std.cc index 68a8ff85d..24bfcc05a 100644 --- a/src/search_engines/ac_std.cc +++ b/src/search_engines/ac_std.cc @@ -64,7 +64,7 @@ public: int print_info() override { return acsmPrintDetailInfo(obj); } - int get_pattern_count() override + int get_pattern_count() const override { return acsmPatternCount(obj); } }; diff --git a/src/search_engines/hyperscan.cc b/src/search_engines/hyperscan.cc index 856d4bdf2..c04e58867 100644 --- a/src/search_engines/hyperscan.cc +++ b/src/search_engines/hyperscan.cc @@ -138,7 +138,7 @@ public: 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); diff --git a/src/stream/stream.cc b/src/stream/stream.cc index efdda8c41..a1356ad4c 100644 --- a/src/stream/stream.cc +++ b/src/stream/stream.cc @@ -166,7 +166,7 @@ void Stream::check_flow_closed(Packet* p) // 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; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 9ec49c31e..806cb0c02 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -4,6 +4,7 @@ set( UTIL_INCLUDES cpp_macros.h endian.h kmap.h + primed_allocator.h safec.h segment_mem.h sflsq.h diff --git a/src/utils/primed_allocator.h b/src/utils/primed_allocator.h new file mode 100644 index 000000000..102e20279 --- /dev/null +++ b/src/utils/primed_allocator.h @@ -0,0 +1,119 @@ +//-------------------------------------------------------------------------- +// 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 + +#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 +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 struct rebind { typedef PrimedAllocator 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(state->free_list); + state->free_list = state->free_list->next; + return ret; + } + return reinterpret_cast(operator new(sizeof(T))); + } + + void deallocate(pointer p, size_type) noexcept + { + Node* node = reinterpret_cast(p); + node->next = state->free_list; + state->free_list = node; + } + +private: + State* state; +}; +} + +#endif diff --git a/src/utils/stats.cc b/src/utils/stats.cc index c622d8ade..ac071c723 100644 --- a/src/utils/stats.cc +++ b/src/utils/stats.cc @@ -189,6 +189,7 @@ const PegInfo pc_names[] = { 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 } }; diff --git a/src/utils/stats.h b/src/utils/stats.h index 3509c8d28..62965f410 100644 --- a/src/utils/stats.h +++ b/src/utils/stats.h @@ -54,6 +54,7 @@ struct PacketCount PegCount log_limit; PegCount event_limit; PegCount alert_limit; + PegCount context_stalls; }; struct ProcessCount