use std::hash::Hash;
use std::marker::PhantomData;
use crate::PacketView;
use crate::Timestamp;
use crate::datagram_driver::FlowDatagramDriver;
use crate::extractor::FlowExtractor;
use crate::session::{DatagramParser, SessionEvent, SessionParser};
use crate::session_driver::FlowSessionDriver;
use crate::tracker::FlowTrackerConfig;
use super::Event;
pub(super) trait DriverSlot<K, M>: Send {
fn track(&mut self, view: PacketView<'_>, ts: Timestamp) -> Vec<Event<K, M>>;
fn sweep(&mut self, now: Timestamp) -> Vec<Event<K, M>>;
fn finish(&mut self) -> Vec<Event<K, M>>;
}
pub(super) struct ConcreteSlot<E, P, M, F>
where
E: FlowExtractor,
P: SessionParser,
{
pub(super) driver: FlowSessionDriver<E, P, ()>,
pub(super) lift: F,
pub(super) parser_kind: &'static str,
pub(super) ports: Option<smallvec::SmallVec<[u16; 4]>>,
pub(super) _marker: PhantomData<M>,
}
impl<E, P, M, F> ConcreteSlot<E, P, M, F>
where
E: FlowExtractor,
P: SessionParser + Clone,
{
pub(super) fn new(
extractor: E,
parser: P,
config: FlowTrackerConfig,
ports: Option<smallvec::SmallVec<[u16; 4]>>,
monotonic_timestamps: bool,
lift: F,
) -> Self
where
E: Clone,
{
let parser_kind = parser.parser_kind();
Self {
driver: FlowSessionDriver::with_config(extractor, parser, config)
.with_monotonic_timestamps(monotonic_timestamps),
lift,
parser_kind,
ports,
_marker: PhantomData,
}
}
}
impl<E, P, M, F> DriverSlot<E::Key, M> for ConcreteSlot<E, P, M, F>
where
E: FlowExtractor + Send,
E::Key: Hash + Eq + Clone + Send + 'static,
P: SessionParser + Send + 'static,
P::Message: Send + 'static,
F: Fn(P::Message) -> M + Send + 'static,
M: Send + 'static,
{
fn track(&mut self, view: PacketView<'_>, ts: Timestamp) -> Vec<Event<E::Key, M>> {
if let Some(ports) = &self.ports
&& !view_matches_ports(view, ports)
{
return Vec::new();
}
let parser_kind = self.parser_kind;
self.driver
.track(view)
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, ts, parser_kind))
.collect()
}
fn sweep(&mut self, now: Timestamp) -> Vec<Event<E::Key, M>> {
let parser_kind = self.parser_kind;
self.driver
.sweep(now)
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, now, parser_kind))
.collect()
}
fn finish(&mut self) -> Vec<Event<E::Key, M>> {
let parser_kind = self.parser_kind;
self.driver
.finish()
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, Timestamp::MAX, parser_kind))
.collect()
}
}
pub(super) struct ConcreteDatagramSlot<E, D, M, F>
where
E: FlowExtractor,
D: DatagramParser,
{
pub(super) driver: FlowDatagramDriver<E, D, ()>,
pub(super) lift: F,
pub(super) parser_kind: &'static str,
pub(super) ports: Option<smallvec::SmallVec<[u16; 4]>>,
pub(super) _marker: PhantomData<M>,
}
impl<E, D, M, F> ConcreteDatagramSlot<E, D, M, F>
where
E: FlowExtractor,
D: DatagramParser + Clone,
{
pub(super) fn new(
extractor: E,
parser: D,
config: FlowTrackerConfig,
ports: Option<smallvec::SmallVec<[u16; 4]>>,
monotonic_timestamps: bool,
lift: F,
) -> Self
where
E: Clone,
{
let parser_kind = parser.parser_kind();
Self {
driver: FlowDatagramDriver::with_config(extractor, parser, config)
.with_monotonic_timestamps(monotonic_timestamps),
lift,
parser_kind,
ports,
_marker: PhantomData,
}
}
}
impl<E, D, M, F> DriverSlot<E::Key, M> for ConcreteDatagramSlot<E, D, M, F>
where
E: FlowExtractor + Send,
E::Key: Hash + Eq + Clone + Send + 'static,
D: DatagramParser + Send + 'static,
D::Message: Send + 'static,
F: Fn(D::Message) -> M + Send + 'static,
M: Send + 'static,
{
fn track(&mut self, view: PacketView<'_>, ts: Timestamp) -> Vec<Event<E::Key, M>> {
if let Some(ports) = &self.ports
&& !view_matches_ports(view, ports)
{
return Vec::new();
}
let parser_kind = self.parser_kind;
self.driver
.track(view)
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, ts, parser_kind))
.collect()
}
fn sweep(&mut self, now: Timestamp) -> Vec<Event<E::Key, M>> {
let parser_kind = self.parser_kind;
self.driver
.sweep(now)
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, now, parser_kind))
.collect()
}
fn finish(&mut self) -> Vec<Event<E::Key, M>> {
let parser_kind = self.parser_kind;
self.driver
.finish()
.into_iter()
.filter_map(|e| lift_event(e, &self.lift, Timestamp::MAX, parser_kind))
.collect()
}
}
fn view_matches_ports(view: PacketView<'_>, ports: &[u16]) -> bool {
use crate::extract::parse::{self, ParsedL4};
let Some(parsed) = parse::parse_eth(view.frame) else {
return false;
};
let (sport, dport) = match parsed.l4 {
Some(ParsedL4::Tcp(t)) => (t.src_port, t.dst_port),
Some(ParsedL4::Udp(u)) => (u.src_port, u.dst_port),
_ => return false,
};
ports.contains(&sport) || ports.contains(&dport)
}
pub(super) fn lift_event_pub<K, A, B, F>(
ev: SessionEvent<K, A>,
lift: &F,
fallback_ts: Timestamp,
slot_kind: &'static str,
) -> Option<Event<K, B>>
where
F: Fn(A) -> B,
{
lift_event(ev, lift, fallback_ts, slot_kind)
}
fn lift_event<K, A, B, F>(
ev: SessionEvent<K, A>,
lift: &F,
fallback_ts: Timestamp,
slot_kind: &'static str,
) -> Option<Event<K, B>>
where
F: Fn(A) -> B,
{
match ev {
SessionEvent::Application {
key,
side,
message,
ts,
parser_kind,
} => Some(Event::Message {
key,
side,
message: lift(message),
ts,
parser_kind,
}),
SessionEvent::Closed {
key,
reason,
stats,
l4: _,
} => Some(Event::ParserClosed {
key,
parser_kind: slot_kind,
reason,
ts: if stats.last_seen == Timestamp::default() {
fallback_ts
} else {
stats.last_seen
},
}),
SessionEvent::Started { .. }
| SessionEvent::FlowAnomaly { .. }
| SessionEvent::TrackerAnomaly { .. }
| SessionEvent::FlowTick { .. } => None,
}
}