diff --git a/.gitignore b/.gitignore index 82dd8837c..e38087f8f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ docs/html-documentation-generated* integration_tests/external golang/pkg/client/version.go docs/internals/html -appimage/*.AppImage \ No newline at end of file +appimage/*.AppImage +/test-config*.yaml diff --git a/RFCs/2021-04-16-75-taps.md b/RFCs/2021-04-16-75-taps.md index 4f4b810e1..dc31338b0 100644 --- a/RFCs/2021-04-16-75-taps.md +++ b/RFCs/2021-04-16-75-taps.md @@ -24,18 +24,18 @@ visor: taps: # a pcap tap which uses eth0 and is referenced by the identifier "anycast" anycast: - type: pcap + input_type: pcap config: iface: eth0 # an sflow tap which listens on the given IP and port, referenced by the identifier "pop_switch" pop_switch: - type: sflow + input_type: sflow config: port: 6343 bind: 192.168.1.1 # a dnstap tap which gets its stream from the given socket, named "trex_tap" trex_tap: - type: dnstap + input_type: dnstap config: socket: /var/dns.sock ``` diff --git a/cmd/pktvisor-pcap/main.cpp b/cmd/pktvisor-pcap/main.cpp index 005cd2ae9..f1652e629 100644 --- a/cmd/pktvisor-pcap/main.cpp +++ b/cmd/pktvisor-pcap/main.cpp @@ -9,12 +9,7 @@ #include -#include "HandlerManager.h" -#include "HandlerModulePlugin.h" -#include "InputModulePlugin.h" -#include "InputStreamManager.h" -#include -#include +#include "CoreManagers.h" #include #include "handlers/static_plugins.h" @@ -60,10 +55,6 @@ void signal_handler(int signal) using namespace visor; -typedef Corrade::PluginManager::Manager InputPluginRegistry; -typedef Corrade::PluginManager::Manager HandlerPluginRegistry; -typedef Corrade::Containers::Pointer InputPluginPtr; -typedef Corrade::Containers::Pointer HandlerPluginPtr; void initialize_geo(const docopt::value &city, const docopt::value &asn) { @@ -84,48 +75,12 @@ int main(int argc, char *argv[]) true, // show help if requested VISOR_VERSION); // version string - auto logger = spdlog::stderr_color_mt("pktvisor"); + auto logger = spdlog::stderr_color_mt("visor"); if (args["-v"].asBool()) { logger->set_level(spdlog::level::debug); } - // inputs - InputPluginRegistry input_registry; - auto input_manager = std::make_unique(); - std::vector input_plugins; - - // initialize input plugins - for (auto &s : input_registry.pluginList()) { - InputPluginPtr mod = input_registry.instantiate(s); - logger->info("Load input plugin: {} {}", mod->name(), mod->pluginInterface()); - mod->init_module(input_manager.get()); - input_plugins.emplace_back(std::move(mod)); - } - - // handlers - HandlerPluginRegistry handler_registry; - auto handler_manager = std::make_unique(); - std::vector handler_plugins; - - // initialize handler plugins - for (auto &s : handler_registry.pluginList()) { - HandlerPluginPtr mod = handler_registry.instantiate(s); - logger->info("Load handler plugin: {} {}", mod->name(), mod->pluginInterface()); - mod->init_module(input_manager.get(), handler_manager.get()); - handler_plugins.emplace_back(std::move(mod)); - } - - shutdown_handler = [&]([[maybe_unused]] int signal) { - // gracefully close all inputs and handlers - auto [input_modules, im_lock] = input_manager->module_get_all_locked(); - for (auto &[name, mod] : input_modules) { - mod->stop(); - } - auto [handler_modules, hm_lock] = handler_manager->module_get_all_locked(); - for (auto &[name, mod] : handler_modules) { - mod->stop(); - } - }; + CoreManagers mgrs(nullptr); std::signal(SIGINT, signal_handler); std::signal(SIGTERM, signal_handler); @@ -164,8 +119,8 @@ int main(int argc, char *argv[]) input_stream->info_json(j["info"]); logger->info("{}", j.dump(4)); - input_manager->module_add(std::move(input_stream), false); - auto [input_stream_, stream_mgr_lock] = input_manager->module_get_locked("pcap"); + mgrs.input_manager()->module_add(std::move(input_stream)); + auto [input_stream_, stream_mgr_lock] = mgrs.input_manager()->module_get_locked("pcap"); stream_mgr_lock.unlock(); auto pcap_stream = dynamic_cast(input_stream_); @@ -173,8 +128,9 @@ int main(int argc, char *argv[]) { auto handler_module = std::make_unique("net", pcap_stream, periods, sample_rate); handler_module->config_set("recorded_stream", true); - handler_manager->module_add(std::move(handler_module)); - auto [handler, handler_mgr_lock] = handler_manager->module_get_locked("net"); + handler_module->start(); + mgrs.handler_manager()->module_add(std::move(handler_module)); + auto [handler, handler_mgr_lock] = mgrs.handler_manager()->module_get_locked("net"); handler_mgr_lock.unlock(); net_handler = dynamic_cast(handler); } @@ -182,8 +138,9 @@ int main(int argc, char *argv[]) { auto handler_module = std::make_unique("dns", pcap_stream, periods, sample_rate); handler_module->config_set("recorded_stream", true); - handler_manager->module_add(std::move(handler_module)); - auto [handler, handler_mgr_lock] = handler_manager->module_get_locked("dns"); + handler_module->start(); + mgrs.handler_manager()->module_add(std::move(handler_module)); + auto [handler, handler_mgr_lock] = mgrs.handler_manager()->module_get_locked("dns"); handler_mgr_lock.unlock(); dns_handler = dynamic_cast(handler); } diff --git a/cmd/pktvisord/main.cpp b/cmd/pktvisord/main.cpp index f8c53680c..e999f9033 100644 --- a/cmd/pktvisord/main.cpp +++ b/cmd/pktvisord/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "GeoDB.h" #include "handlers/dns/DnsStreamHandler.h" @@ -47,6 +48,8 @@ static const char USAGE[] = --version Show version --geo-city FILE GeoLite2 City database to use for IP to Geo mapping --geo-asn FILE GeoLite2 ASN database to use for IP to ASN mapping + Configuration: + --config FILE Use specified YAML configuration to configure options, Taps, and Collection Policies Logging Options: --log-file FILE Log to the given output file name --syslog Log to syslog @@ -165,15 +168,15 @@ int main(int argc, char *argv[]) std::shared_ptr logger; if (args["--log-file"]) { try { - logger = spdlog::basic_logger_mt("pktvisor", args["--log-file"].asString()); + logger = spdlog::basic_logger_mt("visor", args["--log-file"].asString()); } catch (const spdlog::spdlog_ex &ex) { std::cerr << "Log init failed: " << ex.what() << std::endl; exit(EXIT_FAILURE); } } else if (args["--syslog"].asBool()) { - logger = spdlog::syslog_logger_mt("pktvisor", "pktvisord", LOG_PID); + logger = spdlog::syslog_logger_mt("visor", "pktvisord", LOG_PID); } else { - logger = spdlog::stdout_color_mt("pktvisor"); + logger = spdlog::stdout_color_mt("visor"); } if (args["-v"].asBool()) { logger->set_level(spdlog::level::debug); @@ -186,7 +189,7 @@ int main(int argc, char *argv[]) prom_config.instance = args["--prom-instance"].asString(); } } - CoreServer svr(!args["--admin-api"].asBool(), logger, prom_config); + CoreServer svr(!args["--admin-api"].asBool(), prom_config); svr.set_http_logger([&logger](const auto &req, const auto &res) { logger->info("REQUEST: {} {} {}", req.method, req.path, res.status); if (res.status == 500) { @@ -194,6 +197,39 @@ int main(int argc, char *argv[]) } }); + // local config file + if (args["--config"]) { + logger->info("loading config file: {}", args["--config"].asString()); + YAML::Node config_file; + // look for local options + try { + config_file = YAML::LoadFile(args["--config"].asString()); + + if (!config_file.IsMap() || !config_file["visor"]) { + throw std::runtime_error("invalid schema"); + } + if (!config_file["version"] || !config_file["version"].IsScalar() || config_file["version"].as() != "1.0") { + throw std::runtime_error("missing or unsupported version"); + } + + if (config_file["visor"]["config"] && config_file["visor"]["config"].IsMap()) { + // todo more config items + auto config = config_file["visor"]["config"]; + if (config["verbose"] && config["verbose"].as()) { + logger->set_level(spdlog::level::debug); + } + } + + // then pass to CoreManagers + svr.mgrs()->configure_from_file(args["--config"].asString()); + + } catch (std::runtime_error &e) { + logger->error("configuration error: {}", e.what()); + exit(EXIT_FAILURE); + } + + } + shutdown_handler = [&]([[maybe_unused]] int signal) { logger->info("Shutting down"); svr.stop(); @@ -261,9 +297,10 @@ int main(int argc, char *argv[]) input_stream->config_set("bpf", bpf); input_stream->config_set("host_spec", host_spec); - auto input_manager = svr.input_manager(); - auto handler_manager = svr.handler_manager(); + auto input_manager = svr.mgrs()->input_manager(); + auto handler_manager = svr.mgrs()->handler_manager(); + input_stream->start(); input_manager->module_add(std::move(input_stream)); auto [input_stream_, stream_mgr_lock] = input_manager->module_get_locked("pcap"); stream_mgr_lock.unlock(); @@ -271,10 +308,12 @@ int main(int argc, char *argv[]) { auto handler_module = std::make_unique("net", pcap_stream, periods, sample_rate); + handler_module->start(); handler_manager->module_add(std::move(handler_module)); } { auto handler_module = std::make_unique("dns", pcap_stream, periods, sample_rate); + handler_module->start(); handler_manager->module_add(std::move(handler_module)); } diff --git a/conanfile.txt b/conanfile.txt index dc69654b9..ed109fc10 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -7,6 +7,8 @@ cpp-httplib/0.8.0 corrade/2020.06 pcapplusplus/ns1-dev json-schema-validator/2.1.0 +yaml-cpp/0.6.3 +fmt/7.1.3 [build_requires] benchmark/1.5.2 diff --git a/src/AbstractManager.h b/src/AbstractManager.h index e45f690b0..baee369db 100644 --- a/src/AbstractManager.h +++ b/src/AbstractManager.h @@ -32,6 +32,10 @@ class AbstractManager { } + virtual ~AbstractManager() + { + } + auto module_get_all_locked() { struct retVals { @@ -42,16 +46,12 @@ class AbstractManager return retVals{_map, std::move(lock)}; } - // atomically ensure module starts before arriving in registry, if requested - virtual void module_add(std::unique_ptr &&m, bool start = true) + virtual void module_add(std::unique_ptr &&m) { std::unique_lock lock(_map_mutex); if (_map.count(m->name())) { throw std::runtime_error("module name already exists"); } - if (start) { - m->start(); - } _map.emplace(m->name(), std::move(m)); } @@ -64,7 +64,7 @@ class AbstractManager throw std::runtime_error("module name does not exist"); } struct retVals { - ModuleType *map; + ModuleType *module; std::unique_lock lock; }; return retVals{_map[name].get(), std::move(lock)}; @@ -76,7 +76,6 @@ class AbstractManager if (_map.count(name) == 0) { throw std::runtime_error("module name does not exist"); } - _map[name]->stop(); _map.erase(name); } diff --git a/src/AbstractModule.h b/src/AbstractModule.h index 6423f3483..8f644ac1f 100644 --- a/src/AbstractModule.h +++ b/src/AbstractModule.h @@ -4,9 +4,11 @@ #pragma once +#include "Configurable.h" #include #include #include +#include #include #include #include @@ -16,23 +18,10 @@ namespace visor { using json = nlohmann::json; -class ConfigException : public std::runtime_error +class AbstractModule : public Configurable { -public: - explicit ConfigException(const std::string &msg) - : std::runtime_error(msg) - { - } -}; - -class AbstractModule -{ -private: - std::unordered_map> _config; - mutable std::shared_mutex _config_mutex; protected: - std::atomic_bool _running = false; /** * the module instance identifier: unique name associated with this instance @@ -42,7 +31,6 @@ class AbstractModule void _common_info_json(json &j) const { j["module"]["name"] = _name; - j["module"]["running"] = _running.load(); config_json(j["module"]["config"]); } @@ -50,74 +38,54 @@ class AbstractModule AbstractModule(const std::string &name) : _name(name) { + if (!std::regex_match(name, std::regex("[a-zA-Z_][a-zA-Z0-9_-]*"))) { + throw std::runtime_error("invalid module name: " + name); + } } virtual ~AbstractModule(){}; - virtual void start() = 0; - virtual void stop() = 0; - virtual void info_json(json &j) const = 0; const std::string &name() const { return _name; } +}; - /** - * the module schema key: the same for all instances of this module - * used in schemas such as json - */ - virtual std::string schema_key() const = 0; +class AbstractRunnableModule : public AbstractModule +{ - bool running() const - { - return _running; - } +protected: + std::atomic_bool _running = false; - template - auto config_get(const std::string &key) + void _common_info_json(json &j) const { - std::shared_lock lock(_config_mutex); - if (_config.count(key) == 0) { - throw ConfigException("missing key: " + key); - } - auto val = std::get_if(&_config[key]); - if (!val) { - throw ConfigException("wrong type for key: " + key); - } - return *val; + j["module"]["name"] = _name; + j["module"]["running"] = _running.load(); + config_json(j["module"]["config"]); } - template - void config_set(const std::string &key, const T &val) +public: + AbstractRunnableModule(const std::string &name) + : AbstractModule(name) { - std::unique_lock lock(_config_mutex); - _config[key] = val; } - // specialize to ensure a string literal is interpreted as a std::string - void config_set(const std::string &key, const char *val) - { - std::unique_lock lock(_config_mutex); - _config[key] = std::string(val); - } + virtual ~AbstractRunnableModule(){}; - bool config_exists(const std::string &name) const - { - std::shared_lock lock(_config_mutex); - return _config.count(name) == 1; - } + virtual void start() = 0; + virtual void stop() = 0; - void config_json(json &j) const + /** + * the module schema key: the same for all instances of this module + * used in schemas such as json + */ + virtual std::string schema_key() const = 0; + + bool running() const { - std::shared_lock lock(_config_mutex); - for (const auto &[key, value] : _config) { - std::visit([&j, key = key](auto &&arg) { - j[key] = arg; - }, - value); - } + return _running; } }; diff --git a/src/AbstractPlugin.h b/src/AbstractPlugin.h index a292b2f42..66f184394 100644 --- a/src/AbstractPlugin.h +++ b/src/AbstractPlugin.h @@ -32,7 +32,7 @@ class AbstractPlugin : public Corrade::PluginManager::AbstractPlugin protected: void _check_schema(json obj, SchemaMap &required); void _check_schema(json obj, SchemaMap &required, SchemaMap &optional); - virtual void _setup_routes(HttpServer &svr) = 0; + virtual void _setup_routes(HttpServer *svr) = 0; public: static std::vector pluginSearchPaths() @@ -45,7 +45,6 @@ class AbstractPlugin : public Corrade::PluginManager::AbstractPlugin { } - virtual std::string name() const = 0; }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b5fce816d..3bc7092c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,9 @@ add_library(visor-core HandlerModulePlugin.cpp GeoDB.cpp CoreServer.cpp - Metrics.cpp Metrics.h) + CoreManagers.cpp + Metrics.cpp + Taps.cpp) add_library(Visor::Core ALIAS visor-core) target_include_directories(visor-core @@ -30,6 +32,7 @@ target_link_libraries(visor-core ${CONAN_LIBS_CORRADE} ${CONAN_LIBS_SPDLOG} ${CONAN_LIBS_FMT} + ${CONAN_LIBS_YAML-CPP} ${VISOR_STATIC_PLUGINS} ) @@ -46,13 +49,14 @@ add_executable(unit-tests-vizor-core tests/test_sketches.cpp tests/test_metrics.cpp tests/test_geoip.cpp + tests/test_taps.cpp ) target_include_directories(unit-tests-vizor-core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(unit-tests-vizor-core PRIVATE Visor::Core) +target_link_libraries(unit-tests-vizor-core PRIVATE Visor::Core ${VISOR_STATIC_PLUGINS}) add_test(NAME unit-tests-vizor-core WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src diff --git a/src/Configurable.h b/src/Configurable.h new file mode 100644 index 000000000..1983af8eb --- /dev/null +++ b/src/Configurable.h @@ -0,0 +1,108 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace visor { + +using json = nlohmann::json; + +class ConfigException : public std::runtime_error +{ +public: + explicit ConfigException(const std::string &msg) + : std::runtime_error(msg) + { + } +}; + +class Configurable +{ +private: + std::unordered_map> _config; + mutable std::shared_mutex _config_mutex; + +public: + template + auto config_get(const std::string &key) + { + std::shared_lock lock(_config_mutex); + if (_config.count(key) == 0) { + throw ConfigException(fmt::format("missing key: {}", key)); + } + auto val = std::get_if(&_config[key]); + if (!val) { + throw ConfigException(fmt::format("wrong type for key: {}", key)); + } + return *val; + } + + template + void config_set(const std::string &key, const T &val) + { + std::unique_lock lock(_config_mutex); + _config[key] = val; + } + + // specialize to ensure a string literal is interpreted as a std::string + void config_set(const std::string &key, const char *val) + { + std::unique_lock lock(_config_mutex); + _config[key] = std::string(val); + } + + bool config_exists(const std::string &name) const + { + std::shared_lock lock(_config_mutex); + return _config.count(name) == 1; + } + + void config_json(json &j) const + { + std::shared_lock lock(_config_mutex); + for (const auto &[key, value] : _config) { + std::visit([&j, key = key](auto &&arg) { + j[key] = arg; + }, + value); + } + } + + void config_set_yaml(const YAML::Node &config_yaml) + { + std::unique_lock lock(_config_mutex); + assert(config_yaml.IsMap()); + for (YAML::const_iterator it = config_yaml.begin(); it != config_yaml.end(); ++it) { + auto key = it->first.as(); + + if (!it->second.IsScalar()) { + throw ConfigException(fmt::format("invalid value for key: {}", key)); + } + + auto value = it->second.as(); + + // the yaml library doesn't discriminate between scalar types, so we have to do that ourselves + if (std::regex_match(value, std::regex("[0-9]+"))) { + _config[key] = it->second.as(); + } else if (std::regex_match(value, std::regex("true|false", std::regex_constants::icase))) { + _config[key] = it->second.as(); + } else { + _config[key] = value; + } + } + } +}; + +} diff --git a/src/CoreManagers.cpp b/src/CoreManagers.cpp new file mode 100644 index 000000000..41c244052 --- /dev/null +++ b/src/CoreManagers.cpp @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "CoreManagers.h" +#include +#include + +namespace visor { + +CoreManagers::CoreManagers(HttpServer *svr) + : _svr(svr) +{ + + _logger = spdlog::get("visor"); + if (!_logger) { + _logger = spdlog::stderr_color_mt("visor"); + } + + if (!svr) { + _logger->warn("initializing modules with no HttpServer"); + } + + // inputs + _input_manager = std::make_unique(); + + // initialize input plugins + { + auto alias_list = _input_registry.aliasList(); + auto plugin_list = _input_registry.pluginList(); + std::vector by_alias; + std::set_difference(alias_list.begin(), alias_list.end(), + plugin_list.begin(), plugin_list.end(), std::inserter(by_alias, by_alias.begin())); + for (auto &s : by_alias) { + InputPluginPtr mod = _input_registry.instantiate(s); + _logger->info("Load input stream plugin: {} {}", s, mod->pluginInterface()); + mod->init_module(_input_manager.get(), _svr); + _input_plugins.emplace_back(std::move(mod)); + } + } + + // handlers + _handler_manager = std::make_unique(); + + // initialize handler plugins + { + auto alias_list = _handler_registry.aliasList(); + auto plugin_list = _handler_registry.pluginList(); + std::vector by_alias; + std::set_difference(alias_list.begin(), alias_list.end(), + plugin_list.begin(), plugin_list.end(), std::inserter(by_alias, by_alias.begin())); + for (auto &s : by_alias) { + HandlerPluginPtr mod = _handler_registry.instantiate(s); + _logger->info("Load stream handler plugin: {} {}", s, mod->pluginInterface()); + mod->init_module(_input_manager.get(), _handler_manager.get(), _svr); + _handler_plugins.emplace_back(std::move(mod)); + } + } + + // taps + _tap_manager = std::make_unique(&_input_registry); +} + +visor::CoreManagers::~CoreManagers() +{ + // gracefully close all inputs and handlers + auto [input_modules, im_lock] = _input_manager->module_get_all_locked(); + for (auto &[name, mod] : input_modules) { + if (mod->running()) { + _logger->info("Stopping input instance: {}", mod->name()); + mod->stop(); + } + } + auto [handler_modules, hm_lock] = _handler_manager->module_get_all_locked(); + for (auto &[name, mod] : handler_modules) { + if (mod->running()) { + _logger->info("Stopping handler instance: {}", mod->name()); + mod->stop(); + } + } +} + +void visor::CoreManagers::configure_from_file(const std::string &filename) +{ + YAML::Node config_file = YAML::LoadFile(filename); + + if (!config_file.IsMap() || !config_file["visor"]) { + throw ConfigException("invalid schema"); + } + if (!config_file["version"] || !config_file["version"].IsScalar() || config_file["version"].as() != "1.0") { + throw ConfigException("missing or unsupported version"); + } + + // taps + if (config_file["visor"]["taps"] && config_file["visor"]["taps"].IsMap()) { + _tap_manager->load(config_file["visor"]["taps"], true); + } +} + +} \ No newline at end of file diff --git a/src/CoreManagers.h b/src/CoreManagers.h new file mode 100644 index 000000000..a8cf4d453 --- /dev/null +++ b/src/CoreManagers.h @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include "HandlerModulePlugin.h" +#include "InputModulePlugin.h" +#include "Taps.h" +#include + +namespace visor { + +class CoreManagers +{ + + // these hold plugin instances: these are the types of modules available for instantiation + InputPluginRegistry _input_registry; + std::vector _input_plugins; + + HandlerPluginRegistry _handler_registry; + std::vector _handler_plugins; + + // these hold instances of active modules + std::unique_ptr _input_manager; + std::unique_ptr _handler_manager; + + std::unique_ptr _tap_manager; + + std::shared_ptr _logger; + HttpServer *_svr; + +public: + CoreManagers(HttpServer *svr); + ~CoreManagers(); + + void configure_from_file(const std::string &filename); + + [[nodiscard]] const InputStreamManager *input_manager() const + { + return _input_manager.get(); + } + [[nodiscard]] const HandlerManager *handler_manager() const + { + return _handler_manager.get(); + } + [[nodiscard]] const TapManager *tap_manager() const + { + return _tap_manager.get(); + } + [[nodiscard]] const InputPluginRegistry *input_plugin_registry() const + { + return &_input_registry; + } + + [[nodiscard]] InputStreamManager *input_manager() + { + return _input_manager.get(); + } + + [[nodiscard]] HandlerManager *handler_manager() + { + return _handler_manager.get(); + } + + [[nodiscard]] TapManager *tap_manager() + { + return _tap_manager.get(); + } + +}; + +} \ No newline at end of file diff --git a/src/CoreServer.cpp b/src/CoreServer.cpp index 8490ff101..9f8037b4a 100644 --- a/src/CoreServer.cpp +++ b/src/CoreServer.cpp @@ -6,43 +6,32 @@ #include "Metrics.h" #include "visor_config.h" #include +#include +#include #include #include -visor::CoreServer::CoreServer(bool read_only, std::shared_ptr logger, const PrometheusConfig &prom_config) +namespace visor { + +CoreServer::CoreServer(bool read_only, const PrometheusConfig &prom_config) : _svr(read_only) - , _logger(logger) + , _mgrs(&_svr) , _start_time(std::chrono::system_clock::now()) { - // inputs - _input_manager = std::make_unique(); - - // initialize input plugins - for (auto &s : _input_registry.pluginList()) { - InputPluginPtr mod = _input_registry.instantiate(s); - _logger->info("Load input plugin: {} {}", mod->name(), mod->pluginInterface()); - mod->init_module(_input_manager.get(), _svr); - _input_plugins.emplace_back(std::move(mod)); - } - - // handlers - _handler_manager = std::make_unique(); - - // initialize handler plugins - for (auto &s : _handler_registry.pluginList()) { - HandlerPluginPtr mod = _handler_registry.instantiate(s); - _logger->info("Load handler plugin: {} {}", mod->name(), mod->pluginInterface()); - mod->init_module(_input_manager.get(), _handler_manager.get(), _svr); - _handler_plugins.emplace_back(std::move(mod)); + _logger = spdlog::get("visor"); + if (!_logger) { + _logger = spdlog::stderr_color_mt("visor"); } _setup_routes(prom_config); + if (!prom_config.instance.empty()) { Metric::add_base_label("instance", prom_config.instance); } } -void visor::CoreServer::start(const std::string &host, int port) + +void CoreServer::start(const std::string &host, int port) { if (!_svr.bind_to_port(host.c_str(), port)) { throw std::runtime_error("unable to bind host/port"); @@ -52,31 +41,18 @@ void visor::CoreServer::start(const std::string &host, int port) throw std::runtime_error("error during listen"); } } -void visor::CoreServer::stop() + +void CoreServer::stop() { _svr.stop(); - - // gracefully close all inputs and handlers - auto [input_modules, im_lock] = _input_manager->module_get_all_locked(); - for (auto &[name, mod] : input_modules) { - if (mod->running()) { - _logger->info("Stopping input instance: {}", mod->name()); - mod->stop(); - } - } - auto [handler_modules, hm_lock] = _handler_manager->module_get_all_locked(); - for (auto &[name, mod] : handler_modules) { - if (mod->running()) { - _logger->info("Stopping handler instance: {}", mod->name()); - mod->stop(); - } - } } -visor::CoreServer::~CoreServer() + +CoreServer::~CoreServer() { stop(); } -void visor::CoreServer::_setup_routes(const PrometheusConfig &prom_config) + +void CoreServer::_setup_routes(const PrometheusConfig &prom_config) { _logger->info("Initialize server control plane"); @@ -120,7 +96,7 @@ void visor::CoreServer::_setup_routes(const PrometheusConfig &prom_config) bool bc_period{false}; try { uint64_t period(std::stol(req.matches[1])); - auto [handler_modules, hm_lock] = _handler_manager->module_get_all_locked(); + auto [handler_modules, hm_lock] = _mgrs.handler_manager()->module_get_all_locked(); for (auto &[name, mod] : handler_modules) { auto hmod = dynamic_cast(mod.get()); // TODO need to add policy name, break backwards compatible since multiple otherwise policies will overwrite @@ -146,7 +122,7 @@ void visor::CoreServer::_setup_routes(const PrometheusConfig &prom_config) json j; try { uint64_t period(std::stol(req.matches[1])); - auto [handler_modules, hm_lock] = _handler_manager->module_get_all_locked(); + auto [handler_modules, hm_lock] = _mgrs.handler_manager()->module_get_all_locked(); for (auto &[name, mod] : handler_modules) { auto hmod = dynamic_cast(mod.get()); // TODO need to add policy name, break backwards compatible since multiple otherwise policies will overwrite @@ -162,12 +138,28 @@ void visor::CoreServer::_setup_routes(const PrometheusConfig &prom_config) res.set_content(e.what(), "text/plain"); } }); + _svr.Get(R"(/api/v1/taps)", [&]([[maybe_unused]] const httplib::Request &req, httplib::Response &res) { + json j; + try { + auto [handler_modules, hm_lock] = _mgrs.tap_manager()->module_get_all_locked(); + for (auto &[name, mod] : handler_modules) { + auto tmod = dynamic_cast(mod.get()); + if (tmod) { + tmod->info_json(j[tmod->name()]); + } + } + res.set_content(j.dump(), "text/json"); + } catch (const std::exception &e) { + res.status = 500; + res.set_content(e.what(), "text/plain"); + } + }); if (!prom_config.path.empty()) { _logger->info("enabling prometheus metrics on: {}", prom_config.path); _svr.Get(prom_config.path.c_str(), [&]([[maybe_unused]] const httplib::Request &req, httplib::Response &res) { std::stringstream output; try { - auto [handler_modules, hm_lock] = _handler_manager->module_get_all_locked(); + auto [handler_modules, hm_lock] = _mgrs.handler_manager()->module_get_all_locked(); for (auto &[name, mod] : handler_modules) { auto hmod = dynamic_cast(mod.get()); if (hmod) { @@ -184,3 +176,5 @@ void visor::CoreServer::_setup_routes(const PrometheusConfig &prom_config) }); } } + +} \ No newline at end of file diff --git a/src/CoreServer.h b/src/CoreServer.h index 66c34ce20..6bd099810 100644 --- a/src/CoreServer.h +++ b/src/CoreServer.h @@ -4,14 +4,9 @@ #pragma once -#include "HandlerManager.h" -#include "HandlerModulePlugin.h" +#include "CoreManagers.h" #include "HttpServer.h" -#include "InputModulePlugin.h" -#include "InputStreamManager.h" -#include -#include -#include +#include #include namespace visor { @@ -23,23 +18,9 @@ struct PrometheusConfig { class CoreServer { -public: -private: - typedef Corrade::PluginManager::Manager InputPluginRegistry; - typedef Corrade::PluginManager::Manager HandlerPluginRegistry; - typedef Corrade::Containers::Pointer InputPluginPtr; - typedef Corrade::Containers::Pointer HandlerPluginPtr; - - InputPluginRegistry _input_registry; - std::vector _input_plugins; - - HandlerPluginRegistry _handler_registry; - std::vector _handler_plugins; - visor::HttpServer _svr; - - std::unique_ptr _input_manager; - std::unique_ptr _handler_manager; + HttpServer _svr; + CoreManagers _mgrs; std::shared_ptr _logger; std::chrono::system_clock::time_point _start_time; @@ -47,24 +28,25 @@ class CoreServer void _setup_routes(const PrometheusConfig &prom_config); public: - CoreServer(bool read_only, std::shared_ptr logger, const PrometheusConfig &prom_config); + CoreServer(bool read_only, const PrometheusConfig &prom_config); ~CoreServer(); void start(const std::string &host, int port); void stop(); - void set_http_logger(httplib::Logger logger) + const CoreManagers *mgrs() const { - _svr.set_logger(logger); + return &_mgrs; } - InputStreamManager *input_manager() + CoreManagers *mgrs() { - return _input_manager.get(); + return &_mgrs; } - HandlerManager *handler_manager() + + void set_http_logger(httplib::Logger logger) { - return _handler_manager.get(); + _svr.set_logger(logger); } }; diff --git a/src/HandlerModulePlugin.cpp b/src/HandlerModulePlugin.cpp index 8859c0d3f..d1609a427 100644 --- a/src/HandlerModulePlugin.cpp +++ b/src/HandlerModulePlugin.cpp @@ -3,26 +3,19 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "HandlerModulePlugin.h" -#include -#include namespace visor { void HandlerModulePlugin::init_module(InputStreamManager *im, - HandlerManager *hm, HttpServer &svr) -{ - assert(hm); - assert(im); - _input_manager = im; - _handler_manager = hm; - _setup_routes(svr); -} -void HandlerModulePlugin::init_module(InputStreamManager *im, HandlerManager *hm) + HandlerManager *hm, HttpServer *svr) { assert(hm); assert(im); _input_manager = im; _handler_manager = hm; + if (svr) { + _setup_routes(svr); + } } } diff --git a/src/HandlerModulePlugin.h b/src/HandlerModulePlugin.h index b568ff16e..ec7e0f282 100644 --- a/src/HandlerModulePlugin.h +++ b/src/HandlerModulePlugin.h @@ -7,6 +7,8 @@ #include "AbstractPlugin.h" #include "HandlerManager.h" #include "InputStreamManager.h" +#include +#include #include namespace visor { @@ -17,12 +19,12 @@ class HandlerModulePlugin : public AbstractPlugin visor::InputStreamManager *_input_manager; visor::HandlerManager *_handler_manager; - virtual void _setup_routes(HttpServer &svr) = 0; + virtual void _setup_routes(HttpServer *svr) = 0; public: static std::string pluginInterface() { - return "dev.visor.module.handler/1.0"; + return "visor.module.handler/1.0"; } static std::vector pluginSearchPaths() @@ -35,15 +37,12 @@ class HandlerModulePlugin : public AbstractPlugin { } - virtual std::string name() const = 0; - void init_module(InputStreamManager *im, HandlerManager *hm, - HttpServer &svr); - - void init_module(InputStreamManager *im, - HandlerManager *hm); + HttpServer *svr); }; -} +typedef Corrade::PluginManager::Manager HandlerPluginRegistry; +typedef Corrade::Containers::Pointer HandlerPluginPtr; +} diff --git a/src/HttpServer.h b/src/HttpServer.h index b890818da..e6e22635d 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -20,7 +20,7 @@ class HttpServer : public httplib::Server Server &Get(const char *pattern, Handler handler) { - spdlog::get("pktvisor")->info("Registering GET {}", pattern); + spdlog::get("visor")->info("Registering GET {}", pattern); return httplib::Server::Get(pattern, handler); } Server &Post(const char *pattern, Handler handler) @@ -28,7 +28,7 @@ class HttpServer : public httplib::Server if (_read_only) { return *this; } - spdlog::get("pktvisor")->info("Registering POST {}", pattern); + spdlog::get("visor")->info("Registering POST {}", pattern); return httplib::Server::Post(pattern, handler); } Server &Put(const char *pattern, Handler handler) @@ -36,7 +36,7 @@ class HttpServer : public httplib::Server if (_read_only) { return *this; } - spdlog::get("pktvisor")->info("Registering PUT {}", pattern); + spdlog::get("visor")->info("Registering PUT {}", pattern); return httplib::Server::Put(pattern, handler); } Server &Delete(const char *pattern, Handler handler) @@ -44,7 +44,7 @@ class HttpServer : public httplib::Server if (_read_only) { return *this; } - spdlog::get("pktvisor")->info("Registering DELETE {}", pattern); + spdlog::get("visor")->info("Registering DELETE {}", pattern); return httplib::Server::Delete(pattern, handler); } }; diff --git a/src/InputModulePlugin.cpp b/src/InputModulePlugin.cpp index 3b571c788..6a232957d 100644 --- a/src/InputModulePlugin.cpp +++ b/src/InputModulePlugin.cpp @@ -3,21 +3,16 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "InputModulePlugin.h" -#include -#include namespace visor { -void InputModulePlugin::init_module(InputStreamManager *im, HttpServer &svr) -{ - assert(im); - _input_manager = im; - _setup_routes(svr); -} -void InputModulePlugin::init_module(InputStreamManager *im) +void InputModulePlugin::init_module(InputStreamManager *im, HttpServer *svr) { assert(im); _input_manager = im; + if (svr) { + _setup_routes(svr); + } } } diff --git a/src/InputModulePlugin.h b/src/InputModulePlugin.h index 079395a87..32772d33b 100644 --- a/src/InputModulePlugin.h +++ b/src/InputModulePlugin.h @@ -6,6 +6,8 @@ #include "AbstractPlugin.h" #include "InputStreamManager.h" +#include +#include #include namespace visor { @@ -14,14 +16,14 @@ class InputModulePlugin : public AbstractPlugin { protected: - visor::InputStreamManager *_input_manager; + InputStreamManager *_input_manager; - virtual void _setup_routes(HttpServer &svr) = 0; + virtual void _setup_routes(HttpServer *svr) = 0; public: static std::string pluginInterface() { - return "dev.visor.module.input/1.0"; + return "visor.module.input/1.0"; } static std::vector pluginSearchPaths() @@ -34,11 +36,10 @@ class InputModulePlugin : public AbstractPlugin { } - virtual std::string name() const = 0; - - void init_module(InputStreamManager *im, HttpServer &svr); - void init_module(InputStreamManager *im); + void init_module(InputStreamManager *im, HttpServer *svr); }; -} +typedef Corrade::PluginManager::Manager InputPluginRegistry; +typedef Corrade::Containers::Pointer InputPluginPtr; +} diff --git a/src/InputStream.h b/src/InputStream.h index 19d5a6852..7fe1c4a54 100644 --- a/src/InputStream.h +++ b/src/InputStream.h @@ -9,12 +9,12 @@ namespace visor { -class InputStream : public AbstractModule +class InputStream : public AbstractRunnableModule { public: InputStream(const std::string &name) - : AbstractModule(name) + : AbstractRunnableModule(name) { } diff --git a/src/Metrics.h b/src/Metrics.h index fd5bfa081..3d611a55f 100644 --- a/src/Metrics.h +++ b/src/Metrics.h @@ -16,6 +16,7 @@ #include #pragma GCC diagnostic pop #include +#include #include #include @@ -40,12 +41,25 @@ class Metric std::string _desc; std::string _schema_key; + void _check_names() + { + for (const auto &name : _name) { + if (!std::regex_match(name, std::regex("[a-zA-Z_][a-zA-Z0-9_]*"))) { + throw std::runtime_error("invalid metric name: " + name); + } + } + if (!std::regex_match(_schema_key, std::regex("[a-zA-Z_][a-zA-Z0-9_]*"))) { + throw std::runtime_error("invalid schema name: " + _schema_key); + } + } + public: Metric(std::string schema_key, std::initializer_list names, std::string desc) : _name(names) , _desc(std::move(desc)) , _schema_key(schema_key) { + _check_names(); } void set_info(std::string schema_key, std::initializer_list names, const std::string &desc) @@ -54,6 +68,7 @@ class Metric _name = names; _desc = desc; _schema_key = schema_key; + _check_names(); } static void add_base_label(const std::string &label, const std::string &value) diff --git a/src/StreamHandler.h b/src/StreamHandler.h index 159a00c05..0413862e1 100644 --- a/src/StreamHandler.h +++ b/src/StreamHandler.h @@ -13,12 +13,12 @@ namespace visor { using json = nlohmann::json; -class StreamHandler : public AbstractModule +class StreamHandler : public AbstractRunnableModule { public: StreamHandler(const std::string &name) - : AbstractModule(name) + : AbstractRunnableModule(name) { } diff --git a/src/Taps.cpp b/src/Taps.cpp new file mode 100644 index 000000000..f76d476fc --- /dev/null +++ b/src/Taps.cpp @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "Taps.h" +#include +#include +#include + +void visor::TapManager::load(const YAML::Node &tap_yaml, bool strict) +{ + assert(tap_yaml.IsMap()); + assert(spdlog::get("visor")); + + auto input_plugins = _input_plugin_registry->aliasList(); + + for (YAML::const_iterator it = tap_yaml.begin(); it != tap_yaml.end(); ++it) { + if (!it->first.IsScalar()) { + throw ConfigException("expecting tap identifier"); + } + auto tap_name = it->first.as(); + spdlog::get("visor")->info("loading Tap: {}", tap_name); + if (!it->second.IsMap()) { + throw ConfigException("expecting tap configuration map"); + } + if (!it->second["input_type"] || !it->second["input_type"].IsScalar()) { + throw ConfigException("missing or invalid tap type key 'input_type'"); + } + auto input_type = it->second["input_type"].as(); + if (std::find(input_plugins.begin(), input_plugins.end(), input_type) == input_plugins.end()) { + if (strict) { + throw ConfigException(fmt::format("Tap '{}' requires input stream type '{}' which is not available", tap_name, input_type)); + } else { + spdlog::get("visor")->warn("Tap '{}' requires input stream type '{}' which is not available; skipping", tap_name, input_type); + continue; + } + } + + auto tap_module = std::make_unique(tap_name, input_type); + + if (it->second["config"]) { + if (!it->second["config"].IsMap()) { + throw ConfigException("tap configuration is not a map"); + } + tap_module->config_set_yaml(it->second["config"]); + } + + module_add(std::move(tap_module)); + } +} diff --git a/src/Taps.h b/src/Taps.h new file mode 100644 index 000000000..b7af0b35d --- /dev/null +++ b/src/Taps.h @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include "AbstractManager.h" +#include "AbstractModule.h" +#include "Configurable.h" +#include "InputModulePlugin.h" +#include + +namespace visor { + +class Tap : public AbstractModule +{ + + std::string _input_type; + +public: + Tap(const std::string &name, const std::string &input_type) + : AbstractModule(name) + , _input_type(input_type) + { + } + + void info_json(json &j) const override + { + j["input_type"] = _input_type; + config_json(j["config"]); + } +}; + +class TapManager : public AbstractManager +{ + + const InputPluginRegistry *_input_plugin_registry; + +public: + TapManager(const InputPluginRegistry *inputManager) + : _input_plugin_registry(inputManager) + { + } + + virtual ~TapManager() + { + } + + void load(const YAML::Node &tap_yaml, bool strict); +}; + +} \ No newline at end of file diff --git a/src/handlers/dns/DnsHandler.conf b/src/handlers/dns/DnsHandler.conf index d2ce66614..39036eda5 100644 --- a/src/handlers/dns/DnsHandler.conf +++ b/src/handlers/dns/DnsHandler.conf @@ -1,2 +1,4 @@ +# Aliases +provides=dns [data] desc=DNS analyzer diff --git a/src/handlers/dns/DnsHandlerModulePlugin.cpp b/src/handlers/dns/DnsHandlerModulePlugin.cpp index 126ca040b..cce45f903 100644 --- a/src/handlers/dns/DnsHandlerModulePlugin.cpp +++ b/src/handlers/dns/DnsHandlerModulePlugin.cpp @@ -9,17 +9,17 @@ #include CORRADE_PLUGIN_REGISTER(VisorHandlerDns, visor::handler::dns::DnsHandlerModulePlugin, - "dev.visor.module.handler/1.0") + "visor.module.handler/1.0") namespace visor::handler::dns { using namespace visor::input::pcap; using json = nlohmann::json; -void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) +void DnsHandlerModulePlugin::_setup_routes(HttpServer *svr) { // CREATE - svr.Post("/api/v1/inputs/pcap/(\\w+)/handlers/dns", [this](const httplib::Request &req, httplib::Response &res) { + svr->Post("/api/v1/inputs/pcap/(\\w+)/handlers/dns", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto body = json::parse(req.body); @@ -72,6 +72,7 @@ void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) deep_sample_rate = body["deep_sample_rate"]; } auto handler_module = std::make_unique(body["name"], pcap_stream, periods, deep_sample_rate); + handler_module->start(); _handler_manager->module_add(std::move(handler_module)); result["name"] = body["name"]; result["periods"] = periods; @@ -83,7 +84,7 @@ void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); } }); - svr.Get("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Get("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -116,7 +117,7 @@ void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); } }); - svr.Get("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)/bucket/(\\d+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Get("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)/bucket/(\\d+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -150,7 +151,7 @@ void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) } }); // DELETE - svr.Delete("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Delete("/api/v1/inputs/pcap/(\\w+)/handlers/dns/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -167,6 +168,9 @@ void DnsHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); return; } + auto [handler, handler_mgr_lock] = _handler_manager->module_get_locked(handler_name); + handler->stop(); + handler_mgr_lock.unlock(); _handler_manager->module_remove(handler_name); res.set_content(result.dump(), "text/json"); } catch (const std::exception &e) { diff --git a/src/handlers/dns/DnsHandlerModulePlugin.h b/src/handlers/dns/DnsHandlerModulePlugin.h index 0f84102ee..8ed03d48c 100644 --- a/src/handlers/dns/DnsHandlerModulePlugin.h +++ b/src/handlers/dns/DnsHandlerModulePlugin.h @@ -12,18 +12,13 @@ class DnsHandlerModulePlugin : public HandlerModulePlugin { protected: - void _setup_routes(HttpServer &svr) override; + void _setup_routes(HttpServer *svr) override; public: explicit DnsHandlerModulePlugin(Corrade::PluginManager::AbstractManager &manager, const std::string &plugin) : visor::HandlerModulePlugin{manager, plugin} { } - - std::string name() const override - { - return "DnsHandler"; - } }; } diff --git a/src/handlers/net/NetHandler.conf b/src/handlers/net/NetHandler.conf index 0c7ecf850..ebfecf878 100644 --- a/src/handlers/net/NetHandler.conf +++ b/src/handlers/net/NetHandler.conf @@ -1,2 +1,4 @@ +# Aliases +provides=net [data] desc=Network (L3-L4) analyzer diff --git a/src/handlers/net/NetHandlerModulePlugin.cpp b/src/handlers/net/NetHandlerModulePlugin.cpp index 66f93be53..a781ae2e9 100644 --- a/src/handlers/net/NetHandlerModulePlugin.cpp +++ b/src/handlers/net/NetHandlerModulePlugin.cpp @@ -9,17 +9,17 @@ #include CORRADE_PLUGIN_REGISTER(VisorHandlerNet, visor::handler::net::NetHandlerModulePlugin, - "dev.visor.module.handler/1.0") + "visor.module.handler/1.0") namespace visor::handler::net { using namespace visor::input::pcap; using json = nlohmann::json; -void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) +void NetHandlerModulePlugin::_setup_routes(HttpServer *svr) { // CREATE - svr.Post("/api/v1/inputs/pcap/(\\w+)/handlers/net", [this](const httplib::Request &req, httplib::Response &res) { + svr->Post("/api/v1/inputs/pcap/(\\w+)/handlers/net", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto body = json::parse(req.body); @@ -72,6 +72,7 @@ void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) deep_sample_rate = body["deep_sample_rate"]; } auto handler_module = std::make_unique(body["name"], pcap_stream, periods, deep_sample_rate); + handler_module->start(); _handler_manager->module_add(std::move(handler_module)); result["name"] = body["name"]; result["periods"] = periods; @@ -84,7 +85,7 @@ void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); } }); - svr.Get("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Get("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -118,7 +119,7 @@ void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); } }); - svr.Get("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)/bucket/(\\d+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Get("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)/bucket/(\\d+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -153,7 +154,7 @@ void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) } }); // DELETE - svr.Delete("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { + svr->Delete("/api/v1/inputs/pcap/(\\w+)/handlers/net/(\\w+)", [this](const httplib::Request &req, httplib::Response &res) { json result; try { auto input_name = req.matches[1]; @@ -170,6 +171,9 @@ void NetHandlerModulePlugin::_setup_routes(HttpServer &svr) res.set_content(result.dump(), "text/json"); return; } + auto [handler, handler_mgr_lock] = _handler_manager->module_get_locked(handler_name); + handler->stop(); + handler_mgr_lock.unlock(); _handler_manager->module_remove(handler_name); res.set_content(result.dump(), "text/json"); } catch (const std::exception &e) { diff --git a/src/handlers/net/NetHandlerModulePlugin.h b/src/handlers/net/NetHandlerModulePlugin.h index 6344f5b9d..94baa7e0c 100644 --- a/src/handlers/net/NetHandlerModulePlugin.h +++ b/src/handlers/net/NetHandlerModulePlugin.h @@ -13,7 +13,7 @@ class NetHandlerModulePlugin : public HandlerModulePlugin { protected: - void _setup_routes(HttpServer &svr) override; + void _setup_routes(HttpServer *svr) override; public: explicit NetHandlerModulePlugin(Corrade::PluginManager::AbstractManager &manager, const std::string &plugin) @@ -21,10 +21,6 @@ class NetHandlerModulePlugin : public HandlerModulePlugin { } - std::string name() const override - { - return "NetHandler"; - } }; } diff --git a/src/inputs/pcap/PcapInput.conf b/src/inputs/pcap/PcapInput.conf index b424b5568..8a3630f85 100644 --- a/src/inputs/pcap/PcapInput.conf +++ b/src/inputs/pcap/PcapInput.conf @@ -1,2 +1,4 @@ +# Aliases +provides=pcap [data] -desc=packet capture stream input (libpcap,DPDK,PF_RING) +desc=packet capture stream input diff --git a/src/inputs/pcap/PcapInputModulePlugin.cpp b/src/inputs/pcap/PcapInputModulePlugin.cpp index b718f50f1..4d2b53d0a 100644 --- a/src/inputs/pcap/PcapInputModulePlugin.cpp +++ b/src/inputs/pcap/PcapInputModulePlugin.cpp @@ -7,21 +7,21 @@ #include CORRADE_PLUGIN_REGISTER(VisorInputPcap, visor::input::pcap::PcapInputModulePlugin, - "dev.visor.module.input/1.0") + "visor.module.input/1.0") namespace visor::input::pcap { -void PcapInputModulePlugin::_setup_routes(HttpServer &svr) +void PcapInputModulePlugin::_setup_routes(HttpServer *svr) { // CREATE - svr.Post("/api/v1/inputs/pcap", std::bind(&PcapInputModulePlugin::_create, this, std::placeholders::_1, std::placeholders::_2)); + svr->Post("/api/v1/inputs/pcap", std::bind(&PcapInputModulePlugin::_create, this, std::placeholders::_1, std::placeholders::_2)); // DELETE - svr.Delete("/api/v1/inputs/pcap/(\\w+)", std::bind(&PcapInputModulePlugin::_delete, this, std::placeholders::_1, std::placeholders::_2)); + svr->Delete("/api/v1/inputs/pcap/(\\w+)", std::bind(&PcapInputModulePlugin::_delete, this, std::placeholders::_1, std::placeholders::_2)); // GET - svr.Get("/api/v1/inputs/pcap/(\\w+)", std::bind(&PcapInputModulePlugin::_read, this, std::placeholders::_1, std::placeholders::_2)); + svr->Get("/api/v1/inputs/pcap/(\\w+)", std::bind(&PcapInputModulePlugin::_read, this, std::placeholders::_1, std::placeholders::_2)); } void PcapInputModulePlugin::_create(const httplib::Request &req, httplib::Response &res) @@ -60,8 +60,8 @@ void PcapInputModulePlugin::_create(const httplib::Request &req, httplib::Respon if (body.contains("pcap_source")) { input_stream->config_set("pcap_source", body["pcap_source"].get()); } + input_stream->start(); _input_manager->module_add(std::move(input_stream)); - // the module is now started and owned by the manager } auto [input_stream, stream_mgr_lock] = _input_manager->module_get_locked(body["name"]); diff --git a/src/inputs/pcap/PcapInputModulePlugin.h b/src/inputs/pcap/PcapInputModulePlugin.h index 4334a0d63..fe62b9fa6 100644 --- a/src/inputs/pcap/PcapInputModulePlugin.h +++ b/src/inputs/pcap/PcapInputModulePlugin.h @@ -14,7 +14,7 @@ class PcapInputModulePlugin : public visor::InputModulePlugin { protected: - void _setup_routes(HttpServer &svr) override; + void _setup_routes(HttpServer *svr) override; void _create(const httplib::Request &req, httplib::Response &res); void _read(const httplib::Request &req, httplib::Response &res); @@ -25,11 +25,6 @@ class PcapInputModulePlugin : public visor::InputModulePlugin : visor::InputModulePlugin{manager, plugin} { } - - std::string name() const override - { - return "PcapInputModulePlugin"; - } }; } diff --git a/src/tests/test_taps.cpp b/src/tests/test_taps.cpp new file mode 100644 index 000000000..69fae2844 --- /dev/null +++ b/src/tests/test_taps.cpp @@ -0,0 +1,70 @@ +#include "CoreManagers.h" +#include "InputModulePlugin.h" +#include "inputs/static_plugins.h" +#include +#include +#include + +using namespace visor; + +auto sample_config = R"( +version: "1.0" + +visor: + config: + verbose: true + taps: + wired: + input_type: pcap + config: + iface: en7 + number: 123 + boolean: true + wireless: + input_type: pcap + config: + iface: en0 +)"; + +auto sample_config_bad = R"( +version: "1.0" + +visor: + config: + verbose: true + taps: + wired: + input_type: nonexistent + config: + iface: en7 +)"; + +TEST_CASE("Taps", "[taps]") +{ + + SECTION("Good Config") + { + CoreManagers mgrs(nullptr); + YAML::Node config_file = YAML::Load(sample_config); + + CHECK(config_file["visor"]["taps"]); + CHECK(config_file["visor"]["taps"].IsMap()); + CHECK_NOTHROW(mgrs.tap_manager()->load(config_file["visor"]["taps"], true)); + + auto [tap, lock] = mgrs.tap_manager()->module_get_locked("wired"); + CHECK(tap->name() == "wired"); + CHECK(tap->config_get("iface") == "en7"); + CHECK(tap->config_get("number") == 123); + CHECK(tap->config_get("boolean") == true); + } + + SECTION("Bad Config") + { + CoreManagers mgrs(nullptr); + YAML::Node config_file = YAML::Load(sample_config_bad); + + CHECK(config_file["visor"]["taps"]); + CHECK(config_file["visor"]["taps"].IsMap()); + CHECK_THROWS(mgrs.tap_manager()->load(config_file["visor"]["taps"], true)); + } +}