]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2230 in SNORT/snort3 from ~OSERHIIE/snort3:trace_control_command...
authorBhagya Tholpady (bbantwal) <bbantwal@cisco.com>
Thu, 4 Jun 2020 17:13:03 +0000 (17:13 +0000)
committerBhagya Tholpady (bbantwal) <bbantwal@cisco.com>
Thu, 4 Jun 2020 17:13:03 +0000 (17:13 +0000)
Squashed commit of the following:

commit ad8de0f3f4f5499eac67d3e0d9e8ab0391434308
Author: Oleksandr Serhiienko <oserhiie@cisco.com>
Date:   Fri May 29 13:07:54 2020 +0300

    trace: fix for trace messages in the test-mode ('-T' option)

commit e9e654d6301f4c81c8086d84581380432272299f
Author: Oleksandr Serhiienko <oserhiie@cisco.com>
Date:   Fri May 15 11:45:34 2020 +0300

    trace: add control channel command

14 files changed:
src/main/snort_config.cc
src/main/snort_config.h
src/trace/CMakeLists.txt
src/trace/dev_notes.txt
src/trace/trace.cc
src/trace/trace.h
src/trace/trace_config.cc
src/trace/trace_config.h
src/trace/trace_module.cc
src/trace/trace_module.h
src/trace/trace_parser.cc [new file with mode: 0644]
src/trace/trace_parser.h [new file with mode: 0644]
src/trace/trace_swap.cc [new file with mode: 0644]
src/trace/trace_swap.h [new file with mode: 0644]

index 8e3e1a5336fb28e6f77e3ab1e022af31d3f51810..25924d1329780b6821d0a28a7bcd6a219e2190e3 100644 (file)
@@ -259,6 +259,7 @@ SnortConfig::~SnortConfig()
     delete[] state;
     delete thread_config;
     delete trace_config;
+    delete overlay_trace_config;
     delete ha_config;
     delete global_dbus;
 
@@ -789,6 +790,12 @@ void SnortConfig::set_verbose(bool enabled)
         logging_flags &= ~LOGGING_FLAG__VERBOSE;
 }
 
+void SnortConfig::set_overlay_trace_config(TraceConfig* tc)
+{
+    delete overlay_trace_config;
+    overlay_trace_config = tc;
+}
+
 void SnortConfig::set_tunnel_verdicts(const char* args)
 {
     char* tmp, * tok;
index 5faf5edefd21df12b0f25f6972c68bd036b07227..846bb0cb6894465567a8fe1b25f725af71b1bcb6 100644 (file)
@@ -414,6 +414,9 @@ public:
     HighAvailabilityConfig* ha_config = nullptr;
     TraceConfig* trace_config = nullptr;
 
+    // TraceConfig instance which used by TraceSwap control channel command
+    TraceConfig* overlay_trace_config = nullptr;
+
     //------------------------------------------------------
     //Reload inspector related
 
@@ -472,6 +475,7 @@ public:
     void set_umask(uint32_t);
     void set_utc(bool);
     void set_verbose(bool);
+    void set_overlay_trace_config(TraceConfig*);
 
     //------------------------------------------------------
     // accessor methods
index da956f98a66df7500aa5e104a032e351dcb7edbe..23426f4eb534a18cd721974939ca1c247d659739 100644 (file)
@@ -13,6 +13,10 @@ set ( TRACE_SOURCES
     trace_log_base.h
     trace_module.cc
     trace_module.h
+    trace_parser.cc
+    trace_parser.h
+    trace_swap.cc
+    trace_swap.h
     ${INCLUDES}
 )
 
index 2949148bf5c704d7ab5c92b1531e1c3f582eab6f..97d9ffcb2544a284c9ee911694ac4275249b0355 100644 (file)
@@ -39,6 +39,24 @@ This directory contains the trace logger framework.
     TraceModule ctor should be called after all existed modules due to TraceModule
         dynamic params restriction.
 
+* TraceSwap
+
+    This class extends snort::AnalyzerCommand and represents control channel CLI commands
+    for setting/clearing trace configuration from the shell. Commands parameters are encapsulated in
+    the TraceSwapParams class.
+    Available commands:
+        1) trace.set({ modules = {}, constraints = {} }) -- set new modules traces and constraints;
+        2) trace.clear() -- clear modules traces and constraints.
+
+* TraceSwapParams
+
+    This is a helper class for TraceSwap which encapsulates dynamic initialization and storing of
+    snort::Parameter and snort::Command lists based on TraceModule's parameters.
+
+* TraceParser
+
+    This class encapsulates module trace options and packet constraints parsing and setting logic.
+
 * TraceApi
 
     TraceApi is a facade API class used to init/reinit/term thread-local trace logger and module's
index d6ddf07c2fb654c3e6931ad9128a5cb6d07f4e67..550b3188112f50a9f18449dfea19366ed45c9911 100644 (file)
@@ -23,6 +23,8 @@
 
 #include "trace.h"
 
+#include <algorithm>
+
 #include "framework/module.h"
 
 using namespace snort;
@@ -39,7 +41,7 @@ Trace::Trace(const Module& m) : module(m)
     mod_name = module.get_name();
     const snort::TraceOption* trace_options = module.get_trace_options();
     options = ( trace_options->name ) ? trace_options : s_default_trace_options;
-    
+
     size_t options_size = 0;
     trace_options = options;
     while ( trace_options->name )
@@ -50,6 +52,16 @@ Trace::Trace(const Module& m) : module(m)
     option_levels.resize(options_size, 0);
 }
 
+Trace& Trace::operator=(const Trace& other)
+{
+    if ( this != &other )
+    {
+        option_levels = other.option_levels;
+        options = other.options;
+    }
+    return *this;
+}
+
 bool Trace::set(const std::string& trace_option_name, uint8_t trace_level)
 {
     size_t size = option_levels.size();
@@ -69,6 +81,9 @@ void Trace::set_module_trace() const
     module.set_trace(this);
 }
 
+void Trace::clear()
+{ std::fill(option_levels.begin(), option_levels.end(), 0); }
+
 #ifdef CATCH_TEST_BUILD
 
 #include "catch/catch.hpp"
index 454d01d112f8ed2e2a2f8a538a00d6b3868aee1d..ff7f6259b039f6a0b8db58ef33c3b5b1e7291075 100644 (file)
@@ -59,10 +59,13 @@ class Trace
 {
 public:
     Trace(const Module& m);
+    Trace& operator=(const Trace&);
 
     bool set(const std::string& option_name, uint8_t option_level);
     void set_module_trace() const;
 
+    void clear();
+
     const char* module_name() const
     { return mod_name.c_str(); }
 
index 44ec62581ff0b2a6454ad77d35288913e23f9672..1c9d2db6f09628391f3decc0e76dac9f6ad6ee98 100644 (file)
 
 #include "trace_log_base.h"
 
+using namespace snort;
+
 TraceConfig::TraceConfig()
 {
-    auto modules = snort::ModuleManager::get_all_modules();
+    auto modules = ModuleManager::get_all_modules();
     for ( auto* module : modules )
     {
         if ( module->get_trace_options() )
@@ -41,6 +43,14 @@ TraceConfig::TraceConfig()
     }
 }
 
+TraceConfig::TraceConfig(const TraceConfig& other)
+    : TraceConfig()
+{
+    traces = other.traces;
+    if ( other.constraints )
+        constraints = new PacketConstraints(*other.constraints);
+}
+
 TraceConfig::~TraceConfig()
 {
     delete logger_factory;
@@ -61,6 +71,12 @@ bool TraceConfig::set_trace(const std::string& module_name, const std::string& t
     return false;
 }
 
+void TraceConfig::clear_traces()
+{
+    for ( auto& trace : traces )
+        trace.clear();
+}
+
 void TraceConfig::setup_module_trace() const
 {
     for ( const auto& trace : traces )
index 7ad3d94bb6fa5392509f4ab131a50d36f64009dd..e25675541fa5665db147a828efb89a9a2ad7bc59 100644 (file)
@@ -32,12 +32,16 @@ class TraceConfig
 {
 public:
     TraceConfig();
+    TraceConfig(const TraceConfig&);
     ~TraceConfig();
 
     void setup_module_trace() const;
     bool set_trace(const std::string& module_name,
         const std::string& trace_option_name, uint8_t trace_level);
 
+    void clear_traces();
+
+public:
     snort::TraceLoggerFactory* logger_factory = nullptr;
     snort::PacketConstraints* constraints = nullptr;
 
index 420f3612df78addaa6441c820b2d527691b47ee3..0966b91e47d29f643a9cef181cdeff1e918d2af0 100644 (file)
 
 #include <syslog.h>
 
-#include "framework/packet_constraints.h"
 #include "main/snort_config.h"
 #include "managers/module_manager.h"
 
 #include "trace_config.h"
 #include "trace_log.h"
+#include "trace_parser.h"
+#include "trace_swap.h"
 
 using namespace snort;
 
@@ -40,8 +41,15 @@ using namespace snort;
 TraceModule::TraceModule() : Module(s_name, trace_help)
 {
     generate_params();
+    TraceSwapParams::set_params(get_parameters());
 }
 
+TraceModule::~TraceModule()
+{ delete trace_parser; }
+
+const Command* TraceModule::get_commands() const
+{ return TraceSwapParams::get_commands(); }
+
 void TraceModule::generate_params()
 {
     auto modules = snort::ModuleManager::get_all_modules();
@@ -50,7 +58,6 @@ void TraceModule::generate_params()
         const TraceOption* trace_options = module->get_trace_options();
         if ( trace_options )
         {
-            auto& module_trace_options = configured_trace_options[module->get_name()];
             std::string module_trace_help(module->get_name());
             module_trace_help += " module trace options";
             modules_help.emplace_back(module_trace_help);
@@ -61,15 +68,11 @@ void TraceModule::generate_params()
             module_range.emplace_back(DEFAULT_TRACE_OPTION_NAME, Parameter::PT_INT, "0:255", nullptr,
                 "enable all trace options");
 
-            if ( !trace_options->name )
-                module_trace_options[DEFAULT_TRACE_OPTION_NAME] = false;
-
             while ( trace_options->name )
             {
                 module_range.emplace_back(trace_options->name,
                     Parameter::PT_INT, "0:255", nullptr, trace_options->help);
 
-                module_trace_options[trace_options->name] = false;
                 ++trace_options;
             }
 
@@ -122,33 +125,22 @@ bool TraceModule::begin(const char* fqn, int, SnortConfig* sc)
 {
     if ( !strcmp(fqn, "trace") )
     {
+        trace_parser = new TraceParser(sc->trace_config);
+
         // Init default output type based on Snort run-mode
-        if ( sc->test_mode() )
-            log_output_type = OUTPUT_TYPE_NO_INIT;
-        else if ( sc->daemon_mode() or sc->log_syslog() )
+        if ( sc->daemon_mode() or sc->log_syslog() )
             log_output_type = OUTPUT_TYPE_SYSLOG;
         else
             log_output_type = OUTPUT_TYPE_STDOUT;
 
-        reset_configured_trace_options();
     }
     return true;
 }
 
-void TraceModule::reset_configured_trace_options()
-{
-    for ( auto& module_trace_options : configured_trace_options )
-        for ( auto& trace_options : module_trace_options.second )
-            trace_options.second = false;
-}
-
-bool TraceModule::set(const char* fqn, Value& v, SnortConfig* sc)
+bool TraceModule::set(const char* fqn, Value& v, SnortConfig*)
 {
     if ( v.is("output") )
     {
-        if ( sc->test_mode() )
-            return true;
-
         switch ( v.get_uint8() )
         {
             case OUTPUT_TYPE_STDOUT:
@@ -165,62 +157,10 @@ bool TraceModule::set(const char* fqn, Value& v, SnortConfig* sc)
     else if ( strstr(fqn, "trace.modules.") == fqn )
     {
         std::string module_name = find_module(fqn);
-        if ( strcmp(v.get_name(), DEFAULT_TRACE_OPTION_NAME) == 0 )
-        {
-            const auto& trace_options = configured_trace_options[module_name];
-            for ( const auto& trace_option : trace_options )
-                if ( !trace_option.second )
-                    sc->trace_config->set_trace(module_name, trace_option.first, v.get_uint8());
-            return true;
-        }
-        else
-        {
-            bool res = sc->trace_config->set_trace(module_name, v.get_name(), v.get_uint8());
-            configured_trace_options[module_name][v.get_name()] = res;
-            return res;
-        }
+        return trace_parser->set_traces(module_name, v);
     }
     else if ( strstr(fqn, "trace.constraints.") == fqn )
-    {
-        if ( !sc->trace_config->constraints )
-            sc->trace_config->constraints = new snort::PacketConstraints;
-
-        auto& cs = *sc->trace_config->constraints;
-
-        if ( v.is("ip_proto") )
-        {
-            cs.ip_proto = static_cast<IpProtocol>(v.get_uint8());
-            cs.set_bits |= PacketConstraints::SetBits::IP_PROTO;
-        }
-        else if ( v.is("src_port") )
-        {
-            cs.src_port = v.get_uint16();
-            cs.set_bits |= PacketConstraints::SetBits::SRC_PORT;
-        }
-        else if ( v.is("dst_port") )
-        {
-            cs.dst_port = v.get_uint16();
-            cs.set_bits |= PacketConstraints::SetBits::DST_PORT;
-        }
-        else if ( v.is("src_ip") )
-        {
-            const char* str = v.get_string();
-            if ( cs.src_ip.set(str) != SFIP_SUCCESS )
-                return false;
-
-            cs.set_bits |= PacketConstraints::SetBits::SRC_IP;
-        }
-        else if ( v.is("dst_ip") )
-        {
-            const char* str = v.get_string();
-            if ( cs.dst_ip.set(str) != SFIP_SUCCESS )
-                return false;
-
-            cs.set_bits |= PacketConstraints::SetBits::DST_IP;
-        }
-
-        return true;
-    }
+        return trace_parser->set_constraints(v);
 
     return false;
 }
@@ -229,16 +169,15 @@ bool TraceModule::end(const char* fqn, int, SnortConfig* sc)
 {
     if ( !strcmp(fqn, "trace") )
     {
+        assert(trace_parser);
         switch ( log_output_type )
         {
             case OUTPUT_TYPE_STDOUT:
-                sc->trace_config->logger_factory = new StdoutLoggerFactory();
+                trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory();
                 break;
             case OUTPUT_TYPE_SYSLOG:
-                sc->trace_config->logger_factory = new SyslogLoggerFactory();
+                trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory();
                 break;
-            case OUTPUT_TYPE_NO_INIT:
-                sc->trace_config->logger_factory = nullptr;
             default:
                 break;
         }
@@ -251,6 +190,9 @@ bool TraceModule::end(const char* fqn, int, SnortConfig* sc)
             local_syslog = true;
             openlog("snort", LOG_PID | LOG_CONS, LOG_DAEMON);
         }
+
+        delete trace_parser;
+        trace_parser = nullptr;
     }
 
     return true;
index 9c369d3b639c1fe7bed3efa772d9917058f9c9c3..f9d88afa8b1fe174fe9b94f9ff0135ddc1b3b488 100644 (file)
 #ifndef TRACE_MODULE_H
 #define TRACE_MODULE_H
 
-#include <map>
-
 #include "framework/module.h"
 
+class TraceParser;
+
 class TraceModule : public snort::Module
 {
 private:
     enum OutputType
     {
-        OUTPUT_TYPE_STDOUT,
+        OUTPUT_TYPE_STDOUT = 0,
         OUTPUT_TYPE_SYSLOG,
         OUTPUT_TYPE_NO_INIT
     };
 
 public:
     TraceModule();
+    ~TraceModule() override;
 
+    const snort::Command* get_commands() const override;
     bool begin(const char*, int, snort::SnortConfig*) override;
     bool set(const char*, snort::Value&, snort::SnortConfig*) override;
     bool end(const char*, int, snort::SnortConfig*) override;
@@ -47,15 +49,16 @@ public:
 private:
     std::string find_module(const char* config_name) const;
     void generate_params();
-    void reset_configured_trace_options();
 
 private:
     OutputType log_output_type = OUTPUT_TYPE_NO_INIT;
     bool local_syslog = false;
+
     std::vector<snort::Parameter> modules_params;
     std::vector<std::vector<snort::Parameter>> module_ranges;
     std::vector<std::string> modules_help;
-    std::map<std::string, std::map<std::string, bool>> configured_trace_options;
+
+    TraceParser* trace_parser = nullptr;
 };
 
 #endif  // TRACE_MODULE_H
diff --git a/src/trace/trace_parser.cc b/src/trace/trace_parser.cc
new file mode 100644 (file)
index 0000000..696ecd4
--- /dev/null
@@ -0,0 +1,299 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// trace_parser.cc author Oleksandr Serhiienko <oserhiie@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "trace_parser.h"
+
+#include "framework/module.h"
+#include "framework/packet_constraints.h"
+#include "managers/module_manager.h"
+
+#include "trace_config.h"
+
+using namespace snort;
+
+std::map<std::string, std::map<std::string, bool>> TraceParser::s_configured_trace_options;
+
+TraceParser::TraceParser(TraceConfig* tc)
+    : trace_config(tc)
+{
+    assert(trace_config);
+
+    // Will be initialized only once when first TraceParser instance created
+    if ( s_configured_trace_options.empty() )
+        init_configured_trace_options();
+    else
+        reset_configured_trace_options();
+}
+
+bool TraceParser::set_traces(const std::string& module_name, const Value& val)
+{
+    if ( !s_configured_trace_options.count(module_name) )
+        return false;
+
+    if ( val.is(DEFAULT_TRACE_OPTION_NAME) )
+    {
+        const auto& trace_options = s_configured_trace_options[module_name];
+        for ( const auto& trace_option : trace_options )
+        {
+            if ( !trace_option.second )
+                trace_config->set_trace(module_name, trace_option.first, val.get_uint8());
+        }
+
+        return true;
+    }
+    else
+    {
+        bool res = trace_config->set_trace(module_name, val.get_name(), val.get_uint8());
+        s_configured_trace_options[module_name][val.get_name()] = res;
+        return res;
+    }
+}
+
+bool TraceParser::set_constraints(const Value& val)
+{
+    if ( !trace_config->constraints )
+        trace_config->constraints = new PacketConstraints;
+
+    auto& cs = *trace_config->constraints;
+
+    if ( val.is("ip_proto") )
+    {
+        cs.ip_proto = static_cast<IpProtocol>(val.get_uint8());
+        cs.set_bits |= PacketConstraints::SetBits::IP_PROTO;
+    }
+    else if ( val.is("src_port") )
+    {
+        cs.src_port = val.get_uint16();
+        cs.set_bits |= PacketConstraints::SetBits::SRC_PORT;
+    }
+    else if ( val.is("dst_port") )
+    {
+        cs.dst_port = val.get_uint16();
+        cs.set_bits |= PacketConstraints::SetBits::DST_PORT;
+    }
+    else if ( val.is("src_ip") )
+    {
+        const char* str = val.get_string();
+        if ( cs.src_ip.set(str) != SFIP_SUCCESS )
+            return false;
+
+        cs.set_bits |= PacketConstraints::SetBits::SRC_IP;
+    }
+    else if ( val.is("dst_ip") )
+    {
+        const char* str = val.get_string();
+        if ( cs.dst_ip.set(str) != SFIP_SUCCESS )
+            return false;
+
+        cs.set_bits |= PacketConstraints::SetBits::DST_IP;
+    }
+    else
+        return false;
+
+    return true;
+}
+
+void TraceParser::clear_traces()
+{ trace_config->clear_traces(); }
+
+void TraceParser::clear_constraints()
+{
+    delete trace_config->constraints;
+    trace_config->constraints = nullptr;
+}
+
+void TraceParser::reset_configured_trace_options()
+{
+    for ( auto& module_trace_options : s_configured_trace_options )
+    {
+        for ( auto& trace_options : module_trace_options.second )
+            trace_options.second = false;
+    }
+}
+
+void TraceParser::init_configured_trace_options()
+{
+    auto trace_modules = ModuleManager::get_all_modules();
+    for ( const auto* module : trace_modules )
+    {
+        const TraceOption* trace_options = module->get_trace_options();
+        if ( trace_options )
+        {
+            auto& module_trace_options = s_configured_trace_options[module->get_name()];
+            if ( !trace_options->name )
+                module_trace_options[DEFAULT_TRACE_OPTION_NAME] = false;
+
+            while ( trace_options->name )
+            {
+                module_trace_options[trace_options->name] = false;
+                ++trace_options;
+            }
+        }
+    }
+}
+
+#ifdef UNIT_TEST
+
+#include <catch/snort_catch.h>
+
+TEST_CASE("packet constraints", "[TraceParser]")
+{
+    TraceConfig tc;
+    TraceParser tp(&tc);
+
+    SECTION("ip_proto")
+    {
+        const Parameter ip_proto_param("ip_proto", Parameter::PT_INT, "0:255", nullptr,
+            "test ip_proto param");
+
+        Value val(false);
+        val.set(&ip_proto_param);
+
+        val.set(6l);
+        CHECK( tp.set_constraints(val) );
+    }
+    SECTION("src_ip")
+    {
+        const Parameter src_ip_param("src_ip", Parameter::PT_STRING, nullptr, nullptr,
+          "test src_ip param");
+
+        Value val(false);
+        val.set(&src_ip_param);
+
+        val.set("10.1.2.3");
+        CHECK( tp.set_constraints(val) );
+
+        val.set("10.1.2.300");
+        CHECK( !tp.set_constraints(val) );
+    }
+    SECTION("src_port")
+    {
+        const Parameter src_port_param("src_port", Parameter::PT_INT, "0:65535", nullptr,
+          "test src_port param");
+
+        Value val(false);
+        val.set(&src_port_param);
+
+        val.set(100l);
+        CHECK( tp.set_constraints(val) );
+    }
+    SECTION("dst_ip")
+    {
+        const Parameter dst_ip_param("dst_ip", Parameter::PT_STRING, nullptr, nullptr,
+          "test dst_ip param");
+
+        Value val(false);
+        val.set(&dst_ip_param);
+
+        val.set("10.3.2.1");
+        CHECK( tp.set_constraints(val) );
+
+        val.set("10.300.2.1");
+        CHECK( !tp.set_constraints(val) );
+    }
+    SECTION("dst_port")
+    {
+        const Parameter dst_port_param("dst_port", Parameter::PT_INT, "0:65535", nullptr,
+          "test dst_port param");
+
+        Value val(false);
+        val.set(&dst_port_param);
+
+        val.set(200l);
+        CHECK( tp.set_constraints(val) );
+    }
+    SECTION("invalid_param")
+    {
+        const Parameter invalid_param("invalid_param", Parameter::PT_INT, "0:8", nullptr,
+          "test invalid param");
+
+        Value val(false);
+        val.set(&invalid_param);
+
+        val.set(5l);
+        CHECK( !tp.set_constraints(val) );
+    }
+}
+
+TEST_CASE("modules traces", "[TraceParser]")
+{
+    TraceConfig tc;
+    TraceParser tp(&tc);
+
+    SECTION("set_option")
+    {
+        const Parameter detection_rule_eval("rule_eval", Parameter::PT_INT, "0:255", nullptr,
+            "test detection_rule_eval param");
+
+        const Parameter detection_detect_engine("detect_engine", Parameter::PT_INT, "0:255", nullptr,
+            "test detection_detect_engine param");
+
+        Value val_opt1(false);
+        Value val_opt2(false);
+        val_opt1.set(&detection_rule_eval);
+        val_opt2.set(&detection_detect_engine);
+
+        val_opt1.set(1l);
+        CHECK( tp.set_traces("detection", val_opt1) );
+
+        val_opt2.set(1l);
+        CHECK( tp.set_traces("detection", val_opt2) );
+    }
+    SECTION("set_all")
+    {
+        const Parameter decode_all(DEFAULT_TRACE_OPTION_NAME, Parameter::PT_INT, "0:255", nullptr,
+            "test decode_all param");
+
+        Value val_all(false);
+        val_all.set(&decode_all);
+
+        val_all.set(1l);
+        CHECK( tp.set_traces("decode", val_all) );
+        CHECK( tp.set_traces("detection", val_all) );
+    }
+    SECTION("set_invalid_option")
+    {
+        const Parameter invalid_param("invalid_opt", Parameter::PT_INT, "0:255", nullptr,
+            "test invalid param");
+
+        Value invalid_val(false);
+        invalid_val.set(&invalid_param);
+
+        invalid_val.set(1l);
+        CHECK( !tp.set_traces("detection", invalid_val) );
+    }
+    SECTION("set_invalid_module")
+    {
+        const Parameter all_param(DEFAULT_TRACE_OPTION_NAME, Parameter::PT_INT, "0:255", nullptr,
+            "test all param");
+
+        Value val(false);
+        val.set(&all_param);
+
+        val.set(1l);
+        CHECK( !tp.set_traces("invalid_module", val) );
+    }
+}
+
+#endif // UNIT_TEST
+
diff --git a/src/trace/trace_parser.h b/src/trace/trace_parser.h
new file mode 100644 (file)
index 0000000..e9a26a9
--- /dev/null
@@ -0,0 +1,59 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// trace_parser.h author Oleksandr Serhiienko <oserhiie@cisco.com>
+
+#ifndef TRACE_PARSER_H
+#define TRACE_PARSER_H
+
+#include <map>
+#include <string>
+
+namespace snort
+{
+class Module;
+class Value;
+}
+
+class TraceConfig;
+
+class TraceParser
+{
+public:
+    TraceParser(TraceConfig*);
+
+    bool set_traces(const std::string& module_name, const snort::Value& val);
+    bool set_constraints(const snort::Value& val);
+
+    void clear_traces();
+    void clear_constraints();
+
+    void reset_configured_trace_options();
+
+    TraceConfig* get_trace_config() const
+    { return trace_config; }
+
+private:
+    void init_configured_trace_options();
+
+private:
+    TraceConfig* trace_config = nullptr;
+    static std::map<std::string, std::map<std::string, bool>> s_configured_trace_options;
+};
+
+#endif // TRACE_PARSER_H
+
diff --git a/src/trace/trace_swap.cc b/src/trace/trace_swap.cc
new file mode 100644 (file)
index 0000000..bc2eb77
--- /dev/null
@@ -0,0 +1,296 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// trace_swap.cc author Oleksandr Serhiienko <oserhiie@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "trace_swap.h"
+
+#include <lua.hpp>
+
+#include "framework/module.h"
+#include "log/messages.h"
+#include "main/analyzer_command.h"
+#include "main/snort_config.h"
+
+#include "trace_api.h"
+#include "trace_config.h"
+#include "trace_parser.h"
+
+using namespace snort;
+
+const Command* TraceSwapParams::s_commands = nullptr;
+const Parameter* TraceSwapParams::s_params = nullptr;
+
+static int set(lua_State*);
+static int clear(lua_State*);
+
+void TraceSwapParams::set_params(const Parameter* params)
+{
+    const Parameter* modules_params = Parameter::find(params, "modules");
+    const Parameter* constraints_params = Parameter::find(params, "constraints");
+
+    assert(modules_params);
+    assert(constraints_params);
+
+    static const Parameter trace_params[] =
+    {
+        *modules_params,
+
+        *constraints_params,
+
+        { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+    };
+
+    static const Command commands[] =
+    {
+        { "set", set, trace_params, "set modules traces and constraints" },
+
+        { "clear", clear, nullptr, "clear modules traces and constraints" },
+
+        { nullptr, nullptr, nullptr, nullptr }
+    };
+
+    s_params = trace_params;
+    s_commands = commands;
+}
+
+const Command* TraceSwapParams::get_commands()
+{ return s_commands; }
+
+const Parameter* TraceSwapParams::get_params()
+{ return s_params; }
+
+class TraceSwap : public AnalyzerCommand
+{
+public:
+    TraceSwap(TraceConfig* tc, bool set_traces = false, bool set_constraints = false)
+        : trace_config(tc),
+          is_set_traces(set_traces),
+          is_set_constraints(set_constraints)
+    { assert(trace_config); }
+    ~TraceSwap() override;
+
+    bool execute(Analyzer&, void**) override;
+    const char* stringify() override
+    { return "TRACE_SWAP"; }
+
+private:
+    void print_msg() const;
+
+private:
+    TraceConfig* trace_config = nullptr;
+    bool is_set_traces;
+    bool is_set_constraints;
+};
+
+TraceSwap::~TraceSwap()
+{
+    // Update configuration for the main thread
+    // and set overlay TraceConfig
+    TraceApi::thread_reinit(trace_config);
+    SnortConfig::get_main_conf()->set_overlay_trace_config(trace_config);
+
+    print_msg();
+}
+
+bool TraceSwap::execute(Analyzer&, void**)
+{
+    // Update configuration for packet threads
+    TraceApi::thread_reinit(trace_config);
+
+    print_msg();
+    return true;
+}
+
+void TraceSwap::print_msg() const
+{
+    if ( is_set_traces and is_set_constraints )
+        LogMessage("== set modules traces and constraints\n");
+    else if ( !is_set_traces and !is_set_constraints )
+        LogMessage("== clear modules traces and constraints\n");
+    else if ( is_set_traces )
+        LogMessage("== set modules traces\n");
+    else if ( is_set_constraints )
+        LogMessage("== set constraints\n");
+}
+
+static int set(lua_State* L)
+{
+    // Create an overlay TraceConfig based on the current configuration
+    // It will be set in a SnortConfig during TraceSwap execution and owned by it after
+    const SnortConfig* sc = SnortConfig::get_conf();
+    TraceConfig* trace_config = new TraceConfig(sc->overlay_trace_config
+        ? *sc->overlay_trace_config : *sc->trace_config);
+
+    TraceParser trace_parser(trace_config);
+
+    const Parameter* params_tree = TraceSwapParams::get_params();
+    bool parse_err = false;
+    bool set_traces = false;
+    bool set_constraints = false;
+
+    // Passed Lua entry check
+    if ( lua_gettop(L) != 1 or !lua_istable(L, 1) )
+    {
+        LogMessage("== invalid Lua entry is provided, %s: %s\n",
+            "use the outer table and pass options inside of it",
+            "{ modules = {}, constraints = {} }");
+
+        delete trace_config;
+        return 0;
+    }
+
+    // Outer table traversal
+    lua_pushnil(L);
+    while ( lua_next(L, 1) )
+    {
+        const char* root_tbl_name = luaL_checkstring(L, -2);
+        const Parameter* root_tbl_param = Parameter::find(params_tree, root_tbl_name);
+
+        if ( !lua_istable(L, -1) or !root_tbl_param )
+        {
+            LogMessage("== invalid table is provided: %s\n", root_tbl_name);
+            parse_err = true;
+            lua_pop(L, 1);
+            continue;
+        }
+
+        // "modules" table traversal
+        if ( !strcmp(root_tbl_name, params_tree[0].name) )
+        {
+            set_traces = true;
+            trace_parser.clear_traces();
+
+            const Parameter* modules_param = (const Parameter*)root_tbl_param->range;
+
+            int modules_tbl_idx = lua_gettop(L);
+            lua_pushnil(L);
+            while ( lua_next(L, modules_tbl_idx) )
+            {
+                const char* module_name = luaL_checkstring(L, -2);
+                const Parameter* module_param = Parameter::find(modules_param, module_name);
+
+                if ( !lua_istable(L, -1) or !module_param )
+                {
+                    LogMessage("== invalid table is provided: %s.%s\n", root_tbl_name,
+                        module_name);
+
+                    parse_err = true;
+                    lua_pop(L, 1);
+                    continue;
+                }
+
+                // Trace table traversal
+                int module_tbl_idx = lua_gettop(L);
+                lua_pushnil(L);
+                while ( lua_next(L, module_tbl_idx) )
+                {
+                    const char* val_name = luaL_checkstring(L, -2);
+                    const Parameter* trace_param = Parameter::find(
+                        (const Parameter*)module_param->range, val_name);
+
+                    Value val(false);
+                    val.set(trace_param);
+
+                    if ( lua_isnumber(L, -1) )
+                        val.set((double)lua_tointeger(L, -1));
+                    else
+                        val.set(luaL_checkstring(L, -1));
+
+                    if ( !trace_param or !trace_param->validate(val) or
+                         !trace_parser.set_traces(module_name, val) )
+                    {
+                        LogMessage("== invalid trace value is provided: %s.%s.%s = %s\n",
+                            root_tbl_name, module_name, val_name, val.get_as_string());
+
+                        parse_err = true;
+                    }
+
+                    lua_pop(L, 1);
+                }
+
+                lua_pop(L, 1);
+            }
+        }
+        // "constraints" table traversal
+        else if ( !strcmp(root_tbl_name, params_tree[1].name) )
+        {
+            set_constraints = true;
+            trace_parser.clear_constraints();
+
+            const Parameter* constraints_param = (const Parameter*)root_tbl_param->range;
+
+            int constraints_tbl_idx = lua_gettop(L);
+            lua_pushnil(L);
+            while ( lua_next(L, constraints_tbl_idx) )
+            {
+                const char* val_name = luaL_checkstring(L, -2);
+                const Parameter* filter_param = Parameter::find(constraints_param, val_name);
+                Value val(false);
+                val.set(filter_param);
+
+                if ( lua_isnumber(L, -1) )
+                    val.set((double)lua_tointeger(L, -1));
+                else
+                    val.set(luaL_checkstring(L, -1));
+
+                if ( !filter_param or !filter_param->validate(val) or
+                     !trace_parser.set_constraints(val) )
+                {
+                    LogMessage("== invalid constraints value is provided: %s.%s = %s\n",
+                        root_tbl_name, val_name, val.get_as_string());
+
+                    parse_err = true;
+                }
+
+                lua_pop(L, 1);
+            }
+        }
+
+        lua_pop(L, 1);
+    }
+
+    if ( !parse_err )
+    {
+        if ( !set_traces and !set_constraints )
+        {
+            trace_parser.clear_traces();
+            trace_parser.clear_constraints();
+        }
+
+        main_broadcast_command(new TraceSwap(
+            trace_parser.get_trace_config(), set_traces, set_constraints),
+            true);
+    }
+    else
+        delete trace_config;
+
+    return 0;
+}
+
+static int clear(lua_State*)
+{
+    // Create an empty overlay TraceConfig
+    // It will be set in a SnortConfig during TraceSwap execution and owned by it after
+    main_broadcast_command(new TraceSwap(new TraceConfig()), true);
+    return 0;
+}
+
diff --git a/src/trace/trace_swap.h b/src/trace/trace_swap.h
new file mode 100644 (file)
index 0000000..573f2d8
--- /dev/null
@@ -0,0 +1,43 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// trace_swap.h author Oleksandr Serhiienko <oserhiie@cisco.com>
+
+#ifndef TRACE_SWAP_H
+#define TRACE_SWAP_H
+
+namespace snort
+{
+struct Command;
+struct Parameter;
+}
+
+class TraceSwapParams
+{
+public:
+    static void set_params(const snort::Parameter* params);
+
+    static const snort::Command* get_commands();
+    static const snort::Parameter* get_params();
+
+private:
+    static const snort::Command* s_commands;
+    static const snort::Parameter* s_params;
+};
+
+#endif // TRACE_SWAP_H
+