Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux
endif()
endif()

option(BUILD_FALCO_MODERN_BPF "Build modern BPF support for Falco" OFF)
if (BUILD_FALCO_MODERN_BPF)
add_definitions(-DHAS_MODERN_BPF)
endif()

# We shouldn't need to set this, see https://gitlab.kitware.com/cmake/cmake/-/issues/16419
option(EP_UPDATE_DISCONNECTED "ExternalProject update disconnected" OFF)
if (${EP_UPDATE_DISCONNECTED})
Expand Down Expand Up @@ -114,6 +119,11 @@ set(DRIVER_NAME "falco")
set(DRIVER_DEVICE_NAME "falco")
set(DRIVERS_REPO "https://download.falco.org/driver")

# If no path is provided, try to search the BPF probe in: `home/.falco/falco-bpf.o`
# This is the same fallback that we had in the libraries: `SCAP_PROBE_BPF_FILEPATH`.
set(FALCO_PROBE_BPF_FILEPATH ".${DRIVER_NAME}/${DRIVER_NAME}-bpf.o")
add_definitions(-DFALCO_PROBE_BPF_FILEPATH="${FALCO_PROBE_BPF_FILEPATH}")

if(NOT DEFINED FALCO_COMPONENT_NAME)
set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}")
endif()
Expand Down
6 changes: 3 additions & 3 deletions cmake/modules/driver.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ else()
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
# ie., `cmake -DDRIVER_VERSION=dev ..`
if(NOT DRIVER_VERSION)
set(DRIVER_VERSION "b4c198773bf05486e122f6d3f7f63be125242413")
set(DRIVER_CHECKSUM "SHA256=e85fa42a0b58ba21ca7efb38c20ce25207f4816245bdf154e6b9a037a1cce930")
set(DRIVER_VERSION "ec10e7b93aa3605208a36f2e301e79e0c66a9da9")
set(DRIVER_CHECKSUM "SHA256=707b751998534bf1d4537c945868a6f81239d57d1c8e2dd3ec7d48eaa5e9ea53")
endif()

# cd /path/to/build && cmake /path/to/source
Expand All @@ -45,4 +45,4 @@ set(DRIVER_NAME "falco")
set(DRIVER_PACKAGE_NAME "falco")
set(DRIVER_COMPONENT_NAME "falco-driver")

add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
10 changes: 6 additions & 4 deletions cmake/modules/falcosecurity-libs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ else()
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "b4c198773bf05486e122f6d3f7f63be125242413")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=e85fa42a0b58ba21ca7efb38c20ce25207f4816245bdf154e6b9a037a1cce930")
set(FALCOSECURITY_LIBS_VERSION "ec10e7b93aa3605208a36f2e301e79e0c66a9da9")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=707b751998534bf1d4537c945868a6f81239d57d1c8e2dd3ec7d48eaa5e9ea53")
endif()

# cd /path/to/build && cmake /path/to/source
Expand All @@ -49,7 +49,6 @@ if(MUSL_OPTIMIZED_BUILD)
add_definitions(-DMUSL_OPTIMIZED)
endif()

set(SCAP_BPF_PROBE_ENV_VAR_NAME "FALCO_BPF_PROBE")
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")

if(NOT LIBSCAP_DIR)
Expand All @@ -61,6 +60,9 @@ set(LIBSINSP_DIR "${FALCOSECURITY_LIBS_SOURCE_DIR}")
# configure gVisor support
set(BUILD_LIBSCAP_GVISOR ${BUILD_FALCO_GVISOR} CACHE BOOL "")

# configure momdern BPF support
set(BUILD_LIBSCAP_MODERN_BPF ${BUILD_FALCO_MODERN_BPF} CACHE BOOL "")

# explicitly disable the tests/examples of this dependency
set(CREATE_TEST_TARGETS OFF CACHE BOOL "")
set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
Expand All @@ -84,4 +86,4 @@ endif()

include(driver)
include(libscap)
include(libsinsp)
include(libsinsp)
7 changes: 7 additions & 0 deletions falco.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,10 @@ metadata_download:
max_mb: 100
chunk_wait_us: 1000
watch_freq_sec: 1

# This is the dimension that a single buffer in our drivers will have. (BPF, kmod, modern BPF)
# Please note:
# - This number is expressed in bytes.
# - This number must be a multiple of your system page size, otherwise the allocation will fail.
# - If you leave `0`, every driver will set its internal default dimension.
single_buffer_dimension: 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
single_buffer_dimension: 0
syscall_buffer_size: 0

Since this option affects the syscall data source only, I'd prefix it with syscall_ and move it close to other syscall_ options.
Also, I'd simplify the name and explain in the comment what that size means and how it affects the operations (eg. the driver will create a buffer per CPU, syscall_buffer_size specifies the size in bytes of each buffer...)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

completely agree with the new name!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to have this param be just a multiple of page size need to adjust the _size at the end and explain.

[nit] More additional explanations for end users less familiar with architecture. Something like:

Falco uses a shared buffer between kernel and userspace to serve events up to userspace for subsequent processing and rules matching. With the TBD_BUFFER_PARAM_NAME you can increase or decrease the default buffer size for either driver (BPF, kmod, modern BPF) wrt the system call tracing functionality. In more detail, Falco uses one buffer for every online CPU, but you generically adjust the size once with this parameter. Increasing the buffer can help reducing kernel side syscall drops while decreasing the buffer can help speed up the entire engine.

Copy link
Copy Markdown
Contributor

@incertum incertum Aug 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thought that just occurred to me, maybe have default value be actual default value and not 0 to be consistent with other parameters? @Andreagit97 @leogr
But now I can see an issue for kmod vs eBPF default values, hmmm ... and we should always gracefully start up Falco with default value like it's the case right now.

3 changes: 2 additions & 1 deletion userspace/falco/app_actions/load_plugins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ application::run_result application::load_plugins()
}
loaded_plugin = plugin;
m_state->enabled_sources = {plugin->event_source()};
m_state->inspector->set_input_plugin(p.m_name, p.m_open_params);
m_state->m_plugin_name = p.m_name;
m_state->m_plugin_open_params = p.m_open_params;
}

// Init filtercheck list for the plugin's source and add the
Expand Down
128 changes: 90 additions & 38 deletions userspace/falco/app_actions/open_inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,73 +22,125 @@ limitations under the License.

using namespace falco::app;

#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
#define MAX_PROBE_PATH_SIZE 4096

typedef std::function<void(std::shared_ptr<sinsp> inspector)> open_t;

application::run_result application::open_inspector()
{
// Notify engine that we finished loading and enabling all rules
m_state->engine->complete_rule_loading();

if(is_capture_mode())

/// TODO: in a future change we can unify how we open different engines...
/// today we use: flags, env variables, configuration files...
if(is_capture_mode()) /* Savefile engine. */
{
// Try to open the trace file as a
// capture file first.
try {
m_state->inspector->open(m_options.trace_filename);
try
{
m_state->inspector->open_savefile(m_options.trace_filename, 0);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
return run_result::fatal("Cannot open trace filename " + m_options.trace_filename + " for reading: " + e.what());
}
}
else
else if(m_state->m_plugin_name != "") /* plugin engine. */
{
try
{
if(m_options.userspace)
{
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
//
// Falco uses a ptrace(2) based userspace implementation.
// Regardless of the implementation, the underlying method remains the same.
m_state->inspector->open_udig();
}
else if(m_options.gvisor_config != "")
{
falco_logger::log(LOG_INFO, "Enabled event collection from gVisor. Configuration path: " + m_options.gvisor_config);
m_state->inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
}
else
{
m_state->inspector->open();
}
m_state->inspector->open_plugin(m_state->m_plugin_name, m_state->m_plugin_open_params);
falco_logger::log(LOG_INFO, "Starting capture with plugin: " + m_state->m_plugin_name + "\n");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Cannot use '" + m_state->m_plugin_name + "' plugin: " + e.what());
}
}
else if(m_options.userspace) /* udig engine. */
{
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
//
// Falco uses a ptrace(2) based userspace implementation.
// Regardless of the implementation, the underlying method remains the same.
try
{
m_state->inspector->open_udig(m_state->config->m_single_buffer_dimension);
falco_logger::log(LOG_INFO, "Starting capture with udig\n");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Cannot use udig: " + std::string(e.what()));
}
}
else if(m_options.gvisor_config != "") /* gvisor engine. */
{
try
{
m_state->inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
falco_logger::log(LOG_INFO, "Starting capture with gVisor. Configuration path: " + m_options.gvisor_config);
}
catch(sinsp_exception &e)
{
// If syscall input source is enabled and not through userspace instrumentation
if (is_syscall_source_enabled() && !m_options.userspace)
return run_result::fatal("Cannot use gVisor: " + std::string(e.what()));
}
}
else if(m_options.modern_bpf) /* modern BPF engine. */
{
try
{
m_state->inspector->open_modern_bpf(m_state->config->m_single_buffer_dimension);
falco_logger::log(LOG_INFO, "Starting capture with modern BPF probe.");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Cannot use the modern BPF probe: " + std::string(e.what()));
}
}
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
{
try
{
const char *bpf_probe_path = std::getenv(FALCO_BPF_ENV_VARIABLE);
/* If the path is empty try to load the probe from the default path. */
if(strncmp(bpf_probe_path, "", 1) == 0)
{
// Try to insert the Falco kernel module
if(system("modprobe " DRIVER_NAME " > /dev/null 2> /dev/null"))
const char *home = std::getenv("HOME");
if(!home)
{
falco_logger::log(LOG_ERR, "Unable to load the driver.\n");
return run_result::fatal("Cannot get the env variable 'HOME'");
}
m_state->inspector->open();
}
else
{
return run_result::fatal(e.what());
char full_path[MAX_PROBE_PATH_SIZE];
snprintf(full_path, MAX_PROBE_PATH_SIZE, "%s/%s", home, FALCO_PROBE_BPF_FILEPATH);
bpf_probe_path = full_path;
}
m_state->inspector->open_bpf(m_state->config->m_single_buffer_dimension, bpf_probe_path);
falco_logger::log(LOG_INFO, "Starting capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
}
catch(sinsp_exception &e)
{
return run_result::fatal("Cannot use the BPF probe: " + std::string(e.what()));
}
}
else /* Kernel module (default). */
{
try
{
m_state->inspector->open_kmod(m_state->config->m_single_buffer_dimension);
falco_logger::log(LOG_INFO, "Starting capture with Kernel module.");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Cannot use the kernel module: " + std::string(e.what()));
}
}

/// TODO: we can add a method to the inspector that tells us what
/// is the underline engine used. Right now we print something only
/// in case of BPF engine
if(m_state->inspector->is_bpf_enabled())
if(m_state->config->m_single_buffer_dimension != 0)
{
falco_logger::log(LOG_INFO, "Falco is using the BPF probe\n");
falco_logger::log(LOG_INFO, "Buffer dimension: " + std::to_string(m_state->config->m_single_buffer_dimension) + " bytes\n");
}

// This must be done after the open
Expand Down
3 changes: 3 additions & 0 deletions userspace/falco/app_cmdline_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ void cmdline_options::define()
("g,gvisor-config", "Parse events from gVisor using the specified configuration file. A falco-compatible configuration file can be generated with --gvisor-generate-config and can be used for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/tmp/gvisor.sock"), "<socket_path>")
("gvisor-root", "gVisor root directory for storage of container state. Equivalent to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
#endif
#ifdef HAS_MODERN_BPF
("modern-bpf", "Use BPF modern probe to capture new events.", cxxopts::value(modern_bpf)->default_value("false"))
Comment on lines +171 to +172
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to write here something about the fact that is experimental

#endif
("i", "Print all events that are ignored by default (i.e. without the -A flag) and exit.", cxxopts::value(print_ignored_events)->default_value("false"))
#ifndef MINIMAL_BUILD
Expand Down
1 change: 1 addition & 0 deletions userspace/falco/app_cmdline_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class cmdline_options {
std::vector<std::string> validate_rules_filenames;
bool verbose;
bool print_version_info;
bool modern_bpf;

bool parse(int argc, char **argv, std::string &errstr);

Expand Down
3 changes: 3 additions & 0 deletions userspace/falco/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class application {
bool terminate;
bool reopen_outputs;

std::string m_plugin_name;
std::string m_plugin_open_params;

std::shared_ptr<falco_configuration> config;
std::shared_ptr<falco_outputs> outputs;
std::shared_ptr<falco_engine> engine;
Expand Down
4 changes: 4 additions & 0 deletions userspace/falco/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
m_webserver_ssl_enabled = m_config->get_scalar<bool>("webserver.ssl_enabled", false);
m_webserver_ssl_certificate = m_config->get_scalar<string>("webserver.ssl_certificate", "/etc/falco/falco.pem");

// we put this value in the configuration file because in this way we can change the dimension
// at every reload.
m_single_buffer_dimension = m_config->get_scalar<uint64_t>("single_buffer_dimension", 0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should or could we be doing more to ensure end user parameter input is not bogus? This would apply to any parameter and is not specific to this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what concern the validity checks for sure we have to implement them in the final drivers since they are the end users of this info, we can put also something in Falco, BTW if we use the page number as a value there is not too much to check probably that it is greater than 0 and that it is a power of 2, right?


std::list<string> syscall_event_drop_acts;
m_config->get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions");

Expand Down
3 changes: 3 additions & 0 deletions userspace/falco/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ class falco_configuration
uint32_t m_metadata_download_chunk_wait_us;
uint32_t m_metadata_download_watch_freq_sec;

// Dimension of a single buffer in our drivers (BPF, kmod, modern BPF)
uint64_t m_single_buffer_dimension;

std::vector<plugin_config> m_plugins;

private:
Expand Down