mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.4 KiB
ReStructuredText
176 lines
5.4 KiB
ReStructuredText
Flow Life Cycle Callbacks
|
|
#########################
|
|
|
|
Flow lifecycle callbacks let plugins and library users observe when
|
|
Suricata initializes a flow, updates a flow with a packet, and finishes
|
|
with a flow.
|
|
|
|
These callbacks are useful for maintaining plugin state that follows the
|
|
lifetime of a Suricata flow. For example, a plugin can allocate per-flow
|
|
state from the init callback, update it as packets are seen, and perform
|
|
final accounting from the finish callback.
|
|
|
|
C API
|
|
*****
|
|
|
|
Flow Init Callback
|
|
==================
|
|
|
|
The init callback is called when Suricata initializes a flow.
|
|
|
|
.. literalinclude:: ../../../../src/flow-callbacks.h
|
|
:language: c
|
|
:start-at: /** \brief Function type for flow initialization callbacks.
|
|
:end-at: typedef void (*SCFlowInitCallbackFn)(ThreadVars *tv, Flow *f, const Packet *p, void *user);
|
|
|
|
Register an init callback with ``SCFlowRegisterInitCallback``.
|
|
|
|
.. literalinclude:: ../../../../src/flow-callbacks.h
|
|
:language: c
|
|
:start-at: /** \brief Register a flow init callback.
|
|
:end-at: bool SCFlowRegisterInitCallback(SCFlowInitCallbackFn fn, void *user);
|
|
|
|
Flow Update Callback
|
|
====================
|
|
|
|
The update callback is called when Suricata updates a flow with a packet.
|
|
|
|
.. literalinclude:: ../../../../src/flow-callbacks.h
|
|
:language: c
|
|
:start-at: /** \brief Function type for flow update callbacks.
|
|
:end-at: typedef void (*SCFlowUpdateCallbackFn)(ThreadVars *tv, Flow *f, Packet *p, void *user);
|
|
|
|
Register an update callback with ``SCFlowRegisterUpdateCallback``.
|
|
|
|
.. literalinclude:: ../../../../src/flow-callbacks.h
|
|
:language: c
|
|
:start-at: /** \brief Register a flow update callback.
|
|
:end-at: bool SCFlowRegisterUpdateCallback(SCFlowUpdateCallbackFn fn, void *user);
|
|
|
|
Flow Finish Callback
|
|
====================
|
|
|
|
The finish callback is called when Suricata is done with a flow.
|
|
|
|
.. literalinclude:: ../../../../src/flow-callbacks.h
|
|
:language: c
|
|
:start-at: /** \brief Function type for flow finish callbacks.
|
|
:end-at: typedef void (*SCFlowFinishCallbackFn)(ThreadVars *tv, Flow *f, void *user);
|
|
|
|
Register a finish callback with ``SCFlowRegisterFinishCallback``.
|
|
|
|
.. code-block:: c
|
|
|
|
bool SCFlowRegisterFinishCallback(SCFlowFinishCallbackFn fn, void *user);
|
|
|
|
Example
|
|
=======
|
|
|
|
.. code-block:: c
|
|
|
|
static void ExampleFlowInit(ThreadVars *tv, Flow *f, const Packet *p, void *user)
|
|
{
|
|
SCLogNotice("flow initialized: %p", f);
|
|
}
|
|
|
|
static void ExampleFlowUpdate(ThreadVars *tv, Flow *f, Packet *p, void *user)
|
|
{
|
|
SCLogNotice("flow updated: %p packet: %p", f, p);
|
|
}
|
|
|
|
static void ExampleFlowFinish(ThreadVars *tv, Flow *f, void *user)
|
|
{
|
|
SCLogNotice("flow finished: %p", f);
|
|
}
|
|
|
|
static void ExampleInit(void)
|
|
{
|
|
SCFlowRegisterInitCallback(ExampleFlowInit, NULL);
|
|
SCFlowRegisterUpdateCallback(ExampleFlowUpdate, NULL);
|
|
SCFlowRegisterFinishCallback(ExampleFlowFinish, NULL);
|
|
}
|
|
|
|
Rust API
|
|
********
|
|
|
|
In Rust, use the ``suricata_ffi::flow`` module:
|
|
|
|
- ``flow::register_init_callback``
|
|
- ``flow::register_update_callback``
|
|
- ``flow::register_finish_callback``
|
|
|
|
The Rust wrappers register closures or function items and return
|
|
``Result<(), &'static str>``.
|
|
|
|
.. code-block:: rust
|
|
|
|
use suricata_ffi::flow::{self, Flow};
|
|
use suricata_ffi::thread::ThreadVars;
|
|
use suricata_sys::sys::Packet;
|
|
use suricata_ffi::SCLogNotice;
|
|
|
|
fn flow_init(_tv: &mut ThreadVars, f: &mut Flow, _p: *const Packet) {
|
|
SCLogNotice!("flow initialized: {:p}", f.as_ptr());
|
|
}
|
|
|
|
fn flow_update(_tv: &mut ThreadVars, f: &mut Flow, p: *mut Packet) {
|
|
SCLogNotice!("flow updated: {:p} packet: {:p}", f.as_ptr(), p);
|
|
}
|
|
|
|
fn flow_finish(_tv: &mut ThreadVars, f: &mut Flow) {
|
|
SCLogNotice!("flow finished: {:p}", f.as_ptr());
|
|
}
|
|
|
|
fn register_flow_callbacks() -> Result<(), &'static str> {
|
|
flow::register_init_callback(flow_init)?;
|
|
flow::register_update_callback(flow_update)?;
|
|
flow::register_finish_callback(flow_finish)?;
|
|
Ok(())
|
|
}
|
|
|
|
The raw pointers passed into callbacks are only valid for the duration
|
|
of the callback invocation and must not be stored. Rust callbacks must
|
|
not panic.
|
|
|
|
Flow Storage
|
|
============
|
|
|
|
``flow::FlowStorage<T>`` provides typed, per-flow storage backed by
|
|
Suricata's flow storage API. Each registered slot holds an independent value
|
|
of type ``T`` for every flow.
|
|
|
|
Register a slot once during initialization with
|
|
``FlowStorage::<T>::register``. Registration must happen before Suricata
|
|
finalizes its storage registration, which is the case during plugin
|
|
initialization.
|
|
|
|
.. code-block:: rust
|
|
|
|
use suricata_ffi::flow::{self, Flow, FlowStorage};
|
|
use suricata_ffi::thread::ThreadVars;
|
|
use suricata_sys::sys::Packet;
|
|
|
|
#[derive(Default)]
|
|
struct FlowState {
|
|
packets: u64,
|
|
}
|
|
|
|
fn register(storage: FlowStorage<FlowState>) -> Result<(), &'static str> {
|
|
flow::register_update_callback(move |tv, f, p| on_flow_update(storage, tv, f, p))
|
|
}
|
|
|
|
Values are owned by Suricata's flow storage and are dropped automatically when
|
|
the flow's storage is freed.
|
|
|
|
.. code-block:: rust
|
|
|
|
fn on_flow_init(storage: FlowStorage<FlowState>, _tv: &mut ThreadVars, f: &mut Flow, _p: *const Packet) {
|
|
let _ = storage.get_or_insert_with(f, FlowState::default);
|
|
}
|
|
|
|
fn on_flow_update(storage: FlowStorage<FlowState>, _tv: &mut ThreadVars, f: &mut Flow, _p: *mut Packet) {
|
|
if let Some(state) = storage.get_mut(f) {
|
|
state.packets += 1;
|
|
}
|
|
}
|