From cec9f8f9efa28cdd18279a2093badfe6b23a329e Mon Sep 17 00:00:00 2001 From: Russ Combs Date: Sun, 16 Oct 2016 08:44:29 -0400 Subject: [PATCH] initial ContextSwitcher --- src/detection/context_switcher.cc | 252 ++++++++++++++++++++++++++++++ src/detection/context_switcher.h | 82 ++++++++++ 2 files changed, 334 insertions(+) create mode 100644 src/detection/context_switcher.cc create mode 100644 src/detection/context_switcher.h diff --git a/src/detection/context_switcher.cc b/src/detection/context_switcher.cc new file mode 100644 index 000000000..ee4c08a23 --- /dev/null +++ b/src/detection/context_switcher.cc @@ -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 + +#include "context_switcher.h" + +#include + +#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 index 000000000..a775216e9 --- /dev/null +++ b/src/detection/context_switcher.h @@ -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 + +#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 + +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 idle; + std::vector busy; + std::vector hold; +}; + +#endif + -- 2.47.2