From 91b9dda0bf13c8fff692081dbd1a7effc7b10a25 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Mon, 4 May 2026 16:28:01 -0600 Subject: [PATCH] doc: document flow life cycle callback API Document for C and Rust, as the C documentation was missing. Ticket: #8446 --- .../extending/flow-lifecycle-callbacks.rst | 131 ++++++++++++++++++ doc/userguide/devguide/extending/index.rst | 1 + 2 files changed, 132 insertions(+) create mode 100644 doc/userguide/devguide/extending/flow-lifecycle-callbacks.rst diff --git a/doc/userguide/devguide/extending/flow-lifecycle-callbacks.rst b/doc/userguide/devguide/extending/flow-lifecycle-callbacks.rst new file mode 100644 index 0000000000..ff1195ce3a --- /dev/null +++ b/doc/userguide/devguide/extending/flow-lifecycle-callbacks.rst @@ -0,0 +1,131 @@ +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, Packet, ThreadVars}; + use suricata_ffi::SCLogNotice; + + fn flow_init(_tv: *mut ThreadVars, f: *mut Flow, _p: *const Packet) { + SCLogNotice!("flow initialized: {:p}", f); + } + + fn flow_update(_tv: *mut ThreadVars, f: *mut Flow, p: *mut Packet) { + SCLogNotice!("flow updated: {:p} packet: {:p}", f, p); + } + + fn flow_finish(_tv: *mut ThreadVars, f: *mut Flow) { + SCLogNotice!("flow finished: {:p}", f); + } + + 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. diff --git a/doc/userguide/devguide/extending/index.rst b/doc/userguide/devguide/extending/index.rst index 043b372594..5c755ea9f8 100644 --- a/doc/userguide/devguide/extending/index.rst +++ b/doc/userguide/devguide/extending/index.rst @@ -12,3 +12,4 @@ Extending Suricata output/index.rst output/eve-filetypes.rst output/eve-hooks.rst + flow-lifecycle-callbacks.rst