use crate::view::PacketView;
use bitflags::bitflags;
pub trait FlowExtractor: Send + Sync + 'static {
type Key: Eq + std::hash::Hash + Clone + Send + Sync + 'static;
fn extract(&self, view: PacketView<'_>) -> Option<Extracted<Self::Key>>;
}
#[derive(Debug, Clone)]
pub struct Extracted<K> {
pub key: K,
pub orientation: Orientation,
pub l4: Option<L4Proto>,
pub tcp: Option<TcpInfo>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum Orientation {
Forward,
Reverse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "kind", content = "value", rename_all = "snake_case")
)]
pub enum L4Proto {
Tcp,
Udp,
Icmp,
IcmpV6,
Sctp,
Other(u8),
}
#[cfg(feature = "tracker")]
impl std::fmt::Display for L4Proto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(crate::obs::l4_label(Some(*self)))
}
}
impl crate::KeyFields for L4Proto {
fn proto_str(&self) -> Option<&'static str> {
Some(match self {
L4Proto::Tcp => "TCP",
L4Proto::Udp => "UDP",
L4Proto::Icmp => "ICMP",
L4Proto::IcmpV6 => "ICMPv6",
L4Proto::Sctp => "SCTP",
L4Proto::Other(_) => return None,
})
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct TcpInfo {
pub flags: TcpFlags,
pub seq: u32,
pub ack: u32,
pub payload_offset: usize,
pub payload_len: usize,
pub window: u16,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TcpFlags: u8 {
const FIN = 0b0000_0001;
const SYN = 0b0000_0010;
const RST = 0b0000_0100;
const PSH = 0b0000_1000;
const ACK = 0b0001_0000;
const URG = 0b0010_0000;
const ECE = 0b0100_0000;
const CWR = 0b1000_0000;
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for TcpFlags {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.bits().serialize(s)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for TcpFlags {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let raw = u8::deserialize(d)?;
Ok(TcpFlags::from_bits_retain(raw))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tcp_flags_basic() {
let f = TcpFlags::SYN | TcpFlags::ACK;
assert!(f.contains(TcpFlags::SYN));
assert!(f.contains(TcpFlags::ACK));
assert!(!f.contains(TcpFlags::FIN));
}
#[test]
fn extracted_clone() {
let e: Extracted<u32> = Extracted {
key: 42,
orientation: Orientation::Forward,
l4: Some(L4Proto::Tcp),
tcp: Some(TcpInfo {
flags: TcpFlags::SYN,
seq: 1,
ack: 0,
payload_offset: 54,
payload_len: 0,
window: 8192,
}),
};
let cloned = e.clone();
assert_eq!(cloned.key, 42);
assert_eq!(cloned.orientation, Orientation::Forward);
}
#[test]
fn l4_proto_eq() {
assert_eq!(L4Proto::Tcp, L4Proto::Tcp);
assert_ne!(L4Proto::Tcp, L4Proto::Udp);
assert_eq!(L4Proto::Other(1), L4Proto::Other(1));
assert_ne!(L4Proto::Other(1), L4Proto::Other(2));
}
}