use flowscope::{AnomalyKind, EndReason, FlowSide, FlowStats, L4Proto, TcpInfo, Timestamp};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ProtocolEvent<K> {
FlowStarted {
key: K,
ts: Timestamp,
l4: Option<L4Proto>,
},
FlowEstablished {
key: K,
ts: Timestamp,
l4: Option<L4Proto>,
},
FlowPacket {
key: K,
side: FlowSide,
len: usize,
ts: Timestamp,
tcp: Option<TcpInfo>,
},
FlowEnded {
key: K,
reason: EndReason,
stats: FlowStats,
history: flowscope::HistoryString,
l4: Option<L4Proto>,
ts: Timestamp,
},
FlowTick {
key: K,
stats: FlowStats,
ts: Timestamp,
},
ParserClosed {
key: K,
parser_kind: &'static str,
reason: EndReason,
ts: Timestamp,
},
FlowAnomaly {
key: K,
kind: AnomalyKind,
ts: Timestamp,
},
TrackerAnomaly {
kind: AnomalyKind,
ts: Timestamp,
},
Message {
key: K,
side: FlowSide,
parser_kind: &'static str,
message: ProtocolMessage,
ts: Timestamp,
},
}
impl<K> ProtocolEvent<K> {
pub fn key(&self) -> Option<&K> {
match self {
ProtocolEvent::FlowStarted { key, .. }
| ProtocolEvent::FlowEstablished { key, .. }
| ProtocolEvent::FlowPacket { key, .. }
| ProtocolEvent::FlowEnded { key, .. }
| ProtocolEvent::FlowTick { key, .. }
| ProtocolEvent::ParserClosed { key, .. }
| ProtocolEvent::FlowAnomaly { key, .. }
| ProtocolEvent::Message { key, .. } => Some(key),
ProtocolEvent::TrackerAnomaly { .. } => None,
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
ProtocolEvent::FlowStarted { ts, .. }
| ProtocolEvent::FlowEstablished { ts, .. }
| ProtocolEvent::FlowPacket { ts, .. }
| ProtocolEvent::FlowEnded { ts, .. }
| ProtocolEvent::FlowTick { ts, .. }
| ProtocolEvent::ParserClosed { ts, .. }
| ProtocolEvent::FlowAnomaly { ts, .. }
| ProtocolEvent::TrackerAnomaly { ts, .. }
| ProtocolEvent::Message { ts, .. } => *ts,
}
}
pub fn parser_kind(&self) -> Option<&'static str> {
match self {
ProtocolEvent::Message { parser_kind, .. }
| ProtocolEvent::ParserClosed { parser_kind, .. } => Some(parser_kind),
_ => None,
}
}
pub fn is_flow_event(&self) -> bool {
!matches!(
self,
ProtocolEvent::Message { .. } | ProtocolEvent::ParserClosed { .. }
)
}
pub fn is_parser_event(&self) -> bool {
matches!(
self,
ProtocolEvent::Message { .. } | ProtocolEvent::ParserClosed { .. }
)
}
pub fn anomaly_kind(&self) -> Option<&AnomalyKind> {
match self {
ProtocolEvent::FlowAnomaly { kind, .. }
| ProtocolEvent::TrackerAnomaly { kind, .. } => Some(kind),
_ => None,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ProtocolMessage {
#[cfg(feature = "http")]
Http(flowscope::http::HttpMessage),
#[cfg(feature = "dns")]
Dns(flowscope::dns::DnsMessage),
#[cfg(feature = "tls")]
Tls(flowscope::tls::TlsMessage),
#[cfg(feature = "tls")]
TlsHandshake(flowscope::tls::TlsHandshake),
#[cfg(feature = "icmp")]
Icmp(flowscope::icmp::IcmpMessage),
}
#[cfg(test)]
mod tests {
use super::*;
type Key = u32;
#[test]
fn protocol_event_timestamp_flow_started() {
let evt: ProtocolEvent<Key> = ProtocolEvent::FlowStarted {
key: 7,
l4: None,
ts: Timestamp::new(100, 0),
};
assert_eq!(evt.timestamp(), Timestamp::new(100, 0));
assert_eq!(evt.key(), Some(&7));
assert!(evt.is_flow_event());
assert!(!evt.is_parser_event());
assert_eq!(evt.parser_kind(), None);
}
#[test]
fn protocol_event_tracker_anomaly_has_no_key() {
let evt: ProtocolEvent<Key> = ProtocolEvent::TrackerAnomaly {
kind: AnomalyKind::FlowTableEvictionPressure {
evicted_in_tick: 1,
evicted_total: 1,
},
ts: Timestamp::new(200, 0),
};
assert_eq!(evt.key(), None);
assert_eq!(evt.timestamp(), Timestamp::new(200, 0));
assert!(evt.anomaly_kind().is_some());
}
#[test]
fn parser_kind_visible_on_parser_events() {
let evt: ProtocolEvent<Key> = ProtocolEvent::ParserClosed {
key: 1,
parser_kind: "http/1",
reason: EndReason::Fin,
ts: Timestamp::new(5, 0),
};
assert_eq!(evt.parser_kind(), Some("http/1"));
assert!(evt.is_parser_event());
assert!(!evt.is_flow_event());
}
}