--dump-config-text, --dump-config=all, --dump-config=top.
They are described in detail below.
+The --gen-dump-config <file> option enables Snort to generate a dump
+configuration file with a timestamp and config generation ID
+during startup and reload.
+
The simple configuration is used in examples.
The output contains applied configurations (defaults and configured).
To simplify the output we show a brief list of default options.
"show_rebuilt_packets": true
},
}
+
+==== Configuration Dump Generation During Startup and Reload
+
+The --gen-dump-config <file> option dumps configuration in a file in
+JSON format, similar to the --dump-config=all option. It creates a file
+during startup and reload, with the specified name, timestamp and config
+generation ID appended.
+
+Example: snort -c snort.lua --gen-dump-config dump_output
+
+After execution, the "dump_output_<timestamp>_<config_gen_id>" file
+will be generated.
#include "config_data.h"
-void ConfigOutput::dump_config(ConfigData& config_data)
+void ConfigOutput::dump_config(ConfigData& config_data, bool to_clear)
{
config_data.sort();
dump(config_data);
- config_data.clear();
+ if (to_clear)
+ config_data.clear();
}
ConfigOutput() = default;
virtual ~ConfigOutput() = default;
- void dump_config(ConfigData&);
+ void dump_config(ConfigData&, bool to_clear = true);
private:
virtual void dump(const ConfigData&) = 0;
#include "config.h"
#endif
+#include <cstring>
+#include <cerrno>
+
+#include <log/messages.h>
+
#include "json_config_output.h"
#include "config_data.h"
dump_value(json, node_name_cstr, node);
}
-JsonAllConfigOutput::JsonAllConfigOutput() :
- ConfigOutput(), json(std::cout)
-{ json.open_array(); }
+JsonAllConfigOutput::JsonAllConfigOutput(const char *file_name) :
+ ConfigOutput(), file(nullptr), json(nullptr)
+{
+ if (file_name)
+ file = new std::fstream(std::string(file_name), std::ios::out);
+
+ if (file and !file->is_open())
+ {
+ ErrorMessage("File %s is not opened: %s\n", file_name, std::strerror(errno));
+ delete file;
+ file = nullptr;
+ return;
+ }
+
+ json = file ? new snort::JsonStream(*file) : new snort::JsonStream(std::cout);
+
+ json->open_array();
+}
JsonAllConfigOutput::~JsonAllConfigOutput()
-{ json.close_array(); }
+{
+ if (json)
+ {
+ json->close_array();
+ delete json;
+ }
+ if (file)
+ delete file;
+}
void JsonAllConfigOutput::dump(const ConfigData& config_data)
{
- json.open();
- json.put("filename", config_data.file_name);
- json.open("config");
+ if (!json)
+ return;
+ json->open();
+ json->put("filename", config_data.file_name);
+ json->open("config");
for ( const auto config_tree: config_data.config_trees )
- dump_tree(json, config_tree);
+ dump_tree(*json, config_tree);
- json.close();
- json.close();
+ json->close();
+ json->close();
}
void JsonTopConfigOutput::dump(const ConfigData& config_data)
#ifndef JSON_CONFIG_OUTPUT_H
#define JSON_CONFIG_OUTPUT_H
+#include <fstream>
+
#include "config_output.h"
#include "helpers/json_stream.h"
class JsonAllConfigOutput : public ConfigOutput
{
public:
- JsonAllConfigOutput();
+ JsonAllConfigOutput(const char *file_name = nullptr);
~JsonAllConfigOutput() override;
private:
void dump(const ConfigData&) override;
private:
- snort::JsonStream json;
+ std::fstream *file;
+ snort::JsonStream *json;
};
class JsonTopConfigOutput : public ConfigOutput
void Shell::config_open_table(bool is_root_node, bool is_list, int idx,
const std::string& table_name, const Parameter* p)
{
- if ( !s_config_output )
+ if ( !dump_enabled() )
return;
Parameter::Type node_type = is_list ? Parameter::PT_LIST : Parameter::PT_TABLE;
void Shell::add_config_child_node(const std::string& node_name, snort::Parameter::Type type,
bool is_root_list_item)
{
- if ( !s_config_output || !s_current_node )
+ if ( !dump_enabled() or !s_current_node )
return;
// element of the top-level list is anonymous
void Shell::add_config_root_node(const std::string& root_name, snort::Parameter::Type node_type)
{
- if ( !s_config_output )
+ if ( !dump_enabled() )
return;
Shell* sh = Shell::get_current_shell();
return;
sh->s_current_node = new TreeConfigNode(nullptr, root_name, node_type);
- sh->config_data.add_config_tree(sh->s_current_node);
+ sh->config_data->add_config_tree(sh->s_current_node);
}
void Shell::update_current_config_node(const std::string& node_name)
{
- if ( !s_config_output || !s_current_node )
+ if ( !dump_enabled() or !s_current_node )
return;
// node has been added during setting default options
void Shell::config_close_table()
{
- if ( !s_config_output )
+ if ( !dump_enabled() )
return;
if ( !s_close_table )
void Shell::set_config_value(const std::string& fqn, const snort::Value& value)
{
- if ( !s_config_output || !s_current_node )
+ if ( !dump_enabled() or !s_current_node )
return;
// don't give names to list elements
return true;
}
+bool Shell::dump_enabled()
+{
+ return s_config_output or snort::SnortConfig::get_conf()->gen_dump_config();
+}
+
bool Shell::load_config(const char* file, bool load_in_sandbox)
{
if ( load_in_sandbox )
//-------------------------------------------------------------------------
Shell::Shell(const char* s, bool load_defaults) :
- config_data(s), load_defaults(load_defaults)
+ config_data(new ConfigData(s)), load_defaults(load_defaults)
{
// FIXIT-M should wrap in Lua::State
lua = luaL_newstate();
Shell::~Shell()
{
lua_close(lua);
+ if (config_data)
+ delete config_data;
}
void Shell::set_file(const char* s)
{
file = s;
- config_data.file_name = file;
+ config_data->file_name = file;
}
void Shell::set_overrides(const char* s)
overrides += sh->overrides;
}
-bool Shell::configure(SnortConfig* sc, bool is_root)
+bool Shell::configure(SnortConfig* sc, bool is_root, std::list<ConfigData*> *config_data_to_dump)
{
assert(file.size());
ModuleManager::set_config(sc);
auto config_output = Shell::get_current_shell()->s_config_output;
if ( config_output )
- config_output->dump_config(config_data);
+ {
+ bool to_clear = sc->gen_dump_config() ? false : true;
+ config_output->dump_config(*config_data, to_clear);
+ }
+
+ if ( sc->gen_dump_config() )
+ {
+ config_data_to_dump->push_back(config_data);
+ config_data = nullptr;
+ }
current_shells.pop();
void set_overrides(const char*);
void set_overrides(Shell*);
- bool configure(snort::SnortConfig*, bool is_root = false);
+ bool configure(snort::SnortConfig*, bool is_root = false, std::list<ConfigData*>* = nullptr);
void install(const char*, const struct luaL_Reg*);
void execute(const char*, std::string&);
bool set_sandbox_env();
bool load_string(const char* s, bool load_in_sandbox, const char* message);
bool load_config(const char* file, bool load_in_sandbox);
+ static bool dump_enabled();
private:
bool loaded;
Allowlist allowlist;
Allowlist internal_allowlist;
Allowlist allowlist_prefixes;
- ConfigData config_data;
+ ConfigData* config_data;
uint64_t network_user_policy_id = UNDEFINED_NETWORK_USER_POLICY_ID;
bool load_defaults;
};
#include "snort_config.h"
+#include <atomic>
#include <grp.h>
#include <mutex>
#include <pwd.h>
#include "detection/detection_engine.h"
#include "detection/fp_config.h"
#include "detection/fp_create.h"
+#include "dump_config/config_data.h"
#include "dump_config/json_config_output.h"
#include "dump_config/text_config_output.h"
#include "events/event_queue.h"
}
}
+static const int threads_max = 16;
+static std::atomic<int> threads_cnt = 0;
+
+static void generate_config_dump(std::list<ConfigData*> *config_data, time_t timestamp,
+ unsigned int reload_id, std::string file_name)
+{
+ ++threads_cnt;
+
+ file_name += "_";
+ file_name += std::to_string(timestamp);
+ file_name += "_";
+ file_name += std::to_string(reload_id);
+
+ ConfigOutput* o = new JsonAllConfigOutput(file_name.c_str());
+ for (auto i : *config_data)
+ {
+ o->dump_config(*i);
+ delete i;
+ }
+ delete o;
+ delete config_data;
+
+ --threads_cnt;
+}
+
//-------------------------------------------------------------------------
// public methods
//-------------------------------------------------------------------------
InspectorManager::delete_config(this);
ActionManager::delete_config(this);
+ if (config_dumper)
+ {
+ config_dumper->join();
+ delete config_dumper;
+ }
+
delete[] state;
delete thread_config;
delete trace_config;
reload_id = ++reload_id_tracker;
}
+void SnortConfig::generate_dump(std::list<ConfigData*> *config_data_to_dump)
+{
+ if (threads_cnt < threads_max)
+ {
+ config_dumper = new std::thread(generate_config_dump, config_data_to_dump,
+ time(nullptr), SnortConfig::get_conf()->get_reload_id(), dump_config_file);
+ }
+ else
+ {
+ delete config_data_to_dump;
+ }
+}
+
void SnortConfig::cleanup_fatal_error()
{
// FIXIT-L need a generic way to manage type other threads
#include <list>
#include <mutex>
+#include <thread>
#include <unordered_map>
#include <vector>
RUN_FLAG__TRACK_ON_SYN = 0x00100000,
RUN_FLAG__IP_FRAGS_ONLY = 0x00200000,
RUN_FLAG__TEST_FEATURES = 0x00400000,
+ RUN_FLAG__GEN_DUMP_CONFIG = 0x00800000,
#ifdef SHELL
RUN_FLAG__SHELL = 0x01000000,
class FastPatternConfig;
class RuleStateMap;
class TraceConfig;
+class ConfigData;
struct srmm_table_t;
struct sopg_table_t;
SoRules* so_rules = nullptr;
DumpConfigType dump_config_type = DUMP_CONFIG_NONE;
+
+ std::string dump_config_file;
+ std::thread* config_dumper = nullptr;
private:
std::list<ReloadResourceTuner*> reload_tuners;
static std::mutex reload_id_mutex;
bool test_features() const
{ return run_flags & RUN_FLAG__TEST_FEATURES; }
+ bool gen_dump_config() const
+ { return run_flags & RUN_FLAG__GEN_DUMP_CONFIG; }
+
// other stuff
uint8_t min_ttl() const
{ return get_network_policy()->min_ttl; }
unsigned get_reload_id() const
{ return reload_id; }
+ void generate_dump(std::list<ConfigData*>*);
+
bool get_default_rule_state() const;
ConfigOutput* create_config_output() const;
{ "--enable-test-features", Parameter::PT_IMPLIED, nullptr, nullptr,
"enable features used in testing" },
+ { "--gen-dump-config", Parameter::PT_STRING, nullptr, nullptr,
+ "<file> dump configuration to <file_timestamp> during startup and configuration reload" },
+
{ "--gen-msg-map", Parameter::PT_IMPLIED, nullptr, nullptr,
"dump configured rules in gen-msg.map format for use by other tools" },
sc->run_flags |= RUN_FLAG__TEST_FEATURES;
SfIp::test_features = true;
}
-
+ else if ( is(v, "--gen-dump-config") )
+ {
+ sc->run_flags |= RUN_FLAG__GEN_DUMP_CONFIG;
+ sc->dump_config_file = v.get_string();
+ }
else if ( is(v, "--gen-msg-map") )
{
sc->run_flags |= (RUN_FLAG__DUMP_MSG_MAP | RUN_FLAG__TEST);
s_current = unique_key;
}
- if ( s_config->dump_config_mode() )
+ if ( s_config->dump_config_mode() or s_config->gen_dump_config() )
{
std::string table_name = get_sub_table(s);
bool is_top_level = false;
return ordered_list;
}
-static bool parse_file(SnortConfig* sc, Shell* sh, bool is_root)
+static bool parse_file(SnortConfig* sc, Shell* sh, bool is_root, std::list<ConfigData*> *config_data_to_dump)
{
const char* fname = sh->get_file();
if ( !fname || !*fname )
return false;
- bool success = sh->configure(sc, is_root);
+ bool success = sh->configure(sc, is_root, config_data_to_dump);
return success;
}
sc->output_flags = cmd_line_conf->output_flags;
sc->tweaks = cmd_line_conf->tweaks;
sc->dump_config_type = cmd_line_conf->dump_config_type;
+ sc->dump_config_file = cmd_line_conf->dump_config_file;
if ( !fname )
fname = get_snort_conf();
bool parse_file_failed = false;
auto output = current_conf->create_config_output();
bool is_top = current_conf->dump_config_type == DUMP_CONFIG_JSON_TOP;
+ std::list<ConfigData*> *config_data_to_dump = new std::list<ConfigData*>;
+
for ( unsigned i = 0; true; i++ )
{
sh = sc->policy_map->get_shell(i);
if ( !sh )
+ {
+ if (!sc->gen_dump_config())
+ break;
+ sc->generate_dump(config_data_to_dump);
+ config_data_to_dump = nullptr;
break;
+ }
auto shell_output = ( i != 0 && is_top ) ? nullptr : output;
Shell::set_config_output(shell_output);
set_policies(sc, sh);
- if (!parse_file(sc, sh, (i == 0)))
+ if (!parse_file(sc, sh, (i == 0), config_data_to_dump))
{
parse_file_failed = true;
break;
}
}
+ if (config_data_to_dump)
+ {
+ for (auto i : *config_data_to_dump)
+ delete i;
+ delete config_data_to_dump;
+ }
+
delete output;
Shell::clear_config_output();