diff options
Diffstat (limited to 'src/expr/mod.rs')
-rw-r--r-- | src/expr/mod.rs | 314 |
1 files changed, 143 insertions, 171 deletions
diff --git a/src/expr/mod.rs b/src/expr/mod.rs index dc59507..058b0cb 100644 --- a/src/expr/mod.rs +++ b/src/expr/mod.rs @@ -3,14 +3,14 @@ //! //! [`Rule`]: struct.Rule.html -use std::borrow::Cow; -use std::net::IpAddr; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; +use std::fmt::Debug; -use super::rule::Rule; -use crate::sys::{self, libc}; -use thiserror::Error; +use rustables_macros::nfnetlink_struct; + +use crate::error::DecodeError; +use crate::nlmsg::{NfNetlinkAttribute, NfNetlinkDeserializable}; +use crate::parser_impls::NfNetlinkList; +use crate::sys::{self, NFTA_EXPR_DATA, NFTA_EXPR_NAME}; mod bitwise; pub use self::bitwise::*; @@ -46,7 +46,7 @@ mod payload; pub use self::payload::*; mod reject; -pub use self::reject::{IcmpCode, Reject}; +pub use self::reject::{IcmpCode, Reject, RejectType}; mod register; pub use self::register::Register; @@ -54,189 +54,161 @@ pub use self::register::Register; mod verdict; pub use self::verdict::*; -mod wrapper; -pub use self::wrapper::ExpressionWrapper; - -#[derive(Debug, Error)] -pub enum DeserializationError { - #[error("The expected expression type doesn't match the name of the raw expression")] - /// The expected expression type doesn't match the name of the raw expression. - InvalidExpressionKind, - - #[error("Deserializing the requested type isn't implemented yet")] - /// Deserializing the requested type isn't implemented yet. - NotImplemented, - - #[error("The expression value cannot be deserialized to the requested type")] - /// The expression value cannot be deserialized to the requested type. - InvalidValue, - - #[error("A pointer was null while a non-null pointer was expected")] - /// A pointer was null while a non-null pointer was expected. - NullPointer, - - #[error( - "The size of a raw value was incoherent with the expected type of the deserialized value" - )] - /// The size of a raw value was incoherent with the expected type of the deserialized value/ - InvalidDataSize, - - #[error(transparent)] - /// Couldn't find a matching protocol. - InvalidProtolFamily(#[from] super::InvalidProtocolFamily), -} - -/// Trait for every safe wrapper of an nftables expression. pub trait Expression { - /// Returns the raw name used by nftables to identify the rule. - fn get_raw_name() -> *const libc::c_char; - - /// Try to parse the expression from a raw nftables expression, returning a - /// [DeserializationError] if the attempted parsing failed. - fn from_expr(_expr: *const sys::nftnl_expr) -> Result<Self, DeserializationError> - where - Self: Sized, - { - Err(DeserializationError::NotImplemented) - } - - /// Allocates and returns the low level `nftnl_expr` representation of this expression. The - /// caller to this method is responsible for freeing the expression. - fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr; + fn get_name() -> &'static str; } -/// A type that can be converted into a byte buffer. -pub trait ToSlice { - /// Returns the data this type represents. - fn to_slice(&self) -> Cow<'_, [u8]>; +#[derive(Clone, PartialEq, Eq, Default, Debug)] +#[nfnetlink_struct(nested = true, derive_decoder = false)] +pub struct RawExpression { + #[field(NFTA_EXPR_NAME)] + name: String, + #[field(NFTA_EXPR_DATA)] + data: ExpressionVariant, } -impl<'a> ToSlice for &'a [u8] { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Borrowed(self) +impl<T> From<T> for RawExpression +where + T: Expression, + ExpressionVariant: From<T>, +{ + fn from(val: T) -> Self { + RawExpression::default() + .with_name(T::get_name()) + .with_data(ExpressionVariant::from(val)) } } -impl<'a> ToSlice for &'a [u16] { - fn to_slice(&self) -> Cow<'_, [u8]> { - let ptr = self.as_ptr() as *const u8; - let len = self.len() * 2; - Cow::Borrowed(unsafe { std::slice::from_raw_parts(ptr, len) }) - } -} - -impl ToSlice for IpAddr { - fn to_slice(&self) -> Cow<'_, [u8]> { - match *self { - IpAddr::V4(ref addr) => addr.to_slice(), - IpAddr::V6(ref addr) => addr.to_slice(), +macro_rules! create_expr_variant { + ($enum:ident $(, [$name:ident, $type:ty])+) => { + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum $enum { + $( + $name($type), + )+ } - } -} -impl ToSlice for Ipv4Addr { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(self.octets().to_vec()) - } -} - -impl ToSlice for Ipv6Addr { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(self.octets().to_vec()) - } -} + impl $crate::nlmsg::NfNetlinkAttribute for $enum { + fn is_nested(&self) -> bool { + true + } + + fn get_size(&self) -> usize { + match self { + $( + $enum::$name(val) => val.get_size(), + )+ + } + } + + unsafe fn write_payload(&self, addr: *mut u8) { + match self { + $( + $enum::$name(val) => val.write_payload(addr), + )+ + } + } + } -impl ToSlice for u8 { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(vec![*self]) - } + $( + impl From<$type> for $enum { + fn from(val: $type) -> Self { + $enum::$name(val) + } + } + )+ + + impl $crate::nlmsg::AttributeDecoder for RawExpression { + fn decode_attribute( + &mut self, + attr_type: u16, + buf: &[u8], + ) -> Result<(), $crate::error::DecodeError> { + debug!("Decoding attribute {} in an expression", attr_type); + match attr_type { + x if x == sys::NFTA_EXPR_NAME => { + debug!("Calling {}::deserialize()", std::any::type_name::<String>()); + let (val, remaining) = String::deserialize(buf)?; + if remaining.len() != 0 { + return Err($crate::error::DecodeError::InvalidDataSize); + } + self.name = Some(val); + Ok(()) + }, + x if x == sys::NFTA_EXPR_DATA => { + // we can assume we have already the name parsed, as that's how we identify the + // type of expression + let name = self.name.as_ref() + .ok_or($crate::error::DecodeError::MissingExpressionName)?; + match name { + $( + x if x == <$type>::get_name() => { + debug!("Calling {}::deserialize()", std::any::type_name::<$type>()); + let (res, remaining) = <$type>::deserialize(buf)?; + if remaining.len() != 0 { + return Err($crate::error::DecodeError::InvalidDataSize); + } + self.data = Some(ExpressionVariant::from(res)); + Ok(()) + }, + )+ + name => { + info!("Unrecognized expression '{}', generating an ExpressionRaw", name); + self.data = Some(ExpressionVariant::ExpressionRaw(ExpressionRaw::deserialize(buf)?.0)); + Ok(()) + } + } + }, + _ => Err(DecodeError::UnsupportedAttributeType(attr_type)), + } + } + } + }; } -impl ToSlice for u16 { - fn to_slice(&self) -> Cow<'_, [u8]> { - let b0 = (*self & 0x00ff) as u8; - let b1 = (*self >> 8) as u8; - Cow::Owned(vec![b0, b1]) +create_expr_variant!( + ExpressionVariant, + [Bitwise, Bitwise], + [Cmp, Cmp], + [Conntrack, Conntrack], + [Counter, Counter], + [ExpressionRaw, ExpressionRaw], + [Immediate, Immediate], + [Log, Log], + [Lookup, Lookup], + [Masquerade, Masquerade], + [Meta, Meta], + [Nat, Nat], + [Payload, Payload], + [Reject, Reject] +); + +pub type ExpressionList = NfNetlinkList<RawExpression>; + +// default type for expressions that we do not handle yet +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExpressionRaw(Vec<u8>); + +impl NfNetlinkAttribute for ExpressionRaw { + fn get_size(&self) -> usize { + self.0.get_size() } -} -impl ToSlice for u32 { - fn to_slice(&self) -> Cow<'_, [u8]> { - let b0 = *self as u8; - let b1 = (*self >> 8) as u8; - let b2 = (*self >> 16) as u8; - let b3 = (*self >> 24) as u8; - Cow::Owned(vec![b0, b1, b2, b3]) + unsafe fn write_payload(&self, addr: *mut u8) { + self.0.write_payload(addr); } } -impl ToSlice for i32 { - fn to_slice(&self) -> Cow<'_, [u8]> { - let b0 = *self as u8; - let b1 = (*self >> 8) as u8; - let b2 = (*self >> 16) as u8; - let b3 = (*self >> 24) as u8; - Cow::Owned(vec![b0, b1, b2, b3]) +impl NfNetlinkDeserializable for ExpressionRaw { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + Ok((ExpressionRaw(buf.to_vec()), &[])) } } -impl<'a> ToSlice for &'a str { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::from(self.as_bytes()) +// Because we loose the name of the expression when parsing, this is the only expression +// where deserializing a message and then reserializing it is invalid +impl Expression for ExpressionRaw { + fn get_name() -> &'static str { + "unknown_expression" } } - -#[macro_export(local_inner_macros)] -macro_rules! nft_expr { - (bitwise mask $mask:expr,xor $xor:expr) => { - nft_expr_bitwise!(mask $mask, xor $xor) - }; - (cmp $op:tt $data:expr) => { - nft_expr_cmp!($op $data) - }; - (counter) => { - $crate::expr::Counter { nb_bytes: 0, nb_packets: 0} - }; - (ct $key:ident set) => { - nft_expr_ct!($key set) - }; - (ct $key:ident) => { - nft_expr_ct!($key) - }; - (immediate $expr:ident $value:expr) => { - nft_expr_immediate!($expr $value) - }; - (log group $group:ident prefix $prefix:expr) => { - nft_expr_log!(group $group prefix $prefix) - }; - (log group $group:ident) => { - nft_expr_log!(group $group) - }; - (log prefix $prefix:expr) => { - nft_expr_log!(prefix $prefix) - }; - (log) => { - nft_expr_log!() - }; - (lookup $set:expr) => { - nft_expr_lookup!($set) - }; - (masquerade) => { - $crate::expr::Masquerade - }; - (meta $expr:ident set) => { - nft_expr_meta!($expr set) - }; - (meta $expr:ident) => { - nft_expr_meta!($expr) - }; - (payload $proto:ident $field:ident) => { - nft_expr_payload!($proto $field) - }; - (verdict $verdict:ident) => { - nft_expr_verdict!($verdict) - }; - (verdict $verdict:ident $chain:expr) => { - nft_expr_verdict!($verdict $chain) - }; -} |