#![cfg(all(feature = "tokio", feature = "flow"))]
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use flowscope::Timestamp;
use netring::anomaly::sink::NoopSink;
use netring::ctx::{CounterRegistry, Ctx, FlowStateRegistry, SourceIdx, StateMap};
use netring::monitor::{Dispatcher, HandlerRegistry};
use netring::protocol::builtin::Tcp;
use netring::protocol::event_typed::FlowStarted;
#[derive(Default)]
struct PerFlowBytes {
bytes: u64,
}
fn key() -> flowscope::extract::FiveTupleKey {
flowscope::extract::FiveTupleKey::new(
flowscope::L4Proto::Tcp,
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 12345),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 80),
)
}
#[test]
fn flow_state_mut_lazy_creates_then_returns_same() {
let last_seen = Arc::new(AtomicU64::new(0));
let l = Arc::clone(&last_seen);
let mut reg = HandlerRegistry::default();
reg.register::<FlowStarted<Tcp>, _, _>(move |_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
let s = ctx
.flow_state_mut::<PerFlowBytes>()
.expect("flow_state registered + ctx has a flow key");
s.bytes += 100;
l.store(s.bytes, Ordering::Relaxed);
Ok(())
});
let mut disp: Dispatcher = reg.into_dispatcher().unwrap();
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = FlowStateRegistry::default();
flow_states.register::<PerFlowBytes>(Duration::from_secs(60));
let evt = FlowStarted::<Tcp>::new(key(), Some(flowscope::L4Proto::Tcp), Timestamp::new(0, 0));
let mut ctx = Ctx::new(
Some(key()),
Timestamp::new(0, 0),
SourceIdx(0),
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
);
disp.dispatch::<FlowStarted<Tcp>>(&evt, &mut ctx).unwrap();
assert_eq!(last_seen.load(Ordering::Relaxed), 100);
disp.dispatch::<FlowStarted<Tcp>>(&evt, &mut ctx).unwrap();
assert_eq!(last_seen.load(Ordering::Relaxed), 200);
disp.dispatch::<FlowStarted<Tcp>>(&evt, &mut ctx).unwrap();
assert_eq!(last_seen.load(Ordering::Relaxed), 300);
}
#[test]
fn flow_state_mut_returns_none_without_flow_key() {
let observed = Arc::new(std::sync::Mutex::new(None::<bool>));
let o = Arc::clone(&observed);
let mut reg = HandlerRegistry::default();
reg.register::<FlowStarted<Tcp>, _, _>(move |_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
let opt = ctx.flow_state_mut::<PerFlowBytes>();
*o.lock().unwrap() = Some(opt.is_some());
Ok(())
});
let mut disp: Dispatcher = reg.into_dispatcher().unwrap();
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = FlowStateRegistry::default();
flow_states.register::<PerFlowBytes>(Duration::from_secs(60));
let evt = FlowStarted::<Tcp>::new(key(), Some(flowscope::L4Proto::Tcp), Timestamp::new(0, 0));
let mut ctx = Ctx::new(
None,
Timestamp::new(0, 0),
SourceIdx(0),
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
);
disp.dispatch::<FlowStarted<Tcp>>(&evt, &mut ctx).unwrap();
assert_eq!(*observed.lock().unwrap(), Some(false));
}
#[test]
fn flow_state_mut_returns_none_for_unregistered_type() {
let observed = Arc::new(std::sync::Mutex::new(None::<bool>));
let o = Arc::clone(&observed);
#[derive(Default)]
struct Unregistered {
_x: u32,
}
let mut reg = HandlerRegistry::default();
reg.register::<FlowStarted<Tcp>, _, _>(move |_evt: &FlowStarted<Tcp>, ctx: &mut Ctx<'_>| {
let opt = ctx.flow_state_mut::<Unregistered>();
*o.lock().unwrap() = Some(opt.is_some());
Ok(())
});
let mut disp: Dispatcher = reg.into_dispatcher().unwrap();
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = FlowStateRegistry::default();
let evt = FlowStarted::<Tcp>::new(key(), Some(flowscope::L4Proto::Tcp), Timestamp::new(0, 0));
let mut ctx = Ctx::new(
Some(key()),
Timestamp::new(0, 0),
SourceIdx(0),
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
);
disp.dispatch::<FlowStarted<Tcp>>(&evt, &mut ctx).unwrap();
assert_eq!(*observed.lock().unwrap(), Some(false));
}
#[test]
fn flow_state_separate_per_key() {
let key2 = flowscope::extract::FiveTupleKey::new(
flowscope::L4Proto::Tcp,
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 1, 1)), 12345),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 1, 2)), 443),
);
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = FlowStateRegistry::default();
flow_states.register::<PerFlowBytes>(Duration::from_secs(60));
let mut ctx = Ctx::new(
Some(key()),
Timestamp::new(0, 0),
SourceIdx(0),
&mut state,
&mut sink,
&mut counters,
&mut flow_states,
);
{
let s = ctx.flow_state_mut::<PerFlowBytes>().unwrap();
s.bytes = 100;
}
ctx.flow = Some(key2);
{
let s = ctx.flow_state_mut::<PerFlowBytes>().unwrap();
assert_eq!(
s.bytes, 0,
"second flow gets its own default-initialized slot"
);
s.bytes = 7;
}
ctx.flow = Some(key());
{
let s = ctx.flow_state_mut::<PerFlowBytes>().unwrap();
assert_eq!(s.bytes, 100);
}
}