From 17bd53c29cf5362c3eecf93a02f43906606c97f8 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Thu, 4 Jun 2026 16:31:09 -0600 Subject: [PATCH] rust/ffi: add flow accessors to flow wrapper Ticket: #8599 --- examples/plugins/rust/src/mod.rs | 26 +++++++++- rust/ffi/src/flow.rs | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/examples/plugins/rust/src/mod.rs b/examples/plugins/rust/src/mod.rs index f5c34a0a59..b3ec8e99fe 100644 --- a/examples/plugins/rust/src/mod.rs +++ b/examples/plugins/rust/src/mod.rs @@ -98,8 +98,32 @@ fn log_eve_wrapped( jb.set_string("example", "eve-callback")?; jb.set_string("has_flow", if f.is_some() { "true" } else { "false" })?; - // If we have a flow, log something from flow storage. + // If we have a flow, show the `Flow` wrapper accessors and log something + // from flow storage. if let Some(f) = f { + let src_ip = f + .source_address() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "".to_string()); + let dst_ip = f + .destination_address() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "".to_string()); + let toserver = f.to_server_packet_count(); + let toclient = f.to_client_packet_count(); + + jb.open_object("flow_accessors")?; + jb.set_string("src_ip", &src_ip)?; + jb.set_uint("src_port", f.source_port() as u64)?; + jb.set_string("dest_ip", &dst_ip)?; + jb.set_uint("dest_port", f.destination_port() as u64)?; + jb.set_uint("ip_proto", f.ip_protocol() as u64)?; + jb.set_uint("app_proto", f.app_protocol() as u64)?; + jb.set_uint("toserver_pkts", toserver as u64)?; + jb.set_uint("toclient_pkts", toclient as u64)?; + jb.set_uint("last_seen", f.last_time().as_secs())?; + jb.close()?; + if let Some(state) = flow_storage.get(f) { jb.set_uint("flow_packets", state.packets)?; } diff --git a/rust/ffi/src/flow.rs b/rust/ffi/src/flow.rs index 4c6b3760d9..e2a0b4ca64 100644 --- a/rust/ffi/src/flow.rs +++ b/rust/ffi/src/flow.rs @@ -17,7 +17,9 @@ use std::ffi::CString; use std::marker::PhantomData; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::os::raw::c_void; +use std::time::Duration; use suricata_sys::sys::{ self, Packet, SCFlowGetStorageById, SCFlowRegisterFinishCallback, SCFlowRegisterInitCallback, @@ -58,6 +60,92 @@ impl<'a> Flow<'a> { fn as_mut_ptr(&mut self) -> *mut sys::Flow { self.flow } + + /// Return the time of the last flow update as a `Duration` since the epoch. + pub fn last_time(&self) -> Duration { + let mut secs: u64 = 0; + let mut usecs: u64 = 0; + unsafe { + sys::SCFlowGetLastTimeAsParts(self.as_ptr(), &mut secs, &mut usecs); + } + Duration::new(secs, usecs as u32 * 1000) + } + + /// Return the flow flags. + pub fn flags(&self) -> u64 { + unsafe { sys::SCFlowGetFlags(self.as_ptr()) } + } + + /// Return true if the flow is IPv4. + pub fn is_ipv4(&self) -> bool { + unsafe { sys::SCFlowIsIPv4(self.as_ptr()) } + } + + /// Return true if the flow is IPv6. + pub fn is_ipv6(&self) -> bool { + unsafe { sys::SCFlowIsIPv6(self.as_ptr()) } + } + + /// Return the flow IP protocol. + pub fn ip_protocol(&self) -> u8 { + unsafe { sys::SCFlowGetIPProtocol(self.as_ptr()) } + } + + /// Return the flow app-layer protocol. + pub fn app_protocol(&self) -> sys::AppProto { + unsafe { sys::SCFlowGetAppProtocol(self.as_ptr()) } + } + + /// Return the flow source port. + pub fn source_port(&self) -> u16 { + unsafe { sys::SCFlowGetSourcePort(self.as_ptr()) } + } + + /// Return the flow destination port. + pub fn destination_port(&self) -> u16 { + unsafe { sys::SCFlowGetDestinationPort(self.as_ptr()) } + } + + /// Return an owned copy the flow's source address. + pub fn source_address(&self) -> Option { + let ptr = unsafe { sys::SCFlowGetSourceAddressAsRawPtr(self.as_ptr()) }; + self.address_from_ptr(ptr) + } + + /// Return an owned copy the flow's destination address. + pub fn destination_address(&self) -> Option { + let ptr = unsafe { sys::SCFlowGetDestinationAddressAsRawPtr(self.as_ptr()) }; + self.address_from_ptr(ptr) + } + + /// Return the number of packets seen to-server. + pub fn to_server_packet_count(&self) -> u32 { + unsafe { sys::SCFlowGetToServerPacketCount(self.as_ptr()) } + } + + /// Return the number of packets seen to-client. + pub fn to_client_packet_count(&self) -> u32 { + unsafe { sys::SCFlowGetToClientPacketCount(self.as_ptr()) } + } + + fn address_from_ptr(&self, ptr: *const u8) -> Option { + if ptr.is_null() { + return None; + } + if self.is_ipv4() { + let bytes = unsafe { std::slice::from_raw_parts(ptr, 4) }; + Some(IpAddr::V4(Ipv4Addr::new( + bytes[0], bytes[1], bytes[2], bytes[3], + ))) + } else if self.is_ipv6() { + let bytes = unsafe { std::slice::from_raw_parts(ptr, 16) }; + let mut addr = [0; 16]; + addr.copy_from_slice(bytes); + Some(IpAddr::V6(Ipv6Addr::from(addr))) + } else { + None + } + } } /// A typed handle to a per-flow storage slot.