Skip to main content

falco_plugin/tables/import/table/
mod.rs

1use crate::tables::data::{seal, FieldTypeId, Key, TableData, Value};
2use crate::tables::import::field::Field;
3use crate::tables::import::runtime::NoMetadata;
4use crate::tables::import::runtime_table_validator::RuntimeTableValidator;
5use crate::tables::import::table::raw::{IterationResult, RawTable};
6use crate::tables::import::traits::{Entry, TableAccess, TableMetadata};
7use crate::tables::import::{entry, FieldInfo};
8use crate::tables::TableReader;
9use crate::tables::TableWriter;
10use crate::tables::TablesInput;
11use anyhow::Error;
12use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_field_t};
13use std::ffi::CStr;
14use std::marker::PhantomData;
15use std::ops::ControlFlow;
16
17pub(crate) mod raw;
18
19/// # A table imported via the Falco plugin API
20#[derive(Debug)]
21pub struct Table<K, E = entry::Entry<NoMetadata<()>>, M = <E as Entry>::Metadata> {
22    pub(crate) raw_table: RawTable,
23    pub(crate) metadata: M,
24    pub(crate) is_nested: bool,
25    pub(crate) key_type: PhantomData<K>,
26    pub(crate) entry_type: PhantomData<E>,
27}
28
29impl<K, E, M> TableAccess for Table<K, E, M>
30where
31    K: Key,
32    E: Entry<Metadata = M>,
33    M: TableMetadata + Clone,
34{
35    type Key = K;
36    type Entry = E;
37    type Metadata = M;
38
39    fn new(raw_table: RawTable, metadata: Self::Metadata, is_nested: bool) -> Self {
40        Self {
41            raw_table,
42            metadata,
43            is_nested,
44            key_type: PhantomData,
45            entry_type: PhantomData,
46        }
47    }
48
49    fn get_entry(
50        &self,
51        reader_vtable: &impl TableReader,
52        key: &Self::Key,
53    ) -> Result<Self::Entry, Error>
54    where
55        Self::Key: Key,
56        Self::Entry: Entry,
57    {
58        Table::get_entry(self, reader_vtable, key)
59    }
60}
61
62impl<K, E, M> Table<K, E, M>
63where
64    K: Key,
65    E: Entry<Metadata = M>,
66    M: TableMetadata + Clone,
67{
68    /// Look up an entry in `table` corresponding to `key`
69    pub fn get_entry(&self, reader_vtable: &impl TableReader, key: &K) -> Result<E, Error> {
70        let raw_entry = self.raw_table.get_entry(reader_vtable, key)?;
71        Ok(E::new(
72            raw_entry,
73            self.raw_table.table,
74            self.metadata.clone(),
75        ))
76    }
77
78    /// Erase a table entry by key
79    pub fn erase(&self, writer_vtable: &impl TableWriter, key: &K) -> Result<(), Error> {
80        unsafe { self.raw_table.erase(writer_vtable, key) }
81    }
82
83    /// Attach an entry to a table key (insert an entry to the table)
84    pub fn insert(
85        &self,
86        reader_vtable: &impl TableReader,
87        writer_vtable: &impl TableWriter,
88        key: &K,
89        entry: E,
90    ) -> Result<E, Error> {
91        let raw_entry = unsafe {
92            self.raw_table
93                .insert(reader_vtable, writer_vtable, key, entry.into_raw())
94        }?;
95        Ok(E::new(
96            raw_entry,
97            self.raw_table.table,
98            self.metadata.clone(),
99        ))
100    }
101}
102
103impl<K, E, M> Table<K, E, M>
104where
105    E: Entry<Metadata = M>,
106    M: TableMetadata + Clone,
107{
108    pub(crate) fn new_without_key(
109        raw_table: RawTable,
110        metadata: E::Metadata,
111        is_nested: bool,
112    ) -> Self {
113        Self {
114            raw_table,
115            metadata,
116            is_nested,
117            key_type: PhantomData,
118            entry_type: PhantomData,
119        }
120    }
121
122    pub(crate) fn table_validator(&self) -> RuntimeTableValidator {
123        let ptr = if self.is_nested {
124            std::ptr::null_mut()
125        } else {
126            self.raw_table.table
127        };
128        RuntimeTableValidator::new(ptr)
129    }
130
131    /// Create a new table entry (not yet attached to a key)
132    pub fn create_entry(&self, writer_vtable: &impl TableWriter) -> Result<E, Error> {
133        let raw_entry = self.raw_table.create_entry(writer_vtable)?;
134        Ok(E::new(
135            raw_entry,
136            self.raw_table.table,
137            self.metadata.clone(),
138        ))
139    }
140
141    /// Remove all entries from the table
142    pub fn clear(&self, writer_vtable: &impl TableWriter) -> Result<(), Error> {
143        self.raw_table.clear(writer_vtable)
144    }
145
146    /// # List the available fields
147    pub fn list_fields(&self, tables_input: &TablesInput) -> &[FieldInfo] {
148        self.raw_table.list_fields(tables_input)
149    }
150
151    /// # Get a table field by name
152    ///
153    /// The field must exist in the table and must be of the type `V`, otherwise an error
154    /// will be returned.
155    ///
156    /// Note that for regular tables the field objects remembers the table it was retrieved from
157    /// and accessing an entry from a different table will cause an error at runtime.
158    /// However, for nested tables there's no such validation in the Rust SDK (because using
159    /// the same fields across different nested tables is critical for supporting nested tables)
160    pub fn get_field<V: Value + ?Sized>(
161        &self,
162        tables_input: &TablesInput,
163        name: &CStr,
164    ) -> Result<Field<V, E>, Error> {
165        let field = self.raw_table.get_field(tables_input, name)?;
166        Ok(Field::new(field, self.table_validator()))
167    }
168
169    /// # Get a nested table field
170    ///
171    /// This method takes a closure and executes it with a nested table as an argument.
172    /// It's used to get (at runtime) field descriptors for nested table fields.
173    ///
174    /// You will usually just use the derive macro which hides all the complexity, but if you
175    /// need to handle nested tables at runtime, you can use this method to get the table field
176    /// and all subfields you need like this:
177    ///
178    /// ```ignore
179    /// // get the parent table
180    /// let thread_table: Table<i64> = input.get_table(c"threads")?;
181    ///
182    /// let (fd_field, fd_type_field) =
183    ///     thread_table.get_table_field(input, c"file_descriptors", |table| {
184    ///         table.get_field(input, c"type")
185    ///     })?;
186    /// ```
187    ///
188    /// The call to `get_table_field` takes a closure, which is passed a reference to
189    /// a sample entry in the nested table (enough to manage its fields) and returns a tuple
190    /// of `(the table field, (whatever_the_closure_returns))`
191    pub fn get_table_field<NK, V, U, F, R>(
192        &self,
193        tables_input: &TablesInput,
194        name: &CStr,
195        func: F,
196    ) -> Result<(Field<V, E>, R), Error>
197    where
198        NK: Key,
199        for<'a> V::AssocData: From<&'a M>,
200        V: Value + ?Sized,
201        U: Entry,
202        U::Metadata: for<'a> From<&'a V::AssocData>,
203        F: FnOnce(&Table<(), U>) -> Result<R, Error>,
204    {
205        let field = self.raw_table.get_field::<V>(tables_input, name)?;
206        let metadata = U::Metadata::from(&field.assoc_data);
207
208        let fields = unsafe {
209            self.raw_table
210                .with_subtable::<NK, _, _>(field.field, tables_input, |subtable| {
211                    let owned = RawTable {
212                        table: subtable.table,
213                    };
214                    let table = Table::new_without_key(owned, metadata, true);
215                    func(&table)
216                })??
217        };
218
219        let field = Field::new(field, self.table_validator());
220        Ok((field, fields))
221    }
222
223    /// # Add a table field
224    ///
225    /// The field will have the specified name and the type is derived from the generic argument.
226    ///
227    /// Note that for regular tables the field objects remembers the table it was retrieved from
228    /// and accessing an entry from a different table will cause an error at runtime.
229    /// However, for nested tables there's no such validation in the Rust SDK (because using
230    /// the same fields across different nested tables is critical for supporting nested tables)
231    pub fn add_field<V: Value<AssocData = ()> + ?Sized>(
232        &self,
233        tables_input: &TablesInput,
234        name: &CStr,
235    ) -> Result<Field<V, E>, Error> {
236        let field = self.raw_table.add_field(tables_input, name)?;
237        Ok(Field::new(field, self.table_validator()))
238    }
239
240    /// # Get the table name
241    ///
242    /// This method returns an error if the name cannot be represented as UTF-8
243    pub fn get_name(&self, reader_vtable: &impl TableReader) -> anyhow::Result<&str> {
244        self.raw_table.get_name(reader_vtable)
245    }
246
247    /// # Get the table size
248    ///
249    /// Return the number of entries in the table
250    pub fn get_size(&self, reader_vtable: &impl TableReader) -> anyhow::Result<usize> {
251        self.raw_table.get_size(reader_vtable)
252    }
253
254    /// # Iterate over all entries in a table with mutable access
255    ///
256    /// The closure is called once for each table entry with a corresponding entry
257    /// object as a parameter.
258    ///
259    /// The iteration stops when either all entries have been processed or the closure returns
260    /// [`ControlFlow::Break`].
261    pub fn iter_entries_mut<F>(
262        &self,
263        reader_vtable: &impl TableReader,
264        mut func: F,
265    ) -> anyhow::Result<IterationResult>
266    where
267        F: FnMut(&mut E) -> ControlFlow<()>,
268    {
269        self.raw_table.iter_entries_mut(reader_vtable, move |raw| {
270            let mut entry = E::new(raw, self.raw_table.table, self.metadata.clone());
271            func(&mut entry)
272        })
273    }
274}
275
276impl<K, E, M> seal::Sealed for Table<K, E, M> {}
277
278impl<K, E, M> TableData for Table<K, E, M> {
279    const TYPE_ID: FieldTypeId = FieldTypeId::Table;
280
281    fn to_data(&self) -> ss_plugin_state_data {
282        ss_plugin_state_data {
283            table: self.raw_table.table,
284        }
285    }
286}
287
288impl<K, E, M> Value for Table<K, E, M>
289where
290    K: Key + 'static,
291    E: Entry<Metadata = M> + 'static,
292    M: TableMetadata + Clone + 'static,
293{
294    type AssocData = M;
295    type Value<'a>
296        = Self
297    where
298        Self: 'a;
299
300    unsafe fn from_data_with_assoc<'a>(
301        data: &ss_plugin_state_data,
302        assoc: &Self::AssocData,
303    ) -> Self::Value<'a> {
304        let table = unsafe { RawTable { table: data.table } };
305        Table::new(table, assoc.clone(), true)
306    }
307
308    unsafe fn get_assoc_from_raw_table(
309        table: &RawTable,
310        field: *mut ss_plugin_table_field_t,
311        tables_input: &TablesInput,
312    ) -> Result<Self::AssocData, Error> {
313        unsafe {
314            table.with_subtable::<K, _, _>(field, tables_input, |subtable| {
315                M::new(subtable, tables_input)
316            })?
317        }
318    }
319}