From: Russ Combs (rucombs) Date: Mon, 2 Dec 2019 22:28:25 +0000 (+0000) Subject: Merge pull request #1847 in SNORT/snort3 from ~RUCOMBS/snort3:rule_hacks to master X-Git-Tag: 3.0.0-266~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68de3e5dacd2d4082d37c0c7373d14928683eca7;p=thirdparty%2Fsnort3.git Merge pull request #1847 in SNORT/snort3 from ~RUCOMBS/snort3:rule_hacks to master Squashed commit of the following: commit a273b19fd7256ab43c4639b064695a1d11f8030f Author: russ Date: Fri Nov 29 08:28:33 2019 -0500 http_inspect: implement show method for verbose config output commit 6e1f40e01c95f0afd8ef4d0b609df25db9b757c6 Author: russ Date: Thu Nov 28 18:44:33 2019 -0500 appid: format detected apps stats in columns akin to file stats commit 4eb7cbdbfa223d6e6c998822c4db06d8c6f6a681 Author: russ Date: Sun Nov 24 17:58:51 2019 -0500 profiler: fix module profile for multithreaded runs The checks and time(us) are aggregated from all packet threads so the total time can be N times greater than elapsed real time for N packet threads. The "other" bucket has checks equal to the total number of packets and time equal to the sum of all packet thread run times less the sum of all other times accounted for. commit 9f7e9ec1fec03fc8681438a89f680d7b248f2326 Author: russ Date: Wed Nov 27 13:42:13 2019 -0500 search_engine: raise an error if any MPSE compilation fails commit 75bd85542994fb88da80754668679b46cfb3caca Author: russ Date: Wed Nov 27 09:08:10 2019 -0500 search_engine: process intermediate fast-pattern matches in batches of 32 same as Snort 2 commit b76f0fc78432d4056e9b940441fd8803d7a5035b Author: russ Date: Sun Nov 24 18:01:12 2019 -0500 ips: support 2 rule vars same as Snort 2 commit 67ee953c4c7c9d13e4f95a4e527d87cb8a365b44 Author: russ Date: Fri Nov 22 16:30:41 2019 -0500 appid: minor cleanup commit 6b66d0839ca6cb14e8dd37010d69a47f97c6c5b6 Author: russ Date: Fri Nov 22 16:24:56 2019 -0500 search_engine: ensure configured search_method is applied to search tools commit 039f452cea4f183a469aa555275c7f47d37cd14d Author: russ Date: Mon Nov 18 18:14:14 2019 -0500 ips: only use multiple threads for rule group compilation at startup A typical deployment will have N packet threads, each pinned to a separate core. N threads can be used to speed up startup but shouldn't be used during reload since that could impact detection. Reload is also not as time critical as startup. commit a23500a9baf5773592653648d1a2cf32cfb22487 Author: russ Date: Fri Nov 15 13:59:18 2019 -0500 hyperscan: select max scratch from among all compiler threads commit 5b918976e0fad0f706675635852c74870175b4ad Author: russ Date: Thu Nov 14 16:45:34 2019 -0500 mpse: only hyperscan currently supports parallel compilation commit 5ceb74b43af4b3bd7fafe61da7c53f2900b6b3cd Author: russ Date: Thu Nov 14 15:52:41 2019 -0500 ips: add support for parallel fast-pattern MPSE FSM compilation --- diff --git a/src/detection/detection_options.cc b/src/detection/detection_options.cc index 883aa8dbe..bf374e465 100644 --- a/src/detection/detection_options.cc +++ b/src/detection/detection_options.cc @@ -32,6 +32,7 @@ #include "detection_options.h" +#include #include #include "filters/detection_filter.h" @@ -327,6 +328,9 @@ void print_option_tree(detection_option_tree_node_t* node, int level) void* add_detection_option_tree(SnortConfig* sc, detection_option_tree_node_t* option_tree) { + static std::mutex build_mutex; + std::lock_guard lock(build_mutex); + if ( !sc->detection_option_tree_hash_table ) sc->detection_option_tree_hash_table = DetectionTreeHashTableNew(); diff --git a/src/detection/fp_create.cc b/src/detection/fp_create.cc index bcce9ad5e..2914a4b23 100644 --- a/src/detection/fp_create.cc +++ b/src/detection/fp_create.cc @@ -38,7 +38,9 @@ #include "framework/mpse_batch.h" #include "hash/ghash.h" #include "log/messages.h" +#include "main/snort.h" #include "main/snort_config.h" +#include "main/thread_config.h" #include "managers/mpse_manager.h" #include "parser/parse_rule.h" #include "parser/parser.h" @@ -428,15 +430,11 @@ static int fpFinishPortGroup( { if (pg->mpsegrp[i]->normal_mpse->get_pattern_count() != 0) { - if ( !sc->test_mode() or sc->mem_check() ) - { - if ( pg->mpsegrp[i]->normal_mpse->prep_patterns(sc) != 0 ) - FatalError("Failed to compile port group patterns for normal " - "search engine.\n"); - } + queue_mpse(pg->mpsegrp[i]->normal_mpse); if (fp->get_debug_mode()) pg->mpsegrp[i]->normal_mpse->print_info(); + rules = 1; } else @@ -449,15 +447,11 @@ static int fpFinishPortGroup( { if (pg->mpsegrp[i]->offload_mpse->get_pattern_count() != 0) { - if ( !sc->test_mode() or sc->mem_check() ) - { - if ( pg->mpsegrp[i]->offload_mpse->prep_patterns(sc) != 0 ) - FatalError("Failed to compile port group patterns for offload " - "search engine.\n"); - } + queue_mpse(pg->mpsegrp[i]->offload_mpse); if (fp->get_debug_mode()) pg->mpsegrp[i]->offload_mpse->print_info(); + rules = 1; } else @@ -1649,6 +1643,25 @@ static int fpCreateServicePortGroups(SnortConfig* sc) return 0; } +static unsigned can_build_mt(FastPatternConfig* fp) +{ + if ( Snort::is_reloading() ) + return false; + + const MpseApi* search_api = fp->get_search_api(); + assert(search_api); + + if ( !MpseManager::parallel_compiles(search_api) ) + return false; + + const MpseApi* offload_search_api = fp->get_offload_search_api(); + + if ( offload_search_api and !MpseManager::parallel_compiles(offload_search_api) ) + return false; + + return true; +} + /* * Port list version * @@ -1712,6 +1725,14 @@ int fpCreateFastPacketDetection(SnortConfig* sc) if (fp->get_debug_print_rule_group_build_details()) LogMessage("Service Based Rule Maps Done....\n"); + if ( !sc->test_mode() or sc->mem_check() ) + { + unsigned c = compile_mpses(sc, can_build_mt(fp)); + + if ( c != mpse_count ) + ParseError("Failed to compile %u search engines", mpse_count - c); + } + fp_print_port_groups(port_tables); fp_print_service_groups(sc->spgmmTable); diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index 1aa2ce4fa..7985f9254 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -761,6 +761,13 @@ private: unsigned count; unsigned max; std::vector queue; + + // perf trade-off, same as Snort 2 + // queue to keep mpse search cache warm + // but limit to avoid the O(n**2) effect of inserts + // and to get any rule hits before exhaustive searching + // consider a map in lieu of vector + const unsigned queue_limit = 32; }; // uniquely insert into q, should splay elements for performance @@ -780,21 +787,21 @@ bool MpseStash::push(void* user, void* tree, int index, void* list) if ( max and ( count == max ) ) { pmqs.tot_inq_overruns++; - return true; + return false; } - if ( !max or ( count < max ) ) - { - Node node; - node.user = user; - node.tree = tree; - node.index = index; - node.list = list; - queue.push_back(node); - pmqs.tot_inq_uinserts++; - pmqs.tot_inq_inserts++; - count++; - } + Node node; + node.user = user; + node.tree = tree; + node.index = index; + node.list = list; + queue.push_back(node); + pmqs.tot_inq_uinserts++; + pmqs.tot_inq_inserts++; + count++; + + if ( queue.size() == queue_limit ) + return true; // process now return false; } @@ -824,14 +831,12 @@ bool MpseStash::process(MpseMatch match, void* context) if ( res > 0 ) { /* terminate matching */ - pmqs.tot_inq_flush += count; - count = 0; + pmqs.tot_inq_flush += i; queue.clear(); return true; } } - pmqs.tot_inq_flush += count; - count = 0; + pmqs.tot_inq_flush += i; queue.clear(); return false; } @@ -858,7 +863,12 @@ static int rule_tree_queue( { MpseStash* stash = ((IpsContext*)context)->stash; - return stash->push(user, tree, index, list) ? 1 : 0; + if ( stash->push(user, tree, index, list) ) + { + if ( stash->process(rule_tree_match, context) ) + return 1; + } + return 0; } static inline int batch_search( diff --git a/src/detection/fp_utils.cc b/src/detection/fp_utils.cc index 5fee5d735..6a238ff56 100644 --- a/src/detection/fp_utils.cc +++ b/src/detection/fp_utils.cc @@ -24,21 +24,24 @@ #include #include +#include +#include +#include #include "ips_options/ips_flow.h" #include "log/messages.h" +#include "main/snort_config.h" #include "main/thread_config.h" +#include "pattern_match_data.h" #include "ports/port_group.h" #include "target_based/snort_protocols.h" +#include "treenodes.h" #include "utils/util.h" #ifdef UNIT_TEST #include "catch/snort_catch.h" #endif -#include "pattern_match_data.h" -#include "treenodes.h" - using namespace snort; //-------------------------------------------------------------------------- @@ -368,6 +371,68 @@ PatternMatchVector get_fp_content( return pmds; } +//-------------------------------------------------------------------------- +// mpse compile threads +//-------------------------------------------------------------------------- + +static std::list s_tbd; +static std::mutex s_mutex; + +static Mpse* get_mpse() +{ + std::lock_guard lock(s_mutex); + + if ( s_tbd.empty() ) + return nullptr; + + Mpse* m = s_tbd.front(); + s_tbd.pop_front(); + + return m; +} + +static void compile_mpse(SnortConfig* sc, unsigned id, unsigned* count) +{ + set_instance_id(id); + unsigned c = 0; + + while ( Mpse* m = get_mpse() ) + { + if ( !m->prep_patterns(sc) ) + c++; + } + std::lock_guard lock(s_mutex); + *count += c; +} + +void queue_mpse(Mpse* m) +{ + s_tbd.push_back(m); +} + +unsigned compile_mpses(struct SnortConfig* sc, bool parallel) +{ + std::list workers; + unsigned max = parallel ? sc->num_slots : 1; + unsigned count = 0; + + if ( max == 1 ) + { + compile_mpse(sc, get_instance_id(), &count); + return count; + } + + for ( unsigned i = 0; i < max; ++i ) + workers.push_back(new std::thread(compile_mpse, sc, i, &count)); + + for ( auto* w : workers ) + { + w->join(); + delete w; + } + return count; +} + //-------------------------------------------------------------------------- // unit tests //-------------------------------------------------------------------------- diff --git a/src/detection/fp_utils.h b/src/detection/fp_utils.h index 5e0ac19e3..5341ef4df 100644 --- a/src/detection/fp_utils.h +++ b/src/detection/fp_utils.h @@ -40,5 +40,8 @@ bool set_fp_content(OptTreeNode*); std::vector get_fp_content( OptTreeNode*, OptFpList*&, bool srvc, bool only_literals, bool& exclude); +void queue_mpse(snort::Mpse*); +unsigned compile_mpses(struct snort::SnortConfig*, bool parallel = false); + #endif diff --git a/src/framework/mpse.h b/src/framework/mpse.h index d6fabc0a4..e2a86117a 100644 --- a/src/framework/mpse.h +++ b/src/framework/mpse.h @@ -129,10 +129,11 @@ typedef void (* MpseDelFunc)(Mpse*); typedef Mpse::MpseRespType (* MpsePollFunc)(MpseBatch*&, Mpse::MpseType); -#define MPSE_BASE 0x00 -#define MPSE_TRIM 0x01 -#define MPSE_REGEX 0x02 -#define MPSE_ASYNC 0x04 +#define MPSE_BASE 0x00 // no optional features +#define MPSE_TRIM 0x01 // should trim leading zero bytes from patterns +#define MPSE_REGEX 0x02 // supports regex patterns +#define MPSE_ASYNC 0x04 // does asynchronous (lookaside) searches +#define MPSE_MTBLD 0x08 // support multithreaded / parallel compilation struct MpseApi { diff --git a/src/framework/mpse_batch.cc b/src/framework/mpse_batch.cc index 84a8c4737..9758be3d4 100644 --- a/src/framework/mpse_batch.cc +++ b/src/framework/mpse_batch.cc @@ -102,10 +102,10 @@ bool MpseGroup::create_normal_mpse(SnortConfig* sc, const MpseAgent* agent) } } -bool MpseGroup::create_normal_mpse(const char* type) +bool MpseGroup::create_normal_mpse(SnortConfig* sc, const char* type) { - if ( !type and SnortConfig::get_conf()->fast_pattern_config ) - type = SnortConfig::get_conf()->fast_pattern_config->get_search_method(); + if ( !type and sc->fast_pattern_config ) + type = sc->fast_pattern_config->get_search_method(); if ( !type ) type = "ac_bnfa"; @@ -115,7 +115,7 @@ bool MpseGroup::create_normal_mpse(const char* type) if (search_api) { Module* mod = ModuleManager::get_module(search_api->base.name); - normal_mpse = search_api->ctor(nullptr, mod, nullptr); + normal_mpse = search_api->ctor(sc, mod, nullptr); normal_mpse->set_api(search_api); return true; } @@ -150,21 +150,21 @@ bool MpseGroup::create_offload_mpse(SnortConfig* sc, const MpseAgent* agent) } } -bool MpseGroup::create_offload_mpse() +bool MpseGroup::create_offload_mpse(SnortConfig* sc) { const MpseApi* search_api = nullptr; const MpseApi* offload_search_api = nullptr; - if (SnortConfig::get_conf()->fast_pattern_config ) + if (sc->fast_pattern_config ) { - search_api = SnortConfig::get_conf()->fast_pattern_config->get_search_api(); - offload_search_api = SnortConfig::get_conf()->fast_pattern_config->get_offload_search_api(); + search_api = sc->fast_pattern_config->get_search_api(); + offload_search_api = sc->fast_pattern_config->get_offload_search_api(); } if (offload_search_api and (offload_search_api != search_api)) { Module* mod = ModuleManager::get_module(offload_search_api->base.name); - offload_mpse = offload_search_api->ctor(nullptr, mod, nullptr); + offload_mpse = offload_search_api->ctor(sc, mod, nullptr); offload_mpse->set_api(offload_search_api); return true; } diff --git a/src/framework/mpse_batch.h b/src/framework/mpse_batch.h index d09e0ad37..bd91a5c75 100644 --- a/src/framework/mpse_batch.h +++ b/src/framework/mpse_batch.h @@ -48,10 +48,10 @@ public: { return offload_mpse ? offload_mpse : normal_mpse; } bool create_normal_mpse(SnortConfig*, const MpseAgent* agent); - bool create_normal_mpse(const char*); + bool create_normal_mpse(SnortConfig*, const char*); bool create_offload_mpse(SnortConfig*, const MpseAgent* agent); - bool create_offload_mpse(); + bool create_offload_mpse(SnortConfig*); inline bool can_fallback() const { return get_offload_mpse() != normal_mpse; } diff --git a/src/ips_options/extract.cc b/src/ips_options/extract.cc index dd7e2001b..14da84842 100644 --- a/src/ips_options/extract.cc +++ b/src/ips_options/extract.cc @@ -329,7 +329,7 @@ TEST_CASE("ips options vars") int8_t ind2 = AddVarNameToList("VALUE"); REQUIRE((ind2 == 1)); int8_t ind3 = AddVarNameToList("VAR3"); - REQUIRE((ind3 == 2)); + REQUIRE((ind3 == IPS_OPTIONS_NO_VAR)); // Insert same name twice REQUIRE((AddVarNameToList("VALUE") == 1)); diff --git a/src/ips_options/extract.h b/src/ips_options/extract.h index 31397f2f5..0a177a828 100644 --- a/src/ips_options/extract.h +++ b/src/ips_options/extract.h @@ -30,7 +30,7 @@ #define PARSELEN 10 #define MAX_BYTES_TO_GRAB 4 -#define NUM_IPS_OPTIONS_VARS 3 +#define NUM_IPS_OPTIONS_VARS 2 #define IPS_OPTIONS_NO_VAR (-1) #define INVALID_VAR_ERR_STR "%s uses an undefined rule option variable (%s)" diff --git a/src/ips_options/ips_byte_math.cc b/src/ips_options/ips_byte_math.cc index 1f4dd1b6c..a56426826 100644 --- a/src/ips_options/ips_byte_math.cc +++ b/src/ips_options/ips_byte_math.cc @@ -518,8 +518,7 @@ static IpsOption* byte_math_ctor(Module* p, OptTreeNode*) data.result_var = AddVarNameToList(data.result_name); if (data.result_var == IPS_OPTIONS_NO_VAR) { - ParseError("Rule has more than %d variables.", - NUM_IPS_OPTIONS_VARS); + ParseError("Rule has more than %d variables.", NUM_IPS_OPTIONS_VARS); return nullptr; } return new ByteMathOption(m->data); diff --git a/src/main.cc b/src/main.cc index c16b8a546..717d426c2 100644 --- a/src/main.cc +++ b/src/main.cc @@ -346,6 +346,8 @@ int main_reload_config(lua_State* L) current_request->respond("== reload failed - restart required\n"); else current_request->respond("== reload failed - bad config\n"); + + SnortConfig::set_parser_conf(nullptr); return 0; } @@ -355,6 +357,7 @@ int main_reload_config(lua_State* L) if ( !tc ) { current_request->respond("== reload failed - bad config\n"); + SnortConfig::set_parser_conf(nullptr); return 0; } SnortConfig::set_conf(sc); @@ -364,6 +367,7 @@ int main_reload_config(lua_State* L) current_request->respond(".. swapping configuration\n", from_shell); main_broadcast_command(new ACSwap(new Swapper(old, sc, old_tc, tc), current_request, from_shell), from_shell); + SnortConfig::set_parser_conf(nullptr); return 0; } diff --git a/src/main/analyzer.cc b/src/main/analyzer.cc index 350b95c19..91c6f4715 100644 --- a/src/main/analyzer.cc +++ b/src/main/analyzer.cc @@ -661,9 +661,12 @@ void Analyzer::operator()(Swapper* ps, uint16_t run_num) SFDAQ::set_local_instance(daq_instance); set_state(State::INITIALIZED); + Profiler::start(); + // Start the main loop analyze(); + Profiler::stop(pc.analyzed_pkts); term(); set_state(State::STOPPED); diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index f72881101..7df8c0e32 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -81,6 +81,7 @@ using namespace snort; #define OUTPUT_U2 "unified2" #define OUTPUT_FAST "alert_fast" +SnortConfig* parser_conf = nullptr; THREAD_LOCAL SnortConfig* snort_conf = nullptr; uint32_t SnortConfig::warning_flags = 0; @@ -1080,6 +1081,12 @@ SO_PUBLIC int SnortConfig::request_scratch(ScScratchFunc setup, ScScratchFunc cl return scratch_handlers.size() - 1; } +SO_PUBLIC SnortConfig* SnortConfig::get_parser_conf() +{ return parser_conf; } + +void SnortConfig::set_parser_conf(SnortConfig* sc) +{ parser_conf = sc; } + SO_PUBLIC SnortConfig* SnortConfig::get_conf() { return snort_conf; } diff --git a/src/main/snort_config.h b/src/main/snort_config.h index d51864526..9347f9f5d 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -714,8 +714,10 @@ public: // Use this to access current thread's conf from other units static void set_conf(SnortConfig*); + static void set_parser_conf(SnortConfig*); SO_PUBLIC static SnortConfig* get_conf(); + SO_PUBLIC static SnortConfig* get_parser_conf(); // main thread only! SO_PUBLIC void register_reload_resource_tuner(ReloadResourceTuner& rrt) { reload_tuners.push_back(&rrt); } diff --git a/src/managers/mpse_manager.cc b/src/managers/mpse_manager.cc index 2e851fcf4..d0046854d 100644 --- a/src/managers/mpse_manager.cc +++ b/src/managers/mpse_manager.cc @@ -190,6 +190,12 @@ bool MpseManager::is_poll_capable(const MpseApi* api) return (api->poll); } +bool MpseManager::parallel_compiles(const MpseApi* api) +{ + assert(api); + return (api->flags & MPSE_MTBLD) != 0; +} + // 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 806947f66..ccbf0802f 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 parallel_compiles(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/mime/file_mime_process.cc b/src/mime/file_mime_process.cc index f43e71c05..2281877a0 100644 --- a/src/mime/file_mime_process.cc +++ b/src/mime/file_mime_process.cc @@ -778,14 +778,10 @@ void MimeSession::init() MimeDecode::init(); - /* Header search */ mime_hdr_search_mpse = new SearchTool; + if (mime_hdr_search_mpse == nullptr) - { - // FIXIT-M make configurable or at least fall back to any - // available search engine - FatalError("Could not instantiate ac_bnfa search engine.\n"); - } + FatalError("Could not instantiate search engine.\n"); for (tmp = &mime_hdrs[0]; tmp->name != nullptr; tmp++) { diff --git a/src/network_inspectors/appid/appid_peg_counts.cc b/src/network_inspectors/appid/appid_peg_counts.cc index bcfa62b5a..a1173b1f3 100644 --- a/src/network_inspectors/appid/appid_peg_counts.cc +++ b/src/network_inspectors/appid/appid_peg_counts.cc @@ -135,7 +135,11 @@ void AppIdPegCounts::print() if (!print && unknown_pegs->all_zeros()) return; - LogLabel("Appid dynamic stats:"); + LogLabel("Appid Statistics"); + LogLabel("detected apps and services"); + + LogMessage("%25.25s: %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", + "Application", "Flows", "Clients", "Users", "Payloads", "Misc", "Incompat.", "Failed"); for (unsigned i = 0; i < app_num; i++) { @@ -144,17 +148,14 @@ void AppIdPegCounts::print() continue; std::string app_name = AppIdPegCounts::appid_detectors_info[i]; - LogMessage("%s: ", app_name.c_str()); + LogMessage("%25.25s:", app_name.c_str()); pegs->print(); } - // Print unknown app stats if (!unknown_pegs->all_zeros()) { - LogMessage("unknown_app: flows: %" PRIu64 ", clients: %" PRIu64 ", users: %" PRIu64 ", payloads %" - PRIu64 ", misc: %" PRIu64 "\n", - unknown_pegs->stats[0], unknown_pegs->stats[1], unknown_pegs->stats[2], - unknown_pegs->stats[3], unknown_pegs->stats[4]); + LogMessage("%25.25s:", "unknown"); + unknown_pegs->print(); } } diff --git a/src/network_inspectors/appid/appid_peg_counts.h b/src/network_inspectors/appid/appid_peg_counts.h index 1431e6a85..ed9c2affb 100644 --- a/src/network_inspectors/appid/appid_peg_counts.h +++ b/src/network_inspectors/appid/appid_peg_counts.h @@ -70,8 +70,8 @@ public: void print() { - snort::LogMessage("flows: %" PRIu64 ", clients: %" PRIu64 ", users: %" PRIu64 ", payloads %" PRIu64 - ", misc: %" PRIu64 ", incompatible: %" PRIu64 ", failed: %" PRIu64 "\n", + snort::LogMessage(" " FMTu64("-10") " " FMTu64("-10") " " FMTu64("-10") " " FMTu64("-10") + " " FMTu64("-10") " " FMTu64("-10") " " FMTu64("-10")"\n", stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], stats[6]); } }; diff --git a/src/network_inspectors/appid/detector_plugins/http_url_patterns.cc b/src/network_inspectors/appid/detector_plugins/http_url_patterns.cc index 547e290e3..07c2b9fe1 100644 --- a/src/network_inspectors/appid/detector_plugins/http_url_patterns.cc +++ b/src/network_inspectors/appid/detector_plugins/http_url_patterns.cc @@ -652,7 +652,7 @@ static int http_pattern_match(void* id, void*, int match_end_pos, void* data, vo return 0; } -int HttpPatternMatchers::process_host_patterns(DetectorHTTPPatterns patterns) +int HttpPatternMatchers::process_host_patterns(DetectorHTTPPatterns& patterns) { if (!host_url_matcher) host_url_matcher = mlmpCreate(); diff --git a/src/network_inspectors/appid/detector_plugins/http_url_patterns.h b/src/network_inspectors/appid/detector_plugins/http_url_patterns.h index 9cd7043f1..dac7ac155 100644 --- a/src/network_inspectors/appid/detector_plugins/http_url_patterns.h +++ b/src/network_inspectors/appid/detector_plugins/http_url_patterns.h @@ -297,7 +297,7 @@ public: void insert_rtmp_url_pattern(DetectorAppUrlPattern*); void insert_app_url_pattern(DetectorAppUrlPattern*); int process_chp_list(CHPListElement*); - int process_host_patterns(DetectorHTTPPatterns); + int process_host_patterns(DetectorHTTPPatterns&); int process_mlmp_patterns(); void scan_key_chp(ChpMatchDescriptor&); diff --git a/src/network_inspectors/appid/test/appid_http_session_test.cc b/src/network_inspectors/appid/test/appid_http_session_test.cc index 910119285..cb2e4c896 100644 --- a/src/network_inspectors/appid/test/appid_http_session_test.cc +++ b/src/network_inspectors/appid/test/appid_http_session_test.cc @@ -155,33 +155,14 @@ void AppIdDebug::set_constraints(const char*, const AppIdDebugSessionConstraints } // Profiler mock functions -void Profiler::register_module(Module*) -{ -} - -void Profiler::register_module(const char*, const char*, Module*) -{ -} - -void Profiler::consolidate_stats(uint64_t, uint64_t) -{ -} - -void Profiler::reset_stats() -{ -} - -void Profiler::show_stats() -{ -} - -MemoryContext::MemoryContext(MemoryTracker&) -{ -} - -MemoryContext::~MemoryContext() -{ -} +void Profiler::register_module(Module*) { } +void Profiler::register_module(const char*, const char*, Module*) { } +void Profiler::consolidate_stats() { } +void Profiler::reset_stats() { } +void Profiler::show_stats() { } + +MemoryContext::MemoryContext(MemoryTracker&) { } +MemoryContext::~MemoryContext() { } unsigned AppIdSession::inspector_id = 0; THREAD_LOCAL AppIdDebug* appidDebug = nullptr; diff --git a/src/parser/parser.cc b/src/parser/parser.cc index 30c58d1f8..c5b06f85e 100644 --- a/src/parser/parser.cc +++ b/src/parser/parser.cc @@ -253,6 +253,7 @@ void parser_term(SnortConfig*) SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, bool is_fatal) { SnortConfig* sc = new SnortConfig(SnortConfig::get_conf()->proto_ref); + SnortConfig::set_parser_conf(sc); sc->logging_flags = boot_conf->logging_flags; sc->tweaks = boot_conf->tweaks; diff --git a/src/profiler/profiler.cc b/src/profiler/profiler.cc index 1fc664654..632c05e39 100644 --- a/src/profiler/profiler.cc +++ b/src/profiler/profiler.cc @@ -28,6 +28,8 @@ #include "framework/module.h" #include "main/snort_config.h" +#include "main/thread_config.h" +#include "time/stopwatch.h" #include "memory_context.h" #include "memory_profiler.h" @@ -45,6 +47,7 @@ THREAD_LOCAL ProfileStats totalPerfStats; THREAD_LOCAL ProfileStats otherPerfStats; THREAD_LOCAL TimeContext* ProfileContext::curr_time = nullptr; +THREAD_LOCAL Stopwatch* run_timer = nullptr; static ProfilerNodeMap s_profiler_nodes; @@ -70,23 +73,24 @@ void Profiler::register_module(const char* n, const char* pn, Module* m) s_profiler_nodes.register_node(n, pn, m); } -void Profiler::consolidate_stats(uint64_t num_pkts, uint64_t usecs) +void Profiler::start() { - totalPerfStats.time.checks = otherPerfStats.time.checks = num_pkts; - -#ifdef USE_TSC_CLOCK - totalPerfStats.time.elapsed = otherPerfStats.time.elapsed = clock_ticks(usecs); -#else - hr_duration dt = TO_DURATION(dt, usecs); - totalPerfStats.time.elapsed = otherPerfStats.time.elapsed = dt; -#endif + run_timer = new Stopwatch; + run_timer->start(); +} - const ProfilerNode& root = s_profiler_nodes.get_root(); - auto children = root.get_children(); +void Profiler::stop(uint64_t checks) +{ + run_timer->stop(); + totalPerfStats.time.elapsed = run_timer->get(); + totalPerfStats.time.checks = checks; - for ( auto pn : children ) - otherPerfStats.time.elapsed -= pn->get_stats().time.elapsed; + delete run_timer; + run_timer = nullptr; +} +void Profiler::consolidate_stats() +{ s_profiler_nodes.accumulate_nodes(); MemoryProfiler::consolidate_fallthrough_stats(); } @@ -99,6 +103,20 @@ void Profiler::reset_stats() void Profiler::show_stats() { + const ProfilerNode& root = s_profiler_nodes.get_root(); + auto children = root.get_children(); + + hr_duration runtime = root.get_stats().time.elapsed; + hr_duration sum = 0_ticks; + + for ( auto pn : children ) + sum += pn->get_stats().time.elapsed; + + otherPerfStats.time.checks = root.get_stats().time.checks; + otherPerfStats.time.elapsed = (runtime > sum) ? (runtime - sum) : 0_ticks; + + s_profiler_nodes.accumulate_flex(); + const auto* config = SnortConfig::get_profiler(); assert(config); diff --git a/src/profiler/profiler.h b/src/profiler/profiler.h index c9e049628..e2cf8d2f3 100644 --- a/src/profiler/profiler.h +++ b/src/profiler/profiler.h @@ -35,7 +35,10 @@ public: static void register_module(snort::Module*); static void register_module(const char*, const char*, snort::Module*); - static void consolidate_stats(uint64_t pkts = 0, uint64_t usecs = 0); + static void start(); + static void stop(uint64_t); + + static void consolidate_stats(); static void reset_stats(); static void show_stats(); diff --git a/src/profiler/profiler_defs.h b/src/profiler/profiler_defs.h index 60cde46da..81407019c 100644 --- a/src/profiler/profiler_defs.h +++ b/src/profiler/profiler_defs.h @@ -31,6 +31,7 @@ namespace snort { #define ROOT_NODE "total" +#define FLEX_NODE "other" struct ProfilerConfig { diff --git a/src/profiler/profiler_nodes.cc b/src/profiler/profiler_nodes.cc index 07bdfbb13..efb770ec9 100644 --- a/src/profiler/profiler_nodes.cc +++ b/src/profiler/profiler_nodes.cc @@ -134,6 +134,14 @@ void ProfilerNodeMap::accumulate_nodes() it->second.accumulate(); } +void ProfilerNodeMap::accumulate_flex() +{ + auto it = nodes.find(FLEX_NODE); + + if ( it != nodes.end() ) + it->second.accumulate(); +} + void ProfilerNodeMap::reset_nodes() { for ( auto it = nodes.begin(); it != nodes.end(); ++it ) diff --git a/src/profiler/profiler_nodes.h b/src/profiler/profiler_nodes.h index 727b74484..65136aed1 100644 --- a/src/profiler/profiler_nodes.h +++ b/src/profiler/profiler_nodes.h @@ -92,6 +92,7 @@ public: void register_node(const std::string&, const char*, snort::Module*); void accumulate_nodes(); + void accumulate_flex(); void reset_nodes(); const ProfilerNode& get_root(); diff --git a/src/search_engines/hyperscan.cc b/src/search_engines/hyperscan.cc index a506e2193..63dc36c58 100644 --- a/src/search_engines/hyperscan.cc +++ b/src/search_engines/hyperscan.cc @@ -31,6 +31,7 @@ #include "framework/mpse.h" #include "log/messages.h" #include "main/snort_config.h" +#include "main/thread.h" #include "utils/stats.h" using namespace snort; @@ -93,11 +94,11 @@ void Pattern::escape(const uint8_t* s, unsigned n, bool literal) typedef std::vector PatternVector; -// we need to update scratch in the main thread as each pattern is processed -// and then clone to thread specific after all rules are loaded. s_scratch is -// a prototype that is large enough for all uses. +// we need to update scratch in each compiler thread as each pattern is processed +// and then select the largest to clone to packet thread specific after all rules +// are loaded. s_scratch is a prototype that is large enough for all uses. -static hs_scratch_t* s_scratch = nullptr; +static std::vector s_scratch; static unsigned int scratch_index; static bool scratch_registered = false; @@ -243,7 +244,7 @@ int HyperscanMpse::prep_patterns(SnortConfig* sc) return -2; } - if ( hs_error_t err = hs_alloc_scratch(hs_db, &s_scratch) ) + if ( hs_error_t err = hs_alloc_scratch(hs_db, &s_scratch[get_instance_id()]) ) { ParseError("can't allocate search scratch space (%d)", err); return -3; @@ -294,20 +295,46 @@ int HyperscanMpse::_search( static void scratch_setup(SnortConfig* sc) { + // find the largest scratch and clone for all slots + hs_scratch_t* max = nullptr; + for ( unsigned i = 0; i < sc->num_slots; ++i ) { - hs_scratch_t** ss = (hs_scratch_t**) &sc->state[i][scratch_index]; + if ( !s_scratch[i] ) + continue; - if ( s_scratch ) - hs_clone_scratch(s_scratch, ss); + if ( !max ) + { + max = s_scratch[i]; + s_scratch[i] = nullptr; + continue; + } + size_t max_sz, idx_sz; + hs_scratch_size(max, &max_sz); + hs_scratch_size(s_scratch[i], &idx_sz); + + if ( idx_sz > max_sz ) + { + hs_free_scratch(max); + max = s_scratch[i]; + } else - ss = nullptr; + { + hs_free_scratch(s_scratch[i]); + } + s_scratch[i] = nullptr; } - if ( s_scratch ) + for ( unsigned i = 0; i < sc->num_slots; ++i ) { - hs_free_scratch(s_scratch); - s_scratch = nullptr; + hs_scratch_t** ss = (hs_scratch_t**) &sc->state[i][scratch_index]; + + if ( max ) + hs_clone_scratch(max, ss); + else + *ss = nullptr; } + if ( max ) + hs_free_scratch(max); } static void scratch_cleanup(SnortConfig* sc) @@ -319,7 +346,7 @@ static void scratch_cleanup(SnortConfig* sc) if ( ss ) { hs_free_scratch(ss); - ss = nullptr; + sc->state[i][scratch_index] = nullptr; } } } @@ -333,6 +360,7 @@ static Mpse* hs_ctor( { if ( !scratch_registered ) { + s_scratch.resize(sc->num_slots); scratch_index = SnortConfig::request_scratch(scratch_setup, scratch_cleanup); scratch_registered = true; } @@ -370,7 +398,7 @@ static const MpseApi hs_api = nullptr, nullptr }, - MPSE_REGEX, + MPSE_REGEX | MPSE_MTBLD, nullptr, // activate nullptr, // setup nullptr, // start diff --git a/src/search_engines/search_tool.cc b/src/search_engines/search_tool.cc index 5a25d3bfb..49f1fb625 100644 --- a/src/search_engines/search_tool.cc +++ b/src/search_engines/search_tool.cc @@ -42,7 +42,10 @@ SearchTool::SearchTool(const char* method, bool dfa) // pattern offload search method. If a method is passed in then an offload // search engine will not be created - if (mpsegrp->create_normal_mpse(method)) + SnortConfig* sc = SnortConfig::get_parser_conf(); + assert(sc); + + if (mpsegrp->create_normal_mpse(sc, method)) { if( dfa ) mpsegrp->normal_mpse->set_opt(1); @@ -50,7 +53,7 @@ SearchTool::SearchTool(const char* method, bool dfa) if (method == nullptr) { - if (mpsegrp->create_offload_mpse()) + if (mpsegrp->create_offload_mpse(sc)) { if ( dfa ) mpsegrp->offload_mpse->set_opt(1); diff --git a/src/search_engines/test/hyperscan_test.cc b/src/search_engines/test/hyperscan_test.cc index 924f36ec6..2a0245248 100644 --- a/src/search_engines/test/hyperscan_test.cc +++ b/src/search_engines/test/hyperscan_test.cc @@ -190,7 +190,7 @@ TEST(mpse_hs_base, base) TEST(mpse_hs_base, mpse) { const MpseApi* mpse_api = (MpseApi*)se_hyperscan; - CHECK(mpse_api->flags == MPSE_REGEX); + CHECK(mpse_api->flags == (MPSE_REGEX | MPSE_MTBLD)); CHECK(mpse_api->ctor); CHECK(mpse_api->dtor); diff --git a/src/search_engines/test/search_tool_test.cc b/src/search_engines/test/search_tool_test.cc index e28286811..e6ffcb3e8 100644 --- a/src/search_engines/test/search_tool_test.cc +++ b/src/search_engines/test/search_tool_test.cc @@ -65,6 +65,9 @@ SnortConfig::~SnortConfig() = default; SnortConfig* SnortConfig::get_conf() { return snort_conf; } +SnortConfig* SnortConfig::get_parser_conf() +{ return snort_conf; } + unsigned get_instance_id() { return 0; } @@ -196,14 +199,14 @@ MpseGroup::~MpseGroup() } } -bool MpseGroup::create_normal_mpse(const char* type) +bool MpseGroup::create_normal_mpse(SnortConfig*, const char* type) { normal_mpse = MpseManager::get_search_engine(type); return true; } -bool MpseGroup::create_offload_mpse() +bool MpseGroup::create_offload_mpse(SnortConfig*) { offload_mpse = nullptr; return false; diff --git a/src/service_inspectors/http_inspect/http_inspect.cc b/src/service_inspectors/http_inspect/http_inspect.cc index 712be4d13..ef1df958c 100644 --- a/src/service_inspectors/http_inspect/http_inspect.cc +++ b/src/service_inspectors/http_inspect/http_inspect.cc @@ -23,6 +23,8 @@ #include "http_inspect.h" +#include + #include "detection/detection_engine.h" #include "detection/detection_util.h" #include "log/unified2.h" @@ -86,6 +88,43 @@ bool HttpInspect::configure(SnortConfig* ) return true; } +void HttpInspect::show(snort::SnortConfig*) +{ + assert(params); + LogMessage("http_inspect\n"); + + if ( params->request_depth == -1 ) + LogMessage(" request_depth: " "%s" "\n", "unlimited"); + else + LogMessage(" request_depth: " STDi64 "\n", params->request_depth); + + if ( params->response_depth == -1 ) + LogMessage(" response_depth: " "%s" "\n", "unlimited"); + else + LogMessage(" response_depth: " STDi64 "\n", params->response_depth); + + LogMessage(" unzip: %s\n", params->unzip ? "yes" : "no"); + LogMessage(" normalize_utf: %s\n", params->normalize_utf ? "yes" : "no"); + LogMessage(" decompress_pdf: %s\n", params->decompress_pdf ? "yes" : "no"); + LogMessage(" decompress_swf: %s\n", params->decompress_swf ? "yes" : "no"); + LogMessage(" decompress_zip: %s\n", params->decompress_zip ? "yes" : "no"); + LogMessage(" detained_inspection: %s\n", params->detained_inspection ? "yes" : "no"); + + LogMessage(" normalize_javascript: %s\n", params->js_norm_param.normalize_javascript ? "yes" : "no"); + LogMessage(" max_javascript_whitespaces: %d\n", params->js_norm_param.max_javascript_whitespaces); + + LogMessage(" percent_u: %s\n", params->uri_param.percent_u ? "yes" : "no"); + LogMessage(" utf8: %s\n", params->uri_param.utf8 ? "yes" : "no"); + LogMessage(" utf8_bare_byte: %s\n", params->uri_param.utf8_bare_byte ? "yes" : "no"); + LogMessage(" oversize_dir_length: %d\n", params->uri_param.oversize_dir_length); + LogMessage(" iis_unicode: %s\n", params->uri_param.iis_unicode ? "yes" : "no"); + LogMessage(" iis_unicode_map_file: %s\n", params->uri_param.iis_unicode_map_file.c_str()); + LogMessage(" iis_double_decode: %s\n", params->uri_param.iis_double_decode ? "yes" : "no"); + LogMessage(" backslash_to_slash: %s\n", params->uri_param.backslash_to_slash ? "yes" : "no"); + LogMessage(" plus_to_space: %s\n", params->uri_param.plus_to_space ? "yes" : "no"); + LogMessage(" simplify_path: %s\n", params->uri_param.simplify_path ? "yes" : "no"); +} + InspectSection HttpInspect::get_latest_is(const Packet* p) { HttpMsgSection* current_section = HttpContextData::get_snapshot(p); diff --git a/src/service_inspectors/http_inspect/http_inspect.h b/src/service_inspectors/http_inspect/http_inspect.h index a43e2f144..db64b9eb0 100644 --- a/src/service_inspectors/http_inspect/http_inspect.h +++ b/src/service_inspectors/http_inspect/http_inspect.h @@ -47,7 +47,7 @@ public: snort::InspectionBuffer& b); bool get_fp_buf(snort::InspectionBuffer::Type ibt, snort::Packet* p, snort::InspectionBuffer& b) override; bool configure(snort::SnortConfig*) override; - void show(snort::SnortConfig*) override { snort::LogMessage("HttpInspect\n"); } + void show(snort::SnortConfig*) override; void eval(snort::Packet* p) override; void clear(snort::Packet* p) override; HttpStreamSplitter* get_splitter(bool is_client_to_server) override diff --git a/src/utils/stats.cc b/src/utils/stats.cc index 8b6d5ed04..f1ba9f641 100644 --- a/src/utils/stats.cc +++ b/src/utils/stats.cc @@ -130,12 +130,11 @@ void TimeStop() gettimeofday(&endtime, nullptr); } -static void timing_stats(uint64_t& num_pkts, uint64_t& usecs) +static void timing_stats() { struct timeval difftime; TIMERSUB(&endtime, &starttime, &difftime); - uint32_t tmp = (uint32_t)difftime.tv_sec; uint32_t total_secs = tmp; if ( total_secs < 1 ) @@ -154,8 +153,7 @@ static void timing_stats(uint64_t& num_pkts, uint64_t& usecs) LogMessage("%25.25s: %lu.%06lu\n", "seconds", (unsigned long)difftime.tv_sec, (unsigned long)difftime.tv_usec); - usecs = (uint64_t)difftime.tv_sec * 1000000 + difftime.tv_usec; - num_pkts = (uint64_t)ModuleManager::get_module("daq")->get_global_count("received"); + uint64_t num_pkts = (uint64_t)ModuleManager::get_module("daq")->get_global_count("received"); LogMessage("%25.25s: " STDu64 "\n", "packets", num_pkts); @@ -239,10 +237,7 @@ void DropStats() void PrintStatistics() { DropStats(); - - PegCount pkts; - uint64_t usecs; - timing_stats(pkts, usecs); + timing_stats(); // FIXIT-L can do flag saving with RAII (much cleaner) int save_quiet_flag = SnortConfig::get_conf()->logging_flags & LOGGING_FLAG__QUIET; @@ -250,7 +245,8 @@ void PrintStatistics() SnortConfig::get_conf()->logging_flags &= ~LOGGING_FLAG__QUIET; // once more for the main thread - Profiler::consolidate_stats(pkts, usecs); + Profiler::consolidate_stats(); + Profiler::show_stats(); SnortConfig::get_conf()->logging_flags |= save_quiet_flag;