From f9a152b24c75029ddc054f65c74ab45b0dc1c2c0 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Tue, 30 Aug 2022 12:49:36 +0000 Subject: [PATCH] refactor(userspace/falco): generalize responsibilities of init_inspector action Now, the action takes care of inizializing all app inspectors (just one in capture mode, one for each evt source in live mode), and of registering and initializing all loaded plugins in the right inspector as needed. The plugin initialization logic, which also involves the filtercheck list population and checks, was moved and refactored from the previous implementation of the load_plugins action. Signed-off-by: Jason Dellaluce --- .../falco/app_actions/init_inspector.cpp | 165 ++++++++++++++++-- 1 file changed, 154 insertions(+), 11 deletions(-) diff --git a/userspace/falco/app_actions/init_inspector.cpp b/userspace/falco/app_actions/init_inspector.cpp index 6695b153..5b1faa1d 100644 --- a/userspace/falco/app_actions/init_inspector.cpp +++ b/userspace/falco/app_actions/init_inspector.cpp @@ -15,44 +15,187 @@ limitations under the License. */ #include "application.h" +#include +#include using namespace falco::app; -application::run_result application::init_inspector() +static void init_syscall_inspector( + std::shared_ptr inspector, + const falco::app::cmdline_options& opts) { - m_state->inspector->set_buffer_format(m_options.event_buffer_format); + inspector->set_buffer_format(opts.event_buffer_format); // If required, set the CRI paths - for (auto &p : m_options.cri_socket_paths) + for (auto &p : opts.cri_socket_paths) { if (!p.empty()) { - m_state->inspector->add_cri_socket_path(p); + inspector->add_cri_socket_path(p); } } // Decide whether to do sync or async for CRI metadata fetch - m_state->inspector->set_cri_async(!m_options.disable_cri_async); + inspector->set_cri_async(!opts.disable_cri_async); // // If required, set the snaplen // - if(m_options.snaplen != 0) + if(opts.snaplen != 0) { - m_state->inspector->set_snaplen(m_options.snaplen); + inspector->set_snaplen(opts.snaplen); } - if(!m_options.all_events) + if(!opts.all_events) { // Drop EF_DROP_SIMPLE_CONS kernel side - m_state->inspector->set_simple_consumer(); + inspector->set_simple_consumer(); // Eventually, drop any EF_DROP_SIMPLE_CONS event // that reached userspace (there are some events that are not syscall-based // like signaldeliver, that have the EF_DROP_SIMPLE_CONS flag) - m_state->inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS); + inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS); } - m_state->inspector->set_hostname_and_port_resolution_mode(false); + inspector->set_hostname_and_port_resolution_mode(false); +} + +static bool populate_filterchecks( + std::shared_ptr inspector, + const std::string& source, + filter_check_list& filterchecks, + std::unordered_set& used_plugins, + std::string& err) +{ + std::vector info; + for(const auto& p : inspector->get_plugin_manager()->plugins()) + { + if (!(p->caps() & CAP_EXTRACTION)) + { + continue; + } + + // check if some fields are overlapping on this event sources + info.clear(); + filterchecks.get_all_fields(info); + for (auto &info : info) + { + for (int32_t i = 0; i < info->m_nfields; i++) + { + // check if one of the fields extractable by the plugin + // is already provided by another filtercheck for this source + std::string fname = info->m_fields[i].m_name; + for (auto &f : p->fields()) + { + if (std::string(f.m_name) == fname) + { + err = "Plugin '" + p->name() + + "' supports extraction of field '" + fname + + "' that is overlapping for source '" + source + "'"; + return false; + } + } + } + } + + // add plugin filterchecks to the event source + filterchecks.add_filter_check(sinsp_plugin::new_filtercheck(p)); + used_plugins.insert(p->name()); + } + return true; +} + +// todo(XXX): rename this to init_inspectors() +application::run_result application::init_inspector() +{ + std::string err; + std::unordered_set used_plugins; + const auto& all_plugins = m_state->offline_inspector->get_plugin_manager()->plugins(); + + for (const auto &src : m_state->loaded_sources) + { + auto src_info = m_state->sources.at(src); + + // in capture mode, every event source uses the offline inspector. + // in live mode, we create a new inspector for each event source + src_info->inspector = is_capture_mode() + ? m_state->offline_inspector + : std::make_shared(); + + // handle syscall and plugin sources differently + // todo(jasondellaluce): change this once we support extracting plugin fields from syscalls too + if (src == falco_common::syscall_source) + { + init_syscall_inspector(src_info->inspector, m_options); + continue; + } + + // load and init all plugins compatible with this event source + // (if in capture mode, all plugins will be inited on the same inspector) + for (const auto& p : all_plugins) + { + std::shared_ptr plugin = nullptr; + auto config = m_state->plugin_configs.at(p->name()); + auto is_input = p->caps() & CAP_SOURCING && p->event_source() == src; + + if (is_capture_mode()) + { + // in capture mode, every plugin is already registered + // in the offline inspector by the load_plugins action + plugin = p; + } + else + { + // in live mode, for the inspector assigned to the given + // event source, we must register the plugin supporting + // that event source and also plugins with field extraction + // capability that are compatible with that event source + if (is_input || (p->caps() & CAP_EXTRACTION && p->is_source_compatible(src))) + { + plugin = src_info->inspector->register_plugin(config->m_library_path); + } + } + + // init the plugin, if we registered it into an inspector + // (in capture mode, this is true for every plugin) + if (plugin) + { + if (!plugin->init(config->m_init_config, err)) + { + return run_result::fatal(err); + } + if (is_input) + { + auto gen_check = src_info->inspector->new_generic_filtercheck(); + src_info->filterchecks.add_filter_check(gen_check); + } + used_plugins.insert(plugin->name()); + } + } + + // populate filtercheck list for this inspector + if (!populate_filterchecks( + src_info->inspector, + src, + src_info->filterchecks, + used_plugins, + err)) + { + return run_result::fatal(err); + } + + } + + // check if some plugin with field extraction capability remains unused + for (const auto& p : all_plugins) + { + if(used_plugins.find(p->name()) == used_plugins.end() + && p->caps() & CAP_EXTRACTION + && !(p->caps() & CAP_SOURCING && p->is_source_compatible(p->event_source()))) + { + return run_result::fatal("Plugin '" + p->name() + + "' has field extraction capability but is not compatible with any known event source"); + } + } return run_result::ok(); }