29.4.8. EVE Hooks
The EVE output provides a callback for additional data to be added to an EVE record before it is written.
It is important to note that it does not allow for modification of the EVE record due to the append only nature of Suricata's EVE output.
29.4.8.1. C API
29.4.8.1.1. Registration
In C, registering the callback is done with SCEveRegisterCallback.
/** \brief Register a callback for adding extra information to EVE logs.
*
* Allow users to register a callback for each EVE log. The callback
* is called just before the root object on the SCJsonBuilder is to be
* closed.
*
* New objects and fields can be appended, but existing entries cannot be modified.
*
* Packet and Flow will be provided if available, but will otherwise be
* NULL.
*
* Limitations: At this time the callbacks will only be called for EVE
* loggers that use SCJsonBuilder, notably this means it won't be called
* for stats records at this time.
*
* \returns true if callback is registered, false is not due to memory
* allocation error.
*/
bool SCEveRegisterCallback(SCEveUserCallbackFn fn, void *user);
29.4.8.1.2. Callback
The callback function is provided with an open SCJsonBuilder
instance just before being closed out with a final }. Additional
fields can be added with the SCJsonBuilder API.
/** \brief Function type for EVE callbacks.
*
* The function type for callbacks registered with
* SCEveRegisterCallback. This function will be called with the
* SCJsonBuilder just prior to the top-level object being closed. New
* fields may be added, however, there is no way to alter existing
* objects already added to the SCJsonBuilder.
*
* \param tv The ThreadVars for the thread performing the logging.
* \param p Packet if available.
* \param f Flow if available.
* \param user User data provided during callback registration.
*/
typedef void (*SCEveUserCallbackFn)(
ThreadVars *tv, const Packet *p, Flow *f, SCJsonBuilder *jb, void *user);
29.4.8.1.3. Example
For a real-life C example, see the ndpi plugin included in the
Suricata source.
That example demonstrates:
Registering an EVE callback during plugin initialization
Using thread-local storage to maintain state
Adding protocol-specific information to EVE records
Properly checking for NULL pointers before accessing data
29.4.8.2. Rust API
In Rust, use suricata_ffi::eve::register_callback. This wraps the C
API and lets the callback be registered as a Rust closure instead of a C
function pointer plus user pointer.
The closure receives:
tv: theThreadVarsfor the thread performing the loggingp: thePacket, if availablef: theFlow, if availablejb: a RustJsonBuilderwrapper for the current EVE record
Unlike the C API, the Rust callback returns Result<(), Error>. If it
returns Err, any JSON emitted by that callback is discarded.
use suricata_ffi::eve;
eve::register_callback(|_tv, _p, _f, jb| {
jb.open_object("my_plugin")?;
jb.set_string("key", "value")?;
jb.close()?;
Ok(())
}).expect("failed to register EVE callback");
The Rust callback is invoked at the same point, but it receives a
JsonBuilder wrapper instead of a raw SCJsonBuilder pointer.
The raw pointers passed into the callback are only valid for the duration of the callback and must not be stored. The callback must also not panic.
This API is safe for library and plugins.