use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use arc_swap::ArcSwap;
use flowscope::extract::{FiveTuple, FiveTupleKey};
use flowscope::{FlowExtractor, L4Proto, PacketView};
use super::predicate::{FieldSource, Predicate};
use crate::ctx::Ctx;
use crate::error::Result;
pub type PacketHandler =
Arc<dyn for<'a, 'c> Fn(&PacketView<'a>, &mut Ctx<'c>) -> Result<()> + Send + Sync>;
#[derive(Clone)]
pub struct PacketSubscription {
pub(crate) predicate: Arc<ArcSwap<Predicate>>,
pub(crate) handler: PacketHandler,
}
impl std::fmt::Debug for PacketSubscription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PacketSubscription")
.field("predicate", &self.predicate.load())
.field("handler", &"<fn>")
.finish()
}
}
pub(crate) struct PacketFields {
proto: L4Proto,
src: SocketAddr,
dst: SocketAddr,
}
impl PacketFields {
pub(crate) fn extract(
view: PacketView<'_>,
extractor: &FiveTuple,
) -> Option<(FiveTupleKey, Self)> {
let ex = extractor.extract(view)?;
let key = ex.key;
Some((
key,
Self {
proto: ex.l4.unwrap_or(key.proto),
src: key.a,
dst: key.b,
},
))
}
}
impl FieldSource for PacketFields {
fn l4proto(&self) -> Option<L4Proto> {
Some(self.proto)
}
fn src_port(&self) -> Option<u16> {
Some(self.src.port())
}
fn dst_port(&self) -> Option<u16> {
Some(self.dst.port())
}
fn src_ip(&self) -> Option<IpAddr> {
Some(self.src.ip())
}
fn dst_ip(&self) -> Option<IpAddr> {
Some(self.dst.ip())
}
}
pub(crate) fn packet_field_extractor() -> FiveTuple {
FiveTuple::directional()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::monitor::subscription::predicate::Atom;
fn udp_frame(src_port: u16, dst_port: u16) -> Vec<u8> {
let payload = [0u8; 4];
let mut f = Vec::new();
f.extend_from_slice(&[0x02, 0, 0, 0, 0, 1]); f.extend_from_slice(&[0x02, 0, 0, 0, 0, 2]); f.extend_from_slice(&[0x08, 0x00]); f.push(0x45);
f.push(0);
let ip_total = (20 + 8 + payload.len()) as u16;
f.extend_from_slice(&ip_total.to_be_bytes());
f.extend_from_slice(&[0, 0, 0, 0]);
f.push(64);
f.push(17); f.extend_from_slice(&[0, 0]);
f.extend_from_slice(&[10, 0, 0, 1]); f.extend_from_slice(&[10, 0, 0, 2]); f.extend_from_slice(&src_port.to_be_bytes());
f.extend_from_slice(&dst_port.to_be_bytes());
f.extend_from_slice(&((8 + payload.len()) as u16).to_be_bytes());
f.extend_from_slice(&[0, 0]);
f.extend_from_slice(&payload);
f
}
#[test]
fn packet_fields_extract_directional_src_dst() {
let frame = udp_frame(54321, 53);
let view = PacketView::new(&frame, flowscope::Timestamp::new(0, 0));
let (_key, fields) =
PacketFields::extract(view, &packet_field_extractor()).expect("udp frame extracts");
assert_eq!(fields.l4proto(), Some(L4Proto::Udp));
assert_eq!(fields.src_port(), Some(54321));
assert_eq!(fields.dst_port(), Some(53));
assert_eq!(fields.src_ip(), Some("10.0.0.1".parse().unwrap()));
assert_eq!(fields.dst_ip(), Some("10.0.0.2".parse().unwrap()));
}
#[test]
fn predicate_evaluates_against_packet_fields() {
let frame = udp_frame(54321, 53);
let view = PacketView::new(&frame, flowscope::Timestamp::new(0, 0));
let (_k, fields) = PacketFields::extract(view, &packet_field_extractor()).unwrap();
let dns =
Predicate::Atom(Atom::Proto(L4Proto::Udp)).and(Predicate::Atom(Atom::DstPort(53)));
assert!(dns.eval(&fields));
assert!(!Predicate::Atom(Atom::Proto(L4Proto::Tcp)).eval(&fields));
}
}