Squashed commit of the following:
commit
4c3045b03aaafc429c017dbffd3887c7031773b4
Author: russ <rucombs@cisco.com>
Date: Sun Apr 7 22:09:02 2019 -0400
offload: simplify zero byte bypass
commit
4b038913ceb7598ec61f6bef1b0b5b156ab013f6
Author: William Cochrane <w.cochrane@titan-ic.com>
Date: Tue Mar 26 12:14:29 2019 +0000
offload: Framework changes to support polling for completed
batch searches
When a batch search is issued, currently we poll to
determine if that batch has completed its search.
This change facilitates polling to return any batch
that has completed its search.
commit
65a967dd7731286ba101a144d428554e9ad75cc0
Author: William Cochrane <w.cochrane@titan-ic.com>
Date: Fri Mar 22 16:25:36 2019 +0000
mpse: Adding performance profiling stats to Mpse batch search
The Mpse batch search function does not have any
performance profiling so this function is now wrapped
to facilitate the addition of performance stats
commit
9140669833d97bd5f8e9ada4e2868576e82e5622
Author: William Cochrane <w.cochrane@titan-ic.com>
Date: Thu Mar 21 18:00:34 2019 +0000
detection: Don't send zero size searches to the regex offloader
If a batch search request had nothing in it to be
searched for there is no purpose in sending it to
the offloader
commit
6f1b0ad1baa1a784d70403ef9786ca396d9ba850
Author: William Cochrane <w.cochrane@titan-ic.com>
Date: Thu Mar 21 17:23:27 2019 +0000
detection: Ensure offload search engine started with appropriate regex offloader
If the offload_search_method is not specified then by
default it will be the same as the normal search_method.
If this search method is an async mpse it needs started
using the MpseRegexOffload offloader otherwise it needs
started using the ThreadRegexOffload offloader
// Note: offload_threads is really the maximum number of offload_requests
if (offload_search_api and MpseManager::is_async_capable(offload_search_api))
{
+ // Check that poll functionality has been provided
+ assert(MpseManager::is_poll_capable(offload_search_api));
+
// If the search method is async capable then the searches will be performed directly
// by the search engine, without requiring a processing thread.
offloader = RegexOffload::get_offloader(sc->offload_threads, false);
}
else
{
- // If the search method is not async capable then offloaded searches will be performed
- // in a separate processing thread that the RegexOffload instance needs to create.
- offloader = RegexOffload::get_offloader(sc->offload_threads, true);
+ const MpseApi* search_api = fp->get_search_api();
+
+ if (MpseManager::is_async_capable(search_api))
+ {
+ assert(MpseManager::is_poll_capable(search_api));
+ offloader = RegexOffload::get_offloader(sc->offload_threads, false);
+ }
+ else
+ {
+ // If the search method is not async capable then offloaded searches will be performed
+ // in a separate processing thread that the RegexOffload instance needs to create.
+ offloader = RegexOffload::get_offloader(sc->offload_threads, true);
+ }
}
}
// offload / onload
//--------------------------------------------------------------------------
-void DetectionEngine::do_offload(Packet* p)
+bool 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();
// Build the searches list in the packet context
fp_partial(p);
- offloader->put(p);
- pc.offloads++;
+ if (p->context->searches.items.size() > 0)
+ {
+ 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->set_offloaded();
+
+ offloader->put(p);
+ pc.offloads++;
+
+ return true;
+ }
+ else
+ {
+ bool depends_on_suspended = p->flow ? p->flow->context_chain.front() :
+ sw->non_flow_chain.front();
+
+ if (!depends_on_suspended)
+ {
+ fp_complete(p);
+ return false;
+ }
+ else
+ {
+ sw->suspend();
+ return true;
+ }
+ }
}
bool DetectionEngine::offload(Packet* p)
if ( can_offload and should_offload )
{
- do_offload(p);
- return true;
+ return do_offload(p);
}
if ( depends_on_suspended )
private:
static struct SF_EVENTQ* get_event_queue();
- static void do_offload(snort::Packet*);
+ static bool do_offload(snort::Packet*);
static void offload_thread(IpsContext*);
static void resume(snort::Packet*);
static void resume_ready_suspends(IpsContextChain&);
void* user, void* tree, int index, void* context, void* neg_list)
{
PMX* pmx = (PMX*)user;
- OtnxMatchData* pomd = (OtnxMatchData*)context;
+ OtnxMatchData* pomd = ((IpsContext*)context)->otnx;
detection_option_tree_root_t* root = (detection_option_tree_root_t*)tree;
detection_option_eval_data_t eval_data;
static int rule_tree_queue(
void* user, void* tree, int index, void* context, void* list)
{
- OtnxMatchData* pomd = (OtnxMatchData*)context;
+ OtnxMatchData* pomd = ((IpsContext*)context)->otnx;
MpseStash* stash = pomd->p->context->stash;
if ( stash->push(user, tree, index, list) )
int start_state = 0;
MpseStash* stash = omd->p->context->stash;
stash->init();
- so->get_normal_mpse()->search(buf, len, rule_tree_queue, omd, &start_state);
- stash->process(rule_tree_match, omd);
+ so->get_normal_mpse()->search(buf, len, rule_tree_queue, omd->p->context, &start_state);
+ stash->process(rule_tree_match, omd->p->context);
}
else
{
init_match_info(c->otnx);
c->searches.mf = rule_tree_queue;
- c->searches.context = c->otnx;
+ c->searches.context = c;
fpEvalPacket(p, FPTask::BOTH);
if ( c->searches.search_sync() )
- stash->process(rule_tree_match, c->otnx);
+ stash->process(rule_tree_match, c);
fpFinalSelectEvent(c->otnx, p);
}
stash->disable_process();
init_match_info(c->otnx);
c->searches.mf = rule_tree_queue;
- c->searches.context = c->otnx;
+ c->searches.context = c;
fpEvalPacket(p, FPTask::FP);
}
IpsContext* c = p->context;
MpseStash* stash = c->stash;
stash->enable_process();
- stash->process(rule_tree_match, c->otnx);
+ stash->process(rule_tree_match, c);
fpEvalPacket(p, FPTask::NON_FP);
fpFinalSelectEvent(c->otnx, p);
}
// single packet. the state is stored in IpsContextData instances, which
// are accessed by id.
-#include "main/snort_types.h"
+#include <list>
+
+#include "detection/detection_util.h"
#include "framework/codec.h"
#include "framework/mpse.h"
#include "framework/mpse_batch.h"
-
-// required to get a decent decl of pkth
-#include "protocols/packet.h"
-
-#include "detection/detection_util.h"
+#include "main/snort_types.h"
+#include "protocols/packet.h" // required to get a decent decl of pkth
class MpseStash;
struct OtnxMatchData;
struct SF_EVENTQ;
+struct RegexRequest;
namespace snort
{
MpseBatch searches;
MpseStash* stash;
OtnxMatchData* otnx;
+ std::list<RegexRequest*>::iterator regex_req_it;
SF_EVENTQ* equeue;
DataPointer file_data;
{
assert(p);
assert(!idle.empty());
+ assert(p->context->searches.items.size() > 0);
RegexRequest* req = idle.front();
idle.pop_front(); // FIXIT-H use splice to move instead
+
busy.emplace_back(req);
+ // Because a list is a doubly linked list we can store the iterator
+ // for later quick removal of this item from the list
+ p->context->regex_req_it = std::prev(busy.end());
req->packet = p;
-
- if (p->context->searches.items.size() > 0)
- p->context->searches.offload_search();
+ p->context->searches.offload_search();
}
bool MpseRegexOffload::get(snort::Packet*& p)
{
assert(!busy.empty());
- for ( auto i = busy.begin(); i != busy.end(); i++ )
- {
- RegexRequest* req = *i;
- snort::IpsContext* c = req->packet->context;
-
- if ( c->searches.items.size() > 0 )
- {
- snort::Mpse::MpseRespType resp_ret = c->searches.receive_offload_responses();
+ snort::Mpse::MpseRespType resp_ret;
+ snort::MpseBatch* batch;
- if (resp_ret == snort::Mpse::MPSE_RESP_NOT_COMPLETE)
- continue;
+ resp_ret = snort::MpseBatch::poll_offload_responses(batch);
- else if (resp_ret == snort::Mpse::MPSE_RESP_COMPLETE_FAIL)
+ if (resp_ret != snort::Mpse::MPSE_RESP_NOT_COMPLETE)
+ {
+ if (resp_ret == snort::Mpse::MPSE_RESP_COMPLETE_FAIL)
+ {
+ if (batch->can_fallback())
{
- if (c->searches.can_fallback())
- {
- // FIXIT-M Add peg counts to record offload search fallback attempts
- c->searches.search_sync();
- }
- // FIXIT-M else Add peg counts to record offload search failures
+ // FIXIT-M Add peg counts to record offload search fallback attempts
+ batch->search_sync();
}
- c->searches.items.clear();
+ // FIXIT-M else Add peg counts to record offload search failures
}
- p = req->packet;
- req->packet = nullptr;
+ snort::IpsContext* c = (snort::IpsContext*)(batch->context);
+ p = c->packet;
- busy.erase(i);
+ // Finished with items in batch so clear
+ batch->items.clear();
+
+ RegexRequest* req = *(c->regex_req_it);
+ req->packet = nullptr;
+ busy.erase(c->regex_req_it);
idle.emplace_back(req);
return true;
{
assert(p);
assert(!idle.empty());
+ assert(p->context->searches.items.size() > 0);
RegexRequest* req = idle.front();
idle.pop_front(); // FIXIT-H use splice to move instead
+
busy.emplace_back(req);
+ p->context->regex_req_it = std::prev(busy.end());
std::unique_lock<std::mutex> lock(req->mutex);
req->packet = p;
- if (p->context->searches.items.size() > 0)
- {
- req->offload = true;
- req->cond.notify_one();
- }
+ req->offload = true;
+ req->cond.notify_one();
}
bool ThreadRegexOffload::get(snort::Packet*& p)
continue;
p = req->packet;
+ assert(p->context->regex_req_it == i);
req->packet = nullptr;
busy.erase(i);
assert(req->packet->is_offloaded());
assert(req->packet->context->searches.items.size() > 0);
- snort::MpseBatch& batch = req->packet->context->searches;
- batch.offload_search();
+ snort::IpsContext* c = req->packet->context;
snort::Mpse::MpseRespType resp_ret;
+ c->searches.offload_search();
do
{
- resp_ret = batch.receive_offload_responses();
+ resp_ret = c->searches.receive_offload_responses();
}
while (resp_ret == snort::Mpse::MPSE_RESP_NOT_COMPLETE);
if (resp_ret == snort::Mpse::MPSE_RESP_COMPLETE_FAIL)
{
- if (batch.can_fallback())
+ if (c->searches.can_fallback())
{
// FIXIT-M Add peg counts to record offload search fallback attempts
- batch.search_sync();
+ c->searches.search_sync();
}
// FIXIT-M else Add peg counts to record offload search failures
}
- batch.items.clear();
+ c->searches.items.clear();
req->offload = false;
}
snort::ModuleManager::accumulate_offload("search_engine");
}
void Mpse::search(MpseBatch& batch, MpseType mpse_type)
+{
+ DeepProfile profile(mpsePerfStats);
+ _search(batch, mpse_type);
+}
+
+void Mpse::_search(MpseBatch& batch, MpseType mpse_type)
{
int start_state;
}
}
+Mpse::MpseRespType Mpse::poll_responses(MpseBatch*& batch, MpseType mpse_type)
+{
+ const MpseApi* search_api = nullptr;
+
+ if ( SnortConfig::get_conf()->fast_pattern_config )
+ {
+ switch (mpse_type)
+ {
+ case MPSE_TYPE_NORMAL:
+ search_api = SnortConfig::get_conf()->fast_pattern_config->get_search_api();
+ break;
+ case MPSE_TYPE_OFFLOAD:
+ search_api = SnortConfig::get_conf()->fast_pattern_config->get_offload_search_api();
+ if (!search_api)
+ search_api = SnortConfig::get_conf()->fast_pattern_config->get_search_api();
+ break;
+ }
+ }
+
+ if (search_api)
+ {
+ assert(search_api->poll);
+ return search_api->poll(batch, mpse_type);
+ }
+
+ return MPSE_RESP_NOT_COMPLETE;
+}
+
}
virtual int search_all(
const uint8_t* T, int n, MpseMatch, void* context, int* current_state);
- virtual void search(MpseBatch& batch, MpseType mpse_type);
+ void search(MpseBatch&, MpseType);
- virtual MpseRespType receive_responses(MpseBatch&)
+ virtual MpseRespType receive_responses(MpseBatch&, MpseType)
{ return MPSE_RESP_COMPLETE_SUCCESS; }
+ static MpseRespType poll_responses(MpseBatch*&, MpseType);
+
virtual void set_opt(int) { }
virtual int print_info() { return 0; }
virtual int get_pattern_count() const { return 0; }
virtual int _search(
const uint8_t* T, int n, MpseMatch, void* context, int* current_state) = 0;
+ virtual void _search(MpseBatch&, MpseType);
+
private:
std::string method;
int verbose;
typedef void (* MpseDelFunc)(Mpse*);
+typedef Mpse::MpseRespType (* MpsePollFunc)(MpseBatch*&, Mpse::MpseType);
+
#define MPSE_BASE 0x00
#define MPSE_TRIM 0x01
#define MPSE_REGEX 0x02
MpseDelFunc dtor;
MpseExeFunc init;
MpseExeFunc print;
+ MpsePollFunc poll;
};
}
#endif
bool search_sync();
bool can_fallback() const;
+
+ static Mpse::MpseRespType poll_responses(MpseBatch*& batch)
+ { return Mpse::poll_responses(batch, snort::Mpse::MPSE_TYPE_NORMAL); }
+
+ static Mpse::MpseRespType poll_offload_responses(MpseBatch*& batch)
+ { return Mpse::poll_responses(batch, snort::Mpse::MPSE_TYPE_OFFLOAD); }
+
};
inline void MpseBatch::search()
inline Mpse::MpseRespType MpseBatch::receive_responses()
{
- return items.begin()->second.so[0]->get_normal_mpse()->receive_responses(*this);
+ return items.begin()->second.so[0]->get_normal_mpse()->
+ receive_responses(*this, Mpse::MPSE_TYPE_NORMAL);
}
inline void MpseBatch::offload_search()
assert(items.begin()->second.so[0]->get_offload_mpse());
return items.begin()->second.so[0]->get_offload_mpse()->
- receive_responses(*this);
+ receive_responses(*this, Mpse::MPSE_TYPE_OFFLOAD);
}
inline bool MpseBatch::can_fallback() const
return (api->flags & MPSE_REGEX) != 0;
}
+bool MpseManager::is_poll_capable(const MpseApi* api)
+{
+ assert(api);
+ return (api->poll);
+}
+
// was called during drop stats but actually commented out
// FIXIT-M this one has to accumulate across threads
#if 0
static bool search_engine_trim(const snort::MpseApi*);
static bool is_async_capable(const snort::MpseApi*);
static bool is_regex_capable(const snort::MpseApi*);
+ static bool is_poll_capable(const snort::MpseApi* api);
static void print_mpse_summary(const snort::MpseApi*);
static void print_search_engine_stats();
acb_dtor,
acb_init,
acb_print,
+ nullptr,
};
const BaseApi* se_ac_banded = &acb_api.base;
bnfa_dtor,
bnfa_init,
bnfa_print,
+ nullptr,
};
const BaseApi* se_ac_bnfa[] =
acf_dtor,
acf_init,
acf_print,
+ nullptr,
};
const BaseApi* se_ac_full = &acf_api.base;
acs_dtor,
acs_init,
acs_print,
+ nullptr,
};
const BaseApi* se_ac_sparse = &acs_api.base;
acsb_dtor,
acsb_init,
acsb_print,
+ nullptr,
};
const BaseApi* se_ac_sparse_bands = &acsb_api.base;
ac_dtor,
ac_init,
ac_print,
+ nullptr,
};
#ifdef BUILDING_SO
hs_dtor,
hs_init,
hs_print,
+ nullptr,
};
#ifdef BUILDING_SO
}
void Mpse::search(MpseBatch& batch, MpseType mpse_type)
+{
+ _search(batch, mpse_type);
+}
+
+void Mpse::_search(MpseBatch& batch, MpseType mpse_type)
{
int start_state;
}
void Mpse::search(MpseBatch& batch, MpseType mpse_type)
+{
+ _search(batch, mpse_type);
+}
+
+void Mpse::_search(MpseBatch& batch, MpseType mpse_type)
{
int start_state;