falco_plugin/tables/
mod.rs

1//! # Creating and accessing tables
2//!
3//! Tables are a mechanism to share data between plugins (and Falco core). There are three major
4//! concepts that relate to working with Falco plugin tables:
5//! - a table is a collection of entries, each under a different key, like a hash map or a SQL
6//!   table with a single primary key
7//! - an entry is a struct containing the actual values (corresponding to an entry in the hash map
8//!   or a row in the SQL table)
9//! - a field is a descriptor for a particular item in an entry. It does not have an equivalent
10//!   in the hash map analogy, but corresponds to a column in the SQL table. In particular, a field
11//!   is not attached to any particular entry.
12//!
13//! ## Example (in pseudocode)
14//!
15//! Consider a table called `threads` that has two fields:
16//! ```ignore
17//! struct Thread {
18//!     uid: u64,
19//!     comm: CString,
20//! }
21//! ```
22//!
23//! and uses the thread id (`tid: u64`) as the key. To read the `comm` of the thread with tid 1,
24//! you would need the following operations:
25//!
26//! ```ignore
27//! // get the table (at initialization time)
28//! let threads_table = get_table("threads");
29//!
30//! // get the field (at initialization time)
31//! let comm_field = threads_table.get_field("comm");
32//!
33//! // get an entry in the table (during parsing or extraction)
34//! let tid1_entry = threads_table.get_entry(1);
35//!
36//! // get the field value from an entry
37//! let comm = tid1_entry.get_field_value(comm_field);
38//! ```
39//!
40//! The Rust SDK tries to hide this and expose a more struct-oriented approach, though you can
41//! access fields in entries manually if you want (e.g. if you only know the field name at runtime).
42//!
43//! # Supported field types
44//!
45//! The following types can be used in fields visible over the plugin API:
46//! - integer types (u8/i8, u16/i16, u32/i32, u64/i64)
47//! - the bool type
48//! - CString
49//!
50//! Any other types are not supported, including in particular e.g. collections (`Vec<T>`),
51//! enums or any structs.
52//!
53//! # Nested tables
54//!
55//! Fields can also have a table type. This amounts to nested tables, like:
56//! ```ignore
57//! let fd_type = threads[tid].file_descriptors[fd].fd_type;
58//! ```
59//!
60//! One important limitation is that you cannot add a nested table at runtime, so the only
61//! nested tables that exist are defined by the plugin (or Falco core) which owns the parent table.
62//!
63//! # Exporting and importing tables
64//!
65//! Tables can be exported (exposed to other plugins) using the [`export`] module.
66//!
67//! Existing tables (from other plugins) can be imported using the [`import`] module.
68//!
69//! See the corresponding modules' documentation for details.
70//!
71//! # Access control
72//!
73//! Not all plugins are created equal when it comes to accessing tables. Only
74//! [parse plugins](`crate::parse::ParsePlugin`), [listen plugins](`crate::listen::CaptureListenPlugin`)
75//! and [extract plugins](`crate::extract::ExtractPlugin`) can access tables. Moreover, during field
76//! extraction you can only read tables, not write them.
77//!
78//! To summarize:
79//!
80//! | Plugin type | Initialization phase | Action phase ^1 |
81//! |-------------|----------------------|-----------------|
82//! | source      | no access            | no access       |
83//! | parse       | full access          | read/write      |
84//! | extract     | full access ^2       | read only       |
85//! | listen      | full access          | n/a ^3          |
86//! | async       | no access            | no access       |
87//!
88//! **Notes**:
89//! 1. "Action phase" is anything that happens after [`crate::base::Plugin::new`] returns, i.e.
90//!    event generation, parsing/extraction or any background activity (in async plugins).
91//!
92//! 2. Even though you can create tables and fields during initialization of an extract plugin,
93//!    there's no way to modify them later (create table entries or write to fields), so it's
94//!    more useful to constrain yourself to looking up existing tables/fields.
95//!
96//! 3. Listen plugins don't really have an action phase as they only expose methods to run
97//!    on capture start/stop. The routines they spawn cannot access tables, since the table
98//!    API is explicitly not thread safe (but with the `thread-safe-tables` feature you can
99//!    safely access tables from Rust plugins across many threads).
100//!
101//! ## Access control implementation
102//!
103//! Access control is implemented by requiring a particular object to actually perform table
104//! operations:
105//! - [`TablesInput`] to manage (look up/create) tables and fields
106//! - [`TableReader`] to look up table entries and get field values
107//! - [`TableWriter`] to create entries and write field values
108//!
109//! These get passed to your plugin whenever a particular class of operations is allowed.
110//! Note that [`crate::base::Plugin::new`] receives an `Option<&TablesInput>` and the option
111//! is populated only for parsing and extraction plugins (source and async plugins receive `None`).
112//!
113//! # The flow of using tables
114//!
115//! The access controls described above push you into structuring your plugins in a specific way.
116//! You cannot e.g. define tables in a source plugin, which is good, since that would break
117//! when reading capture files (the source plugin is not involved in that case). To provide
118//! a full-featured plugin that generates events, maintains some state and exposes it via
119//! extracted fields, you need separate capabilities (that may live in a single plugin or be
120//! spread across different ones):
121//! - a source plugin *only* generates events
122//! - a parse plugin creates the state tables and updates them during event parsing
123//! - an extract plugin reads the tables and returns field values
124//!
125//! # Dynamic fields
126//!
127//! Tables can have fields added to them at runtime, from other plugins than the one that
128//! created them (you can add dynamic fields to tables you created too, but that makes little sense).
129//!
130//! These fields behave just like fields defined statically in the table and can be used by plugins
131//! loaded after the current one. This can be used to e.g. add some data to an existing table
132//! in a parse plugin and expose it in an extract plugin.
133//!
134//! # Thread safety
135//!
136//! Tables in the Falco plugin API are explicitly *not* thread safe. However, when you enable
137//! the `thread-safe-tables` feature, tables exported from your plugin become thread-safe, so you
138//! can use them from your plugin (e.g. in a separate thread) concurrently to other plugins
139//! (in the main thread).
140
141pub(crate) use vtable::fields::TableFields;
142pub(crate) use vtable::reader::private::TableReaderImpl;
143pub use vtable::reader::LazyTableReader;
144pub use vtable::reader::TableReader;
145pub use vtable::reader::ValidatedTableReader;
146pub(crate) use vtable::writer::private::TableWriterImpl;
147pub use vtable::writer::LazyTableWriter;
148pub use vtable::writer::TableWriter;
149pub use vtable::writer::ValidatedTableWriter;
150pub use vtable::TablesInput;
151
152mod data;
153pub mod export;
154pub mod import;
155mod vtable;
156
157// for macro use only
158#[doc(hidden)]
159pub use crate::tables::data::{Key, Value};
160
161// for macro use only
162#[doc(hidden)]
163pub use crate::tables::data::FieldTypeId;