diff options
author | Simon THOBY <git@nightmared.fr> | 2022-11-13 18:32:22 +0100 |
---|---|---|
committer | Simon THOBY <git@nightmared.fr> | 2022-11-13 18:32:22 +0100 |
commit | 22edb0197854bf4f504e833e69b0e545d382f065 (patch) | |
tree | f7af8b1f7375de41c2e528ba8ae0a97bd3186d5c /src/expr | |
parent | f8effdd348e38f51f6ec7b24c4c27e6602538445 (diff) |
wip: exprs
Diffstat (limited to 'src/expr')
-rw-r--r-- | src/expr/immediate.rs | 174 | ||||
-rw-r--r-- | src/expr/log.rs | 112 | ||||
-rw-r--r-- | src/expr/mod.rs | 361 | ||||
-rw-r--r-- | src/expr/register.rs | 50 | ||||
-rw-r--r-- | src/expr/verdict.rs | 232 |
5 files changed, 531 insertions, 398 deletions
diff --git a/src/expr/immediate.rs b/src/expr/immediate.rs index 71453b3..e9f7b5b 100644 --- a/src/expr/immediate.rs +++ b/src/expr/immediate.rs @@ -1,124 +1,60 @@ -use super::{DeserializationError, Expression, Register, Rule, ToSlice}; -use crate::sys; -use std::ffi::c_void; -use std::os::raw::c_char; - -/// An immediate expression. Used to set immediate data. Verdicts are handled separately by -/// [crate::expr::Verdict]. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct Immediate<T> { - pub data: T, - pub register: Register, -} - -impl<T> Immediate<T> { - pub fn new(data: T, register: Register) -> Self { - Self { data, register } - } -} - -impl<T: ToSlice> Expression for Immediate<T> { - fn get_raw_name() -> *const c_char { - b"immediate\0" as *const _ as *const c_char - } - - fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { - unsafe { - let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); - - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_DREG as u16, - self.register.to_raw(), - ); - - let data = self.data.to_slice(); - sys::nftnl_expr_set( - expr, - sys::NFTNL_EXPR_IMM_DATA as u16, - data.as_ptr() as *const c_void, - data.len() as u32, - ); - - expr - } +use super::{Expression, Register, VerdictAttribute}; +use crate::{create_expr_type, sys}; + +create_expr_type!( + nested with_builder : ImmediateData, + [ + ( + get_value, + set_value, + with_value, + sys::NFTA_DATA_VALUE, + VecU8, + Vec<u8> + ), + ( + get_verdict, + set_verdict, + with_verdict, + sys::NFTA_DATA_VERDICT, + ExprVerdictAttribute, + VerdictAttribute + ) + ] +); + +create_expr_type!( + inline with_builder : Immediate, + [ + ( + get_dreg, + set_dreg, + with_dreg, + sys::NFTA_IMMEDIATE_DREG, + Register, + Register + ), + ( + get_data, + set_data, + with_data, + sys::NFTA_IMMEDIATE_DATA, + ExprImmediateData, + ImmediateData + ) + ] +); + +impl Immediate { + pub fn new_data(data: Vec<u8>, register: Register) -> Self { + Immediate::builder() + .with_dreg(register) + .with_data(ImmediateData::builder().with_value(data)) } } -impl<const N: usize> Expression for Immediate<[u8; N]> { - fn get_raw_name() -> *const c_char { - Immediate::<u8>::get_raw_name() +impl Expression for Immediate { + fn get_name() -> &'static str { + "immediate" } - - /// The raw data contained inside `Immediate` expressions can only be deserialized to arrays of - /// bytes, to ensure that the memory layout of retrieved data cannot be violated. It is your - /// responsibility to provide the correct length of the byte data. If the data size is invalid, - /// you will get the error `DeserializationError::InvalidDataSize`. - /// - /// Example (warning, no error checking!): - /// ```rust - /// use std::ffi::CString; - /// use std::net::Ipv4Addr; - /// use std::rc::Rc; - /// - /// use rustables::{Chain, expr::{Immediate, Register}, ProtoFamily, Rule, Table}; - /// - /// let table = Rc::new(Table::new(&CString::new("mytable").unwrap(), ProtoFamily::Inet)); - /// let chain = Rc::new(Chain::new(&CString::new("mychain").unwrap(), table)); - /// let mut rule = Rule::new(chain); - /// rule.add_expr(&Immediate::new(42u8, Register::Reg1)); - /// for expr in Rc::new(rule).get_exprs() { - /// println!("{:?}", expr.decode_expr::<Immediate<[u8; 1]>>().unwrap()); - /// } - /// ``` - /// These limitations occur because casting bytes to any type of the same size as the raw input - /// would be *extremely* dangerous in terms of memory safety. - // As casting bytes to any type of the same size as the input would be *extremely* dangerous in - // terms of memory safety, rustables only accept to deserialize expressions with variable-size - // data to arrays of bytes, so that the memory layout cannot be invalid. - fn from_expr(expr: *const sys::nftnl_expr) -> Result<Self, DeserializationError> { - unsafe { - let ref_len = std::mem::size_of::<[u8; N]>() as u32; - let mut data_len = 0; - let data = sys::nftnl_expr_get( - expr, - sys::NFTNL_EXPR_IMM_DATA as u16, - &mut data_len as *mut u32, - ); - - if data.is_null() { - return Err(DeserializationError::NullPointer); - } else if data_len != ref_len { - return Err(DeserializationError::InvalidDataSize); - } - - let data = *(data as *const [u8; N]); - - let register = Register::from_raw(sys::nftnl_expr_get_u32( - expr, - sys::NFTNL_EXPR_IMM_DREG as u16, - ))?; - - Ok(Immediate { data, register }) - } - } - - // call to the other implementation to generate the expression - fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { - Immediate { - register: self.register, - data: &self.data as &[u8], - } - .to_expr(rule) - } -} - -#[macro_export] -macro_rules! nft_expr_immediate { - (data $value:expr) => { - $crate::expr::Immediate { - data: $value, - register: $crate::expr::Register::Reg1, - } - }; } diff --git a/src/expr/log.rs b/src/expr/log.rs index 8d20b48..cf50cb2 100644 --- a/src/expr/log.rs +++ b/src/expr/log.rs @@ -1,21 +1,61 @@ -use super::{DeserializationError, Expression, Rule}; +use super::{Expression, ExpressionError}; +use crate::create_expr_type; +use crate::nlmsg::NfNetlinkAttributes; use crate::sys; -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use thiserror::Error; -/// A Log expression will log all packets that match the rule. -#[derive(Debug, PartialEq)] -pub struct Log { - pub group: Option<LogGroup>, - pub prefix: Option<LogPrefix>, +// A Log expression will log all packets that match the rule. +create_expr_type!( + inline with_builder : Log, + [ + ( + get_group, + set_group, + with_group, + sys::NFTA_LOG_GROUP, + U32, + u32 + ), + ( + get_prefix, + set_prefix, + with_prefix, + sys::NFTA_LOG_PREFIX, + String, + String + ) + ] +); + +impl Log { + pub fn new( + group: Option<u16>, + prefix: Option<impl Into<String>>, + ) -> Result<Log, ExpressionError> { + let mut res = Log { + inner: NfNetlinkAttributes::new(), + //pub group: Option<LogGroup>, + //pub prefix: Option<LogPrefix>, + }; + if let Some(group) = group { + res.set_group(group); + } + if let Some(prefix) = prefix { + let prefix = prefix.into(); + + if prefix.bytes().count() > 127 { + return Err(ExpressionError::TooLongLogPrefix); + } + res.set_prefix(prefix); + } + Ok(res) + } } impl Expression for Log { - fn get_raw_name() -> *const sys::libc::c_char { - b"log\0" as *const _ as *const c_char + fn get_name() -> &'static str { + "log" } - + /* fn from_expr(expr: *const sys::nftnl_expr) -> Result<Self, DeserializationError> where Self: Sized, @@ -54,59 +94,21 @@ impl Expression for Log { expr } } -} - -#[derive(Error, Debug)] -pub enum LogPrefixError { - #[error("The log prefix string is more than 128 characters long")] - TooLongPrefix, - #[error("The log prefix string contains an invalid Nul character.")] - PrefixContainsANul(#[from] std::ffi::NulError), -} - -/// The NFLOG group that will be assigned to each log line. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct LogGroup(pub u16); - -/// A prefix that will get prepended to each log line. -#[derive(Debug, Clone, PartialEq)] -pub struct LogPrefix(CString); - -impl LogPrefix { - /// Creates a new LogPrefix from a String. Converts it to CString as needed by nftnl. Note that - /// LogPrefix should not be more than 127 characters long. - pub fn new(prefix: &str) -> Result<Self, LogPrefixError> { - if prefix.chars().count() > 127 { - return Err(LogPrefixError::TooLongPrefix); - } - Ok(LogPrefix(CString::new(prefix)?)) - } + */ } #[macro_export] macro_rules! nft_expr_log { (group $group:ident prefix $prefix:expr) => { - $crate::expr::Log { - group: $group, - prefix: $prefix, - } + $crate::expr::Log::new(Some($group), Some($prefix)) }; (prefix $prefix:expr) => { - $crate::expr::Log { - group: None, - prefix: $prefix, - } + $crate::expr::Log::new(None, Some($prefix)) }; (group $group:ident) => { - $crate::expr::Log { - group: $group, - prefix: None, - } + $crate::expr::Log::new(Some($group), None) }; () => { - $crate::expr::Log { - group: None, - prefix: None, - } + $crate::expr::Log::new(None, None) }; } diff --git a/src/expr/mod.rs b/src/expr/mod.rs index dc59507..4c702b2 100644 --- a/src/expr/mod.rs +++ b/src/expr/mod.rs @@ -4,14 +4,29 @@ //! [`Rule`]: struct.Rule.html use std::borrow::Cow; +use std::fmt::Debug; +use std::mem::transmute; use std::net::IpAddr; use std::net::Ipv4Addr; use std::net::Ipv6Addr; +use std::slice::Iter; use super::rule::Rule; -use crate::sys::{self, libc}; +use crate::nlmsg::AttributeDecoder; +use crate::nlmsg::NfNetlinkAttribute; +use crate::nlmsg::NfNetlinkAttributes; +use crate::nlmsg::NfNetlinkDeserializable; +use crate::parser::pad_netlink_object; +use crate::parser::pad_netlink_object_with_variable_size; +use crate::parser::write_attribute; +use crate::parser::AttributeType; +use crate::parser::DecodeError; +use crate::parser::InnerFormat; +use crate::sys::{self, nlattr}; +use libc::NLA_TYPE_MASK; use thiserror::Error; +/* mod bitwise; pub use self::bitwise::*; @@ -23,12 +38,14 @@ pub use self::counter::*; pub mod ct; pub use self::ct::*; +*/ mod immediate; pub use self::immediate::*; mod log; pub use self::log::*; +/* mod lookup; pub use self::lookup::*; @@ -47,6 +64,7 @@ pub use self::payload::*; mod reject; pub use self::reject::{IcmpCode, Reject}; +*/ mod register; pub use self::register::Register; @@ -54,11 +72,18 @@ pub use self::register::Register; mod verdict; pub use self::verdict::*; +/* + mod wrapper; pub use self::wrapper::ExpressionWrapper; +*/ #[derive(Debug, Error)] -pub enum DeserializationError { +pub enum ExpressionError { + #[error("The log prefix string is more than 127 characters long")] + /// The log prefix string is more than 127 characters long + TooLongLogPrefix, + #[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, @@ -80,109 +105,295 @@ pub enum DeserializationError { )] /// 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; + fn get_name() -> &'static str; +} + +// wrapper for the general case, as we need to create many holder types given the depth of some +// netlink expressions +#[macro_export] +macro_rules! create_expr_type { + (without_decoder : $struct:ident, [$(($getter_name:ident, $setter_name:ident, $in_place_edit_name:ident, $attr_name:expr, $internal_name:ident, $type:ty)),+]) => { + #[derive(Clone, PartialEq, Eq)] + pub struct $struct { + inner: $crate::nlmsg::NfNetlinkAttributes, + } + + + $crate::impl_attr_getters_and_setters!(without_decoder $struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + + impl std::fmt::Debug for $struct { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use $crate::parser::InnerFormat; + self.inner_format_struct(f.debug_struct(stringify!($struct)))? + .finish() + } + } + + + impl $crate::nlmsg::NfNetlinkDeserializable for $struct { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), $crate::parser::DecodeError> { + let reader = $crate::parser::NfNetlinkAttributeReader::new(buf, buf.len())?; + let inner = reader.decode::<Self>()?; + Ok(($struct { inner }, &[])) + } + } + + }; + ($struct:ident, [$(($getter_name:ident, $setter_name:ident, $in_place_edit_name:ident, $attr_name:expr, $internal_name:ident, $type:ty)),+]) => { + create_expr_type!(without_decoder : $struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + $crate::impl_attr_getters_and_setters!(decoder $struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + }; + (with_builder : $struct:ident, [$(($getter_name:ident, $setter_name:ident, $in_place_edit_name:ident, $attr_name:expr, $internal_name:ident, $type:ty)),+]) => { + create_expr_type!($struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + + impl $struct { + pub fn builder() -> Self { + Self { inner: $crate::nlmsg::NfNetlinkAttributes::new() } + } + } + }; + (inline $($($attrs:ident) +)? : $struct:ident, [$(($getter_name:ident, $setter_name:ident, $in_place_edit_name:ident, $attr_name:expr, $internal_name:ident, $type:ty)),+]) => { + create_expr_type!($($($attrs) + :)? $struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + + impl $crate::nlmsg::NfNetlinkAttribute for $struct { + fn get_size(&self) -> usize { + self.inner.get_size() + } + + unsafe fn write_payload(&self, addr: *mut u8) { + self.inner.write_payload(addr) + } + } + }; + (nested $($($attrs:ident) +)? : $struct:ident, [$(($getter_name:ident, $setter_name:ident, $in_place_edit_name:ident, $attr_name:expr, $internal_name:ident, $type:ty)),+]) => { + create_expr_type!($($($attrs) + :)? $struct, [$(($getter_name, $setter_name, $in_place_edit_name, $attr_name, $internal_name, $type)),+]); + + impl $crate::nlmsg::NfNetlinkAttribute for $struct { + fn is_nested(&self) -> bool { + true + } + + fn get_size(&self) -> usize { + self.inner.get_size() + } + + unsafe fn write_payload(&self, addr: *mut u8) { + self.inner.write_payload(addr) + } + } + }; +} - /// 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> +create_expr_type!( + nested without_decoder : ExpressionHolder, [ + // Define the action netfilter will apply to packets processed by this chain, but that did not match any rules in it. + ( + get_name, + set_name, + with_name, + sys::NFTA_EXPR_NAME, + String, + String + ), + ( + get_data, + set_data, + with_data, + sys::NFTA_EXPR_DATA, + ExpressionVariant, + ExpressionVariant + ) +]); + +impl ExpressionHolder { + pub fn new<T>(expr: T) -> Self where - Self: Sized, + T: Expression, + ExpressionVariant: From<T>, { - Err(DeserializationError::NotImplemented) + ExpressionHolder { + inner: NfNetlinkAttributes::new(), + } + .with_name(T::get_name()) + .with_data(ExpressionVariant::from(expr)) } - - /// 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; } -/// 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]>; -} +#[macro_export] +macro_rules! create_expr_variant { + ($enum:ident $(, [$name:ident, $type:ty])+) => { + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum $enum { + $( + $name($type), + )+ + } -impl<'a> ToSlice for &'a [u8] { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Borrowed(self) - } + 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 From<$type> for $enum { + fn from(val: $type) -> Self { + $enum::$name(val) + } + } + )+ + + impl AttributeDecoder for ExpressionHolder { + fn decode_attribute( + attrs: &NfNetlinkAttributes, + attr_type: u16, + buf: &[u8], + ) -> Result<AttributeType, 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(DecodeError::InvalidDataSize); + } + Ok(AttributeType::String(val)) + }, + 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 = attrs + .get_attr(sys::NFTA_EXPR_NAME) + .ok_or(DecodeError::MissingExpressionName)?; + match name { + $( + AttributeType::String(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::parser::DecodeError::InvalidDataSize); + } + Ok(AttributeType::ExpressionVariant(ExpressionVariant::from(res))) + }, + )+ + AttributeType::String(name) => Err(DecodeError::UnknownExpressionName(name.to_string())), + _ => unreachable!() + } + }, + _ => Err(DecodeError::UnsupportedAttributeType(attr_type)), + } + } + } + }; } -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) }) - } +create_expr_variant!(ExpressionVariant, [Log, Log], [Immediate, Immediate]); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExpressionList { + exprs: Vec<AttributeType>, } -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(), - } +impl ExpressionList { + pub fn builder() -> Self { + Self { exprs: Vec::new() } } -} -impl ToSlice for Ipv4Addr { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(self.octets().to_vec()) + pub fn add_expression<T>(&mut self, e: T) + where + T: Expression, + ExpressionVariant: From<T>, + { + self.exprs + .push(AttributeType::Expression(ExpressionHolder::new(e))); } -} -impl ToSlice for Ipv6Addr { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(self.octets().to_vec()) + pub fn with_expression<T>(mut self, e: T) -> Self + where + T: Expression, + ExpressionVariant: From<T>, + { + self.add_expression(e); + self } -} -impl ToSlice for u8 { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(vec![*self]) + pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a ExpressionVariant> { + self.exprs.iter().map(|t| match t { + AttributeType::Expression(e) => e.get_data().unwrap(), + _ => unreachable!(), + }) } } -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]) +impl NfNetlinkAttribute for ExpressionList { + fn is_nested(&self) -> bool { + true } -} -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]) + fn get_size(&self) -> usize { + // one nlattr LIST_ELEM per object + self.exprs.iter().fold(0, |acc, item| { + acc + item.get_size() + pad_netlink_object::<nlattr>() + }) } -} -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]) + unsafe fn write_payload(&self, mut addr: *mut u8) { + for item in &self.exprs { + write_attribute(sys::NFTA_LIST_ELEM, item, addr); + addr = addr.offset((pad_netlink_object::<nlattr>() + item.get_size()) as isize); + } } } -impl<'a> ToSlice for &'a str { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::from(self.as_bytes()) +impl NfNetlinkDeserializable for ExpressionList { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let mut exprs = Vec::new(); + + let mut pos = 0; + while buf.len() - pos > pad_netlink_object::<nlattr>() { + let nlattr = unsafe { *transmute::<*const u8, *const nlattr>(buf[pos..].as_ptr()) }; + // ignore the byteorder and nested attributes + let nla_type = nlattr.nla_type & NLA_TYPE_MASK as u16; + + if nla_type != sys::NFTA_LIST_ELEM { + return Err(DecodeError::UnsupportedAttributeType(nla_type)); + } + + let (expr, remaining) = ExpressionHolder::deserialize( + &buf[pos + pad_netlink_object::<nlattr>()..pos + nlattr.nla_len as usize], + )?; + if remaining.len() != 0 { + return Err(DecodeError::InvalidDataSize); + } + exprs.push(AttributeType::Expression(expr)); + + pos += pad_netlink_object_with_variable_size(nlattr.nla_len as usize); + } + + if pos != buf.len() { + Err(DecodeError::InvalidDataSize) + } else { + Ok((Self { exprs }, &[])) + } } } diff --git a/src/expr/register.rs b/src/expr/register.rs index a05af7e..def58a5 100644 --- a/src/expr/register.rs +++ b/src/expr/register.rs @@ -1,34 +1,42 @@ use std::fmt::Debug; -use crate::sys::libc; - -use super::DeserializationError; +use crate::{ + nlmsg::{NfNetlinkAttribute, NfNetlinkDeserializable}, + parser::DecodeError, + sys::{NFT_REG_1, NFT_REG_2, NFT_REG_3, NFT_REG_4, NFT_REG_VERDICT}, +}; /// A netfilter data register. The expressions store and read data to and from these when /// evaluating rule statements. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[repr(i32)] +#[repr(u32)] pub enum Register { - Verdict = libc::NFT_REG_VERDICT, - Reg1 = libc::NFT_REG_1, - Reg2 = libc::NFT_REG_2, - Reg3 = libc::NFT_REG_3, - Reg4 = libc::NFT_REG_4, + Verdict = NFT_REG_VERDICT, + Reg1 = NFT_REG_1, + Reg2 = NFT_REG_2, + Reg3 = NFT_REG_3, + Reg4 = NFT_REG_4, } -impl Register { - pub fn to_raw(self) -> u32 { - self as u32 +impl NfNetlinkAttribute for Register { + unsafe fn write_payload(&self, addr: *mut u8) { + (*self as u32).write_payload(addr); } +} - pub fn from_raw(val: u32) -> Result<Self, DeserializationError> { - match val as i32 { - libc::NFT_REG_VERDICT => Ok(Self::Verdict), - libc::NFT_REG_1 => Ok(Self::Reg1), - libc::NFT_REG_2 => Ok(Self::Reg2), - libc::NFT_REG_3 => Ok(Self::Reg3), - libc::NFT_REG_4 => Ok(Self::Reg4), - _ => Err(DeserializationError::InvalidValue), - } +impl NfNetlinkDeserializable for Register { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), crate::parser::DecodeError> { + let (val, remaining) = u32::deserialize(buf)?; + Ok(( + match val { + NFT_REG_VERDICT => Self::Verdict, + NFT_REG_1 => Self::Reg1, + NFT_REG_2 => Self::Reg2, + NFT_REG_3 => Self::Reg3, + NFT_REG_4 => Self::Reg4, + _ => return Err(DecodeError::UnknownRegisterValue), + }, + remaining, + )) } } diff --git a/src/expr/verdict.rs b/src/expr/verdict.rs index 3c4c374..326ef3b 100644 --- a/src/expr/verdict.rs +++ b/src/expr/verdict.rs @@ -1,11 +1,90 @@ -use super::{DeserializationError, Expression, Rule}; -use crate::sys::{self, libc::{self, c_char}}; -use std::ffi::{CStr, CString}; +use std::fmt::Debug; + +use libc::{NF_ACCEPT, NF_DROP, NF_QUEUE}; + +use super::{Expression, Immediate, ImmediateData, Register, Rule}; +use crate::{ + create_expr_type, impl_attr_getters_and_setters, + nlmsg::{NfNetlinkAttribute, NfNetlinkAttributes, NfNetlinkDeserializable}, + parser::{DecodeError, InnerFormat}, + sys::{self, NFT_BREAK, NFT_CONTINUE, NFT_GOTO, NFT_JUMP, NFT_REG_VERDICT, NFT_RETURN}, +}; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[repr(i32)] +pub enum VerdictType { + Drop = NF_DROP, + Accept = NF_ACCEPT, + Queue = NF_QUEUE, + Continue = NFT_CONTINUE, + Break = NFT_BREAK, + Jump = NFT_JUMP, + Goto = NFT_GOTO, + Return = NFT_RETURN, +} + +impl NfNetlinkAttribute for VerdictType { + fn get_size(&self) -> usize { + (*self as i32).get_size() + } + + unsafe fn write_payload(&self, addr: *mut u8) { + (*self as i32).write_payload(addr); + } +} + +impl NfNetlinkDeserializable for VerdictType { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let (v, remaining_data) = i32::deserialize(buf)?; + Ok(( + match v { + NF_DROP => VerdictType::Drop, + NF_ACCEPT => VerdictType::Accept, + NF_QUEUE => VerdictType::Queue, + NFT_CONTINUE => VerdictType::Continue, + NFT_BREAK => VerdictType::Break, + NFT_JUMP => VerdictType::Jump, + NFT_GOTO => VerdictType::Goto, + NFT_RETURN => VerdictType::Goto, + _ => return Err(DecodeError::UnknownExpressionVerdictType), + }, + remaining_data, + )) + } +} + +create_expr_type!( + nested with_builder : VerdictAttribute, + [ + ( + get_code, + set_code, + with_code, + sys::NFTA_VERDICT_CODE, + ExprVerdictType, + VerdictType + ), + ( + get_chain, + set_chain, + with_chain, + sys::NFTA_VERDICT_CHAIN, + String, + String + ), + ( + get_chain_id, + set_chain_id, + with_chain_id, + sys::NFTA_VERDICT_CHAIN_ID, + U32, + u32 + ) + ] +); -/// A verdict expression. In the background, this is usually an "Immediate" expression in nftnl -/// terms, but here it is simplified to only represent a verdict. #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum Verdict { +pub enum VerdictKind { /// Silently drop the packet. Drop, /// Accept the packet and let it pass. @@ -14,135 +93,32 @@ pub enum Verdict { Continue, Break, Jump { - chain: CString, + chain: String, }, Goto { - chain: CString, + chain: String, }, Return, } -impl Verdict { - fn chain(&self) -> Option<&CStr> { - match *self { - Verdict::Jump { ref chain } => Some(chain.as_c_str()), - Verdict::Goto { ref chain } => Some(chain.as_c_str()), - _ => None, - } - } -} - -impl Expression for Verdict { - fn get_raw_name() -> *const libc::c_char { - b"immediate\0" as *const _ as *const c_char - } - - fn from_expr(expr: *const sys::nftnl_expr) -> Result<Self, DeserializationError> { - unsafe { - let mut chain = None; - if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_IMM_CHAIN as u16) { - let raw_chain = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_IMM_CHAIN as u16); - - if raw_chain.is_null() { - return Err(DeserializationError::NullPointer); - } - chain = Some(CStr::from_ptr(raw_chain).to_owned()); - } - - let verdict = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_IMM_VERDICT as u16); - - match verdict as i32 { - libc::NF_DROP => Ok(Verdict::Drop), - libc::NF_ACCEPT => Ok(Verdict::Accept), - libc::NF_QUEUE => Ok(Verdict::Queue), - libc::NFT_CONTINUE => Ok(Verdict::Continue), - libc::NFT_BREAK => Ok(Verdict::Break), - libc::NFT_JUMP => { - if let Some(chain) = chain { - Ok(Verdict::Jump { chain }) - } else { - Err(DeserializationError::InvalidValue) - } - } - libc::NFT_GOTO => { - if let Some(chain) = chain { - Ok(Verdict::Goto { chain }) - } else { - Err(DeserializationError::InvalidValue) - } - } - libc::NFT_RETURN => Ok(Verdict::Return), - _ => Err(DeserializationError::InvalidValue), - } - } - } - - fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { - let immediate_const = match *self { - Verdict::Drop => libc::NF_DROP, - Verdict::Accept => libc::NF_ACCEPT, - Verdict::Queue => libc::NF_QUEUE, - Verdict::Continue => libc::NFT_CONTINUE, - Verdict::Break => libc::NFT_BREAK, - Verdict::Jump { .. } => libc::NFT_JUMP, - Verdict::Goto { .. } => libc::NFT_GOTO, - Verdict::Return => libc::NFT_RETURN, +impl Immediate { + pub fn new_verdict(kind: VerdictKind) -> Self { + let code = match kind { + VerdictKind::Drop => VerdictType::Drop, + VerdictKind::Accept => VerdictType::Accept, + VerdictKind::Queue => VerdictType::Queue, + VerdictKind::Continue => VerdictType::Continue, + VerdictKind::Break => VerdictType::Break, + VerdictKind::Jump { .. } => VerdictType::Jump, + VerdictKind::Goto { .. } => VerdictType::Goto, + VerdictKind::Return => VerdictType::Return, }; - unsafe { - let expr = try_alloc!(sys::nftnl_expr_alloc( - b"immediate\0" as *const _ as *const c_char - )); - - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_DREG as u16, - libc::NFT_REG_VERDICT as u32, - ); - - if let Some(chain) = self.chain() { - sys::nftnl_expr_set_str(expr, sys::NFTNL_EXPR_IMM_CHAIN as u16, chain.as_ptr()); - } - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_VERDICT as u16, - immediate_const as u32, - ); - - expr + let mut data = VerdictAttribute::builder().with_code(code); + if let VerdictKind::Jump { chain } | VerdictKind::Goto { chain } = kind { + data.set_chain(chain); } + Immediate::builder() + .with_dreg(Register::Verdict) + .with_data(ImmediateData::builder().with_verdict(data)) } } - -#[macro_export] -macro_rules! nft_expr_verdict { - (drop) => { - $crate::expr::Verdict::Drop - }; - (accept) => { - $crate::expr::Verdict::Accept - }; - (reject icmp $code:expr) => { - $crate::expr::Verdict::Reject(RejectionType::Icmp($code)) - }; - (reject tcp-rst) => { - $crate::expr::Verdict::Reject(RejectionType::TcpRst) - }; - (queue) => { - $crate::expr::Verdict::Queue - }; - (continue) => { - $crate::expr::Verdict::Continue - }; - (break) => { - $crate::expr::Verdict::Break - }; - (jump $chain:expr) => { - $crate::expr::Verdict::Jump { chain: $chain } - }; - (goto $chain:expr) => { - $crate::expr::Verdict::Goto { chain: $chain } - }; - (return) => { - $crate::expr::Verdict::Return - }; -} |