From: Russ Combs (rucombs) Date: Tue, 9 Apr 2019 13:33:49 +0000 (-0400) Subject: Merge pull request #1571 in SNORT/snort3 from ~RUCOMBS/snort3:wcochran53 to master X-Git-Tag: 3.0.0-252~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3996a012a93d7b4be2e9112eb096321e6b7abb4;p=thirdparty%2Fsnort3.git Merge pull request #1571 in SNORT/snort3 from ~RUCOMBS/snort3:wcochran53 to master Squashed commit of the following: commit 4c3045b03aaafc429c017dbffd3887c7031773b4 Author: russ Date: Sun Apr 7 22:09:02 2019 -0400 offload: simplify zero byte bypass commit 4b038913ceb7598ec61f6bef1b0b5b156ab013f6 Author: William Cochrane 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 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 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 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 --- diff --git a/src/detection/detection_engine.cc b/src/detection/detection_engine.cc index 0cc78b38d..1c0522497 100644 --- a/src/detection/detection_engine.cc +++ b/src/detection/detection_engine.cc @@ -71,15 +71,28 @@ void DetectionEngine::thread_init() // 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); + } } } @@ -342,26 +355,48 @@ bool DetectionEngine::get_check_tags() // 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) @@ -374,8 +409,7 @@ bool DetectionEngine::offload(Packet* p) if ( can_offload and should_offload ) { - do_offload(p); - return true; + return do_offload(p); } if ( depends_on_suspended ) diff --git a/src/detection/detection_engine.h b/src/detection/detection_engine.h index 87ef8c25e..07665cb25 100644 --- a/src/detection/detection_engine.h +++ b/src/detection/detection_engine.h @@ -103,7 +103,7 @@ public: 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&); diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index 5fd380f4f..7bb88e0dd 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -358,7 +358,7 @@ static int rule_tree_match( 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; @@ -856,7 +856,7 @@ void fp_clear_context(IpsContext& c) 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) ) @@ -880,8 +880,8 @@ static inline int batch_search( 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 { @@ -1307,11 +1307,11 @@ void fp_full(Packet* p) 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); } @@ -1325,7 +1325,7 @@ void fp_partial(Packet* 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); } @@ -1334,7 +1334,7 @@ void fp_complete(Packet* p) 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); } diff --git a/src/detection/ips_context.h b/src/detection/ips_context.h index 9a68f490c..a501e13f9 100644 --- a/src/detection/ips_context.h +++ b/src/detection/ips_context.h @@ -25,19 +25,19 @@ // single packet. the state is stored in IpsContextData instances, which // are accessed by id. -#include "main/snort_types.h" +#include + +#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 { @@ -143,6 +143,7 @@ public: MpseBatch searches; MpseStash* stash; OtnxMatchData* otnx; + std::list::iterator regex_req_it; SF_EVENTQ* equeue; DataPointer file_data; diff --git a/src/detection/regex_offload.cc b/src/detection/regex_offload.cc index 52def1566..9a426453d 100644 --- a/src/detection/regex_offload.cc +++ b/src/detection/regex_offload.cc @@ -108,49 +108,50 @@ void MpseRegexOffload::put(snort::Packet* p) { 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; @@ -195,19 +196,19 @@ void ThreadRegexOffload::put(snort::Packet* p) { 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 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) @@ -222,6 +223,7 @@ bool ThreadRegexOffload::get(snort::Packet*& p) continue; p = req->packet; + assert(p->context->regex_req_it == i); req->packet = nullptr; busy.erase(i); @@ -260,27 +262,27 @@ void ThreadRegexOffload::worker(RegexRequest* req, snort::SnortConfig* initial_c 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"); diff --git a/src/framework/mpse.cc b/src/framework/mpse.cc index 7d014f3ae..e23529fdd 100644 --- a/src/framework/mpse.cc +++ b/src/framework/mpse.cc @@ -70,6 +70,12 @@ int Mpse::search_all( } 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; @@ -100,5 +106,33 @@ void Mpse::search(MpseBatch& batch, MpseType mpse_type) } } +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; +} + } diff --git a/src/framework/mpse.h b/src/framework/mpse.h index 63b13e9ba..deb4ca6e2 100644 --- a/src/framework/mpse.h +++ b/src/framework/mpse.h @@ -88,11 +88,13 @@ public: 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; } @@ -109,6 +111,8 @@ protected: 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; @@ -125,6 +129,8 @@ typedef Mpse* (* MpseNewFunc)( typedef void (* MpseDelFunc)(Mpse*); +typedef Mpse::MpseRespType (* MpsePollFunc)(MpseBatch*&, Mpse::MpseType); + #define MPSE_BASE 0x00 #define MPSE_TRIM 0x01 #define MPSE_REGEX 0x02 @@ -143,6 +149,7 @@ struct MpseApi MpseDelFunc dtor; MpseExeFunc init; MpseExeFunc print; + MpsePollFunc poll; }; } #endif diff --git a/src/framework/mpse_batch.h b/src/framework/mpse_batch.h index 0903e4c76..d09e0ad37 100644 --- a/src/framework/mpse_batch.h +++ b/src/framework/mpse_batch.h @@ -116,6 +116,13 @@ struct MpseBatch 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() @@ -126,7 +133,8 @@ 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() @@ -142,7 +150,7 @@ inline Mpse::MpseRespType MpseBatch::receive_offload_responses() 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 diff --git a/src/managers/mpse_manager.cc b/src/managers/mpse_manager.cc index 18ae48d8b..656e9d2df 100644 --- a/src/managers/mpse_manager.cc +++ b/src/managers/mpse_manager.cc @@ -183,6 +183,12 @@ bool MpseManager::is_regex_capable(const MpseApi* api) 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 diff --git a/src/managers/mpse_manager.h b/src/managers/mpse_manager.h index d6a84616b..806947f66 100644 --- a/src/managers/mpse_manager.h +++ b/src/managers/mpse_manager.h @@ -78,6 +78,7 @@ public: 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(); diff --git a/src/search_engines/ac_banded.cc b/src/search_engines/ac_banded.cc index cfaf00d7e..e7009da36 100644 --- a/src/search_engines/ac_banded.cc +++ b/src/search_engines/ac_banded.cc @@ -131,6 +131,7 @@ static const MpseApi acb_api = acb_dtor, acb_init, acb_print, + nullptr, }; const BaseApi* se_ac_banded = &acb_api.base; diff --git a/src/search_engines/ac_bnfa.cc b/src/search_engines/ac_bnfa.cc index 902296089..8010ec515 100644 --- a/src/search_engines/ac_bnfa.cc +++ b/src/search_engines/ac_bnfa.cc @@ -149,6 +149,7 @@ static const MpseApi bnfa_api = bnfa_dtor, bnfa_init, bnfa_print, + nullptr, }; const BaseApi* se_ac_bnfa[] = diff --git a/src/search_engines/ac_full.cc b/src/search_engines/ac_full.cc index 04a5f6bbe..ad542c7f1 100644 --- a/src/search_engines/ac_full.cc +++ b/src/search_engines/ac_full.cc @@ -138,6 +138,7 @@ static const MpseApi acf_api = acf_dtor, acf_init, acf_print, + nullptr, }; const BaseApi* se_ac_full = &acf_api.base; diff --git a/src/search_engines/ac_sparse.cc b/src/search_engines/ac_sparse.cc index 8ab5f56b7..b5c89c1be 100644 --- a/src/search_engines/ac_sparse.cc +++ b/src/search_engines/ac_sparse.cc @@ -125,6 +125,7 @@ static const MpseApi acs_api = acs_dtor, acs_init, acs_print, + nullptr, }; const BaseApi* se_ac_sparse = &acs_api.base; diff --git a/src/search_engines/ac_sparse_bands.cc b/src/search_engines/ac_sparse_bands.cc index b09101d93..61b3144c1 100644 --- a/src/search_engines/ac_sparse_bands.cc +++ b/src/search_engines/ac_sparse_bands.cc @@ -125,6 +125,7 @@ static const MpseApi acsb_api = acsb_dtor, acsb_init, acsb_print, + nullptr, }; const BaseApi* se_ac_sparse_bands = &acsb_api.base; diff --git a/src/search_engines/ac_std.cc b/src/search_engines/ac_std.cc index 103abf2c2..bd090c5b5 100644 --- a/src/search_engines/ac_std.cc +++ b/src/search_engines/ac_std.cc @@ -116,6 +116,7 @@ static const MpseApi ac_api = ac_dtor, ac_init, ac_print, + nullptr, }; #ifdef BUILDING_SO diff --git a/src/search_engines/hyperscan.cc b/src/search_engines/hyperscan.cc index 5c9013e6b..15c1201f8 100644 --- a/src/search_engines/hyperscan.cc +++ b/src/search_engines/hyperscan.cc @@ -378,6 +378,7 @@ static const MpseApi hs_api = hs_dtor, hs_init, hs_print, + nullptr, }; #ifdef BUILDING_SO diff --git a/src/search_engines/test/hyperscan_test.cc b/src/search_engines/test/hyperscan_test.cc index 9d95fb831..924f36ec6 100644 --- a/src/search_engines/test/hyperscan_test.cc +++ b/src/search_engines/test/hyperscan_test.cc @@ -57,6 +57,11 @@ int Mpse::search_all( } void Mpse::search(MpseBatch& batch, MpseType mpse_type) +{ + _search(batch, mpse_type); +} + +void Mpse::_search(MpseBatch& batch, MpseType mpse_type) { int start_state; diff --git a/src/search_engines/test/search_tool_test.cc b/src/search_engines/test/search_tool_test.cc index 89d90a83b..e28286811 100644 --- a/src/search_engines/test/search_tool_test.cc +++ b/src/search_engines/test/search_tool_test.cc @@ -113,6 +113,11 @@ int Mpse::search_all( } void Mpse::search(MpseBatch& batch, MpseType mpse_type) +{ + _search(batch, mpse_type); +} + +void Mpse::_search(MpseBatch& batch, MpseType mpse_type) { int start_state;