]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
initial ContextSwitcher
authorRuss Combs <rucombs@cisco.com>
Sun, 16 Oct 2016 12:44:29 +0000 (08:44 -0400)
committerRuss Combs <rucombs@cisco.com>
Wed, 18 Jan 2017 12:05:49 +0000 (07:05 -0500)
src/detection/context_switcher.cc [new file with mode: 0644]
src/detection/context_switcher.h [new file with mode: 0644]

diff --git a/src/detection/context_switcher.cc b/src/detection/context_switcher.cc
new file mode 100644 (file)
index 0000000..ee4c08a
--- /dev/null
@@ -0,0 +1,252 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// context_switcher.cc author Russ Combs <rucombs@cisco.com>
+
+#include "context_switcher.h"
+
+#include <assert.h>
+
+#include "ips_context.h"
+
+#define UNIT_TEST
+
+#ifdef UNIT_TEST
+#include "catch.hpp"
+#endif
+
+//--------------------------------------------------------------------------
+// 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 )
+        delete p;
+}
+
+void ContextSwitcher::push(IpsContext* c)
+{
+    c->set_slot(idle.size() + 1);
+    idle.push_back(c);
+}
+
+IpsContext* ContextSwitcher::pop()
+{
+    if ( idle.empty() )
+        return nullptr;
+
+    IpsContext* c = idle.back();
+    idle.pop_back();
+    return c;
+}
+
+void ContextSwitcher::start()
+{
+    assert(busy.empty());
+    assert(idle.size() > 0);
+    busy.push_back(idle.back());
+    idle.pop_back();
+}
+
+void ContextSwitcher::stop()
+{
+    assert(busy.size() == 1);
+    idle.push_back(busy.back());
+    busy.pop_back();
+}
+
+void ContextSwitcher::abort()
+{
+    for ( unsigned i = 0; i < hold.capacity(); ++i )
+    {
+        if ( hold[i] )
+        {
+            idle.push_back(hold[i]);
+            hold[i] = nullptr;
+        }
+    }
+    while ( !busy.empty() )
+    {
+        idle.push_back(busy.back());
+        busy.pop_back();
+    }
+}
+
+IpsContext* ContextSwitcher::interrupt()
+{
+    assert(!idle.empty());
+    busy.push_back(idle.back());
+    idle.pop_back();
+    return busy.back();
+}
+
+IpsContext* ContextSwitcher::complete()
+{
+    assert(!busy.empty());
+    idle.push_back(busy.back());
+    busy.pop_back();
+    return busy.empty() ? nullptr : busy.back();
+}
+
+unsigned ContextSwitcher::suspend()
+{
+    assert(!busy.empty());
+    IpsContext* c = busy.back();
+    busy.pop_back();
+    unsigned slot = c->get_slot();
+    assert(!hold[slot]);
+    hold[slot] = c;
+    return slot;
+}
+
+void ContextSwitcher::resume(unsigned slot)
+{
+    assert(slot <= hold.capacity());
+    busy.push_back(hold[slot]);
+    hold[slot] = nullptr;
+}
+
+void ContextSwitcher::set_context_data(unsigned id, IpsContextData* cd) const
+{
+    assert(!busy.empty());
+    busy.back()->set_context_data(id, cd);
+}
+
+IpsContextData* ContextSwitcher::get_context_data(unsigned id) const
+{
+    assert(!busy.empty());
+    return busy.back()->get_context_data(id);
+}
+
+unsigned ContextSwitcher::idle_count() const
+{ return idle.size(); }
+
+unsigned ContextSwitcher::busy_count() const
+{ return busy.size(); }
+
+unsigned ContextSwitcher::hold_count() const
+{
+    unsigned c = 0;
+
+    for ( auto* p : hold )
+        if ( p ) c++;
+
+    return c;
+}
+
+//--------------------------------------------------------------------------
+// unit tests
+//--------------------------------------------------------------------------
+
+#ifdef UNIT_TEST
+class ContextData : public IpsContextData
+{
+public:
+    ContextData(int) { }
+};
+
+TEST_CASE("normal", "[ContextSwitcher]")
+{
+    const unsigned max = 3;
+    auto mgr = ContextSwitcher(max);
+    auto id = IpsContextData::get_ips_id();
+    CHECK(!mgr.pop());
+
+    for ( unsigned i = 0; i < max; ++i )
+        mgr.push(new IpsContext(id+1));
+
+    SECTION("workflow")
+    {
+        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);
+        IpsContext* p = mgr.interrupt();
+        CHECK(mgr.idle_count() == max-2);
+        CHECK(mgr.busy_count() == 2);
+
+        unsigned u = mgr.suspend();
+        CHECK(mgr.idle_count() == max-2);
+        CHECK(mgr.busy_count() == 1);
+        CHECK(mgr.hold_count() == 1);
+
+        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);
+
+        IpsContextData* b = mgr.get_context_data(1);
+        CHECK(a == b);
+
+        mgr.stop();
+        CHECK(mgr.idle_count() == max);
+    }
+    for ( unsigned i = 0; i < max; ++i )
+    {
+        IpsContext* p = mgr.pop();
+        CHECK(p);
+        delete p;
+    }
+    CHECK(!mgr.pop());
+}
+
+TEST_CASE("abort", "[ContextSwitcher]")
+{
+    const unsigned max = 3;
+    auto mgr = ContextSwitcher(max);
+    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);
+
+        unsigned u = mgr.suspend();
+        CHECK(mgr.busy_count() == 2);
+        CHECK(mgr.hold_count() == 1);
+
+        mgr.abort();
+        CHECK(mgr.idle_count() == max);
+    }
+}
+#endif
+
diff --git a/src/detection/context_switcher.h b/src/detection/context_switcher.h
new file mode 100644 (file)
index 0000000..a775216
--- /dev/null
@@ -0,0 +1,82 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// context_switcher.h author Russ Combs <rucombs@cisco.com>
+
+#ifndef CONTEXT_SWITCHER_H
+#define CONTEXT_SWITCHER_H
+
+// ContextSwitcher maintains a set of contexts, only one of which can be
+// 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
+// available.
+//
+// 2.  during processing interrupt and complete should be called to start
+// and finis processing of a generated pseudo packet.  it is possible to
+// interrupt pseudo packets.
+//
+// 3.  suspend may be called to place the current context on hold and
+// activate the prior.  multiple contexts may be placed on hold.
+//
+// 4.  there is no ordering of idle contexts.  busy contexts are in strict
+// LIFO order.  contexts on hold can be resumed in any order.
+//
+// FIXIT-H the ability to setup and clear contexts as they transition to
+// busy and idle will be added when integrated into Snort.
+
+#include <vector>
+
+class IpsContext;
+class IpsContextData;
+
+class ContextSwitcher
+{
+public:
+    ContextSwitcher(unsigned max);
+    ~ContextSwitcher();
+
+    void push(IpsContext*);
+    IpsContext* pop();
+
+    void start();
+    void stop();
+    void abort();
+
+    IpsContext* interrupt();
+    IpsContext* complete();
+
+    unsigned suspend();
+    void resume(unsigned suspended);
+
+    void set_context_data(unsigned id, IpsContextData*) const;
+    IpsContextData* get_context_data(unsigned id) const;
+
+    unsigned idle_count() const;
+    unsigned busy_count() const;
+    unsigned hold_count() const;
+
+private:
+    std::vector<IpsContext*> idle;
+    std::vector<IpsContext*> busy;
+    std::vector<IpsContext*> hold;
+};
+
+#endif
+