#include <assert.h>
+#include "utils/stats.h"
#include "ips_context.h"
#ifdef UNIT_TEST
{
assert(busy.empty());
assert(idle.size() > 0);
+//printf("%ld cs::start %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, idle.back()->get_slot(), idle.size(), busy.size());
busy.push_back(idle.back());
idle.pop_back();
}
void ContextSwitcher::stop()
{
assert(busy.size() == 1);
+//printf("%ld cs::stop %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, busy.back()->get_slot(), idle.size(), busy.size());
idle.push_back(busy.back());
busy.pop_back();
}
void ContextSwitcher::abort()
{
+//printf("%ld cs::abort (i=%lu, b=%lu)\n",
+// pc.total_from_daq, idle.size(), busy.size());
for ( unsigned i = 0; i < hold.capacity(); ++i )
{
if ( hold[i] )
IpsContext* ContextSwitcher::interrupt()
{
assert(!idle.empty());
+//printf("%ld cs::interrupt %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, idle.back()->get_slot(), idle.size(), busy.size());
busy.push_back(idle.back());
idle.pop_back();
return busy.back();
IpsContext* ContextSwitcher::complete()
{
assert(!busy.empty());
+//printf("%ld cs::complete %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, busy.back()->get_slot(), idle.size(), busy.size());
idle.push_back(busy.back());
busy.pop_back();
return busy.empty() ? nullptr : busy.back();
unsigned ContextSwitcher::suspend()
{
assert(!busy.empty());
+//printf("%ld cs::suspend %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, busy.back()->get_slot(), idle.size(), busy.size());
IpsContext* c = busy.back();
busy.pop_back();
unsigned slot = c->get_slot();
void ContextSwitcher::resume(unsigned slot)
{
assert(slot <= hold.capacity());
+//printf("%ld cs::resume %u (i=%lu, b=%lu)\n",
+// pc.total_from_daq, slot, idle.size(), busy.size());
busy.push_back(hold[slot]);
hold[slot] = nullptr;
}
return c;
}
+IpsContext* ContextSwitcher::get_next() const
+{
+ assert(!idle.empty());
+ return idle.back();
+}
+
IpsContextData* ContextSwitcher::get_context_data(unsigned id) const
{
return get_context()->get_context_data(id);
// available.
//
// 2. during processing interrupt and complete should be called to start
-// and finis processing of a generated pseudo packet. it is possible to
+// and finish 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
IpsContext* get_context() const;
IpsContext* get_context(unsigned) const;
+ IpsContext* get_next() const;
IpsContextData* get_context_data(unsigned id) const;
void set_context_data(unsigned id, IpsContextData*) const;
#include "events/sfeventq.h"
#include "filters/sfthreshold.h"
#include "framework/endianness.h"
+#include "helpers/ring.h"
#include "latency/packet_latency.h"
#include "main/snort.h"
#include "main/snort_config.h"
#include "context_switcher.h"
#include "detection_util.h"
#include "detect.h"
+#include "fp_config.h"
#include "fp_detect.h"
#include "ips_context.h"
+THREAD_LOCAL DetectionEngine::ActiveRules active_rules = DetectionEngine::NONE;
+
static THREAD_LOCAL unsigned s_events = 0;
+static THREAD_LOCAL Ring<unsigned>* offload_ids = nullptr;
-THREAD_LOCAL DetectionEngine::ActiveRules active_rules = DetectionEngine::NONE;
+void DetectionEngine::thread_init()
+{ offload_ids = new Ring<unsigned>(32); } // FIXIT-H get size
+
+void DetectionEngine::thread_term()
+{ delete offload_ids; }
DetectionEngine::DetectionEngine()
-{ Snort::get_switcher()->interrupt(); }
+{ context = Snort::get_switcher()->interrupt(); }
DetectionEngine::~DetectionEngine()
-{ clear_packet(); }
+{
+ if ( context == get_context() )
+ clear_packet();
+}
IpsContext* DetectionEngine::get_context()
{ return Snort::get_switcher()->get_context(); }
// any events while rebuilding will be logged against the current packet
Packet* DetectionEngine::set_packet()
{
- ContextSwitcher* sw = Snort::get_switcher();
-
- // FIXIT-H bypass the interrupt / complete
- const IpsContext* c = sw->interrupt();
+ const IpsContext* c = Snort::get_switcher()->get_next();
Packet* p = c->packet;
- sw->complete();
p->pkth = c->pkth;
p->data = c->buf;
void DetectionEngine::clear_packet()
{
ContextSwitcher* sw = Snort::get_switcher();
- Packet* p = sw->get_context()->packet;
+ IpsContext* c = sw->get_context();
+
+ if ( c->offload )
+ return;
+
+ Packet* p = c->packet;
log_events(p);
reset();
// we do it this way.
void DetectionEngine::set_next_file_data(const DataPointer& dp)
{
- ContextSwitcher* sw = Snort::get_switcher();
-
- // FIXIT-H bypass the interrupt / complete
- IpsContext* c = sw->interrupt();
+ IpsContext* c = Snort::get_switcher()->get_next();
c->file_data = dp;
- sw->complete();
}
void DetectionEngine::get_next_file_data(DataPointer& dp)
{
- ContextSwitcher* sw = Snort::get_switcher();
-
- // FIXIT-H bypass the interrupt / complete
- IpsContext* c = sw->interrupt();
+ const IpsContext* c = Snort::get_switcher()->get_next();
dp = c->file_data;
- sw->complete();
}
void DetectionEngine::set_file_data(const DataPointer& dp)
void DetectionEngine::disable_all()
{ active_rules = NONE; }
+bool DetectionEngine::offloaded(Flow* flow)
+{ return flow->test_session_flags(SSNFLAG_OFFLOAD); }
+
+bool DetectionEngine::offloaded(Packet* p)
+{ return p->flow and offloaded(p->flow); }
+
+void DetectionEngine::idle()
+{
+ while ( !offload_ids->empty() )
+ {
+ const struct timespec blip = { 0, 1 };
+//printf("%lu de::sleep\n", pc.total_from_daq);
+ nanosleep(&blip, nullptr);
+ onload();
+ }
+//printf("%lu de::idle (r=%d)\n", pc.total_from_daq, offload_ids->count());
+}
+
+void DetectionEngine::onload(Flow* flow)
+{
+ while ( flow->test_session_flags(SSNFLAG_OFFLOAD) )
+ {
+ const struct timespec blip = { 0, 1 };
+//printf("%lu de::sleep\n", pc.total_from_daq);
+ nanosleep(&blip, nullptr);
+ onload();
+ }
+}
+
+void DetectionEngine::onload()
+{
+ ContextSwitcher* sw = Snort::get_switcher();
+ unsigned* id = offload_ids->read();
+ IpsContext* c = sw->get_context(*id);
+
+ assert(c->offload);
+
+ if ( !c->onload )
+ return;
+
+//printf("%lu de::onload %u (r=%d)\n", pc.total_from_daq, *id, offload_ids->count());
+ Packet* p = c->packet;
+ p->flow->clear_session_flags(SSNFLAG_OFFLOAD);
+
+ c->offload->join();
+ delete c->offload;
+ c->offload = nullptr;
+
+ offload_ids->pop();
+ sw->resume(*id);
+
+ fp_onload(p);
+ InspectorManager::clear(p);
+ log_events(p);
+ reset();
+ clear_packet();
+}
+
+bool DetectionEngine::offload(Packet* p)
+{
+ ContextSwitcher* sw = Snort::get_switcher();
+ FastPatternConfig* fp = snort_conf->fast_pattern_config;
+
+ if ( p->type() != PktType::PDU or (p->dsize < fp->get_offload_limit()) or !sw->can_hold() )
+ {
+ fp_local(p);
+ return false;
+ }
+ assert(p == p->context->packet);
+ onload(p->flow); // FIXIT-H ensures correct sequencing, suboptimal
+
+ p->flow->set_session_flags(SSNFLAG_OFFLOAD|SSNFLAG_WAS_OFF);
+ pc.offloads++;
+
+ unsigned id = sw->suspend();
+ offload_ids->put(id);
+//printf("%lu de::offload %u (r=%d)\n", pc.total_from_daq, id, offload_ids->count());
+
+ p->context->onload = false;
+ p->context->offload = new std::thread(fp_offload, p, snort_conf);
+
+ return true;
+}
+
bool DetectionEngine::detect(Packet* p)
{
assert(p);
case PktType::ICMP:
case PktType::PDU:
case PktType::FILE:
- return fpEvalPacket(p);
+ return offload(p);
default:
break;
Active::apply_delayed_action(p);
if ( active_rules > NONE )
- detect(p);
+ {
+ if ( detect(p) )
+ return;
+ }
}
enable_tags();
+ // By checking tagging here, we make sure that we log the
+ // tagged packet whether it generates an alert or not.
+
+ if ( p->has_ip() )
+ check_tags(p);
+
+ if ( offloaded(p) )
+ return;
+
// clear closed sessions here after inspection since non-stream
// inspectors may depend on flow information
// FIXIT-H but this result in double clearing? should normal
// clear_session() calls be deleted from stream? this is a
// performance hit on short-lived flows
- Stream::check_flow_closed(p);
- /*
- ** By checking tagging here, we make sure that we log the
- ** tagged packet whether it generates an alert or not.
- */
- if ( p->has_ip() )
- check_tags(p);
+ Stream::check_flow_closed(p);
if ( inspected )
InspectorManager::clear(p);
}
Profile profile(eventqPerfStats);
+
log_events(p);
reset();
}
struct DataPointer;
struct Packet;
+class Flow;
class IpsContext;
class IpsContextData;
Packet* get_packet();
public:
+ static void thread_init();
+ static void thread_term();
+
static IpsContext* get_context();
static Packet* get_current_packet();
static Packet* set_packet();
static void clear_packet();
+ static bool offloaded(Flow*);
+ static bool offloaded(Packet*);
+ static bool offload(Packet*);
+
+ static void onload(Flow*);
+ static void onload();
+ static void idle();
+
static void set_encode_packet(Packet*);
static Packet* get_encode_packet();
{ return get_detects() == CONTENT; }
private:
+ IpsContext* context;
static struct SF_EVENTQ* get_event_queue();
};
search_api = MpseManager::get_search_api("ac_bnfa");
assert(search_api);
trim = MpseManager::search_engine_trim(search_api);
+
+ offload_limit = 99999; // FIXIT-H use common value
}
FastPatternConfig::~FastPatternConfig()
return 0;
}
-static int fp_tsearch(
- PortGroup* port_group, Packet* p, int check_ports, int type, OtnxMatchData* omd,
- SnortConfig* sc)
-{
- snort_conf = sc;
- return fp_search(port_group, p, check_ports, type, omd);
-}
-
-static int fp_offload(
- PortGroup* port_group, Packet* p, int check_ports, int type, OtnxMatchData* omd)
-{
- MpseStash* stash = p->context->stash;
- ContextSwitcher* sw = Snort::get_switcher();
- FastPatternConfig* fp = snort_conf->fast_pattern_config;
-
- if ( p->type() != PktType::PDU or (p->dsize < fp->get_offload_limit()) or
- p->flow->test_session_flags(SSNFLAG_OFFLOAD) or !sw->can_hold() )
- {
- stash->enable_process();
- return fp_search(port_group, p, check_ports, type, omd);
- }
-
- assert(p == p->context->packet);
- stash->init();
- stash->disable_process();
-
- p->flow->set_session_flags(SSNFLAG_OFFLOAD);
- pc.offloads++;
-
- unsigned id = sw->suspend();
-
- std::thread t(fp_tsearch, port_group, p, check_ports, type, omd, snort_conf);
- t.join();
-
- sw->resume(id);
-
- p->flow->clear_session_flags(SSNFLAG_OFFLOAD);
- stash->enable_process();
- stash->process(rule_tree_match, omd);
- fpFinalSelectEvent(omd, p);
-
- return 0;
-}
-
/*
** DESCRIPTION
** This function does a set-wise match on content, and walks an otn list
if ( DetectionEngine::content_enabled() )
{
if ( fp->get_stream_insert() || !(p->packet_flags & PKT_STREAM_INSERT) )
- if ( fp_offload(port_group, p, check_ports, type, omd) )
+ if ( fp_search(port_group, p, check_ports, type, omd) )
return 0;
}
default:
break;
}
- if ( !p->flow or !p->flow->test_session_flags(SSNFLAG_OFFLOAD) )
- return fpFinalSelectEvent(omd, p);
return 0;
}
+void fp_local(Packet* p)
+{
+ IpsContext* c = p->context;
+ MpseStash* stash = c->stash;
+ stash->enable_process();
+ stash->init();
+ fpEvalPacket(p);
+ fpFinalSelectEvent(c->otnx, p);
+}
+
+void fp_offload(Packet* p, SnortConfig* sc)
+{
+ snort_conf = sc; // FIXIT-H reload issue
+ MpseStash* stash = p->context->stash;
+ stash->init();
+ stash->disable_process();
+ fpEvalPacket(p);
+ p->context->onload = true;
+}
+
+void fp_onload(Packet* p)
+{
+ IpsContext* c = p->context;
+ MpseStash* stash = c->stash;
+ stash->enable_process();
+ stash->process(rule_tree_match, c->otnx);
+ fpFinalSelectEvent(c->otnx, p);
+}
+
void fp_set_context(IpsContext&);
void fp_clear_context(IpsContext&);
+void fp_local(Packet*);
+void fp_offload(Packet*, struct SnortConfig*);
+void fp_onload(Packet*);
+
#endif
packet->context = this;
fp_set_context(*this);
+
+ offload = nullptr;
+ onload = false;
}
IpsContext::~IpsContext()
if ( p )
delete p;
+ assert(!offload);
+
sfeventq_free(equeue);
fp_clear_context(*this);
// integration into Snort.
#include <vector>
+#include <thread>
+
#include "main/snort_types.h"
#include "framework/codec.h"
Packet* encode_packet;
DAQ_PktHdr_t* pkth;
uint8_t* buf;
+ std::thread* offload;
DataPointer file_data;
class MpseStash* stash;
struct OtnxMatchData* otnx;
uint64_t pkt_count;
+ bool onload;
struct SF_EVENTQ* equeue;
#define SSNFLAG_PROXIED 0x01000000
#define SSNFLAG_OFFLOAD 0x02000000
+#define SSNFLAG_WAS_OFF 0x04000000 // FIXIT-L debug only
+
#define SSNFLAG_NONE 0x00000000 /* nothing, an MT bag of chips */
#define SSNFLAG_SEEN_BOTH (SSNFLAG_SEEN_SERVER | SSNFLAG_SEEN_CLIENT)
#include "flow/flow_cache.h"
+#include "detection/detection_engine.h"
#include "flow/ha.h"
#include "hash/zhash.h"
#include "helpers/flag_context.h"
int FlowCache::release(Flow* flow, PruneReason reason, bool do_cleanup)
{
+ DetectionEngine::onload(flow);
flow->reset(do_cleanup);
prune_stats.update(reason);
return remove(flow);
break;
}
#endif
+ if ( DetectionEngine::offloaded(flow) )
+ break;
+
if ( flow->last_data_seen + config.pruning_timeout >= thetime )
break;
auto flow = static_cast<Flow*>(hash_table->first());
assert(flow); // holds true because hash_table->get_count() > 0
- if ( (save_me and flow == save_me) or flow->was_blocked() )
+ if ( (save_me and flow == save_me) or flow->was_blocked() or
+ DetectionEngine::offloaded(flow) )
{
// check for non-null save_me above to silence analyzer
// "called C++ object pointer is null" here
if ( flow->last_data_seen + config.nominal_timeout > thetime )
break;
- if ( HighAvailabilityManager::in_standby(flow) )
+ if ( HighAvailabilityManager::in_standby(flow) or
+ DetectionEngine::offloaded(flow) )
{
flow = static_cast<Flow*>(hash_table->next());
continue;
FlowControl::~FlowControl()
{
+ DetectionEngine de;
+
delete ip_cache;
delete icmp_cache;
delete tcp_cache;
if ( p->proto_bits & PROTO_BIT__MPLS )
flow->set_mpls_layer_per_dir(p);
+ if ( p->type() == PktType::PDU ) // FIXIT-H cooked or PDU?
+ DetectionEngine::onload(flow);
+
switch ( flow->flow_state )
{
case Flow::FlowState::SETUP:
{ "inspect_stream_inserts", Parameter::PT_BOOL, nullptr, "false",
"inspect reassembled payload - disabling is good for performance, bad for detection" },
- { "offload_limit", Parameter::PT_INT, nullptr, "99999",
+ { "offload_limit", Parameter::PT_INT, "0:", "99999",
"minimum sizeof PDU to offload fast pattern search (defaults to disabled)" },
{ "search_method", Parameter::PT_DYNAMIC, (void*)&get_search_methods, "ac_bnfa",
InitTag();
EventTrace_Init();
detection_filter_init(snort_conf->detection_filter_config);
+ DetectionEngine::thread_init();
EventManager::open_outputs();
IpsManager::setup_options();
if ( !snort_conf->dirty_pig )
Stream::purge_flows();
+ DetectionEngine::idle();
InspectorManager::thread_stop(snort_conf);
ModuleManager::accumulate(snort_conf);
InspectorManager::thread_term(snort_conf);
Profiler::consolidate_stats();
+ DetectionEngine::thread_term();
detection_filter_term();
EventTrace_Term();
CleanupTag();
int TcpReassembler::_flush_to_seq(uint32_t bytes, Packet* p, uint32_t pkt_flags)
{
Profile profile(s5TcpFlushPerfStats);
+ DetectionEngine::onload(session->flow);
s5_pkt = DetectionEngine::set_packet();
DAQ_PktHdr_t pkth;
// sanity check since this is called externally
assert(p->ptrs.tcph);
+ DetectionEngine::onload(flow);
TcpStreamTracker* talker, * listener;
if (p->is_from_server())
void TcpSession::clear_session(bool free_flow_data, bool flush_segments, bool restart, Packet* p)
{
+ DetectionEngine::onload(flow);
+
if ( client->reassembler )
{
if ( flush_segments )