]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1465 in SNORT/snort3 from ~CWAXMAN/snort3:offload_context_deps...
authorMichael Altizer (mialtize) <mialtize@cisco.com>
Wed, 19 Dec 2018 18:48:38 +0000 (13:48 -0500)
committerMichael Altizer (mialtize) <mialtize@cisco.com>
Wed, 19 Dec 2018 18:48:38 +0000 (13:48 -0500)
Squashed commit of the following:

commit e8c4313927cb73dbca52471a461c129c18460ac2
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Fri Dec 7 07:29:50 2018 -0500

    Mpse: fixed build warning about constness of get_pattern_count

commit 4d3aa71ec4fc7130073aad4ed143407b7c656f6a
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Thu Dec 6 16:29:02 2018 -0500

    Flow: is_offloaded is now is_suspended

commit c8fddc2d61843ad1ff41e1ea721dea890007ff0b
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Thu Dec 6 09:12:27 2018 -0500

    IpsContext: removed useless SUSPENDED_OFFLOAD state

commit ec47d206b928baa3f9c97364980cf99a06f804c1
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Wed Dec 5 15:07:33 2018 -0500

    Packet: fixed thread safety in onload flag checks

commit 8e6969234e2f1ba4d62522938342c1909b21c810
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Wed Dec 5 15:30:11 2018 -0500

    RegexOffload: onload whatever is ready

commit 59618c74d8a449128c82d4bddb0b2399ea77630b
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Thu Dec 6 16:52:37 2018 -0500

    DetectionEngine: make onload safe for reentrance

commit fe186cabc5d0632af8c0555bed88f33529f7fa45
Author: Carter Waxman <cwaxman@cisco.com>
Date:   Thu Dec 6 10:52:25 2018 -0500

    DetectionEngine: stall when out of contexts

commit 3250b20edbe279daa5d22c50381fbe6fbeaaefc0
Author: Carter Waxman <cwaxman@cisco.com>
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 <cwaxman@cisco.com>
Date:   Thu Nov 29 13:26:14 2018 -0500

    detection: allow suspension of entire chains of contexts

32 files changed:
src/detection/CMakeLists.txt
src/detection/context_switcher.cc
src/detection/context_switcher.h
src/detection/detection_engine.cc
src/detection/detection_engine.h
src/detection/fp_detect.cc
src/detection/fp_detect.h
src/detection/ips_context.cc
src/detection/ips_context.h
src/detection/ips_context_chain.cc [new file with mode: 0644]
src/detection/ips_context_chain.h [new file with mode: 0644]
src/detection/regex_offload.cc
src/detection/regex_offload.h
src/flow/flow.cc
src/flow/flow.h
src/flow/flow_cache.cc
src/framework/mpse.h
src/main/snort.cc
src/protocols/packet.cc
src/protocols/packet.h
src/search_engines/ac_banded.cc
src/search_engines/ac_bnfa.cc
src/search_engines/ac_full.cc
src/search_engines/ac_sparse.cc
src/search_engines/ac_sparse_bands.cc
src/search_engines/ac_std.cc
src/search_engines/hyperscan.cc
src/stream/stream.cc
src/utils/CMakeLists.txt
src/utils/primed_allocator.h [new file with mode: 0644]
src/utils/stats.cc
src/utils/stats.h

index 02d3f262940c1b7514200a951f146db96e8f59bd..4620d7e847d80f23a6a4e45d4b0669fbd9e5bbef 100644 (file)
@@ -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
index 385a8a00626d2cc2f11955c5ac3cf25abe857110..f58e22b91acd186c5eb06b0cbbc1386da0f5ad71 100644 (file)
 
 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<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
 
index 2ab04c7387e8ee516bc1367ee0a5894492d03d04..fe4d90fb8a6cdd94055924f64ae24ea1b6ce4876 100644 (file)
 #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;
@@ -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<snort::IpsContext*> idle;
     std::vector<snort::IpsContext*> busy;
-    std::vector<snort::IpsContext*> hold;
+    std::vector<snort::IpsContext*> contexts;
 };
 
 #endif
index 6bc1e34caf5ebdb58beb5181875051c8894394f5..2b3bc11492c072fdded4f86944aa6443761e59b4 100644 (file)
@@ -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);
 }
-
index 55de5bb38529a0e84b122494ee1feaa6d3478bda..4f926694ef8f6c197017665f5ac7580e2cbf642f 100644 (file)
@@ -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;
index fb34ff4502d7adb058aff5a8c86ed63a8ae180a6..083d68b441a958938987650745f2270ca333f2fd 100644 (file)
@@ -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;
index ff9d00dd05bfc506103741a1fbaef10b925ae97d..e0af16168d2e8a7f418cb4e73a94c42a8c03c2b0 100644 (file)
@@ -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
 
index e22495780cd7d5d3272c001dc5ec8a300375e962..d8daeee6339d7d7e1bc6b053dc20840ca9df333e 100644 (file)
 #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
@@ -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
 
index 49f4006f43e20fe470d7481b2b107726f09033ff..2136f92c47f4b144b5d18b4d197764806dc2717d 100644 (file)
@@ -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<Replacement> 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<IpsContextData*> data;
     std::vector<Callback> 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 (file)
index 0000000..c372dbf
--- /dev/null
@@ -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 <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
+
diff --git a/src/detection/ips_context_chain.h b/src/detection/ips_context_chain.h
new file mode 100644 (file)
index 0000000..4a9e103
--- /dev/null
@@ -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 <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
+
index 571b1c7adc7574262c4cb3ebf7633f940739712e..0571e6038e58787e7a227417b6088a5d9a9534f6 100644 (file)
@@ -49,7 +49,6 @@ struct RegexRequest
 
     std::atomic<bool> 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<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)
index 198071569b0d68a5889bb8826db0abde7cc433e8..b42b6ef2acabd3c7a238723affcf52caa1ee2dad 100644 (file)
@@ -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*);
 
index cd8bc9120b16175f1b6ce6bff54a745313a59ee2..46e2e481b808b659dd051744df485fe35e785065 100644 (file)
@@ -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);
 }   
-
index 6b90c3d56970750466e4ce717abe842ee826cf53..c189ae12172dfdb66d02963eeb1102b57561d622 100644 (file)
@@ -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:
index a3ec402238acc89483690850de4bf15595db1f3b..db7d4405c891b31de638f09da58de71e686609c2 100644 (file)
@@ -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<Flow*>(hash_table->next());
             continue;
index 46b03caf992e6b0a9c14d4df6eb1ca103670fae6..f1e7d150f2206fc6c5859247d3a066beef36e45a 100644 (file)
@@ -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; }
index c02133d226b9d377f3018475afd3bcad239f1f96..36ece23043fb34540a3ae870c7a80d10250346fd 100644 (file)
@@ -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);
index 164c0c7b2e035d9466ec4d35c537ec729dcc4bc8..38c9fdd9defdc27b47946175bdc7cec6f3398f96 100644 (file)
@@ -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;
index db38f028f453fe9d7f0b8e9750bbe0640c45dd48..10edbfed840a63bdc80999ead9324b4a1914379e 100644 (file)
@@ -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);
index 8e73a539bb65f9d9db07d2c116a0b59138f35ca9..265c4b00da66c8db448e9ac5477f866b6c6051b0 100644 (file)
@@ -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); }
 };
 
index b21a27956dc9d59386742c266aa3a2b3cce7aaf3..6c93156f59dfa09c63255f199d885a4b6eb5f084 100644 (file)
@@ -94,7 +94,7 @@ public:
         return 0;
     }
 
-    int get_pattern_count() override
+    int get_pattern_count() const override
     {
         return bnfaPatternCount(obj);
     }
index 7a024bf3addf888fdcd6ecf8125250cdb69dc637..cd0338250a6862b3637ed8f048e8cff64f4b7ef3 100644 (file)
@@ -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); }
 };
 
index a67fb6cb941f25cf4b4f69ddb88ff733ee95f757..cfcd64f94ddc4b868d8772ff3e72110200677097 100644 (file)
@@ -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); }
 };
 
index b86997f9df57007a1c3ca69e832e5b44a1bf22d8..cbe8930df3fede79ed3f67daaa9f065487c29d3f 100644 (file)
@@ -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); }
 };
 
index 68a8ff85d7c3ba7bfa85f295afd28c88a2867266..24bfcc05a741ec7010f21f448c29fc163fa203a6 100644 (file)
@@ -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); }
 };
 
index 856d4bdf2fa0f9a9d264d3a949b73913c9f12a98..c04e58867928f1b783b7687fc517385e75d3f013 100644 (file)
@@ -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);
index efdda8c4199bc8e0bafe4a242f384c896d05b5ba..a1356ad4ca6b674601674d133532abc1fcbaad54 100644 (file)
@@ -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;
index 9ec49c31e3a7041d9b69951319655267b00a02fb..806cb0c02c905262884c886cbf2586ec5d838293 100644 (file)
@@ -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 (file)
index 0000000..102e202
--- /dev/null
@@ -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 <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
index c622d8ade15437a2c4d948b5a8e07ab2d9a844d7..ac071c7238e8347b7d188a58523a7e8608c84dc9 100644 (file)
@@ -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 }
 };
 
index 3509c8d2870fefceda1868478a498500257c9d43..62965f410bd3c3aaa6c1f1ec79b4cb8291807b3d 100644 (file)
@@ -54,6 +54,7 @@ struct PacketCount
     PegCount log_limit;
     PegCount event_limit;
     PegCount alert_limit;
+    PegCount context_stalls;
 };
 
 struct ProcessCount