Falco Plugins API Reference
Introduction
This page documents the functions that make up the Falco plugins API. In most cases, you will not need to implement these functions directly. There are Go and C++ SDKs that provide an easier-to-use interface for plugin authors.
At a high level, the API functions can be grouped into three sections:
- Functions that provide info about the plugin, its events, and its fields.
- Functions that create/destroy plugins and open/close instances of the plugin (e.g. capture sessions).
- Functions that provide events and extract information from events.
Remember that there are two kinds of plugins: source plugins and extractor plugins. We'll go through the API functions for both kinds of plugins.
The C header file plugin_info.h enumerates all the API functions and associated structs/types, as they are used by the plugins framework. It can be included in your source code when writing your own plugin, to take advantage of values like PLUGIN_API_VERSION_XXX
, ss_plugin_type
, etc.
Remember, however, that from the perspective of the plugin, each function name has a prefix plugin_
e.g. plugin_get_required_api_version()
, plugin_get_type()
, etc.
Conventions For All Functions
The following conventions apply for all of the below API functions:
- Every function that returns a
const char*
must return a null-terminated C string. - All string values returned across the API are considered owned by the plugin and must remain valid for use by the plugin framework. Specifically, this means:
- For demographic functions like
plugin_get_name()
,plugin_get_description()
, the returned strings must remain valid until the plugin is destroyed. - When returning events via
plugin_next_batch
, both the array of structs and the data payloads inside each struct must remain valid until the next call toplugin_next_batch()
. - When returning extracted string values via
plugin_extract_fields
, every extracted string must remain valid until the next call toplugin_extract_fields()
.
- For demographic functions like
- For every function that returns an error, the plugin should save a meaningful error string that the framework can retrieve via a call to
plugin_get_last_error()
.
Source Plugin API Functions
Info Functions
const char* plugin_get_required_api_version() [Required: yes]
This function returns a string containing a semver version number e.g. "1.0.0", reflecting the version of the plugin API framework that this plugin requires. This is different than the version of the plugin itself, and should only have to change when the plugin API changes.
This is the first function the framework calls when loading a plugin. If the returned value is not semver-compatible with the version of the plugin API framework, the plugin will not be loaded.
For example, if the code implementing the plugin framework has version 1.1.0, and a plugin's plugin_get_required_api_version()
function returns 1.0.0, the plugin API is compatible and the plugin will be loaded. If the code implementing the plugin framework has version 2.0.0, and a plugin's plugin_get_required_api_version()
function returns 1.0.0, the API is not compatible and the plugin will not be loaded.
uint32_t plugin_get_type() [Required: yes]
This function returns the plugin type. The enum ss_plugin_type
in plugin_info.h
defines the possible plugin types:
typedef enum ss_plugin_type
{
TYPE_SOURCE_PLUGIN = 1,
TYPE_EXTRACTOR_PLUGIN = 2
}ss_plugin_type;
For source plugins, this function should always return the value TYPE_SOURCE_PLUGIN
e.g. 1.
uint32_t plugin_get_id() [Required: yes]
This should return the event ID allocated to your plugin. During development and before receiving an official event ID, you can use the "Test" value of 999.
const char* plugin_get_{name,description,contact,version} [Required: yes]
These functions all return an C string, with memory owned by the plugin, that describe the plugin:
plugin_get_name
: Return the name of the plugin.plugin_get_description
: Return a short description of the plugin.plugin_get_contact
: Return a contact url/email/twitter account for the plugin authors.plugin_get_version
: Return the version of the plugin itself.
The recommended format for plugin_get_contact
is a url to a repository that contains the plugin source code (e.g. github.com/falcosecurity/plugins
) if at all possible.
const char* plugin_get_event_source() [Required: yes]
This function returns a C string, with memory owned by the plugin, containing the plugin's event source. This event source is used for:
- Associating Falco rules with plugin events--A Falco rule with a
source: gizmo
property will run on all events returned by the gizmo plugin'snext_batch()
function. - Linking together extractor plugins and source plugins. An extractor plugin can list a given event source like "gizmo" in its
get_extract_event_sources()
function, and the extractor plugin will get an opportunity to extract fields from all events returned by the gizmo plugin. - Ensuring that only one plugin at a time is loaded for a given source.
When defining a source, make sure it accurately describes the events from your plugin (e.g. use "cloudtrail" for aws cloudtrail events, not "json" or "logs") and doesn't overlap with the source of any other source plugin.
The only time where duplicate sources make sense are when a group of plugins can use a standard data format for a given event. For example, plugins might extract k8s_audit events from multiple cloud sources like gcp, azure, aws, etc. If they all format their events as json objects that have identical formats as one could obtain by using K8s Audit hooks, then it would make sense for the plugins to use the same source.
const char* plugin_get_fields() [Required: no]
This function should return the set of fields supported by the plugin. Remember, a field is a name (e.g. proc.name
) that can extract a value (e.g. nginx
) from an event (e.g. a syscall event involving a process). The return value is a string whose memory is owned by the plugin. The string is json formatted and contains an array of objects. Each object describes one field. Here's an example:
[
{"type": "string", "name": "gizmo.field1", "argRequired": true, "desc": "Describing field 1"},
{"type": "uint64", "name": "gizmo.field2", "desc": "Describing field 2", properties: ["hidden"]}
]
Although uncommon, this function is not required--a source plugin might inject events but not define any fields that extract values from events. This might be common if the extraction could be performed by extraction plugins.
Each object has the following properties:
type
: one of "string", "uint64"name
: a string with a name for the field. By convention, this is a dot-separated path of names. Use a consistent first name e.g. "ct.xxx" to help filter authors associate the field with a given plugin.argRequired
: (optional) If present and set to true, notes that the field requires an argument e.g.field[arg]
.display
: (optional) If present, a string that will be used to display the field instead of the name. Used in tools like wireshark.desc
: a string with a short description of the field. This will be used in help output so be concise and descriptive.properties
: (optional) If present, an array of additional properties that apply to the field. The value is an array of strings that can be one of the following:hidden
: Do not display the field when using programs likefalco --list
to list the set of supported fields.conversation
: This field is applicable for use in wireshark conversations, and denotes that the field represents one half of a "conversation" that can be shown in the conversations or endpoints view.info
: Also applicable for use in wireshark, and denotes that it should be appended to the "info" column in the wireshark event list.
When defining fields, keep the following guidelines in mind:
- Field names should generally have the plugin name/event source as the first component, and usually have one or two additional components. For example,
gizmo.pid
is preferred overgizmo.process.id.is
. If a plugin has a moderately large set of fields, using components to group fields may make sense (e.g.cloudtrail.s3.bytes.in
andcloudtrail.s3.bytes.out
). - Fields should be idempotent: for a given event, the value for a field should be the same regardless of when/where the event was generated.
- Fields should be neutral: define fields that extract properties of the event (e.g. "source ip address") rather than judgements (e.g. "source ip address is associated with a botnet").
const char* plugin_get_init_schema(ss_plugin_schema_type* schema_type) [Required: no]
This function returns a schema that describes the configuration to be passed to init()
during plugin initialization. The return value is a C string, with memory owned by the plugin, representing the configuration schema. The type of schema returned is compliant with the ss_plugin_schema_type
enumeration, and is written inside the schema_type
output argument.
Although this function is non-required, it is common to implement it due to the benefits it brings. If get_init_schema
is correctly implemented, the init()
function can assume the passed-in configuration string to always be well-formed. The plugin framework will take care of automatically parsing it against the provided schema and generating ad-hoc errors accordingly. This also serves as a piece of documentation for users about how the plugin needs to be configured.
Currently, the plugin framework only supports the JSON Schema format, which is represented by the SS_PLUGIN_SCHEMA_JSON
enum value. If a plugin defines a JSON Schema, the framework will require the init configuration string to be a valid json-formatted string.
Writing the dummy enum value SS_PLUGIN_SCHEMA_NONE
inside schema_type
is equivalent to avoiding implementing the get_init_schema
function itself, which ends up with the framework treating the init configuration as an opaque string with no additional checks.
const char* plugin_list_open_params(ss_plugin_t* s, ss_plugin_rc* rc) [Required: no]
This function returns a list of suggested values that are valid parameters for the open()
plugin function.
Although non-required, this function is useful to instruct users about potential valid parameters for opening a stream of events. Implementing this function also brings additional usage documentation for the plugin, and allows makes it more usable with automated tools.
The returned value is a json string, with memory owned by the plugin, which contains an array of objects. Each object describes one suggested value for the open()
function. Here's an example:
[
{"value": "resource1", "desc": "An example of openable resource"},
{"value": "resource2", "desc": "Another example of openable resource"}
]
Each object has the following properties:
value
: a string usable as a parameter foropen()
desc
: (optional) a string with that describes the meaning ofvalue
.
Instance/Capture Management Functions
ss_plugin_t* plugin_init(const char* config, int32_t* rc) [Required: yes]
This function passes plugin-level configuration to the plugin to create its plugin-level state. The plugin then returns a pointer to that state, as a ss_plugin_t *
handle. The handle is never examined by the plugin framework and is never freed. It is only provided as the argument to later API functions.
When managing plugin-level state, keep the following in mind:
- It is the plugin's responsibility to allocate plugin state (memory, open files, etc) and free that state later in
plugin_destroy
. - The plugin state should be the only location for state (e.g. no globals, no per-thread state). Although unlikely, the framework may choose to call
plugin_init
multiple times for the same plugin, and this should be supported by the plugin. - The returned rc value should be
SS_PLUGIN_SUCCESS
(0) on success,SS_PLUGIN_FAILURE
(1) on failure. - On failure, make sure to return a meaningful error message in the next call to
plugin_get_last_error()
. - On failure, the plugin framework will not call plugin_destroy, so do not return any allocated state.
The format of the config string is entirely determined by the plugin author, and by default is passed unchanged from Falco/the application using the plugin framework to the plugin. However, semi-structured formats like JSON/YAML are preferable to free-form text. In those cases, the plugin author can provide a schema describing the config string contents by implementing the optional get_init_schema
function. If so, the init()
function can assume the passed-in configuration string to always be well-formed, and can avoid performing any error handling. The plugin framework will take care of automatically parsing it against the provided schema and generating ad-hoc errors accordingly. Please refer to the documentation of get_init_schema
for more details.
void plugin_destroy(ss_plugin_t *s) [Required: yes]
This function frees any resources held in the ss_plugin_t
struct. Afterwards, the handle should be considered destroyed and no further API functions will be called with that handle.
ss_instance_t* plugin_open(ss_plugin_t* s, const char* params, int32_t* rc) [Required: yes]
This function is called to "open" a stream of events. The interpretation of a stream of events is up to the plugin author, but think of plugin_init
as initializing the plugin software, and plugin_open
as configuring the software to return events. Using a streaming audio analogy, plugin_init
turns on the app, and plugin_open
starts a streaming audio channel.
The same general guidelines apply for plugin_open
as do for plugin_init
:
- All state related to sourcing a stream of events should be in the returned
ss_instance_t
pointer. - Return 0 on success, 1 on error. Be ready to return an error via
plugin_get_last_error()
on error. - The plugin should support concurrent open sessions at once. Unlike plugin-level state, it's very likely that the plugin framework might call
plugin_open
multiple times for a given plugin. - On error, do not return any instance struct, as the plugin framework will not call
plugin_close
.
void plugin_close(ss_plugin_t* s, ss_instance_t* h) [Required: yes]
This function closes a stream of events previously started via a call to plugin_open
. Afterwards, the stream should be considered closed and the framework will not call plugin_next_batch
/plugin_extract_fields
with the same ss_instance_t
pointer.
Misc Functions
const char* plugin_get_last_error(ss_plugin_t* s)
This function is called by the framework after a prior call returned an error. The plugin should return a meaningful error string providing more information about the most recent error.
Events/Fields Related Functions
int32_t plugin_next_batch(ss_plugin_t* s, ss_instance_t* h, uint32_t *nevts, ss_plugin_event **evts) [Required: yes]
This function is used to return a set of next events to the plugin framework, given a plugin state and open instance.
*evts
should be updated to an allocated contiguous array of ss_plugin_event
structs. The memory for the structs array is owned by the plugin and should be held until the next call to plugin_next_batch
. *nevts
should be updated with the number of events returned.
An event is represented by a ss_plugin_event
struct, which is the following:
typedef struct ss_plugin_event
{
uint64_t evtnum;
uint8_t *data;
uint32_t datalen;
uint64_t ts;
} ss_plugin_event;
The struct has the following members:
evtnum
: incremented for each event returned. Might not be contiguous.data
: pointer to a memory buffer pointer allocated by the plugin. The plugin will set it to point to the memory containing the next event. This memory is owned by the plugin and should be freed upon the next call toplugin_next_batch()
.datalen
: pointer to a 32bit integer. The plugin will set it the size of the buffer pointed by data.ts
: the event timestamp, in nanoseconds since the epoch. Can be (uint64_t)-1, in which case the engine will automatically fill the event time with the current time.
Filling the evtnum
struct member when returning events via plugin_next_batch
has no effect, because event numbers are assigned by the plugin framework.
This function should return:
SS_PLUGIN_SUCCESS
(0) on successSS_PLUGIN_FAILURE
(1) on failureSS_PLUGIN_TIMEOUT
(-1) on non-error but there are no events to return.SS_PLUGIN_EOF
(6) when the stream of events is complete.
If the plugin receives a SS_PLUGIN_FAILURE, it will close the stream of events by calling plugin_close
.
SS_PLUGIN_TIMEOUT should be returned whenever no events can be returned immediately. This ensures that the plugin framework is not stalled waiting for a response from plugin_next_batch
. When the framework receives a SS_PLUGIN_TIMEOUT, it will keep the stream of events open and call plugin_next_batch
again later.
const char* plugin_get_progress(ss_plugin_t* s, ss_instance_t* h, uint32_t* progress_pct) [Required: no]
This function is optional. If the plugin exports it, the framework will periodically call it after open to return how much of the event stream has been read. If a plugin does not provide a bounded stream of events (for example, events coming from a file or other source that has an ending), it should not export this function.
If not exported, the plugin framework will not print meaningful process indicators while processing event streams.
When called, the progress_pct pointer should be updated with the read progress, as a number between 0 (no data has been read) and 10000 (100% of the data has been read). This encoding allows the engine to print progress decimals without requiring to deal with floating point numbers (which could cause incompatibility problems with some languages).
The return value is an string representation of the read progress, with the memory owned by the plugin. This might include the progress percentage combined with additional context added by the plugin. The plugin can return NULL. In this case, the framework will use the progress_pct value instead.
const char* plugin_event_to_string(ss_plugin_t *s, const uint8_t *data, uint32_t datalen) [Required: yes]
This function is used to return a printable representation of an event. The memory is owned by the plugin and can be freed on the next call to plugin_event_to_string
. It is used in filtering/output expressions as the built-in field evt.plugininfo
.
The string representation should be on a single line and contain important information about the event. It is not necessary to return all information from the event. Simply return the most important fields/properties of the event that provide a useful default representation.
Here is an example output, from the cloudtrail plugin:
us-east-1 masters.some-demo.k8s.local s3 GetObject Size=0 URI=s3://some-demo-env/some-demo.k8s.local/backups/etcd/events/control/etcd-cluster-created
int32_t plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields) [Required: no]
This function is used to return the value for one or more field names that were returned in plugin_get_fields()
. The framework provides an event and an array of ss_plugin_extract_field
structs. Each struct has one field name/type, and the plugin fills in each struct with the corresponding value for that field.
This function is required if plugin_get_fields
is defined. If not defined, the plugin framework will not extract any values for events from this plugin.
The format of the ss_plugin_extract_field
struct is the following:
// The noncontiguous numbers are to maintain equality with underlying
// falcosecurity libs types.
typedef enum ss_plugin_field_type
{
FTYPE_UINT64 = 8,
FTYPE_STRING = 9
}ss_plugin_field_type;
typedef struct ss_plugin_extract_field
{
uint32_t fields_id;
const char* field;
const char* arg;
uint32_t ftype; // Has value from ss_plugin_field_type enum
bool field_present;
const char* res_str;
uint64_t res_u64;
} ss_plugin_extract_field;
For each struct, the plugin fills in field_id
/field
/arg
/ftype
with the field. field_id
is the index into the original list of fields returned by plugin_get_fields
, and allows for faster mapping to a plugin's set of fields. The plugin should fill in field_present
and either res_str
/res_u64
with the value for the field, depending on the field type ftype
. If the field type is FTYPE_STRING
, res_str should be updated to point to an string with the string value, with memory owned by the plugin. The plugin should retain this memory until the next call to plugin_extract_fields
.
If field_present
is set to false, the plugin framework assumes that res_str
/res_u64
is undefined and will not use it.
Extractor Plugin API Functions
With the exception of plugin_get_extract_event_sources
, almost all functions used by the Extractor Plugin API interface are identical to those in the Source Plugin API. See above for the definition and use of:
plugin_get_required_api_version
plugin_get_type
: for extractor plugins, this should returnTYPE_EXTRACTOR_PLUGIN
e.g. 1.plugin_init
plugin_destroy
plugin_get_last_error
plugin_free_mem
plugin_get_name
plugin_get_description
plugin_get_contact
plugin_get_version
plugin_get_fields
plugin_get_init_schema
plugin_extract_fields
The only difference is that for Extractor plugins, plugin_get_fields
/plugin_extract_fields
are both required.
const char* get_extract_event_sources() [Required: no]
This function allows an extractor plugin to restrict the kinds of events where the plugin's get_fields()
method will be called. The return value should be a string containing a json array of compatible event sources, with memory owned by the plugin. Here's an example:
["aws_cloudtrail", "gcp_cloudtrail"]
This implies that the extractor plugin can potentially extract values from events that have a source aws_cloudtrail
or gce_cloudtrail
.
This function is optional. If the plugin does not export this function the framework will assume the extractor plugin can potentially extract values from all events. The extractor plugin's extract_fields()
function will be called for all events for any field returned by the plugin's get_fields()
function.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.