aboutsummaryrefslogtreecommitdiff
path: root/src/expr/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/expr/mod.rs')
-rw-r--r--src/expr/mod.rs314
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)
- };
-}