aboutsummaryrefslogtreecommitdiff
path: root/src/expr
diff options
context:
space:
mode:
authorSimon THOBY <git@nightmared.fr>2022-11-13 18:32:22 +0100
committerSimon THOBY <git@nightmared.fr>2022-11-13 18:32:22 +0100
commit22edb0197854bf4f504e833e69b0e545d382f065 (patch)
treef7af8b1f7375de41c2e528ba8ae0a97bd3186d5c /src/expr
parentf8effdd348e38f51f6ec7b24c4c27e6602538445 (diff)
wip: exprs
Diffstat (limited to 'src/expr')
-rw-r--r--src/expr/immediate.rs174
-rw-r--r--src/expr/log.rs112
-rw-r--r--src/expr/mod.rs361
-rw-r--r--src/expr/register.rs50
-rw-r--r--src/expr/verdict.rs232
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
- };
-}