if ( log_rule_group_details )
LogMessage("Service Based Rule Maps Done....\n");
- if ( !sc->test_mode() or sc->mem_check() )
+ if ( !sc->validation_mode() or sc->mem_check() )
{
unsigned c = compile_mpses(sc, can_build_mt(fp));
unsigned expected = mpse_count + offload_mpse_count;
return str.c_str();
}
+std::string Value::get_origin_string() const
+{
+ std::string value;
+ std::string token;
+
+ stringstream ss(origin_str);
+ while ( ss >> token )
+ {
+ value += token;
+ value += " ";
+ }
+ value.erase(value.size() - 1);
+
+ if ( param && param->type != Parameter::PT_BOOL
+ && param->type != Parameter::PT_REAL
+ && param->type != Parameter::PT_INT
+ && param->type != Parameter::PT_IMPLIED )
+ {
+ value.insert(0, "'");
+ value.insert(value.length(), "'");
+ }
+
+ return value;
+}
+
+Parameter::Type Value::get_param_type() const
+{
+ if ( param )
+ return param->type;
+
+ return Parameter::PT_MAX;
+}
+
void Value::update_mask(uint8_t& mask, uint8_t flag, bool invert)
{
if ( get_bool() xor invert )
enum ValueType { VT_BOOL, VT_NUM, VT_STR };
Value(bool b)
- { set(b); init(); }
+ { set(b); set_origin(b); }
Value(double d)
- { set(d); init(); }
+ { set(d); set_origin(d); }
Value(const char* s)
- { set(s); init(); }
+ { set(s); set_origin(s); }
+
+ Value(const Value& v) :
+ type(v.type),
+ num(v.num),
+ str(v.str),
+ origin_str(v.origin_str),
+ ss(nullptr),
+ param(v.param)
+ {}
+
+ Value& operator=(const Value& v)
+ {
+ if ( this == &v )
+ return *this;
+
+ delete ss;
+ ss = nullptr;
+
+ type = v.type;
+ num = v.num;
+ str = v.str;
+ origin_str = v.origin_str;
+ param = v.param;
+
+ return *this;
+ }
ValueType get_type()
{ return type; }
~Value()
- {
- if ( ss )
- delete ss;
- }
+ { delete ss; }
void set(bool b)
{ type = VT_BOOL; num = b ? 1 : 0; str.clear(); }
+ void set_origin(bool val)
+ { origin_str = val ? "true" : "false"; }
+
void set(double d)
{ type = VT_NUM; num = d; str.clear(); }
+ void set_origin(double val)
+ {
+ auto temp = new std::stringstream;
+ *temp << val;
+ origin_str = temp->str();
+ delete temp;
+ }
+
void set(long n)
{ set((double)n); }
void set(const char* s)
{ type = VT_STR; str = s; num = 0; }
+ void set_origin(const char* val)
+ { origin_str = val; }
+
void set(const uint8_t* s, unsigned len)
{ type = VT_STR; str.assign((const char*)s, len); num = 0; }
bool is(const char* s) const
{ return param ? !strcmp(param->name, s) : false; }
+ bool has_default() const
+ { return param ? param->deflt != nullptr : false; }
+
bool get_bool() const
{ return num != 0; }
{ return str.c_str(); }
const char* get_as_string();
+ Parameter::Type get_param_type() const;
+ std::string get_origin_string() const;
bool strtol(long&) const;
bool strtol(long&, const std::string&) const;
void update_mask(uint32_t& mask, uint32_t flag, bool invert = false);
void update_mask(uint64_t& mask, uint64_t flag, bool invert = false);
-private:
- void init()
- { param = nullptr; ss = nullptr; }
-
private:
ValueType type;
double num;
std::string str;
- std::stringstream* ss;
- const Parameter* param;
+ std::string origin_str;
+ std::stringstream* ss = nullptr;
+ const Parameter* param = nullptr;
};
}
#endif
va_end(ap);
}
+void LogConfig(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+
+ WriteLogMessage(stdout, false, format, ap);
+
+ va_end(ap);
+}
+
/*
* Function: WarningMessage(const char *, ...)
*
SO_PUBLIC void ReloadError(const char*, ...) __attribute__((format (printf, 1, 2)));
[[noreturn]] SO_PUBLIC void ParseAbort(const char*, ...) __attribute__((format (printf, 1, 2)));
+SO_PUBLIC void LogConfig(const char*, ...) __attribute__((format (printf, 1, 2)));
SO_PUBLIC void LogMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
SO_PUBLIC void LogMessage(FILE*, const char*, ...) __attribute__((format (printf, 2, 3)));
SO_PUBLIC void WarningMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
return false;
}
+ if ( sc->dump_config() )
+ return false;
+
if ( just_validate(sc) )
{
LogMessage("\nSnort successfully validated the configuration (with %u warnings).\n",
analyzer.h
analyzer_command.cc
build.h
+ config_tree.cc
+ config_tree.h
help.cc
help.h
modules.cc
--- /dev/null
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// config_tree.cc author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "config_tree.h"
+
+#include <cassert>
+
+#include "log/messages.h"
+
+using namespace snort;
+
+void ConfigTextFormat::print(const BaseConfigNode* parent, const std::string& config_name)
+{
+ static char buf[16];
+ int list_index = 0;
+ for ( const auto node : parent->get_children() )
+ {
+ std::string full_config_name(config_name);
+ std::string node_name = node->get_name();
+
+ if ( !node_name.empty() )
+ {
+ full_config_name += ".";
+ full_config_name += node_name;
+ }
+ else
+ {
+ sprintf(buf, "%i", list_index++);
+ full_config_name += "[";
+ full_config_name += buf;
+ full_config_name += "]";
+ }
+
+ print(node, full_config_name);
+ }
+
+ std::string config_data = parent->data();
+ if ( !config_data.empty() )
+ LogConfig("%s=%s\n", config_name.c_str(), config_data.c_str());
+}
+
+BaseConfigNode::BaseConfigNode(BaseConfigNode* p) :
+ parent(p)
+{}
+
+void BaseConfigNode::add_child_node(BaseConfigNode* node)
+{
+ assert(node);
+ children.push_back(node);
+}
+
+void BaseConfigNode::clear_nodes(BaseConfigNode* node)
+{
+ for ( auto& config_node : node->children )
+ clear_nodes(config_node);
+
+ delete node;
+}
+
+TreeConfigNode::TreeConfigNode(BaseConfigNode* parent_node,
+ const std::string& node_name, const Parameter::Type node_type) :
+ BaseConfigNode(parent_node), name(node_name), type(node_type)
+{}
+
+BaseConfigNode* TreeConfigNode::get_node(const std::string& name)
+{
+ for ( auto node : children )
+ {
+ if ( node->get_name() == name )
+ return node;
+ }
+ return nullptr;
+}
+
+ValueConfigNode::ValueConfigNode(BaseConfigNode* parent_node, const Value& val) :
+ BaseConfigNode(parent_node), value(val)
+{}
+
+BaseConfigNode* ValueConfigNode::get_node(const std::string& name)
+{
+ return value.is(name.c_str()) and value.has_default() ? this : nullptr;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// config_tree.h author Serhii Vlasiuk <svlasiuk@cisco.com>
+
+#ifndef CONFIG_TREE_H
+#define CONFIG_TREE_H
+
+#include <list>
+
+#include "framework/value.h"
+
+class BaseConfigNode;
+
+using ChildrenNodes = std::list<BaseConfigNode*>;
+
+class ConfigTextFormat
+{
+public:
+ static void print(const BaseConfigNode* parent, const std::string& config_name);
+};
+
+class BaseConfigNode
+{
+public:
+ BaseConfigNode(BaseConfigNode* parent);
+ virtual ~BaseConfigNode() = default;
+
+ virtual std::string get_name() const = 0;
+ virtual snort::Parameter::Type get_type() const = 0;
+ virtual BaseConfigNode* get_node(const std::string& name) = 0;
+ virtual std::string data() const { return ""; }
+ virtual void set_value(const snort::Value&) {}
+
+ const ChildrenNodes& get_children() const
+ { return children; }
+
+ BaseConfigNode* get_parent_node() const
+ { return parent; }
+
+ void add_child_node(BaseConfigNode* node);
+
+ static void clear_nodes(BaseConfigNode* root);
+
+protected:
+ ChildrenNodes children;
+ BaseConfigNode* parent = nullptr;
+};
+
+class TreeConfigNode : public BaseConfigNode
+{
+public:
+ TreeConfigNode(BaseConfigNode* parent, const std::string& node_name,
+ const snort::Parameter::Type node_type);
+
+private:
+ virtual std::string get_name() const override
+ { return name; }
+
+ virtual snort::Parameter::Type get_type() const override
+ { return type; }
+
+ virtual BaseConfigNode* get_node(const std::string& name) override;
+
+private:
+ std::string name;
+ snort::Parameter::Type type = snort::Parameter::PT_MAX;
+};
+
+class ValueConfigNode : public BaseConfigNode
+{
+public:
+ ValueConfigNode(BaseConfigNode* parent, const snort::Value& value);
+
+private:
+ virtual std::string get_name() const override
+ { return value.get_name(); }
+
+ virtual snort::Parameter::Type get_type() const override
+ { return value.get_param_type(); }
+
+ virtual std::string data() const override
+ { return value.get_origin_string(); }
+
+ virtual void set_value(const snort::Value& v) override
+ { value = v; }
+
+ virtual BaseConfigNode* get_node(const std::string& name) override;
+
+private:
+ snort::Value value;
+};
+
+#endif // CONFIG_TREE_H
+
information and management. Currently it is being used as a cross-platform
mechanism for managing CPU affinity of threads, but it will be used in the
future for NUMA (non-uniform memory access) awareness among other things.
+
+Consolidated Config output
+
+The snort config parsed is stored as an internal tree structure within the Shell.
+A unique shell is associated with every base and targeted policy file.
+Every module in a lua file is represented by the General Tree.
+The Tree node can be one of the following types: TreeConfigNode, ValueConfigNode.
+The TreeConfigNode represents a table or list.
+The ValueConfigNode represents a config value itself.
#include <unistd.h>
#include <cassert>
-#include <cstring>
#include <stdexcept>
+#include "config_tree.h"
#include "log/messages.h"
#include "lua/lua.h"
#include "main/policy.h"
sh->whitelist_update(keyword, is_prefix);
}
+void Shell::config_open_table(bool is_root_node, bool is_list, int idx,
+ const std::string& table_name, const Parameter* p)
+{
+ Parameter::Type node_type = is_list ? Parameter::PT_LIST : Parameter::PT_TABLE;
+ if ( is_root_node )
+ add_config_root_node(table_name, node_type);
+ else
+ {
+ if ( p )
+ node_type = p->type;
+
+ if ( node_type == Parameter::PT_TABLE )
+ update_current_config_node(table_name);
+ else
+ {
+ if ( idx )
+ node_type = Parameter::PT_TABLE;
+
+ add_config_child_node(table_name, node_type);
+ }
+ }
+}
+
+void Shell::add_config_child_node(const std::string& node_name, snort::Parameter::Type type)
+{
+ Shell* sh = Shell::get_current_shell();
+
+ if ( !sh )
+ return;
+
+ std::string name;
+ if ( sh->s_current_node->get_name() != node_name )
+ name = node_name;
+
+ auto new_node = new TreeConfigNode(sh->s_current_node, name, type);
+ sh->s_current_node->add_child_node(new_node);
+ sh->s_current_node = new_node;
+}
+
+void Shell::add_config_root_node(const std::string& root_name, snort::Parameter::Type node_type)
+{
+ Shell* sh = Shell::get_current_shell();
+
+ if ( !sh )
+ return;
+
+ sh->s_current_node = new TreeConfigNode(nullptr, root_name, node_type);
+ sh->config_trees.push_back(sh->s_current_node);
+}
+
+void Shell::update_current_config_node(const std::string& node_name)
+{
+ Shell* sh = Shell::get_current_shell();
+
+ if ( !sh )
+ return;
+
+ if ( !sh->s_current_node )
+ return;
+
+ // node has been added during setting default options
+ if ( !node_name.empty() )
+ sh->s_current_node = sh->s_current_node->get_node(node_name);
+ else if ( sh->s_current_node->get_parent_node() and
+ sh->s_current_node->get_type() == Parameter::PT_TABLE and
+ !sh->s_current_node->get_name().empty() )
+ sh->s_current_node = sh->s_current_node->get_parent_node();
+
+ assert(sh->s_current_node);
+}
+
+void Shell::config_close_table()
+{
+ Shell* sh = Shell::get_current_shell();
+
+ if ( !sh )
+ return;
+
+ if ( !sh->s_current_node )
+ return;
+
+ sh->s_current_node = sh->s_current_node->get_parent_node();
+}
+
+void Shell::set_config_value(const snort::Value& value)
+{
+ Shell* sh = Shell::get_current_shell();
+
+ if ( !sh )
+ return;
+
+ if ( !sh->s_current_node )
+ return;
+
+ // lua interpreter does not call open_table for simple list items like (string)
+ // we have to add tree node for this item too
+ if ( sh->s_current_node->get_type() == Parameter::PT_LIST )
+ {
+ add_config_child_node("", Parameter::PT_TABLE);
+ sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value));
+ sh->s_current_node = sh->s_current_node->get_parent_node();
+
+ return;
+ }
+
+ BaseConfigNode* child_node = nullptr;
+ for ( auto node : sh->s_current_node->get_children() )
+ {
+ child_node = node->get_node(value.get_name());
+ if ( child_node )
+ break;
+ }
+
+ if ( !child_node )
+ sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value));
+ else
+ child_node->set_value(value);
+}
+
// FIXIT-L shell --pause should stop before loading config so Lua state
// can be examined and modified.
FatalError("can't init overrides: %s\n", lua_tostring(L, -1));
}
-
//-------------------------------------------------------------------------
// public methods
//-------------------------------------------------------------------------
load_string(lua, ModuleManager::get_lua_finalize());
clear_whitelist();
+
+ if ( SnortConfig::get_conf()->dump_config() )
+ {
+ sort_config();
+ print_config_text();
+ clear_config_tree();
+ }
+
current_shells.pop();
set_default_policy(sc);
//-------------------------------------------------------------------------
// private methods
//-------------------------------------------------------------------------
+void Shell::sort_config()
+{
+ config_trees.sort([](const BaseConfigNode* l, const BaseConfigNode* r)
+ { return l->get_name() < r->get_name(); });
+}
+
+void Shell::print_config_text() const
+{
+ std::string output("consolidated config for ");
+ output += file;
+ LogConfig("%s\n", output.c_str());
+
+ for ( const auto config_tree: config_trees )
+ ConfigTextFormat::print(config_tree, config_tree->get_name());
+}
+
+void Shell::clear_config_tree()
+{
+ for ( auto config_tree: config_trees )
+ BaseConfigNode::clear_nodes(config_tree);
+
+ config_trees.clear();
+}
void Shell::print_whitelist() const
{
// Shell encapsulates a Lua state. There is one for each policy file.
+#include <list>
#include <set>
#include <stack>
#include <string>
+#include "framework/parameter.h"
+
struct lua_State;
+class BaseConfigNode;
+
namespace snort
{
struct SnortConfig;
+class Value;
}
class Shell
static bool is_whitelisted(const std::string& key);
static void whitelist_append(const char* keyword, bool is_prefix);
+ static void config_open_table(bool is_root_node, bool is_list, int idx,
+ const std::string& table_name, const snort::Parameter* p);
+ static void set_config_value(const snort::Value& value);
+ static void add_config_child_node(const std::string& node_name, snort::Parameter::Type type);
+ static void update_current_config_node(const std::string& node_name = "");
+ static void config_close_table();
+
+private:
+ static void add_config_root_node(const std::string& root_name, snort::Parameter::Type type);
+
private:
[[noreturn]] static int panic(lua_State*);
static Shell* get_current_shell();
void print_whitelist() const;
void whitelist_update(const char* keyword, bool is_prefix);
+ void sort_config();
+ void print_config_text() const;
+ void clear_config_tree();
+
private:
bool loaded;
bool bootstrapped = false;
Whitelist whitelist;
Whitelist internal_whitelist;
Whitelist whitelist_prefixes;
+ std::list<BaseConfigNode*> config_trees;
+ BaseConfigNode* s_current_node = nullptr;
};
#endif
SFDAQ::term();
FileService::close();
- if ( !SnortConfig::get_conf()->test_mode() ) // FIXIT-M ideally the check is in one place
+ if ( !SnortConfig::get_conf()->validation_mode() ) // FIXIT-M ideally the check is in one place
PrintStatistics();
CloseLogger();
RUN_FLAG__IP_FRAGS_ONLY = 0x08000000,
RUN_FLAG__DUMP_RULE_STATE = 0x10000000,
+ RUN_FLAG__DUMP_CONFIG = 0x20000000,
};
enum OutputFlag
{ return address_anomaly_check_enabled; }
// mode related
+ bool dump_config() const
+ { return run_flags & RUN_FLAG__DUMP_CONFIG; }
+
bool dump_msg_map() const
{ return run_flags & RUN_FLAG__DUMP_MSG_MAP; }
bool test_mode() const
{ return run_flags & RUN_FLAG__TEST; }
+ bool validation_mode() const
+ { return test_mode() or dump_config(); }
+
bool mem_check() const
- { return run_flags & RUN_FLAG__MEM_CHECK; }
+ { return (run_flags & RUN_FLAG__MEM_CHECK) and !dump_config(); }
bool daemon_mode() const
- { return run_flags & RUN_FLAG__DAEMON; }
+ { return (run_flags & RUN_FLAG__DAEMON) and !dump_config(); }
bool read_mode() const
{ return run_flags & RUN_FLAG__READ; }
{ "--dump-builtin-rules", Parameter::PT_STRING, "(optional)", nullptr,
"[<module prefix>] output stub rules for selected modules" },
+ { "--dump-config-text", Parameter::PT_IMPLIED, nullptr, nullptr,
+ "dump config in text format" },
+
// FIXIT-L add --list-dynamic-rules like --list-builtin-rules
{ "--dump-dynamic-rules", Parameter::PT_IMPLIED, nullptr, nullptr,
"output stub rules for all loaded rules libraries" },
{ "--x2s", Parameter::PT_STRING, nullptr, nullptr,
"output ASCII string for given byte code (see also --x2c)" },
- { "--trace", Parameter::PT_IMPLIED, nullptr, nullptr,
- "turn on main loop debug trace" },
-
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
else if ( v.is("--dump-builtin-rules") )
dump_builtin_rules(sc, v.get_string());
+ else if ( v.is("--dump-config-text") )
+ {
+ sc->run_flags |= RUN_FLAG__DUMP_CONFIG;
+ sc->set_quiet(true);
+ }
+
else if ( v.is("--dump-dynamic-rules") )
dump_dynamic_rules(sc, v.get_string());
//-------------------------------------------------------------------------
// helper functions
//-------------------------------------------------------------------------
+static std::string get_sub_table(const std::string& fqn)
+{
+ auto pos = fqn.find_last_of(".");
+ if ( pos != std::string::npos )
+ return fqn.substr(pos + 1);
+ else
+ return fqn;
+}
static void set_type(string& fqn)
{
static bool set_param(Module* mod, const char* fqn, Value& val)
{
+ if ( s_config->dump_config() )
+ Shell::set_config_value(val);
+
if ( !mod->verified_set(fqn, val, s_config) )
{
ParseError("%s is invalid", fqn);
{
const Parameter* table_item_params = reinterpret_cast<const Parameter*>(p->range);
+ if ( s_config->dump_config() )
+ Shell::add_config_child_node(get_sub_table(fqn), p->type);
+
if ( !begin(m, table_item_params, fqn.c_str(), idx, depth+1) )
return false;
}
}
++p;
}
+ if ( s_config->dump_config() )
+ Shell::update_current_config_node();
return true;
}
s_current = unique_key;
}
+ if ( s_config->dump_config() )
+ {
+ std::string table_name = get_sub_table(s);
+ bool is_top_level = false;
+ if ( top_level(s) && !idx )
+ {
+ table_name = s_current;
+ is_top_level = true;
+ }
+
+ Shell::config_open_table(is_top_level, m->is_list(), idx, table_name, p);
+ }
+
if ( !begin(m, p, s, idx, 0) )
{
ParseError("can't open %s", m->get_name());
s_name.clear();
s_type.clear();
}
+
+ if ( s_config->dump_config() )
+ Shell::config_close_table();
}
SO_PUBLIC bool set_bool(const char* fqn, bool b)
}
}
+ std::sort(modules_params.begin(), modules_params.end(),
+ [](const Parameter& l, const Parameter& r) { return (strcmp(l.name, r.name) < 0); });
+
modules_params.emplace_back(nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr);
const static Parameter trace_constraints_params[] =
if ( !strcmp(fqn, "trace") )
{
assert(trace_parser);
- switch ( log_output_type )
- {
- case OUTPUT_TYPE_STDOUT:
- trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory();
- break;
- case OUTPUT_TYPE_SYSLOG:
- trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory();
- break;
- default:
- break;
- }
- // "output=syslog" config override case
- // do not closelog() here since it will be closed in Snort::clean_exit()
- if ( !sc->log_syslog() and log_output_type == OUTPUT_TYPE_SYSLOG
- and !local_syslog )
+ if ( sc->dump_config() )
+ trace_parser->clear_traces();
+ else
{
- local_syslog = true;
- openlog("snort", LOG_PID | LOG_CONS, LOG_DAEMON);
- }
+ switch ( log_output_type )
+ {
+ case OUTPUT_TYPE_STDOUT:
+ trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory();
+ break;
+ case OUTPUT_TYPE_SYSLOG:
+ trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory();
+ break;
+ default:
+ break;
+ }
- trace_parser->finalize_constraints();
+ // "output=syslog" config override case
+ // do not closelog() here since it will be closed in Snort::clean_exit()
+ if ( !sc->log_syslog() and log_output_type == OUTPUT_TYPE_SYSLOG
+ and !local_syslog )
+ {
+ local_syslog = true;
+ openlog("snort", LOG_PID | LOG_CONS, LOG_DAEMON);
+ }
+
+ trace_parser->finalize_constraints();
+ }
delete trace_parser;
trace_parser = nullptr;