From e58ccb7d8b8168b801a820005c1cf8e50f892659 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 20 Oct 2021 20:59:56 +0200 Subject: Adds an iterator over expressions to Rules --- rustables/src/batch.rs | 6 +++--- rustables/src/chain.rs | 4 ++-- rustables/src/expr/mod.rs | 45 +++++++++++++++++++++++++++++++++++++++++++ rustables/src/rule.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++--- rustables/src/set.rs | 6 +++--- 5 files changed, 99 insertions(+), 11 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/batch.rs b/rustables/src/batch.rs index 2af1a7c..c4cd6cb 100644 --- a/rustables/src/batch.rs +++ b/rustables/src/batch.rs @@ -22,9 +22,9 @@ pub fn batch_is_supported() -> std::result::Result { /// A batch of netfilter messages to be performed in one atomic operation. Corresponds to /// `nftnl_batch` in libnftnl. pub struct Batch { - batch: *mut sys::nftnl_batch, - seq: u32, - is_empty: bool, + pub(crate) batch: *mut sys::nftnl_batch, + pub(crate) seq: u32, + pub(crate) is_empty: bool, } impl Batch { diff --git a/rustables/src/chain.rs b/rustables/src/chain.rs index a5e732e..4bf33b0 100644 --- a/rustables/src/chain.rs +++ b/rustables/src/chain.rs @@ -70,8 +70,8 @@ impl ChainType { /// [`Rule`]: struct.Rule.html /// [`set_hook`]: #method.set_hook pub struct Chain { - chain: *mut sys::nftnl_chain, - table: Rc, + pub(crate) chain: *mut sys::nftnl_chain, + pub(crate) table: Rc
, } impl Chain { diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 99ea44b..a86b44d 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -3,9 +3,54 @@ //! //! [`Rule`]: struct.Rule.html +use std::ffi::CStr; +use std::ffi::CString; +use std::fmt::Debug; +use std::rc::Rc; + use super::rule::Rule; use rustables_sys::{self as sys, libc}; +pub struct ExpressionWrapper { + pub(crate) expr: *const sys::nftnl_expr, + // we also need the rule here to ensure that the rule lives as long as the `expr` pointer + #[allow(dead_code)] + pub(crate) rule: Rc, +} + +impl Debug for ExpressionWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.get_str()) + } +} + +impl ExpressionWrapper { + pub fn get_str(&self) -> CString { + let mut descr_buf = vec![0i8; 4096]; + unsafe { + sys::nftnl_expr_snprintf( + descr_buf.as_mut_ptr(), + (descr_buf.len() - 1) as u64, + self.expr, + sys::NFTNL_OUTPUT_DEFAULT, + 0, + ); + CStr::from_ptr(descr_buf.as_ptr()).to_owned() + } + } + + pub fn get_expr_kind(&self) -> Option<&CStr> { + unsafe { + let ptr = sys::nftnl_expr_get_str(self.expr, sys::NFTNL_EXPR_NAME as u16); + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None + } + } + } +} + /// Trait for every safe wrapper of an nftables expression. pub trait Expression { /// Allocates and returns the low level `nftnl_expr` representation of this expression. diff --git a/rustables/src/rule.rs b/rustables/src/rule.rs index 6e15db7..09a465e 100644 --- a/rustables/src/rule.rs +++ b/rustables/src/rule.rs @@ -1,3 +1,4 @@ +use crate::expr::ExpressionWrapper; use crate::{chain::Chain, expr::Expression, MsgType}; use rustables_sys::{self as sys, libc}; use std::ffi::{c_void, CStr, CString}; @@ -7,8 +8,8 @@ use std::rc::Rc; /// A nftables firewall rule. pub struct Rule { - rule: *mut sys::nftnl_rule, - chain: Rc, + pub(crate) rule: *mut sys::nftnl_rule, + pub(crate) chain: Rc, } impl Rule { @@ -89,7 +90,7 @@ impl Rule { } } - /// Update the userdata of this chain. + /// Updates the userdata of this chain. pub fn set_userdata(&self, data: &CStr) { unsafe { sys::nftnl_rule_set_str(self.rule, sys::NFTNL_RULE_USERDATA as u16, data.as_ptr()); @@ -111,6 +112,11 @@ impl Rule { } } + /// Retrieves an iterator to loop over the expressions of the rule + pub fn get_exprs(self: &Rc) -> RuleExprsIter { + RuleExprsIter::new(self.clone()) + } + #[cfg(feature = "unsafe-raw-handles")] /// Returns the raw handle. pub fn as_ptr(&self) -> *const sys::nftnl_rule { @@ -163,6 +169,43 @@ impl Drop for Rule { } } +pub struct RuleExprsIter { + rule: Rc, + iter: *mut sys::nftnl_expr_iter, +} + +impl RuleExprsIter { + fn new(rule: Rc) -> Self { + let iter = + try_alloc!(unsafe { sys::nftnl_expr_iter_create(rule.rule as *const sys::nftnl_rule) }); + RuleExprsIter { rule, iter } + } +} + +impl Iterator for RuleExprsIter { + type Item = ExpressionWrapper; + + fn next(&mut self) -> Option { + let next = unsafe { sys::nftnl_expr_iter_next(self.iter) }; + if next.is_null() { + trace!("RulesExprsIter iterator ending"); + None + } else { + trace!("RulesExprsIter returning new expression"); + Some(ExpressionWrapper { + expr: next, + rule: self.rule.clone(), + }) + } + } +} + +impl Drop for RuleExprsIter { + fn drop(&mut self) { + unsafe { sys::nftnl_expr_iter_destroy(self.iter) }; + } +} + #[cfg(feature = "query")] pub fn get_rules_cb( header: &libc::nlmsghdr, diff --git a/rustables/src/set.rs b/rustables/src/set.rs index d8c84d6..820e368 100644 --- a/rustables/src/set.rs +++ b/rustables/src/set.rs @@ -27,9 +27,9 @@ macro_rules! nft_set { } pub struct Set<'a, K> { - set: *mut sys::nftnl_set, - table: &'a Table, - family: ProtoFamily, + pub(crate) set: *mut sys::nftnl_set, + pub(crate) table: &'a Table, + pub(crate) family: ProtoFamily, _marker: ::std::marker::PhantomData, } -- cgit v1.2.3 From 2036da41552f5e19d3de08e9bbe4d3bdfea761f0 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 20 Oct 2021 21:56:10 +0200 Subject: fix a few cases where we didn't sanitize properly pointers from libnftnl --- rustables/src/chain.rs | 18 ++++++++++-------- rustables/src/expr/lookup.rs | 10 ++++++---- rustables/src/rule.rs | 39 ++++++++++++++++----------------------- rustables/src/set.rs | 12 +++++++++--- rustables/src/table.rs | 15 ++++++++++----- 5 files changed, 51 insertions(+), 43 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/chain.rs b/rustables/src/chain.rs index 4bf33b0..a616f90 100644 --- a/rustables/src/chain.rs +++ b/rustables/src/chain.rs @@ -86,11 +86,9 @@ impl Chain { sys::NFTNL_CHAIN_FAMILY as u16, table.get_family() as u32, ); - sys::nftnl_chain_set_str( - chain, - sys::NFTNL_CHAIN_TABLE as u16, - table.get_name().as_ptr(), - ); + if let Some(table_name) = table.get_name() { + sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_TABLE as u16, table_name.as_ptr()); + } sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_NAME as u16, name.as_ref().as_ptr()); Chain { chain, table } } @@ -153,10 +151,14 @@ impl Chain { } /// Returns the name of this chain. - pub fn get_name(&self) -> &CStr { + pub fn get_name(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_NAME as u16); - CStr::from_ptr(ptr) + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None + } } } @@ -266,7 +268,7 @@ pub fn get_chains_cb<'a>( } }; - if table_name != table.get_name() { + if Some(table_name) != table.get_name() { sys::nftnl_chain_free(chain); return mnl::mnl_sys::MNL_CB_OK; } diff --git a/rustables/src/expr/lookup.rs b/rustables/src/expr/lookup.rs index ac22440..bab09c2 100644 --- a/rustables/src/expr/lookup.rs +++ b/rustables/src/expr/lookup.rs @@ -10,11 +10,13 @@ pub struct Lookup { } impl Lookup { - pub fn new(set: &Set<'_, K>) -> Self { - Lookup { - set_name: set.get_name().to_owned(), + /// Creates a new lookup entry. + /// May return None if the set have no name. + pub fn new(set: &Set<'_, K>) -> Option { + set.get_name().map(|set_name| Lookup { + set_name: set_name.to_owned(), set_id: set.get_id(), - } + }) } } diff --git a/rustables/src/rule.rs b/rustables/src/rule.rs index 09a465e..7ab0de9 100644 --- a/rustables/src/rule.rs +++ b/rustables/src/rule.rs @@ -24,16 +24,12 @@ impl Rule { sys::NFTNL_RULE_FAMILY as u16, chain.get_table().get_family() as u32, ); - sys::nftnl_rule_set_str( - rule, - sys::NFTNL_RULE_TABLE as u16, - chain.get_table().get_name().as_ptr(), - ); - sys::nftnl_rule_set_str( - rule, - sys::NFTNL_RULE_CHAIN as u16, - chain.get_name().as_ptr(), - ); + if let Some(table_name) = chain.get_table().get_name() { + sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_TABLE as u16, table_name.as_ptr()); + } + if let Some(chain_name) = chain.get_name() { + sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_CHAIN as u16, chain_name.as_ptr()); + } Rule { rule, chain } } @@ -83,10 +79,11 @@ impl Rule { pub fn get_userdata(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_rule_get_str(self.rule, sys::NFTNL_RULE_USERDATA as u16); - if ptr == std::ptr::null() { - return None; + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None } - Some(CStr::from_ptr(ptr)) } } @@ -241,21 +238,17 @@ pub fn list_rules_for_chain(chain: &Rc) -> Result, crate::query return Err(crate::query::Error::NetlinkAllocationFailed); } - sys::nftnl_rule_set_str( - rule, - sys::NFTNL_RULE_TABLE as u16, - chain.get_table().get_name().as_ptr(), - ); + if let Some(table_name) = chain.get_table().get_name() { + sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_TABLE as u16, table_name.as_ptr()); + } sys::nftnl_rule_set_u32( rule, sys::NFTNL_RULE_FAMILY as u16, chain.get_table().get_family() as u32, ); - sys::nftnl_rule_set_str( - rule, - sys::NFTNL_RULE_CHAIN as u16, - chain.get_name().as_ptr(), - ); + if let Some(chain_name) = chain.get_name() { + sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_CHAIN as u16, chain_name.as_ptr()); + } sys::nftnl_rule_nlmsg_build_payload(hdr, rule); diff --git a/rustables/src/set.rs b/rustables/src/set.rs index 820e368..c099088 100644 --- a/rustables/src/set.rs +++ b/rustables/src/set.rs @@ -42,7 +42,9 @@ impl<'a, K> Set<'a, K> { let set = try_alloc!(sys::nftnl_set_alloc()); sys::nftnl_set_set_u32(set, sys::NFTNL_SET_FAMILY as u16, family as u32); - sys::nftnl_set_set_str(set, sys::NFTNL_SET_TABLE as u16, table.get_name().as_ptr()); + if let Some(table_name) = table.get_name() { + sys::nftnl_set_set_str(set, sys::NFTNL_SET_TABLE as u16, table_name.as_ptr()); + } sys::nftnl_set_set_str(set, sys::NFTNL_SET_NAME as u16, name.as_ptr()); sys::nftnl_set_set_u32(set, sys::NFTNL_SET_ID as u16, id); @@ -130,10 +132,14 @@ impl<'a, K> Set<'a, K> { } } - pub fn get_name(&self) -> &CStr { + pub fn get_name(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_set_get_str(self.set, sys::NFTNL_SET_NAME as u16); - CStr::from_ptr(ptr) + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None + } } } diff --git a/rustables/src/table.rs b/rustables/src/table.rs index dc09b5e..53a967f 100644 --- a/rustables/src/table.rs +++ b/rustables/src/table.rs @@ -35,10 +35,14 @@ impl Table { } /// Returns the name of this table. - pub fn get_name(&self) -> &CStr { + pub fn get_name(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_table_get_str(self.table, sys::NFTNL_TABLE_NAME as u16); - CStr::from_ptr(ptr) + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None + } } } @@ -66,10 +70,11 @@ impl Table { pub fn get_userdata(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_table_get_str(self.table, sys::NFTNL_TABLE_USERDATA as u16); - if ptr == std::ptr::null() { - return None; + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None } - Some(CStr::from_ptr(ptr)) } } -- cgit v1.2.3 From 180c4d5c8ff86836e0f440d7d0540c02c168c4bf Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 20 Oct 2021 21:33:20 +0200 Subject: counter: add the possibility to start from a non-zero value --- rustables/src/expr/counter.rs | 23 +++++++++++++++++++++-- rustables/src/expr/mod.rs | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/counter.rs b/rustables/src/expr/counter.rs index c2a0b5d..d254543 100644 --- a/rustables/src/expr/counter.rs +++ b/rustables/src/expr/counter.rs @@ -4,10 +4,29 @@ use std::os::raw::c_char; /// A counter expression adds a counter to the rule that is incremented to count number of packets /// and number of bytes for all packets that has matched the rule. -pub struct Counter; +pub struct Counter { + pub nb_bytes: u64, + pub nb_packets: u64, +} + +impl Counter { + pub fn new() -> Self { + Self { + nb_bytes: 0, + nb_packets: 0, + } + } +} impl Expression for Counter { fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { - try_alloc!(unsafe { sys::nftnl_expr_alloc(b"counter\0" as *const _ as *const c_char) }) + unsafe { + let expr = try_alloc!(sys::nftnl_expr_alloc( + b"counter\0" as *const _ as *const c_char + )); + sys::nftnl_expr_set_u64(expr, sys::NFTNL_EXPR_CTR_BYTES as u16, self.nb_bytes); + sys::nftnl_expr_set_u64(expr, sys::NFTNL_EXPR_CTR_PACKETS as u16, self.nb_packets); + expr + } } } diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index a86b44d..4493662 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -120,7 +120,7 @@ macro_rules! nft_expr { nft_expr_cmp!($op $data) }; (counter) => { - $crate::expr::Counter + $crate::expr::Counter { nb_bytes: 0, nb_packets: 0} }; (ct $key:ident set) => { nft_expr_ct!($key set) -- cgit v1.2.3 From 4dc522ae121ef9c8379b9efe248d0dc9625812cb Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Thu, 21 Oct 2021 22:37:23 +0200 Subject: Extend the `Expression` trait to allow for "deserialization" --- rustables/src/expr/bitwise.rs | 8 ++++--- rustables/src/expr/cmp.rs | 6 ++++- rustables/src/expr/counter.rs | 8 ++++--- rustables/src/expr/ct.rs | 6 ++++- rustables/src/expr/immediate.rs | 8 ++++--- rustables/src/expr/log.rs | 50 ++++++++++++++++++++++------------------ rustables/src/expr/lookup.rs | 8 ++++--- rustables/src/expr/masquerade.rs | 6 ++++- rustables/src/expr/meta.rs | 8 ++++--- rustables/src/expr/mod.rs | 12 ++++++++++ rustables/src/expr/nat.rs | 7 ++++-- rustables/src/expr/payload.rs | 8 ++++--- rustables/src/expr/verdict.rs | 8 ++++--- 13 files changed, 94 insertions(+), 49 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/bitwise.rs b/rustables/src/expr/bitwise.rs index 1eb81ab..0c6c33c 100644 --- a/rustables/src/expr/bitwise.rs +++ b/rustables/src/expr/bitwise.rs @@ -19,11 +19,13 @@ impl Bitwise { } impl Expression for Bitwise { + fn get_raw_name() -> *const c_char { + b"bitwise\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( - b"bitwise\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); let mask = self.mask.to_slice(); let xor = self.xor.to_slice(); diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index 5c56492..b14aa1d 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -55,9 +55,13 @@ impl Cmp { } impl Expression for Cmp { + fn get_raw_name() -> *const c_char { + b"cmp\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(b"cmp\0" as *const _ as *const c_char)); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); let data = self.data.to_slice(); trace!("Creating a cmp expr comparing with data {:?}", data); diff --git a/rustables/src/expr/counter.rs b/rustables/src/expr/counter.rs index d254543..2a8ad6f 100644 --- a/rustables/src/expr/counter.rs +++ b/rustables/src/expr/counter.rs @@ -19,11 +19,13 @@ impl Counter { } impl Expression for Counter { + fn get_raw_name() -> *const c_char { + b"counter\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( - b"counter\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); sys::nftnl_expr_set_u64(expr, sys::NFTNL_EXPR_CTR_BYTES as u16, self.nb_bytes); sys::nftnl_expr_set_u64(expr, sys::NFTNL_EXPR_CTR_PACKETS as u16, self.nb_packets); expr diff --git a/rustables/src/expr/ct.rs b/rustables/src/expr/ct.rs index c0349ab..1f15858 100644 --- a/rustables/src/expr/ct.rs +++ b/rustables/src/expr/ct.rs @@ -27,9 +27,13 @@ impl Conntrack { } impl Expression for Conntrack { + fn get_raw_name() -> *const c_char { + b"ct\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(b"ct\0" as *const _ as *const c_char)); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); if let Conntrack::Mark { set: true } = self { sys::nftnl_expr_set_u32( diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index e5ccc2a..15eb452 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -19,11 +19,13 @@ impl Immediate { } impl Expression for Immediate { + 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( - b"immediate\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); sys::nftnl_expr_set_u32( expr, diff --git a/rustables/src/expr/log.rs b/rustables/src/expr/log.rs index aa7a8b7..8f52686 100644 --- a/rustables/src/expr/log.rs +++ b/rustables/src/expr/log.rs @@ -1,34 +1,28 @@ use super::{Expression, Rule}; use rustables_sys as sys; -use std::os::raw::c_char; use std::ffi::CString; +use std::os::raw::c_char; use thiserror::Error; /// A Log expression will log all packets that match the rule. pub struct Log { pub group: Option, - pub prefix: Option + pub prefix: Option, } impl Expression for Log { + fn get_raw_name() -> *const sys::libc::c_char { + b"log\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( - b"log\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(b"log\0" as *const _ as *const c_char)); if let Some(log_group) = self.group { - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_LOG_GROUP as u16, - log_group.0 as u32, - ); + sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_LOG_GROUP as u16, log_group.0 as u32); }; if let Some(LogPrefix(prefix)) = &self.prefix { - sys::nftnl_expr_set_str( - expr, - sys::NFTNL_EXPR_LOG_PREFIX as u16, - prefix.as_ptr() - ); + sys::nftnl_expr_set_str(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16, prefix.as_ptr()); }; expr @@ -41,8 +35,7 @@ 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) - + PrefixContainsANul(#[from] std::ffi::NulError), } /// The NFLOG group that will be assigned to each log line. @@ -58,25 +51,36 @@ impl LogPrefix { /// that LogPrefix should not be more than 127 characters long. pub fn new(prefix: &str) -> Result { if prefix.chars().count() > 127 { - return Err(LogPrefixError::TooLongPrefix) + 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 { + group: $group, + prefix: $prefix, + } }; (prefix $prefix:expr) => { - $crate::expr::Log { group: None, prefix: $prefix } + $crate::expr::Log { + group: None, + prefix: $prefix, + } }; (group $group:ident) => { - $crate::expr::Log { group: $group, prefix: None } + $crate::expr::Log { + group: $group, + prefix: None, + } }; () => { - $crate::expr::Log { group: None, prefix: None } + $crate::expr::Log { + group: None, + prefix: None, + } }; } diff --git a/rustables/src/expr/lookup.rs b/rustables/src/expr/lookup.rs index bab09c2..d9acbe6 100644 --- a/rustables/src/expr/lookup.rs +++ b/rustables/src/expr/lookup.rs @@ -21,11 +21,13 @@ impl Lookup { } impl Expression for Lookup { + fn get_raw_name() -> *const libc::c_char { + b"lookup\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( - b"lookup\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); sys::nftnl_expr_set_u32( expr, diff --git a/rustables/src/expr/masquerade.rs b/rustables/src/expr/masquerade.rs index 66e9e0e..31b98c0 100644 --- a/rustables/src/expr/masquerade.rs +++ b/rustables/src/expr/masquerade.rs @@ -6,7 +6,11 @@ use std::os::raw::c_char; pub struct Masquerade; impl Expression for Masquerade { + fn get_raw_name() -> *const sys::libc::c_char { + b"masq\0" as *const _ as *const c_char + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { - try_alloc!(unsafe { sys::nftnl_expr_alloc(b"masq\0" as *const _ as *const c_char) }) + try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }) } } diff --git a/rustables/src/expr/meta.rs b/rustables/src/expr/meta.rs index a91cb27..f907278 100644 --- a/rustables/src/expr/meta.rs +++ b/rustables/src/expr/meta.rs @@ -59,11 +59,13 @@ impl Meta { } impl Expression for Meta { + fn get_raw_name() -> *const libc::c_char { + b"meta\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( - b"meta\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); if let Meta::Mark { set: true } = self { sys::nftnl_expr_set_u32( diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 4493662..b028c2f 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -53,6 +53,18 @@ impl ExpressionWrapper { /// 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 None if the attempted parsing failed. + fn from_expr(_expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + None + } + /// 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; diff --git a/rustables/src/expr/nat.rs b/rustables/src/expr/nat.rs index d60e5ea..0970134 100644 --- a/rustables/src/expr/nat.rs +++ b/rustables/src/expr/nat.rs @@ -22,9 +22,12 @@ pub struct Nat { } impl Expression for Nat { + fn get_raw_name() -> *const libc::c_char { + b"nat\0" as *const _ as *const c_char + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { - let expr = - try_alloc!(unsafe { sys::nftnl_expr_alloc(b"nat\0" as *const _ as *const c_char) }); + let expr = try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }); unsafe { sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_NAT_TYPE as u16, self.nat_type as u32); diff --git a/rustables/src/expr/payload.rs b/rustables/src/expr/payload.rs index 2da4e1f..de77e0c 100644 --- a/rustables/src/expr/payload.rs +++ b/rustables/src/expr/payload.rs @@ -46,11 +46,13 @@ impl HeaderField for Payload { } impl Expression for Payload { + fn get_raw_name() -> *const libc::c_char { + b"payload\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( - b"payload\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_PAYLOAD_BASE as u16, self.base()); sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_PAYLOAD_OFFSET as u16, self.offset()); diff --git a/rustables/src/expr/verdict.rs b/rustables/src/expr/verdict.rs index dc006bb..0c87a8e 100644 --- a/rustables/src/expr/verdict.rs +++ b/rustables/src/expr/verdict.rs @@ -90,9 +90,7 @@ impl Verdict { reject_type: RejectionType, family: ProtoFamily, ) -> *mut sys::nftnl_expr { - let expr = try_alloc!(sys::nftnl_expr_alloc( - b"reject\0" as *const _ as *const c_char - )); + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); sys::nftnl_expr_set_u32( expr, @@ -120,6 +118,10 @@ impl Verdict { } impl Expression for Verdict { + fn get_raw_name() -> *const libc::c_char { + b"reject\0" as *const _ as *const c_char + } + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { let immediate_const = match *self { Verdict::Drop => libc::NF_DROP, -- cgit v1.2.3 From d63381b5a1cc7cba91187eff7f87eeab35d4bc86 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Thu, 21 Oct 2021 23:43:35 +0200 Subject: First attempt at decoding expressions --- rustables/src/expr/counter.rs | 12 ++++++++++++ rustables/src/expr/mod.rs | 16 +++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/counter.rs b/rustables/src/expr/counter.rs index 2a8ad6f..b507e80 100644 --- a/rustables/src/expr/counter.rs +++ b/rustables/src/expr/counter.rs @@ -4,6 +4,7 @@ use std::os::raw::c_char; /// A counter expression adds a counter to the rule that is incremented to count number of packets /// and number of bytes for all packets that has matched the rule. +#[derive(Debug, PartialEq)] pub struct Counter { pub nb_bytes: u64, pub nb_packets: u64, @@ -23,6 +24,17 @@ impl Expression for Counter { b"counter\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option { + unsafe { + let nb_bytes = sys::nftnl_expr_get_u64(expr, sys::NFTNL_EXPR_CTR_BYTES as u16); + let nb_packets = sys::nftnl_expr_get_u64(expr, sys::NFTNL_EXPR_CTR_PACKETS as u16); + Some(Counter { + nb_bytes, + nb_packets, + }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index b028c2f..39ab2e0 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -25,6 +25,7 @@ impl Debug for ExpressionWrapper { } impl ExpressionWrapper { + /// Retrieves a textual description of the expression. pub fn get_str(&self) -> CString { let mut descr_buf = vec![0i8; 4096]; unsafe { @@ -39,7 +40,8 @@ impl ExpressionWrapper { } } - pub fn get_expr_kind(&self) -> Option<&CStr> { + /// Retrieves the type of expression ("log", "counter", ...). + pub fn get_kind(&self) -> Option<&CStr> { unsafe { let ptr = sys::nftnl_expr_get_str(self.expr, sys::NFTNL_EXPR_NAME as u16); if !ptr.is_null() { @@ -49,6 +51,18 @@ impl ExpressionWrapper { } } } + + /// Attempt to decode the expression as the type T, returning None if such + /// conversion is not possible or failed. + pub fn decode_expr(&self) -> Option { + if let Some(kind) = self.get_kind() { + let raw_name = unsafe { CStr::from_ptr(T::get_raw_name()) }; + if kind == raw_name { + return T::from_expr(self.expr); + } + } + None + } } /// Trait for every safe wrapper of an nftables expression. -- cgit v1.2.3 From 867e2df78a0e92cab93f647c3eeccabca5b869a2 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Fri, 22 Oct 2021 22:56:45 +0200 Subject: Add a (memory unsafe) manner to unserialize a few other expressions --- rustables/src/batch.rs | 1 + rustables/src/chain.rs | 3 +- rustables/src/expr/cmp.rs | 49 ++++++++++++++++++++++++++++-- rustables/src/expr/ct.rs | 18 +++++++++++ rustables/src/expr/immediate.rs | 38 ++++++++++++++++++++++- rustables/src/expr/log.rs | 28 ++++++++++++++++- rustables/src/expr/lookup.rs | 21 ++++++++++++- rustables/src/expr/masquerade.rs | 8 +++++ rustables/src/expr/mod.rs | 12 ++++++++ rustables/src/expr/nat.rs | 65 +++++++++++++++++++++++++++++++++++++++- 10 files changed, 236 insertions(+), 7 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/batch.rs b/rustables/src/batch.rs index c4cd6cb..3cdd52b 100644 --- a/rustables/src/batch.rs +++ b/rustables/src/batch.rs @@ -10,6 +10,7 @@ use thiserror::Error; #[error("Error while communicating with netlink")] pub struct NetlinkError(()); +#[cfg(feature = "query")] /// Check if the kernel supports batched netlink messages to netfilter. pub fn batch_is_supported() -> std::result::Result { match unsafe { sys::nftnl_batch_is_supported() } { diff --git a/rustables/src/chain.rs b/rustables/src/chain.rs index a616f90..3e28ab0 100644 --- a/rustables/src/chain.rs +++ b/rustables/src/chain.rs @@ -1,7 +1,8 @@ use crate::{MsgType, Table}; use rustables_sys::{self as sys, libc}; +#[cfg(feature = "query")] +use std::convert::TryFrom; use std::{ - convert::TryFrom, ffi::{c_void, CStr, CString}, fmt, os::raw::c_char, diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index b14aa1d..e4cfb0f 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -9,7 +9,7 @@ use std::{ }; /// Comparison operator. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum CmpOp { /// Equals. Eq, @@ -38,9 +38,23 @@ impl CmpOp { Gte => libc::NFT_CMP_GTE as u32, } } + + pub fn from_raw(val: u32) -> Option { + use self::CmpOp::*; + match val as i32 { + libc::NFT_CMP_EQ => Some(Eq), + libc::NFT_CMP_NEQ => Some(Neq), + libc::NFT_CMP_LT => Some(Lt), + libc::NFT_CMP_LTE => Some(Lte), + libc::NFT_CMP_GT => Some(Gt), + libc::NFT_CMP_GTE => Some(Gte), + _ => None, + } + } } /// Comparator expression. Allows comparing the content of the netfilter register with any value. +#[derive(Debug, PartialEq)] pub struct Cmp { op: CmpOp, data: T, @@ -54,11 +68,42 @@ impl Cmp { } } -impl Expression for Cmp { +impl Expression for Cmp { fn get_raw_name() -> *const c_char { b"cmp\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let ref_len = std::mem::size_of::() as u32; + let mut data_len = 0; + let data = sys::nftnl_expr_get( + expr, + sys::NFTNL_EXPR_CMP_DATA as u16, + &mut data_len as *mut u32, + ); + + if data.is_null() { + return None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing a 'cmp' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + // Warning: this is *very* dangerous safety wise if the user supply us with + // a type that have the same size as T but a different memory layout. + // Is there a better way? And if there isn't, shouldn't we gate this behind + // an "unsafe" boundary? + let data = *(data as *const T); + + let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16)); + op.map(|op| Cmp { op, data }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/ct.rs b/rustables/src/expr/ct.rs index 1f15858..9d58591 100644 --- a/rustables/src/expr/ct.rs +++ b/rustables/src/expr/ct.rs @@ -31,6 +31,24 @@ impl Expression for Conntrack { b"ct\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let ct_key = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CT_KEY as u16); + let ct_sreg_is_set = sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_CT_SREG as u16); + + match ct_key as i32 { + libc::NFT_CT_STATE => Some(Conntrack::State), + libc::NFT_CT_MARK => Some(Conntrack::Mark { + set: ct_sreg_is_set, + }), + _ => None, + } + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index 15eb452..e8823b1 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -18,11 +18,47 @@ impl Immediate { } } -impl Expression for Immediate { +// The Copy requirement is present to allow us to dereference the newly created raw pointer in `from_expr` +impl Expression for Immediate { fn get_raw_name() -> *const c_char { b"immediate\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let ref_len = std::mem::size_of::() 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 None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing an 'immediate' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + // Warning: this is *very* dangerous safety wise if the user supply us with + // a type that have the same size as T but a different memory layout. + // Is there a better way? And if there isn't, shouldn't we gate this behind + // an "unsafe" boundary? + let data = *(data as *const T); + + let register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_IMM_DREG as u16, + )); + + register.map(|register| Immediate { data, register }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/log.rs b/rustables/src/expr/log.rs index 8f52686..ba1244a 100644 --- a/rustables/src/expr/log.rs +++ b/rustables/src/expr/log.rs @@ -1,10 +1,11 @@ use super::{Expression, Rule}; use rustables_sys as sys; -use std::ffi::CString; +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, pub prefix: Option, @@ -15,6 +16,31 @@ impl Expression for Log { b"log\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let mut group = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_LOG_GROUP as u16) { + group = Some(LogGroup(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_LOG_GROUP as u16, + ) as u16)); + } + let mut prefix = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16) { + let raw_prefix = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16); + if raw_prefix.is_null() { + trace!("Unexpected empty prefix in a 'log' expression"); + } else { + prefix = Some(LogPrefix(CStr::from_ptr(raw_prefix).to_owned())); + } + } + Some(Log { group, prefix }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(b"log\0" as *const _ as *const c_char)); diff --git a/rustables/src/expr/lookup.rs b/rustables/src/expr/lookup.rs index d9acbe6..b9a03d9 100644 --- a/rustables/src/expr/lookup.rs +++ b/rustables/src/expr/lookup.rs @@ -1,9 +1,10 @@ use super::{Expression, Rule}; use crate::set::Set; use rustables_sys::{self as sys, libc}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; +#[derive(Debug, PartialEq)] pub struct Lookup { set_name: CString, set_id: u32, @@ -25,6 +26,24 @@ impl Expression for Lookup { b"lookup\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let set_name = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_LOOKUP_SET as u16); + let set_id = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_LOOKUP_SET_ID as u16); + + if set_name.is_null() { + return None; + } + + let set_name = CStr::from_ptr(set_name).to_owned(); + + Some(Lookup { set_id, set_name }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/masquerade.rs b/rustables/src/expr/masquerade.rs index 31b98c0..bf4e0de 100644 --- a/rustables/src/expr/masquerade.rs +++ b/rustables/src/expr/masquerade.rs @@ -3,6 +3,7 @@ use rustables_sys as sys; use std::os::raw::c_char; /// Sets the source IP to that of the output interface. +#[derive(Debug, PartialEq)] pub struct Masquerade; impl Expression for Masquerade { @@ -10,6 +11,13 @@ impl Expression for Masquerade { b"masq\0" as *const _ as *const c_char } + fn from_expr(_expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + Some(Masquerade) + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }) } diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 39ab2e0..f2a4d5e 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -89,6 +89,7 @@ pub trait Expression { #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] pub enum Register { + Verdict = libc::NFT_REG_VERDICT, Reg1 = libc::NFT_REG_1, Reg2 = libc::NFT_REG_2, Reg3 = libc::NFT_REG_3, @@ -99,6 +100,17 @@ impl Register { pub fn to_raw(self) -> u32 { self as u32 } + + pub fn from_raw(val: u32) -> Option { + match val as i32 { + libc::NFT_REG_VERDICT => Some(Self::Verdict), + libc::NFT_REG_1 => Some(Self::Reg1), + libc::NFT_REG_2 => Some(Self::Reg2), + libc::NFT_REG_3 => Some(Self::Reg3), + libc::NFT_REG_4 => Some(Self::Reg4), + _ => None, + } + } } mod bitwise; diff --git a/rustables/src/expr/nat.rs b/rustables/src/expr/nat.rs index 0970134..6bd0619 100644 --- a/rustables/src/expr/nat.rs +++ b/rustables/src/expr/nat.rs @@ -1,7 +1,7 @@ use super::{Expression, Register, Rule}; use crate::ProtoFamily; use rustables_sys::{self as sys, libc}; -use std::os::raw::c_char; +use std::{convert::TryFrom, os::raw::c_char}; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] @@ -12,8 +12,19 @@ pub enum NatType { DNat = libc::NFT_NAT_DNAT, } +impl NatType { + fn from_raw(val: u32) -> Option { + match val as i32 { + libc::NFT_NAT_SNAT => Some(NatType::SNat), + libc::NFT_NAT_DNAT => Some(NatType::DNat), + _ => None, + } + } +} + /// A source or destination NAT statement. Modifies the source or destination address /// (and possibly port) of packets. +#[derive(Debug, PartialEq)] pub struct Nat { pub nat_type: NatType, pub family: ProtoFamily, @@ -26,6 +37,58 @@ impl Expression for Nat { b"nat\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let nat_type = NatType::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_TYPE as u16, + )); + let nat_type = match nat_type { + Some(x) => x, + None => return None, + }; + + let family = ProtoFamily::try_from(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_FAMILY as u16, + ) as i32); + let family = match family { + Ok(x) => x, + Err(_) => return None, + }; + + let ip_register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_REG_ADDR_MIN as u16, + )); + let ip_register = match ip_register { + Some(x) => x, + None => return None, + }; + + let mut port_register = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16) { + port_register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16, + )); + if port_register.is_none() { + trace!("Invalid register in expression 'nat'"); + } + } + + Some(Nat { + ip_register, + nat_type, + family, + port_register, + }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { let expr = try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }); -- cgit v1.2.3 From 88d35077681438d479c0914c325d7054a589fe21 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 12:24:28 +0200 Subject: add support for deserializing Payload expressions --- rustables/src/expr/payload.rs | 213 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 25 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/payload.rs b/rustables/src/expr/payload.rs index de77e0c..a6b5ddf 100644 --- a/rustables/src/expr/payload.rs +++ b/rustables/src/expr/payload.rs @@ -8,7 +8,7 @@ trait HeaderField { } /// Payload expressions refer to data from the packet's payload. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Payload { LinkLayer(LLHeaderField), Network(NetworkHeaderField), @@ -16,40 +16,99 @@ pub enum Payload { } impl Payload { - fn base(self) -> u32 { + pub fn build(&self) -> RawPayload { + match *self { + Payload::LinkLayer(ref f) => RawPayload::LinkLayer(RawPayloadData { + offset: f.offset(), + len: f.len(), + }), + Payload::Network(ref f) => RawPayload::LinkLayer(RawPayloadData { + offset: f.offset(), + len: f.len(), + }), + Payload::Transport(ref f) => RawPayload::LinkLayer(RawPayloadData { + offset: f.offset(), + len: f.offset(), + }), + } + } +} + +impl Expression for Payload { + fn get_raw_name() -> *const libc::c_char { + RawPayload::get_raw_name() + } + + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + self.build().to_expr(rule) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct RawPayloadData { + offset: u32, + len: u32, +} + +/// Because deserializing a `Payload` expression is not possible (there is not enough information +/// in the expression itself, this enum should be used to deserialize payloads. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum RawPayload { + LinkLayer(RawPayloadData), + Network(RawPayloadData), + Transport(RawPayloadData), +} + +impl RawPayload { + fn base(&self) -> u32 { match self { - Payload::LinkLayer(_) => libc::NFT_PAYLOAD_LL_HEADER as u32, - Payload::Network(_) => libc::NFT_PAYLOAD_NETWORK_HEADER as u32, - Payload::Transport(_) => libc::NFT_PAYLOAD_TRANSPORT_HEADER as u32, + Self::LinkLayer(_) => libc::NFT_PAYLOAD_LL_HEADER as u32, + Self::Network(_) => libc::NFT_PAYLOAD_NETWORK_HEADER as u32, + Self::Transport(_) => libc::NFT_PAYLOAD_TRANSPORT_HEADER as u32, } } } -impl HeaderField for Payload { +impl HeaderField for RawPayload { fn offset(&self) -> u32 { - use self::Payload::*; - match *self { - LinkLayer(ref f) => f.offset(), - Network(ref f) => f.offset(), - Transport(ref f) => f.offset(), + match self { + Self::LinkLayer(ref f) | Self::Network(ref f) | Self::Transport(ref f) => f.offset, } } fn len(&self) -> u32 { - use self::Payload::*; - match *self { - LinkLayer(ref f) => f.len(), - Network(ref f) => f.len(), - Transport(ref f) => f.len(), + match self { + Self::LinkLayer(ref f) | Self::Network(ref f) | Self::Transport(ref f) => f.len, } } } -impl Expression for Payload { +impl Expression for RawPayload { fn get_raw_name() -> *const libc::c_char { b"payload\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option { + unsafe { + let base = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_BASE as u16); + let offset = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_OFFSET as u16); + let len = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_LEN as u16); + match base as i32 { + libc::NFT_PAYLOAD_LL_HEADER => { + Some(Self::LinkLayer(RawPayloadData { offset, len })) + } + libc::NFT_PAYLOAD_NETWORK_HEADER => { + Some(Self::Network(RawPayloadData { offset, len })) + } + libc::NFT_PAYLOAD_TRANSPORT_HEADER => { + Some(Self::Transport(RawPayloadData { offset, len })) + } + + _ => return None, + } + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); @@ -68,7 +127,7 @@ impl Expression for Payload { } } -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum LLHeaderField { Daddr, @@ -96,7 +155,24 @@ impl HeaderField for LLHeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +impl LLHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 6 { + Some(Self::Daddr) + } else if off == 6 && len == 6 { + Some(Self::Saddr) + } else if off == 12 && len == 2 { + Some(Self::EtherType) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum NetworkHeaderField { Ipv4(Ipv4HeaderField), Ipv6(Ipv6HeaderField), @@ -120,7 +196,7 @@ impl HeaderField for NetworkHeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum Ipv4HeaderField { Ttl, @@ -151,7 +227,26 @@ impl HeaderField for Ipv4HeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +impl Ipv4HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 8 && len == 1 { + Some(Self::Ttl) + } else if off == 9 && len == 1 { + Some(Self::Protocol) + } else if off == 12 && len == 4 { + Some(Self::Saddr) + } else if off == 16 && len == 4 { + Some(Self::Daddr) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum Ipv6HeaderField { NextHeader, @@ -182,7 +277,26 @@ impl HeaderField for Ipv6HeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +impl Ipv6HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 6 && len == 1 { + Some(Self::NextHeader) + } else if off == 7 && len == 1 { + Some(Self::HopLimit) + } else if off == 8 && len == 16 { + Some(Self::Saddr) + } else if off == 24 && len == 16 { + Some(Self::Daddr) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum TransportHeaderField { Tcp(TcpHeaderField), @@ -210,7 +324,7 @@ impl HeaderField for TransportHeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum TcpHeaderField { Sport, @@ -235,7 +349,22 @@ impl HeaderField for TcpHeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +impl TcpHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 2 { + Some(Self::Sport) + } else if off == 2 && len == 2 { + Some(Self::Dport) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum UdpHeaderField { Sport, @@ -263,7 +392,24 @@ impl HeaderField for UdpHeaderField { } } -#[derive(Copy, Clone, Eq, PartialEq)] +impl UdpHeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 2 { + Some(Self::Sport) + } else if off == 2 && len == 2 { + Some(Self::Dport) + } else if off == 4 && len == 2 { + Some(Self::Len) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum Icmpv6HeaderField { Type, @@ -291,6 +437,23 @@ impl HeaderField for Icmpv6HeaderField { } } +impl Icmpv6HeaderField { + pub fn from_raw_data(data: &RawPayloadData) -> Option { + let off = data.offset; + let len = data.len; + + if off == 0 && len == 1 { + Some(Self::Type) + } else if off == 1 && len == 1 { + Some(Self::Code) + } else if off == 2 && len == 2 { + Some(Self::Checksum) + } else { + None + } + } +} + #[macro_export(local_inner_macros)] macro_rules! nft_expr_payload { (@ipv4_field ttl) => { -- cgit v1.2.3 From b1e97afbc53be09b5a6bdfb53fabafecf7d8612f Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 13:47:06 +0200 Subject: Add "deserialization" support for Verdict, Reject and Meta --- rustables/src/expr/meta.rs | 43 +++++++++++ rustables/src/expr/verdict.rs | 173 ++++++++++++++++++++++++++++-------------- 2 files changed, 158 insertions(+), 58 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/meta.rs b/rustables/src/expr/meta.rs index f907278..ba803ac 100644 --- a/rustables/src/expr/meta.rs +++ b/rustables/src/expr/meta.rs @@ -3,6 +3,7 @@ use rustables_sys::{self as sys, libc}; use std::os::raw::c_char; /// A meta expression refers to meta data associated with a packet. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum Meta { /// Packet ethertype protocol (skb->protocol), invalid in OUTPUT. @@ -56,6 +57,27 @@ impl Meta { PRandom => libc::NFT_META_PRANDOM as u32, } } + + fn from_raw(val: u32) -> Option { + match val as i32 { + libc::NFT_META_PROTOCOL => Some(Self::Protocol), + libc::NFT_META_MARK => Some(Self::Mark { set: false }), + libc::NFT_META_IIF => Some(Self::Iif), + libc::NFT_META_OIF => Some(Self::Oif), + libc::NFT_META_IIFNAME => Some(Self::IifName), + libc::NFT_META_OIFNAME => Some(Self::OifName), + libc::NFT_META_IIFTYPE => Some(Self::IifType), + libc::NFT_META_OIFTYPE => Some(Self::OifType), + libc::NFT_META_SKUID => Some(Self::SkUid), + libc::NFT_META_SKGID => Some(Self::SkGid), + libc::NFT_META_NFPROTO => Some(Self::NfProto), + libc::NFT_META_L4PROTO => Some(Self::L4Proto), + libc::NFT_META_CGROUP => Some(Self::Cgroup), + libc::NFT_META_PRANDOM => Some(Self::PRandom), + + _ => None, + } + } } impl Expression for Meta { @@ -63,6 +85,27 @@ impl Expression for Meta { b"meta\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + let mut ret = match Self::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_META_KEY as u16, + )) { + Some(x) => x, + None => return None, + }; + + if let Self::Mark { ref mut set } = ret { + *set = sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_META_SREG as u16); + } + + Some(ret) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/verdict.rs b/rustables/src/expr/verdict.rs index 0c87a8e..a1f2b51 100644 --- a/rustables/src/expr/verdict.rs +++ b/rustables/src/expr/verdict.rs @@ -14,8 +14,6 @@ pub enum Verdict { Drop, /// Accept the packet and let it pass. Accept, - /// Reject the packet and return a message. - Reject(RejectionType), Queue, Continue, Break, @@ -30,27 +28,75 @@ pub enum Verdict { /// The type of rejection message sent by the Reject verdict. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum RejectionType { +pub enum Reject { /// Return an ICMP unreachable packet Icmp(IcmpCode), /// Reject by sending a TCP RST packet TcpRst, } -impl RejectionType { +impl Reject { fn to_raw(&self, family: ProtoFamily) -> u32 { use libc::*; let value = match *self { - RejectionType::Icmp(..) => match family { + Self::Icmp(..) => match family { ProtoFamily::Bridge | ProtoFamily::Inet => NFT_REJECT_ICMPX_UNREACH, _ => NFT_REJECT_ICMP_UNREACH, }, - RejectionType::TcpRst => NFT_REJECT_TCP_RST, + Self::TcpRst => NFT_REJECT_TCP_RST, }; value as u32 } } +impl Expression for Reject { + fn get_raw_name() -> *const libc::c_char { + b"reject\0" as *const _ as *const c_char + } + + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + if sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_REJECT_TYPE as u16) + == libc::NFT_REJECT_TCP_RST as u32 + { + Some(Self::TcpRst) + } else { + IcmpCode::from_raw(sys::nftnl_expr_get_u8( + expr, + sys::NFTNL_EXPR_REJECT_CODE as u16, + )) + .map(Self::Icmp) + } + } + } + + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + let family = rule.get_chain().get_table().get_family(); + + unsafe { + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); + + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_REJECT_TYPE as u16, + self.to_raw(family), + ); + + let reject_code = match *self { + Reject::Icmp(code) => code as u8, + Reject::TcpRst => 0, + }; + + sys::nftnl_expr_set_u8(expr, sys::NFTNL_EXPR_REJECT_CODE as u16, reject_code); + + expr + } + } +} + /// An ICMP reject code. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u8)] @@ -61,53 +107,19 @@ pub enum IcmpCode { AdminProhibited = libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED as u8, } -impl Verdict { - unsafe fn to_immediate_expr(&self, immediate_const: i32) -> *mut sys::nftnl_expr { - 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()); +impl IcmpCode { + fn from_raw(code: u8) -> Option { + match code as i32 { + libc::NFT_REJECT_ICMPX_NO_ROUTE => Some(Self::NoRoute), + libc::NFT_REJECT_ICMPX_PORT_UNREACH => Some(Self::PortUnreach), + libc::NFT_REJECT_ICMPX_HOST_UNREACH => Some(Self::HostUnreach), + libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED => Some(Self::AdminProhibited), + _ => None, } - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_VERDICT as u16, - immediate_const as u32, - ); - - expr - } - - unsafe fn to_reject_expr( - &self, - reject_type: RejectionType, - family: ProtoFamily, - ) -> *mut sys::nftnl_expr { - let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); - - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_REJECT_TYPE as u16, - reject_type.to_raw(family), - ); - - let reject_code = match reject_type { - RejectionType::Icmp(code) => code as u8, - RejectionType::TcpRst => 0, - }; - - sys::nftnl_expr_set_u8(expr, sys::NFTNL_EXPR_REJECT_CODE as u16, reject_code); - - expr } +} +impl Verdict { fn chain(&self) -> Option<&CStr> { match *self { Verdict::Jump { ref chain } => Some(chain.as_c_str()), @@ -119,10 +131,39 @@ impl Verdict { impl Expression for Verdict { fn get_raw_name() -> *const libc::c_char { - b"reject\0" as *const _ as *const c_char + b"immediate\0" as *const _ as *const c_char } - fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + fn from_expr(expr: *const sys::nftnl_expr) -> Option { + 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() { + trace!("Unexpected empty chain name when deserializing 'verdict' expression"); + return None; + } + 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 => Some(Verdict::Drop), + libc::NF_ACCEPT => Some(Verdict::Accept), + libc::NF_QUEUE => Some(Verdict::Queue), + libc::NFT_CONTINUE => Some(Verdict::Continue), + libc::NFT_BREAK => Some(Verdict::Break), + libc::NFT_JUMP => chain.map(|chain| Verdict::Jump { chain }), + libc::NFT_GOTO => chain.map(|chain| Verdict::Goto { chain }), + libc::NFT_RETURN => Some(Verdict::Return), + _ => None, + } + } + } + + 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, @@ -132,13 +173,29 @@ impl Expression for Verdict { Verdict::Jump { .. } => libc::NFT_JUMP, Verdict::Goto { .. } => libc::NFT_GOTO, Verdict::Return => libc::NFT_RETURN, - Verdict::Reject(reject_type) => { - return unsafe { - self.to_reject_expr(reject_type, rule.get_chain().get_table().get_family()) - } - } }; - unsafe { self.to_immediate_expr(immediate_const) } + 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 + } } } -- cgit v1.2.3 From 4af236dd27c8c99731e2ad3dc1984ea67866cbb2 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 14:36:21 +0200 Subject: Enforce memory safety of the Cmp/Immediate deserialization step by limiting such operations to byte arrays --- rustables/src/expr/cmp.rs | 84 +++++++++++++++++++----------------- rustables/src/expr/immediate.rs | 94 ++++++++++++++++++++++++++++++----------- 2 files changed, 114 insertions(+), 64 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index e4cfb0f..11825d6 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -55,7 +55,7 @@ impl CmpOp { /// Comparator expression. Allows comparing the content of the netfilter register with any value. #[derive(Debug, PartialEq)] -pub struct Cmp { +pub struct Cmp { op: CmpOp, data: T, } @@ -68,42 +68,11 @@ impl Cmp { } } -impl Expression for Cmp { +impl Expression for Cmp { fn get_raw_name() -> *const c_char { b"cmp\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option - where - Self: Sized, - { - unsafe { - let ref_len = std::mem::size_of::() as u32; - let mut data_len = 0; - let data = sys::nftnl_expr_get( - expr, - sys::NFTNL_EXPR_CMP_DATA as u16, - &mut data_len as *mut u32, - ); - - if data.is_null() { - return None; - } else if data_len != ref_len { - debug!("Invalid size requested for deserializing a 'cmp' expression: expected {} bytes, got {}", ref_len, data_len); - return None; - } - - // Warning: this is *very* dangerous safety wise if the user supply us with - // a type that have the same size as T but a different memory layout. - // Is there a better way? And if there isn't, shouldn't we gate this behind - // an "unsafe" boundary? - let data = *(data as *const T); - - let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16)); - op.map(|op| Cmp { op, data }) - } - } - fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); @@ -129,6 +98,49 @@ impl Expression for Cmp { } } +impl Expression for Cmp<[u8; N]> { + fn get_raw_name() -> *const c_char { + Cmp::::get_raw_name() + } + + // 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) -> Option { + 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_CMP_DATA as u16, + &mut data_len as *mut u32, + ); + + if data.is_null() { + return None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing a 'cmp' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + let data = *(data as *const [u8; N]); + + let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16)); + op.map(|op| Cmp { op, data }) + } + } + + // call to the other implementation to generate the expression + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + Cmp { + data: self.data.as_ref(), + op: self.op, + } + .to_expr(rule) + } +} + #[macro_export(local_inner_macros)] macro_rules! nft_expr_cmp { (@cmp_op ==) => { @@ -160,12 +172,6 @@ pub trait ToSlice { fn to_slice(&self) -> Cow<'_, [u8]>; } -impl<'a> ToSlice for [u8; 0] { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Borrowed(&[]) - } -} - impl<'a> ToSlice for &'a [u8] { fn to_slice(&self) -> Cow<'_, [u8]> { Cow::Borrowed(self) diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index e8823b1..0e26bb4 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -1,4 +1,4 @@ -use super::{Expression, Register, Rule}; +use super::{Expression, Register, Rule, ToSlice}; use rustables_sys as sys; use std::ffi::c_void; use std::mem::size_of_val; @@ -18,18 +18,45 @@ impl Immediate { } } -// The Copy requirement is present to allow us to dereference the newly created raw pointer in `from_expr` -impl Expression for Immediate { +impl Expression for Immediate { fn get_raw_name() -> *const c_char { b"immediate\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option - where - Self: Sized, - { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { - let ref_len = std::mem::size_of::() as u32; + 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(), + ); + + sys::nftnl_expr_set( + expr, + sys::NFTNL_EXPR_IMM_DATA as u16, + &self.data.to_slice() as *const _ as *const c_void, + size_of_val(&self.data) as u32, + ); + + expr + } + } +} + +impl Expression for Immediate<[u8; N]> { + fn get_raw_name() -> *const c_char { + Immediate::::get_raw_name() + } + + // 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) -> Option { + unsafe { + let ref_len = std::mem::size_of::<[u8; N]>() as u32; let mut data_len = 0; let data = sys::nftnl_expr_get( expr, @@ -44,11 +71,7 @@ impl Expression for Immediate { return None; } - // Warning: this is *very* dangerous safety wise if the user supply us with - // a type that have the same size as T but a different memory layout. - // Is there a better way? And if there isn't, shouldn't we gate this behind - // an "unsafe" boundary? - let data = *(data as *const T); + let data = *(data as *const [u8; N]); let register = Register::from_raw(sys::nftnl_expr_get_u32( expr, @@ -59,24 +82,45 @@ impl Expression for Immediate { } } - fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { + // 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_ref(), + } + .to_expr(rule) + } +} +// 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. +impl Immediate<[u8; N]> { + pub fn from_expr(expr: *const sys::nftnl_expr) -> Option { unsafe { - let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); - - sys::nftnl_expr_set_u32( + 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_DREG as u16, - self.register.to_raw(), + sys::NFTNL_EXPR_IMM_DATA as u16, + &mut data_len as *mut u32, ); - sys::nftnl_expr_set( + if data.is_null() { + return None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing an 'immediate' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + let data = *(data as *const [u8; N]); + + let register = Register::from_raw(sys::nftnl_expr_get_u32( expr, - sys::NFTNL_EXPR_IMM_DATA as u16, - &self.data as *const _ as *const c_void, - size_of_val(&self.data) as u32, - ); + sys::NFTNL_EXPR_IMM_DREG as u16, + )); - expr + register.map(|register| Immediate { data, register }) } } } -- cgit v1.2.3 From 2f1261eae4d52165bc4abed0762c1b20a0f2b4bc Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 14:38:31 +0200 Subject: fix a small doc reference issue --- rustables/src/expr/immediate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index 0e26bb4..1196211 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -5,7 +5,7 @@ use std::mem::size_of_val; use std::os::raw::c_char; /// An immediate expression. Used to set immediate data. -/// Verdicts are handled separately by [Verdict]. +/// Verdicts are handled separately by [crate::expr::Verdict]. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct Immediate { pub data: T, -- cgit v1.2.3 From 46dbd76149346ba978c69048a09b3657e3b86f0a Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 18:14:47 +0200 Subject: reorganize rustables/src/expr/mod.rs --- rustables/src/expr/mod.rs | 132 ++++++++--------------------------------- rustables/src/expr/register.rs | 32 ++++++++++ rustables/src/expr/wrapper.rs | 62 +++++++++++++++++++ 3 files changed, 119 insertions(+), 107 deletions(-) create mode 100644 rustables/src/expr/register.rs create mode 100644 rustables/src/expr/wrapper.rs (limited to 'rustables/src') diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index f2a4d5e..1993f15 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -3,116 +3,9 @@ //! //! [`Rule`]: struct.Rule.html -use std::ffi::CStr; -use std::ffi::CString; -use std::fmt::Debug; -use std::rc::Rc; - use super::rule::Rule; use rustables_sys::{self as sys, libc}; -pub struct ExpressionWrapper { - pub(crate) expr: *const sys::nftnl_expr, - // we also need the rule here to ensure that the rule lives as long as the `expr` pointer - #[allow(dead_code)] - pub(crate) rule: Rc, -} - -impl Debug for ExpressionWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.get_str()) - } -} - -impl ExpressionWrapper { - /// Retrieves a textual description of the expression. - pub fn get_str(&self) -> CString { - let mut descr_buf = vec![0i8; 4096]; - unsafe { - sys::nftnl_expr_snprintf( - descr_buf.as_mut_ptr(), - (descr_buf.len() - 1) as u64, - self.expr, - sys::NFTNL_OUTPUT_DEFAULT, - 0, - ); - CStr::from_ptr(descr_buf.as_ptr()).to_owned() - } - } - - /// Retrieves the type of expression ("log", "counter", ...). - pub fn get_kind(&self) -> Option<&CStr> { - unsafe { - let ptr = sys::nftnl_expr_get_str(self.expr, sys::NFTNL_EXPR_NAME as u16); - if !ptr.is_null() { - Some(CStr::from_ptr(ptr)) - } else { - None - } - } - } - - /// Attempt to decode the expression as the type T, returning None if such - /// conversion is not possible or failed. - pub fn decode_expr(&self) -> Option { - if let Some(kind) = self.get_kind() { - let raw_name = unsafe { CStr::from_ptr(T::get_raw_name()) }; - if kind == raw_name { - return T::from_expr(self.expr); - } - } - None - } -} - -/// 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 None if the attempted parsing failed. - fn from_expr(_expr: *const sys::nftnl_expr) -> Option - where - Self: Sized, - { - None - } - - /// 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 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)] -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, -} - -impl Register { - pub fn to_raw(self) -> u32 { - self as u32 - } - - pub fn from_raw(val: u32) -> Option { - match val as i32 { - libc::NFT_REG_VERDICT => Some(Self::Verdict), - libc::NFT_REG_1 => Some(Self::Reg1), - libc::NFT_REG_2 => Some(Self::Reg2), - libc::NFT_REG_3 => Some(Self::Reg3), - libc::NFT_REG_4 => Some(Self::Reg4), - _ => None, - } - } -} - mod bitwise; pub use self::bitwise::*; @@ -149,6 +42,31 @@ pub use self::payload::*; mod verdict; pub use self::verdict::*; +mod register; +pub use self::register::Register; + +mod wrapper; +pub use self::wrapper::ExpressionWrapper; + +/// 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 None if the attempted parsing failed. + fn from_expr(_expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + None + } + + /// 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; +} + #[macro_export(local_inner_macros)] macro_rules! nft_expr { (bitwise mask $mask:expr,xor $xor:expr) => { diff --git a/rustables/src/expr/register.rs b/rustables/src/expr/register.rs new file mode 100644 index 0000000..3013451 --- /dev/null +++ b/rustables/src/expr/register.rs @@ -0,0 +1,32 @@ +use std::fmt::Debug; + +use rustables_sys::libc; + +/// 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)] +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, +} + +impl Register { + pub fn to_raw(self) -> u32 { + self as u32 + } + + pub fn from_raw(val: u32) -> Option { + match val as i32 { + libc::NFT_REG_VERDICT => Some(Self::Verdict), + libc::NFT_REG_1 => Some(Self::Reg1), + libc::NFT_REG_2 => Some(Self::Reg2), + libc::NFT_REG_3 => Some(Self::Reg3), + libc::NFT_REG_4 => Some(Self::Reg4), + _ => None, + } + } +} diff --git a/rustables/src/expr/wrapper.rs b/rustables/src/expr/wrapper.rs new file mode 100644 index 0000000..b9b90b3 --- /dev/null +++ b/rustables/src/expr/wrapper.rs @@ -0,0 +1,62 @@ +use std::ffi::CStr; +use std::ffi::CString; +use std::fmt::Debug; +use std::rc::Rc; + +use super::Expression; +use crate::Rule; +use rustables_sys as sys; + +pub struct ExpressionWrapper { + pub(crate) expr: *const sys::nftnl_expr, + // we also need the rule here to ensure that the rule lives as long as the `expr` pointer + #[allow(dead_code)] + pub(crate) rule: Rc, +} + +impl Debug for ExpressionWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.get_str()) + } +} + +impl ExpressionWrapper { + /// Retrieves a textual description of the expression. + pub fn get_str(&self) -> CString { + let mut descr_buf = vec![0i8; 4096]; + unsafe { + sys::nftnl_expr_snprintf( + descr_buf.as_mut_ptr(), + (descr_buf.len() - 1) as u64, + self.expr, + sys::NFTNL_OUTPUT_DEFAULT, + 0, + ); + CStr::from_ptr(descr_buf.as_ptr()).to_owned() + } + } + + /// Retrieves the type of expression ("log", "counter", ...). + pub fn get_kind(&self) -> Option<&CStr> { + unsafe { + let ptr = sys::nftnl_expr_get_str(self.expr, sys::NFTNL_EXPR_NAME as u16); + if !ptr.is_null() { + Some(CStr::from_ptr(ptr)) + } else { + None + } + } + } + + /// Attempt to decode the expression as the type T, returning None if such + /// conversion is not possible or failed. + pub fn decode_expr(&self) -> Option { + if let Some(kind) = self.get_kind() { + let raw_name = unsafe { CStr::from_ptr(T::get_raw_name()) }; + if kind == raw_name { + return T::from_expr(self.expr); + } + } + None + } +} -- cgit v1.2.3 From 82ebb702c1358ac4af40c7ee43efa6f364fa6d50 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 18:17:59 +0200 Subject: separate the Reject and Verdict in distinct files --- rustables/src/expr/mod.rs | 7 ++- rustables/src/expr/reject.rs | 100 ++++++++++++++++++++++++++++++++++++++++++ rustables/src/expr/verdict.rs | 94 --------------------------------------- 3 files changed, 105 insertions(+), 96 deletions(-) create mode 100644 rustables/src/expr/reject.rs (limited to 'rustables/src') diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 1993f15..431a0b9 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -39,12 +39,15 @@ pub use self::nat::*; mod payload; pub use self::payload::*; -mod verdict; -pub use self::verdict::*; +mod reject; +pub use self::reject::{IcmpCode, Reject}; mod register; pub use self::register::Register; +mod verdict; +pub use self::verdict::*; + mod wrapper; pub use self::wrapper::ExpressionWrapper; diff --git a/rustables/src/expr/reject.rs b/rustables/src/expr/reject.rs new file mode 100644 index 0000000..f94079b --- /dev/null +++ b/rustables/src/expr/reject.rs @@ -0,0 +1,100 @@ +use super::{Expression, Rule}; +use crate::ProtoFamily; +use rustables_sys::{ + self as sys, + libc::{self, c_char}, +}; + +/// A reject expression that defines the type of rejection message sent +/// when discarding a packet. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum Reject { + /// Return an ICMP unreachable packet + Icmp(IcmpCode), + /// Reject by sending a TCP RST packet + TcpRst, +} + +impl Reject { + fn to_raw(&self, family: ProtoFamily) -> u32 { + use libc::*; + let value = match *self { + Self::Icmp(..) => match family { + ProtoFamily::Bridge | ProtoFamily::Inet => NFT_REJECT_ICMPX_UNREACH, + _ => NFT_REJECT_ICMP_UNREACH, + }, + Self::TcpRst => NFT_REJECT_TCP_RST, + }; + value as u32 + } +} + +impl Expression for Reject { + fn get_raw_name() -> *const libc::c_char { + b"reject\0" as *const _ as *const c_char + } + + fn from_expr(expr: *const sys::nftnl_expr) -> Option + where + Self: Sized, + { + unsafe { + if sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_REJECT_TYPE as u16) + == libc::NFT_REJECT_TCP_RST as u32 + { + Some(Self::TcpRst) + } else { + IcmpCode::from_raw(sys::nftnl_expr_get_u8( + expr, + sys::NFTNL_EXPR_REJECT_CODE as u16, + )) + .map(Self::Icmp) + } + } + } + + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + let family = rule.get_chain().get_table().get_family(); + + unsafe { + let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); + + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_REJECT_TYPE as u16, + self.to_raw(family), + ); + + let reject_code = match *self { + Reject::Icmp(code) => code as u8, + Reject::TcpRst => 0, + }; + + sys::nftnl_expr_set_u8(expr, sys::NFTNL_EXPR_REJECT_CODE as u16, reject_code); + + expr + } + } +} + +/// An ICMP reject code. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[repr(u8)] +pub enum IcmpCode { + NoRoute = libc::NFT_REJECT_ICMPX_NO_ROUTE as u8, + PortUnreach = libc::NFT_REJECT_ICMPX_PORT_UNREACH as u8, + HostUnreach = libc::NFT_REJECT_ICMPX_HOST_UNREACH as u8, + AdminProhibited = libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED as u8, +} + +impl IcmpCode { + fn from_raw(code: u8) -> Option { + match code as i32 { + libc::NFT_REJECT_ICMPX_NO_ROUTE => Some(Self::NoRoute), + libc::NFT_REJECT_ICMPX_PORT_UNREACH => Some(Self::PortUnreach), + libc::NFT_REJECT_ICMPX_HOST_UNREACH => Some(Self::HostUnreach), + libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED => Some(Self::AdminProhibited), + _ => None, + } + } +} diff --git a/rustables/src/expr/verdict.rs b/rustables/src/expr/verdict.rs index a1f2b51..772da52 100644 --- a/rustables/src/expr/verdict.rs +++ b/rustables/src/expr/verdict.rs @@ -1,5 +1,4 @@ use super::{Expression, Rule}; -use crate::ProtoFamily; use rustables_sys::{ self as sys, libc::{self, c_char}, @@ -26,99 +25,6 @@ pub enum Verdict { Return, } -/// The type of rejection message sent by the Reject verdict. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub enum Reject { - /// Return an ICMP unreachable packet - Icmp(IcmpCode), - /// Reject by sending a TCP RST packet - TcpRst, -} - -impl Reject { - fn to_raw(&self, family: ProtoFamily) -> u32 { - use libc::*; - let value = match *self { - Self::Icmp(..) => match family { - ProtoFamily::Bridge | ProtoFamily::Inet => NFT_REJECT_ICMPX_UNREACH, - _ => NFT_REJECT_ICMP_UNREACH, - }, - Self::TcpRst => NFT_REJECT_TCP_RST, - }; - value as u32 - } -} - -impl Expression for Reject { - fn get_raw_name() -> *const libc::c_char { - b"reject\0" as *const _ as *const c_char - } - - fn from_expr(expr: *const sys::nftnl_expr) -> Option - where - Self: Sized, - { - unsafe { - if sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_REJECT_TYPE as u16) - == libc::NFT_REJECT_TCP_RST as u32 - { - Some(Self::TcpRst) - } else { - IcmpCode::from_raw(sys::nftnl_expr_get_u8( - expr, - sys::NFTNL_EXPR_REJECT_CODE as u16, - )) - .map(Self::Icmp) - } - } - } - - fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { - let family = rule.get_chain().get_table().get_family(); - - unsafe { - let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); - - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_REJECT_TYPE as u16, - self.to_raw(family), - ); - - let reject_code = match *self { - Reject::Icmp(code) => code as u8, - Reject::TcpRst => 0, - }; - - sys::nftnl_expr_set_u8(expr, sys::NFTNL_EXPR_REJECT_CODE as u16, reject_code); - - expr - } - } -} - -/// An ICMP reject code. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -#[repr(u8)] -pub enum IcmpCode { - NoRoute = libc::NFT_REJECT_ICMPX_NO_ROUTE as u8, - PortUnreach = libc::NFT_REJECT_ICMPX_PORT_UNREACH as u8, - HostUnreach = libc::NFT_REJECT_ICMPX_HOST_UNREACH as u8, - AdminProhibited = libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED as u8, -} - -impl IcmpCode { - fn from_raw(code: u8) -> Option { - match code as i32 { - libc::NFT_REJECT_ICMPX_NO_ROUTE => Some(Self::NoRoute), - libc::NFT_REJECT_ICMPX_PORT_UNREACH => Some(Self::PortUnreach), - libc::NFT_REJECT_ICMPX_HOST_UNREACH => Some(Self::HostUnreach), - libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED => Some(Self::AdminProhibited), - _ => None, - } - } -} - impl Verdict { fn chain(&self) -> Option<&CStr> { match *self { -- cgit v1.2.3 From 7f7b2c3af6e6f7a596a85ada823408bdd0b02118 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Sat, 23 Oct 2021 23:02:22 +0200 Subject: replace Optionnals by Results for a better error propagation when deserializing expressions --- rustables/src/expr/bitwise.rs | 3 +- rustables/src/expr/cmp.rs | 116 +++++-------------------------------- rustables/src/expr/counter.rs | 6 +- rustables/src/expr/ct.rs | 10 ++-- rustables/src/expr/immediate.rs | 54 ++++------------- rustables/src/expr/log.rs | 8 +-- rustables/src/expr/lookup.rs | 8 +-- rustables/src/expr/masquerade.rs | 6 +- rustables/src/expr/meta.rs | 46 +++++++-------- rustables/src/expr/mod.rs | 122 ++++++++++++++++++++++++++++++++++++++- rustables/src/expr/nat.rs | 39 ++++--------- rustables/src/expr/payload.rs | 80 +++++++++++++------------ rustables/src/expr/register.rs | 16 ++--- rustables/src/expr/reject.rs | 23 ++++---- rustables/src/expr/verdict.rs | 37 +++++++----- rustables/src/expr/wrapper.rs | 8 +-- 16 files changed, 285 insertions(+), 297 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/bitwise.rs b/rustables/src/expr/bitwise.rs index 0c6c33c..a5d9343 100644 --- a/rustables/src/expr/bitwise.rs +++ b/rustables/src/expr/bitwise.rs @@ -1,5 +1,4 @@ -use super::{Expression, Rule}; -use crate::expr::cmp::ToSlice; +use super::{Expression, Rule, ToSlice}; use rustables_sys::{self as sys, libc}; use std::ffi::c_void; use std::os::raw::c_char; diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index 11825d6..f24b4ad 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -1,11 +1,9 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule, ToSlice}; use rustables_sys::{self as sys, libc}; use std::{ borrow::Cow, ffi::{c_void, CString}, - net::{IpAddr, Ipv4Addr, Ipv6Addr}, os::raw::c_char, - slice, }; /// Comparison operator. @@ -39,16 +37,16 @@ impl CmpOp { } } - pub fn from_raw(val: u32) -> Option { + pub fn from_raw(val: u32) -> Result { use self::CmpOp::*; match val as i32 { - libc::NFT_CMP_EQ => Some(Eq), - libc::NFT_CMP_NEQ => Some(Neq), - libc::NFT_CMP_LT => Some(Lt), - libc::NFT_CMP_LTE => Some(Lte), - libc::NFT_CMP_GT => Some(Gt), - libc::NFT_CMP_GTE => Some(Gte), - _ => None, + libc::NFT_CMP_EQ => Ok(Eq), + libc::NFT_CMP_NEQ => Ok(Neq), + libc::NFT_CMP_LT => Ok(Lt), + libc::NFT_CMP_LTE => Ok(Lte), + libc::NFT_CMP_GT => Ok(Gt), + libc::NFT_CMP_GTE => Ok(Gte), + _ => Err(DeserializationError::InvalidValue), } } } @@ -89,7 +87,7 @@ impl Expression for Cmp { sys::nftnl_expr_set( expr, sys::NFTNL_EXPR_CMP_DATA as u16, - data.as_ref() as *const _ as *const c_void, + data.as_ptr() as *const c_void, data.len() as u32, ); @@ -107,7 +105,7 @@ impl Expression for Cmp<[u8; N]> { // 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) -> Option { + fn from_expr(expr: *const sys::nftnl_expr) -> Result { unsafe { let ref_len = std::mem::size_of::<[u8; N]>() as u32; let mut data_len = 0; @@ -118,23 +116,22 @@ impl Expression for Cmp<[u8; N]> { ); if data.is_null() { - return None; + return Err(DeserializationError::NullPointer); } else if data_len != ref_len { - debug!("Invalid size requested for deserializing a 'cmp' expression: expected {} bytes, got {}", ref_len, data_len); - return None; + return Err(DeserializationError::InvalidDataSize); } let data = *(data as *const [u8; N]); - let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16)); - op.map(|op| Cmp { op, data }) + let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16))?; + Ok(Cmp { op, data }) } } // call to the other implementation to generate the expression fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { Cmp { - data: self.data.as_ref(), + data: &self.data as &[u8], op: self.op, } .to_expr(rule) @@ -166,87 +163,6 @@ macro_rules! nft_expr_cmp { }; } -/// 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]>; -} - -impl<'a> ToSlice for &'a [u8] { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Borrowed(self) - } -} - -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 { 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(), - } - } -} - -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 ToSlice for u8 { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::Owned(vec![*self]) - } -} - -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 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]) - } -} - -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<'a> ToSlice for &'a str { - fn to_slice(&self) -> Cow<'_, [u8]> { - Cow::from(self.as_bytes()) - } -} - /// Can be used to compare the value loaded by [`Meta::IifName`] and [`Meta::OifName`]. Please /// note that it is faster to check interface index than name. /// diff --git a/rustables/src/expr/counter.rs b/rustables/src/expr/counter.rs index b507e80..099e7fa 100644 --- a/rustables/src/expr/counter.rs +++ b/rustables/src/expr/counter.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys as sys; use std::os::raw::c_char; @@ -24,11 +24,11 @@ impl Expression for Counter { b"counter\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option { + fn from_expr(expr: *const sys::nftnl_expr) -> Result { unsafe { let nb_bytes = sys::nftnl_expr_get_u64(expr, sys::NFTNL_EXPR_CTR_BYTES as u16); let nb_packets = sys::nftnl_expr_get_u64(expr, sys::NFTNL_EXPR_CTR_PACKETS as u16); - Some(Counter { + Ok(Counter { nb_bytes, nb_packets, }) diff --git a/rustables/src/expr/ct.rs b/rustables/src/expr/ct.rs index 9d58591..001aef8 100644 --- a/rustables/src/expr/ct.rs +++ b/rustables/src/expr/ct.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys::{self as sys, libc}; use std::os::raw::c_char; @@ -31,7 +31,7 @@ impl Expression for Conntrack { b"ct\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { @@ -40,11 +40,11 @@ impl Expression for Conntrack { let ct_sreg_is_set = sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_CT_SREG as u16); match ct_key as i32 { - libc::NFT_CT_STATE => Some(Conntrack::State), - libc::NFT_CT_MARK => Some(Conntrack::Mark { + libc::NFT_CT_STATE => Ok(Conntrack::State), + libc::NFT_CT_MARK => Ok(Conntrack::Mark { set: ct_sreg_is_set, }), - _ => None, + _ => Err(DeserializationError::InvalidValue), } } } diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index 1196211..b5be101 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -1,7 +1,6 @@ -use super::{Expression, Register, Rule, ToSlice}; +use super::{DeserializationError, Expression, Register, Rule, ToSlice}; use rustables_sys as sys; use std::ffi::c_void; -use std::mem::size_of_val; use std::os::raw::c_char; /// An immediate expression. Used to set immediate data. @@ -33,11 +32,12 @@ impl Expression for Immediate { self.register.to_raw(), ); + let data = self.data.to_slice(); sys::nftnl_expr_set( expr, sys::NFTNL_EXPR_IMM_DATA as u16, - &self.data.to_slice() as *const _ as *const c_void, - size_of_val(&self.data) as u32, + data.as_ptr() as *const c_void, + data.len() as u32, ); expr @@ -54,7 +54,7 @@ impl Expression for Immediate<[u8; N]> { // 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) -> Option { + fn from_expr(expr: *const sys::nftnl_expr) -> Result { unsafe { let ref_len = std::mem::size_of::<[u8; N]>() as u32; let mut data_len = 0; @@ -65,10 +65,9 @@ impl Expression for Immediate<[u8; N]> { ); if data.is_null() { - return None; + return Err(DeserializationError::NullPointer); } else if data_len != ref_len { - debug!("Invalid size requested for deserializing an 'immediate' expression: expected {} bytes, got {}", ref_len, data_len); - return None; + return Err(DeserializationError::InvalidDataSize); } let data = *(data as *const [u8; N]); @@ -76,9 +75,9 @@ impl Expression for Immediate<[u8; N]> { let register = Register::from_raw(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_IMM_DREG as u16, - )); + ))?; - register.map(|register| Immediate { data, register }) + Ok(Immediate { data, register }) } } @@ -86,44 +85,11 @@ impl Expression for Immediate<[u8; N]> { fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { Immediate { register: self.register, - data: self.data.as_ref(), + data: &self.data as &[u8], } .to_expr(rule) } } -// 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. -impl Immediate<[u8; N]> { - pub fn from_expr(expr: *const sys::nftnl_expr) -> Option { - 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 None; - } else if data_len != ref_len { - debug!("Invalid size requested for deserializing an 'immediate' expression: expected {} bytes, got {}", ref_len, data_len); - return None; - } - - 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, - )); - - register.map(|register| Immediate { data, register }) - } - } -} #[macro_export] macro_rules! nft_expr_immediate { diff --git a/rustables/src/expr/log.rs b/rustables/src/expr/log.rs index ba1244a..db96ba9 100644 --- a/rustables/src/expr/log.rs +++ b/rustables/src/expr/log.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys as sys; use std::ffi::{CStr, CString}; use std::os::raw::c_char; @@ -16,7 +16,7 @@ impl Expression for Log { b"log\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { @@ -32,12 +32,12 @@ impl Expression for Log { if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16) { let raw_prefix = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16); if raw_prefix.is_null() { - trace!("Unexpected empty prefix in a 'log' expression"); + return Err(DeserializationError::NullPointer); } else { prefix = Some(LogPrefix(CStr::from_ptr(raw_prefix).to_owned())); } } - Some(Log { group, prefix }) + Ok(Log { group, prefix }) } } diff --git a/rustables/src/expr/lookup.rs b/rustables/src/expr/lookup.rs index b9a03d9..7796b29 100644 --- a/rustables/src/expr/lookup.rs +++ b/rustables/src/expr/lookup.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use crate::set::Set; use rustables_sys::{self as sys, libc}; use std::ffi::{CStr, CString}; @@ -26,7 +26,7 @@ impl Expression for Lookup { b"lookup\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { @@ -35,12 +35,12 @@ impl Expression for Lookup { let set_id = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_LOOKUP_SET_ID as u16); if set_name.is_null() { - return None; + return Err(DeserializationError::NullPointer); } let set_name = CStr::from_ptr(set_name).to_owned(); - Some(Lookup { set_id, set_name }) + Ok(Lookup { set_id, set_name }) } } diff --git a/rustables/src/expr/masquerade.rs b/rustables/src/expr/masquerade.rs index bf4e0de..40565d5 100644 --- a/rustables/src/expr/masquerade.rs +++ b/rustables/src/expr/masquerade.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys as sys; use std::os::raw::c_char; @@ -11,11 +11,11 @@ impl Expression for Masquerade { b"masq\0" as *const _ as *const c_char } - fn from_expr(_expr: *const sys::nftnl_expr) -> Option + fn from_expr(_expr: *const sys::nftnl_expr) -> Result where Self: Sized, { - Some(Masquerade) + Ok(Masquerade) } fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { diff --git a/rustables/src/expr/meta.rs b/rustables/src/expr/meta.rs index ba803ac..199f3d3 100644 --- a/rustables/src/expr/meta.rs +++ b/rustables/src/expr/meta.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys::{self as sys, libc}; use std::os::raw::c_char; @@ -58,24 +58,23 @@ impl Meta { } } - fn from_raw(val: u32) -> Option { + fn from_raw(val: u32) -> Result { match val as i32 { - libc::NFT_META_PROTOCOL => Some(Self::Protocol), - libc::NFT_META_MARK => Some(Self::Mark { set: false }), - libc::NFT_META_IIF => Some(Self::Iif), - libc::NFT_META_OIF => Some(Self::Oif), - libc::NFT_META_IIFNAME => Some(Self::IifName), - libc::NFT_META_OIFNAME => Some(Self::OifName), - libc::NFT_META_IIFTYPE => Some(Self::IifType), - libc::NFT_META_OIFTYPE => Some(Self::OifType), - libc::NFT_META_SKUID => Some(Self::SkUid), - libc::NFT_META_SKGID => Some(Self::SkGid), - libc::NFT_META_NFPROTO => Some(Self::NfProto), - libc::NFT_META_L4PROTO => Some(Self::L4Proto), - libc::NFT_META_CGROUP => Some(Self::Cgroup), - libc::NFT_META_PRANDOM => Some(Self::PRandom), - - _ => None, + libc::NFT_META_PROTOCOL => Ok(Self::Protocol), + libc::NFT_META_MARK => Ok(Self::Mark { set: false }), + libc::NFT_META_IIF => Ok(Self::Iif), + libc::NFT_META_OIF => Ok(Self::Oif), + libc::NFT_META_IIFNAME => Ok(Self::IifName), + libc::NFT_META_OIFNAME => Ok(Self::OifName), + libc::NFT_META_IIFTYPE => Ok(Self::IifType), + libc::NFT_META_OIFTYPE => Ok(Self::OifType), + libc::NFT_META_SKUID => Ok(Self::SkUid), + libc::NFT_META_SKGID => Ok(Self::SkGid), + libc::NFT_META_NFPROTO => Ok(Self::NfProto), + libc::NFT_META_L4PROTO => Ok(Self::L4Proto), + libc::NFT_META_CGROUP => Ok(Self::Cgroup), + libc::NFT_META_PRANDOM => Ok(Self::PRandom), + _ => Err(DeserializationError::InvalidValue), } } } @@ -85,24 +84,21 @@ impl Expression for Meta { b"meta\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { unsafe { - let mut ret = match Self::from_raw(sys::nftnl_expr_get_u32( + let mut ret = Self::from_raw(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_META_KEY as u16, - )) { - Some(x) => x, - None => return None, - }; + ))?; if let Self::Mark { ref mut set } = ret { *set = sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_META_SREG as u16); } - Some(ret) + Ok(ret) } } diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 431a0b9..b20a752 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -3,8 +3,14 @@ //! //! [`Rule`]: struct.Rule.html +use std::borrow::Cow; +use std::net::IpAddr; +use std::net::Ipv4Addr; +use std::net::Ipv6Addr; + use super::rule::Rule; use rustables_sys::{self as sys, libc}; +use thiserror::Error; mod bitwise; pub use self::bitwise::*; @@ -51,18 +57,47 @@ 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 None if the attempted parsing failed. - fn from_expr(_expr: *const sys::nftnl_expr) -> Option + /// returning a [DeserializationError] if the attempted parsing failed. + fn from_expr(_expr: *const sys::nftnl_expr) -> Result where Self: Sized, { - None + Err(DeserializationError::NotImplemented) } /// Allocates and returns the low level `nftnl_expr` representation of this expression. @@ -70,6 +105,87 @@ pub trait 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]>; +} + +impl<'a> ToSlice for &'a [u8] { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::Borrowed(self) + } +} + +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(), + } + } +} + +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 ToSlice for u8 { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::Owned(vec![*self]) + } +} + +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 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]) + } +} + +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<'a> ToSlice for &'a str { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::from(self.as_bytes()) + } +} + #[macro_export(local_inner_macros)] macro_rules! nft_expr { (bitwise mask $mask:expr,xor $xor:expr) => { diff --git a/rustables/src/expr/nat.rs b/rustables/src/expr/nat.rs index 6bd0619..51f439f 100644 --- a/rustables/src/expr/nat.rs +++ b/rustables/src/expr/nat.rs @@ -1,4 +1,4 @@ -use super::{Expression, Register, Rule}; +use super::{DeserializationError, Expression, Register, Rule}; use crate::ProtoFamily; use rustables_sys::{self as sys, libc}; use std::{convert::TryFrom, os::raw::c_char}; @@ -13,11 +13,11 @@ pub enum NatType { } impl NatType { - fn from_raw(val: u32) -> Option { + fn from_raw(val: u32) -> Result { match val as i32 { - libc::NFT_NAT_SNAT => Some(NatType::SNat), - libc::NFT_NAT_DNAT => Some(NatType::DNat), - _ => None, + libc::NFT_NAT_SNAT => Ok(NatType::SNat), + libc::NFT_NAT_DNAT => Ok(NatType::DNat), + _ => Err(DeserializationError::InvalidValue), } } } @@ -37,7 +37,7 @@ impl Expression for Nat { b"nat\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { @@ -45,42 +45,27 @@ impl Expression for Nat { let nat_type = NatType::from_raw(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_NAT_TYPE as u16, - )); - let nat_type = match nat_type { - Some(x) => x, - None => return None, - }; + ))?; let family = ProtoFamily::try_from(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_NAT_FAMILY as u16, - ) as i32); - let family = match family { - Ok(x) => x, - Err(_) => return None, - }; + ) as i32)?; let ip_register = Register::from_raw(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_NAT_REG_ADDR_MIN as u16, - )); - let ip_register = match ip_register { - Some(x) => x, - None => return None, - }; + ))?; let mut port_register = None; if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16) { - port_register = Register::from_raw(sys::nftnl_expr_get_u32( + port_register = Some(Register::from_raw(sys::nftnl_expr_get_u32( expr, sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16, - )); - if port_register.is_none() { - trace!("Invalid register in expression 'nat'"); - } + ))?); } - Some(Nat { + Ok(Nat { ip_register, nat_type, family, diff --git a/rustables/src/expr/payload.rs b/rustables/src/expr/payload.rs index a6b5ddf..25a71ad 100644 --- a/rustables/src/expr/payload.rs +++ b/rustables/src/expr/payload.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys::{self as sys, libc}; use std::os::raw::c_char; @@ -22,11 +22,11 @@ impl Payload { offset: f.offset(), len: f.len(), }), - Payload::Network(ref f) => RawPayload::LinkLayer(RawPayloadData { + Payload::Network(ref f) => RawPayload::Network(RawPayloadData { offset: f.offset(), len: f.len(), }), - Payload::Transport(ref f) => RawPayload::LinkLayer(RawPayloadData { + Payload::Transport(ref f) => RawPayload::Transport(RawPayloadData { offset: f.offset(), len: f.offset(), }), @@ -88,23 +88,21 @@ impl Expression for RawPayload { b"payload\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option { + fn from_expr(expr: *const sys::nftnl_expr) -> Result { unsafe { let base = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_BASE as u16); let offset = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_OFFSET as u16); let len = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_PAYLOAD_LEN as u16); match base as i32 { - libc::NFT_PAYLOAD_LL_HEADER => { - Some(Self::LinkLayer(RawPayloadData { offset, len })) - } + libc::NFT_PAYLOAD_LL_HEADER => Ok(Self::LinkLayer(RawPayloadData { offset, len })), libc::NFT_PAYLOAD_NETWORK_HEADER => { - Some(Self::Network(RawPayloadData { offset, len })) + Ok(Self::Network(RawPayloadData { offset, len })) } libc::NFT_PAYLOAD_TRANSPORT_HEADER => { - Some(Self::Transport(RawPayloadData { offset, len })) + Ok(Self::Transport(RawPayloadData { offset, len })) } - _ => return None, + _ => return Err(DeserializationError::InvalidValue), } } } @@ -156,18 +154,18 @@ impl HeaderField for LLHeaderField { } impl LLHeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 0 && len == 6 { - Some(Self::Daddr) + Ok(Self::Daddr) } else if off == 6 && len == 6 { - Some(Self::Saddr) + Ok(Self::Saddr) } else if off == 12 && len == 2 { - Some(Self::EtherType) + Ok(Self::EtherType) } else { - None + Err(DeserializationError::InvalidValue) } } } @@ -228,20 +226,20 @@ impl HeaderField for Ipv4HeaderField { } impl Ipv4HeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 8 && len == 1 { - Some(Self::Ttl) + Ok(Self::Ttl) } else if off == 9 && len == 1 { - Some(Self::Protocol) + Ok(Self::Protocol) } else if off == 12 && len == 4 { - Some(Self::Saddr) + Ok(Self::Saddr) } else if off == 16 && len == 4 { - Some(Self::Daddr) + Ok(Self::Daddr) } else { - None + Err(DeserializationError::InvalidValue) } } } @@ -278,20 +276,20 @@ impl HeaderField for Ipv6HeaderField { } impl Ipv6HeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 6 && len == 1 { - Some(Self::NextHeader) + Ok(Self::NextHeader) } else if off == 7 && len == 1 { - Some(Self::HopLimit) + Ok(Self::HopLimit) } else if off == 8 && len == 16 { - Some(Self::Saddr) + Ok(Self::Saddr) } else if off == 24 && len == 16 { - Some(Self::Daddr) + Ok(Self::Daddr) } else { - None + Err(DeserializationError::InvalidValue) } } } @@ -350,16 +348,16 @@ impl HeaderField for TcpHeaderField { } impl TcpHeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 0 && len == 2 { - Some(Self::Sport) + Ok(Self::Sport) } else if off == 2 && len == 2 { - Some(Self::Dport) + Ok(Self::Dport) } else { - None + Err(DeserializationError::InvalidValue) } } } @@ -393,18 +391,18 @@ impl HeaderField for UdpHeaderField { } impl UdpHeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 0 && len == 2 { - Some(Self::Sport) + Ok(Self::Sport) } else if off == 2 && len == 2 { - Some(Self::Dport) + Ok(Self::Dport) } else if off == 4 && len == 2 { - Some(Self::Len) + Ok(Self::Len) } else { - None + Err(DeserializationError::InvalidValue) } } } @@ -438,18 +436,18 @@ impl HeaderField for Icmpv6HeaderField { } impl Icmpv6HeaderField { - pub fn from_raw_data(data: &RawPayloadData) -> Option { + pub fn from_raw_data(data: &RawPayloadData) -> Result { let off = data.offset; let len = data.len; if off == 0 && len == 1 { - Some(Self::Type) + Ok(Self::Type) } else if off == 1 && len == 1 { - Some(Self::Code) + Ok(Self::Code) } else if off == 2 && len == 2 { - Some(Self::Checksum) + Ok(Self::Checksum) } else { - None + Err(DeserializationError::InvalidValue) } } } diff --git a/rustables/src/expr/register.rs b/rustables/src/expr/register.rs index 3013451..2cfcc3b 100644 --- a/rustables/src/expr/register.rs +++ b/rustables/src/expr/register.rs @@ -2,6 +2,8 @@ use std::fmt::Debug; use rustables_sys::libc; +use super::DeserializationError; + /// 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)] @@ -19,14 +21,14 @@ impl Register { self as u32 } - pub fn from_raw(val: u32) -> Option { + pub fn from_raw(val: u32) -> Result { match val as i32 { - libc::NFT_REG_VERDICT => Some(Self::Verdict), - libc::NFT_REG_1 => Some(Self::Reg1), - libc::NFT_REG_2 => Some(Self::Reg2), - libc::NFT_REG_3 => Some(Self::Reg3), - libc::NFT_REG_4 => Some(Self::Reg4), - _ => None, + 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), } } } diff --git a/rustables/src/expr/reject.rs b/rustables/src/expr/reject.rs index f94079b..550a287 100644 --- a/rustables/src/expr/reject.rs +++ b/rustables/src/expr/reject.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use crate::ProtoFamily; use rustables_sys::{ self as sys, @@ -34,7 +34,7 @@ impl Expression for Reject { b"reject\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option + fn from_expr(expr: *const sys::nftnl_expr) -> Result where Self: Sized, { @@ -42,13 +42,12 @@ impl Expression for Reject { if sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_REJECT_TYPE as u16) == libc::NFT_REJECT_TCP_RST as u32 { - Some(Self::TcpRst) + Ok(Self::TcpRst) } else { - IcmpCode::from_raw(sys::nftnl_expr_get_u8( + Ok(Self::Icmp(IcmpCode::from_raw(sys::nftnl_expr_get_u8( expr, sys::NFTNL_EXPR_REJECT_CODE as u16, - )) - .map(Self::Icmp) + ))?)) } } } @@ -88,13 +87,13 @@ pub enum IcmpCode { } impl IcmpCode { - fn from_raw(code: u8) -> Option { + fn from_raw(code: u8) -> Result { match code as i32 { - libc::NFT_REJECT_ICMPX_NO_ROUTE => Some(Self::NoRoute), - libc::NFT_REJECT_ICMPX_PORT_UNREACH => Some(Self::PortUnreach), - libc::NFT_REJECT_ICMPX_HOST_UNREACH => Some(Self::HostUnreach), - libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED => Some(Self::AdminProhibited), - _ => None, + libc::NFT_REJECT_ICMPX_NO_ROUTE => Ok(Self::NoRoute), + libc::NFT_REJECT_ICMPX_PORT_UNREACH => Ok(Self::PortUnreach), + libc::NFT_REJECT_ICMPX_HOST_UNREACH => Ok(Self::HostUnreach), + libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED => Ok(Self::AdminProhibited), + _ => Err(DeserializationError::InvalidValue), } } } diff --git a/rustables/src/expr/verdict.rs b/rustables/src/expr/verdict.rs index 772da52..6a6b802 100644 --- a/rustables/src/expr/verdict.rs +++ b/rustables/src/expr/verdict.rs @@ -1,4 +1,4 @@ -use super::{Expression, Rule}; +use super::{DeserializationError, Expression, Rule}; use rustables_sys::{ self as sys, libc::{self, c_char}, @@ -40,15 +40,14 @@ impl Expression for Verdict { b"immediate\0" as *const _ as *const c_char } - fn from_expr(expr: *const sys::nftnl_expr) -> Option { + fn from_expr(expr: *const sys::nftnl_expr) -> Result { 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() { - trace!("Unexpected empty chain name when deserializing 'verdict' expression"); - return None; + return Err(DeserializationError::NullPointer); } chain = Some(CStr::from_ptr(raw_chain).to_owned()); } @@ -56,15 +55,27 @@ impl Expression for Verdict { let verdict = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_IMM_VERDICT as u16); match verdict as i32 { - libc::NF_DROP => Some(Verdict::Drop), - libc::NF_ACCEPT => Some(Verdict::Accept), - libc::NF_QUEUE => Some(Verdict::Queue), - libc::NFT_CONTINUE => Some(Verdict::Continue), - libc::NFT_BREAK => Some(Verdict::Break), - libc::NFT_JUMP => chain.map(|chain| Verdict::Jump { chain }), - libc::NFT_GOTO => chain.map(|chain| Verdict::Goto { chain }), - libc::NFT_RETURN => Some(Verdict::Return), - _ => None, + 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), } } } diff --git a/rustables/src/expr/wrapper.rs b/rustables/src/expr/wrapper.rs index b9b90b3..5162c21 100644 --- a/rustables/src/expr/wrapper.rs +++ b/rustables/src/expr/wrapper.rs @@ -3,6 +3,7 @@ use std::ffi::CString; use std::fmt::Debug; use std::rc::Rc; +use super::DeserializationError; use super::Expression; use crate::Rule; use rustables_sys as sys; @@ -48,15 +49,14 @@ impl ExpressionWrapper { } } - /// Attempt to decode the expression as the type T, returning None if such - /// conversion is not possible or failed. - pub fn decode_expr(&self) -> Option { + /// Attempt to decode the expression as the type T. + pub fn decode_expr(&self) -> Result { if let Some(kind) = self.get_kind() { let raw_name = unsafe { CStr::from_ptr(T::get_raw_name()) }; if kind == raw_name { return T::from_expr(self.expr); } } - None + Err(DeserializationError::InvalidExpressionKind) } } -- cgit v1.2.3 From 0156ef5a8b0bdc8e07b8ac12e4c99d5047f1c9cc Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 27 Oct 2021 19:54:58 +0200 Subject: fix: retrieving the name of a table or a chain cannot fail --- rustables/src/chain.rs | 18 ++++++++++-------- rustables/src/rule.rs | 32 ++++++++++++++++++++------------ rustables/src/set.rs | 4 +--- rustables/src/table.rs | 8 ++++---- 4 files changed, 35 insertions(+), 27 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/chain.rs b/rustables/src/chain.rs index 3e28ab0..ac9c57d 100644 --- a/rustables/src/chain.rs +++ b/rustables/src/chain.rs @@ -87,9 +87,11 @@ impl Chain { sys::NFTNL_CHAIN_FAMILY as u16, table.get_family() as u32, ); - if let Some(table_name) = table.get_name() { - sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_TABLE as u16, table_name.as_ptr()); - } + sys::nftnl_chain_set_str( + chain, + sys::NFTNL_CHAIN_TABLE as u16, + table.get_name().as_ptr(), + ); sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_NAME as u16, name.as_ref().as_ptr()); Chain { chain, table } } @@ -152,13 +154,13 @@ impl Chain { } /// Returns the name of this chain. - pub fn get_name(&self) -> Option<&CStr> { + pub fn get_name(&self) -> &CStr { unsafe { let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_NAME as u16); - if !ptr.is_null() { - Some(CStr::from_ptr(ptr)) + if ptr.is_null() { + panic!("Impossible situation: retrieving the name of a chain failed") } else { - None + CStr::from_ptr(ptr) } } } @@ -269,7 +271,7 @@ pub fn get_chains_cb<'a>( } }; - if Some(table_name) != table.get_name() { + if table_name != table.get_name() { sys::nftnl_chain_free(chain); return mnl::mnl_sys::MNL_CB_OK; } diff --git a/rustables/src/rule.rs b/rustables/src/rule.rs index 7ab0de9..fcacf6a 100644 --- a/rustables/src/rule.rs +++ b/rustables/src/rule.rs @@ -24,12 +24,16 @@ impl Rule { sys::NFTNL_RULE_FAMILY as u16, chain.get_table().get_family() as u32, ); - if let Some(table_name) = chain.get_table().get_name() { - sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_TABLE as u16, table_name.as_ptr()); - } - if let Some(chain_name) = chain.get_name() { - sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_CHAIN as u16, chain_name.as_ptr()); - } + sys::nftnl_rule_set_str( + rule, + sys::NFTNL_RULE_TABLE as u16, + chain.get_table().get_name().as_ptr(), + ); + sys::nftnl_rule_set_str( + rule, + sys::NFTNL_RULE_CHAIN as u16, + chain.get_name().as_ptr(), + ); Rule { rule, chain } } @@ -238,17 +242,21 @@ pub fn list_rules_for_chain(chain: &Rc) -> Result, crate::query return Err(crate::query::Error::NetlinkAllocationFailed); } - if let Some(table_name) = chain.get_table().get_name() { - sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_TABLE as u16, table_name.as_ptr()); - } + sys::nftnl_rule_set_str( + rule, + sys::NFTNL_RULE_TABLE as u16, + chain.get_table().get_name().as_ptr(), + ); sys::nftnl_rule_set_u32( rule, sys::NFTNL_RULE_FAMILY as u16, chain.get_table().get_family() as u32, ); - if let Some(chain_name) = chain.get_name() { - sys::nftnl_rule_set_str(rule, sys::NFTNL_RULE_CHAIN as u16, chain_name.as_ptr()); - } + sys::nftnl_rule_set_str( + rule, + sys::NFTNL_RULE_CHAIN as u16, + chain.get_name().as_ptr(), + ); sys::nftnl_rule_nlmsg_build_payload(hdr, rule); diff --git a/rustables/src/set.rs b/rustables/src/set.rs index c099088..aef74db 100644 --- a/rustables/src/set.rs +++ b/rustables/src/set.rs @@ -42,9 +42,7 @@ impl<'a, K> Set<'a, K> { let set = try_alloc!(sys::nftnl_set_alloc()); sys::nftnl_set_set_u32(set, sys::NFTNL_SET_FAMILY as u16, family as u32); - if let Some(table_name) = table.get_name() { - sys::nftnl_set_set_str(set, sys::NFTNL_SET_TABLE as u16, table_name.as_ptr()); - } + sys::nftnl_set_set_str(set, sys::NFTNL_SET_TABLE as u16, table.get_name().as_ptr()); sys::nftnl_set_set_str(set, sys::NFTNL_SET_NAME as u16, name.as_ptr()); sys::nftnl_set_set_u32(set, sys::NFTNL_SET_ID as u16, id); diff --git a/rustables/src/table.rs b/rustables/src/table.rs index 53a967f..7cc475f 100644 --- a/rustables/src/table.rs +++ b/rustables/src/table.rs @@ -35,13 +35,13 @@ impl Table { } /// Returns the name of this table. - pub fn get_name(&self) -> Option<&CStr> { + pub fn get_name(&self) -> &CStr { unsafe { let ptr = sys::nftnl_table_get_str(self.table, sys::NFTNL_TABLE_NAME as u16); - if !ptr.is_null() { - Some(CStr::from_ptr(ptr)) + if ptr.is_null() { + panic!("Impossible situation: retrieving the name of a chain failed") } else { - None + CStr::from_ptr(ptr) } } } -- cgit v1.2.3 From 45b71ba1da0a88278d126d5e154ed8ca90e57892 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 27 Oct 2021 19:58:53 +0200 Subject: provide a (deficient) method for a deep comparison of rules --- rustables/src/rule.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'rustables/src') diff --git a/rustables/src/rule.rs b/rustables/src/rule.rs index fcacf6a..b315daf 100644 --- a/rustables/src/rule.rs +++ b/rustables/src/rule.rs @@ -129,6 +129,59 @@ impl Rule { pub fn as_mut_ptr(&mut self) -> *mut sys::nftnl_rule { self.rule } + + /// Perform a deep comparizon of rules, by checking they have the same expressions inside. + /// This is not enabled by default in our PartialEq implementation because of the + /// difficulty to compare an expression generated by the library with the expressions returned + /// by the kernel when iterating over the currently in-use rules. The kernel-returned + /// expressions may have additional attributes despite being generated from the same rule. + /// This is particularly true for the 'nat' expression). + pub fn deep_eq(&self, other: &Self) -> bool { + if self != other { + return false; + } + + let self_exprs = + try_alloc!(unsafe { sys::nftnl_expr_iter_create(self.rule as *const sys::nftnl_rule) }); + let other_exprs = try_alloc!(unsafe { + sys::nftnl_expr_iter_create(other.rule as *const sys::nftnl_rule) + }); + + loop { + let self_next = unsafe { sys::nftnl_expr_iter_next(self_exprs) }; + let other_next = unsafe { sys::nftnl_expr_iter_next(other_exprs) }; + if self_next.is_null() && other_next.is_null() { + return true; + } else if self_next.is_null() || other_next.is_null() { + return false; + } + + // we are falling back on comparing the strings, because there is no easy mechanism to + // perform a memcmp() between the two expressions :/ + let mut self_str = [0; 256]; + let mut other_str = [0; 256]; + unsafe { + sys::nftnl_expr_snprintf( + self_str.as_mut_ptr(), + (self_str.len() - 1) as u64, + self_next, + sys::NFTNL_OUTPUT_DEFAULT, + 0, + ); + sys::nftnl_expr_snprintf( + other_str.as_mut_ptr(), + (other_str.len() - 1) as u64, + other_next, + sys::NFTNL_OUTPUT_DEFAULT, + 0, + ); + } + + if self_str != other_str { + return false; + } + } + } } impl Debug for Rule { @@ -139,7 +192,28 @@ impl Debug for Rule { impl PartialEq for Rule { fn eq(&self, other: &Self) -> bool { - self.get_chain() == other.get_chain() && self.get_handle() == other.get_handle() + if self.get_chain() != other.get_chain() { + return false; + } + + unsafe { + if sys::nftnl_rule_is_set(self.rule, sys::NFTNL_RULE_HANDLE as u16) + && sys::nftnl_rule_is_set(other.rule, sys::NFTNL_RULE_HANDLE as u16) + { + if self.get_handle() != other.get_handle() { + return false; + } + } + if sys::nftnl_rule_is_set(self.rule, sys::NFTNL_RULE_POSITION as u16) + && sys::nftnl_rule_is_set(other.rule, sys::NFTNL_RULE_POSITION as u16) + { + if self.get_position() != other.get_position() { + return false; + } + } + } + + return false; } } -- cgit v1.2.3 From 1bec5a5c30541e47e9c7cff839ac0e7dd3fb6215 Mon Sep 17 00:00:00 2001 From: Simon THOBY Date: Wed, 27 Oct 2021 20:37:41 +0200 Subject: fix: update the comment for the from_expr implementations of Cmp and Immediate --- rustables/src/expr/cmp.rs | 28 ++++++++++++++++++++++++---- rustables/src/expr/immediate.rs | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) (limited to 'rustables/src') diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index f24b4ad..747974d 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -101,10 +101,30 @@ impl Expression for Cmp<[u8; N]> { Cmp::::get_raw_name() } - // 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. + /// The raw data contained inside `Cmp` 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::{Cmp, CmpOp}, 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(&Cmp::new(CmpOp::Eq, 1337u16)); + /// for expr in Rc::new(rule).get_exprs() { + /// println!("{:?}", expr.decode_expr::>().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. fn from_expr(expr: *const sys::nftnl_expr) -> Result { unsafe { let ref_len = std::mem::size_of::<[u8; N]>() as u32; diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index b5be101..ff4ad04 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -50,6 +50,30 @@ impl Expression for Immediate<[u8; N]> { Immediate::::get_raw_name() } + /// 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::>().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 -- cgit v1.2.3