diff --git a/core/src/filter/ptree.rs b/core/src/filter/ptree.rs index 430f8d90..102c0f2a 100644 --- a/core/src/filter/ptree.rs +++ b/core/src/filter/ptree.rs @@ -390,6 +390,16 @@ impl PTree { return; } + if !matches!(self.filter_layer, FilterLayer::PacketContinue) && predicate.req_packet() { + // To get similar behavior, users should subscribe to individual mbufs or + // the mbuf list, then filter within the callback. + // Because (for now) all packets would need to be tracked anyway, doing this + // is equivalent performance-wise to implementing similar functionality in the + // framework. + panic!("Cannot access per-packet fields (e.g., TCP flags, length) after packet filter.\n\ + Subscribe to `ZcFrame` or PacketList instead."); + } + // Predicate is already present if node.has_descendant(predicate) { diff --git a/datatypes/src/static_type.rs b/datatypes/src/static_type.rs index c26f9739..21877aaa 100644 --- a/datatypes/src/static_type.rs +++ b/datatypes/src/static_type.rs @@ -2,11 +2,10 @@ //! A data type is considered "static" if it can be inferred at or before //! the first packet in a connection and it stays constant throughout a connection. -use super::{FromSubscription, StaticData}; +use super::StaticData; use pnet::datalink::MacAddr; use retina_core::conntrack::conn_id::FiveTuple; use retina_core::conntrack::pdu::L4Pdu; -use retina_core::filter::SubscriptionSpec; /// Subscribable alias for [`retina_core::FiveTuple`] impl StaticData for FiveTuple { @@ -32,19 +31,6 @@ impl StaticData for EtherTCI { } } -use proc_macro2::Span; -use quote::quote; - -/// The string literal representing a matched filter. -pub type FilterStr<'a> = &'a str; - -impl<'a> FromSubscription for FilterStr<'a> { - fn from_subscription(spec: &SubscriptionSpec) -> proc_macro2::TokenStream { - let str = syn::LitStr::new(&spec.filter, Span::call_site()); - quote! { &#str } - } -} - /// The src/dst MAC of a connection #[derive(Clone, Debug)] pub struct EthAddr { diff --git a/datatypes/src/typedefs.rs b/datatypes/src/typedefs.rs index fbb85f50..ed169de8 100644 --- a/datatypes/src/typedefs.rs +++ b/datatypes/src/typedefs.rs @@ -2,14 +2,16 @@ //! Newly-defined datatypes must be added to the DATATYPES map in this module. use lazy_static::lazy_static; -use retina_core::filter::{DataType, Level}; +use proc_macro2::Span; +use quote::quote; +use retina_core::filter::{DataType, Level, SubscriptionSpec}; use std::collections::HashMap; use crate::*; -// To add a datatype, add it to the following map -// This is read by the filtergen crate. lazy_static! { + /// To add a datatype, add it to the following map + /// This is read by the filtergen crate. pub static ref DATATYPES: HashMap<&'static str, DataType> = { HashMap::from([ ("ConnRecord", DataType::new_default_connection("ConnRecord")), @@ -84,27 +86,41 @@ lazy_static! { // Special cases: have specific conditions in generated code // \Note ideally these would be implemented more cleanly lazy_static! { - // To avoid copying, the `Tracked` structure in the framework -- - // built at compile time -- will track certain generic, raw datatypes - // if a subset of subscriptions require them. - // - // For example: buffering packets may be required as a pre-match action for a - // packet-level datatype; it may also be required if one or more subscriptions request - // a connection-level `PacketList`. Rather than maintaining these lists separately -- - // one for filtering and one for delivery -- the tracked packets are stored once. - // - // Core ID is a special case, as it cannot be derived from connection, - // session, or packet data. It is simpler to define it as a directly tracked datatype. + /// To avoid copying, the `Tracked` structure in the framework -- + /// built at compile time -- will track certain generic, raw datatypes + /// if a subset of subscriptions require them. + /// + /// For example: buffering packets may be required as a pre-match action for a + /// packet-level datatype; it may also be required if one or more subscriptions request + /// a connection-level `PacketList`. Rather than maintaining these lists separately -- + /// one for filtering and one for delivery -- the tracked packets are stored once. + /// + /// Core ID is a special case, as it cannot be derived from connection, + /// session, or packet data. It is simpler to define it as a directly tracked datatype. + /// + /// The directly tracked datatypes are: PacketList, SessionList, and CoreId pub static ref DIRECTLY_TRACKED: HashMap<&'static str, &'static str> = HashMap::from([ ("PacketList", "packets"), ("SessionList", "sessions"), ("CoreId", "core_id") ]); - // Another special case -- datatype is the matched filter as a string literal. - // \TODO ideally this would be a map to from_subscription function pointers + // See `FilterStr` pub static ref FILTER_STR: &'static str = "FilterStr"; } +/// A list of all packets (zero-copy) seen in the connection. +/// For TCP connections, these packets will be in post-reassembly order. pub type PacketList = Vec; +/// A list of all sessions (zero-copy) parsed in the connection. pub type SessionList = Vec; + +/// The string literal representing a matched filter. +pub type FilterStr<'a> = &'a str; + +impl<'a> FromSubscription for FilterStr<'a> { + fn from_subscription(spec: &SubscriptionSpec) -> proc_macro2::TokenStream { + let str = syn::LitStr::new(&spec.filter, Span::call_site()); + quote! { &#str } + } +}