diff options
author | Simon THOBY <git@nightmared.fr> | 2022-11-11 23:50:29 +0100 |
---|---|---|
committer | Simon THOBY <git@nightmared.fr> | 2022-11-12 12:13:01 +0100 |
commit | 6cd97d19b54eeedffa18fddebb1b09045b0e79cf (patch) | |
tree | 5444a37a1b19618313ca515c6e46c880155f66d5 /src | |
parent | 84fc84c32e62e3d2c5fe7ab6c35b5d05f890e8a6 (diff) |
re-add support for querying chains
Diffstat (limited to 'src')
-rw-r--r-- | src/batch.rs | 3 | ||||
-rw-r--r-- | src/chain.rs | 239 | ||||
-rw-r--r-- | src/lib.rs | 10 | ||||
-rw-r--r-- | src/nlmsg.rs | 83 | ||||
-rw-r--r-- | src/parser.rs | 115 | ||||
-rw-r--r-- | src/table.rs | 49 |
6 files changed, 198 insertions, 301 deletions
diff --git a/src/batch.rs b/src/batch.rs index 01f0c31..a1c7e0f 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -3,6 +3,7 @@ use libc; use thiserror::Error; use crate::nlmsg::{NfNetlinkObject, NfNetlinkWriter}; +use crate::sys::NFNL_SUBSYS_NFTABLES; use crate::{MsgType, ProtoFamily}; use crate::query::Error; @@ -81,7 +82,7 @@ impl Batch { ProtoFamily::Unspec, 0, self.seq, - Some(libc::NFNL_SUBSYS_NFTABLES as u16), + Some(NFNL_SUBSYS_NFTABLES as u16), ); self.writer.finalize_writing_object(); *self.buf diff --git a/src/chain.rs b/src/chain.rs index e29b239..000a196 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -1,17 +1,14 @@ +use crate::nlmsg::NfNetlinkSerializable; use crate::nlmsg::{ - AttributeDecoder, NfNetlinkAttribute, NfNetlinkAttributes, NfNetlinkDeserializable, - NfNetlinkObject, NfNetlinkWriter, + NfNetlinkAttribute, NfNetlinkAttributes, NfNetlinkDeserializable, NfNetlinkObject, + NfNetlinkWriter, +}; +use crate::parser::{ + parse_object, DecodeError, InnerFormat, NestedAttribute, NfNetlinkAttributeReader, }; -use crate::parser::{DecodeError, NestedAttribute, NfNetlinkAttributeReader}; use crate::sys::{self, NFT_MSG_DELCHAIN, NFT_MSG_NEWCHAIN, NLM_F_ACK}; use crate::{impl_attr_getters_and_setters, MsgType, ProtoFamily, Table}; -use std::convert::TryFrom; -use std::{ - ffi::{c_void, CStr, CString}, - fmt, - os::raw::c_char, - rc::Rc, -}; +use std::fmt::Debug; pub type Priority = i32; @@ -31,13 +28,13 @@ pub enum HookClass { PostRouting = libc::NF_INET_POST_ROUTING as u32, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct Hook { inner: NestedAttribute, } impl Hook { - fn new(class: HookClass, priority: Priority) -> Self { + pub fn new(class: HookClass, priority: Priority) -> Self { Hook { inner: NestedAttribute::new(), } @@ -46,6 +43,12 @@ impl Hook { } } +impl Debug for Hook { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner_format_struct(f.debug_struct("Hook"))?.finish() + } +} + impl_attr_getters_and_setters!( Hook, [ @@ -113,15 +116,40 @@ pub enum ChainType { } impl ChainType { - fn as_c_str(&self) -> &'static [u8] { + fn as_str(&self) -> &'static str { match *self { - ChainType::Filter => b"filter\0", - ChainType::Route => b"route\0", - ChainType::Nat => b"nat\0", + ChainType::Filter => "filter", + ChainType::Route => "route", + ChainType::Nat => "nat", } } } +impl NfNetlinkAttribute for ChainType { + fn get_size(&self) -> usize { + self.as_str().len() + } + + unsafe fn write_payload(&self, addr: *mut u8) { + self.as_str().to_string().write_payload(addr); + } +} + +impl NfNetlinkDeserializable for ChainType { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let (s, remaining_data) = String::deserialize(buf)?; + Ok(( + match s.as_str() { + "filter" => ChainType::Filter, + "route" => ChainType::Route, + "nat" => ChainType::Nat, + _ => return Err(DecodeError::UnknownChainType), + }, + remaining_data, + )) + } +} + /// Abstraction of a `nftnl_chain`. Chains reside inside [`Table`]s and they hold [`Rule`]s. /// /// There are two types of chains, "base chain" and "regular chain". See [`set_hook`] for more @@ -130,7 +158,7 @@ impl ChainType { /// [`Table`]: struct.Table.html /// [`Rule`]: struct.Rule.html /// [`set_hook`]: #method.set_hook -#[derive(Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct Chain { inner: NfNetlinkAttributes, } @@ -139,7 +167,7 @@ impl Chain { /// Creates a new chain instance inside the given [`Table`]. /// /// [`Table`]: struct.Table.html - pub fn new<T: AsRef<CStr>>(table: &Table) -> Chain { + pub fn new(table: &Table) -> Chain { let mut chain = Chain { inner: NfNetlinkAttributes::new(), }; @@ -152,20 +180,6 @@ impl Chain { } /* - /// Sets the hook and priority for this chain. Without calling this method the chain will - /// become a "regular chain" without any hook and will thus not receive any traffic unless - /// some rule forward packets to it via goto or jump verdicts. - /// - /// By calling `set_hook` with a hook the chain that is created will be registered with that - /// hook and is thus a "base chain". A "base chain" is an entry point for packets from the - /// networking stack. - pub fn set_hook(&mut self, hook: Hook, priority: Priority) { - self.set_hook_type(hook); - self.set_hook_priority(priority); - } - */ - - /* /// Returns a textual description of the chain. pub fn get_str(&self) -> CString { let mut descr_buf = vec![0i8; 4096]; @@ -184,13 +198,6 @@ impl Chain { } /* -impl fmt::Debug for Chain { - /// Returns a string representation of the chain. - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}", self.get_str()) - } -} - impl PartialEq for Chain { fn eq(&self, other: &Self) -> bool { self.get_table() == other.get_table() && self.get_name() == other.get_name() @@ -198,7 +205,12 @@ impl PartialEq for Chain { } */ -/* +impl Debug for Chain { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner_format_struct(f.debug_struct("Chain"))?.finish() + } +} + impl NfNetlinkObject for Chain { fn add_or_remove<'a>(&self, writer: &mut NfNetlinkWriter<'a>, msg_type: MsgType, seq: u32) { let raw_msg_type = match msg_type { @@ -215,137 +227,29 @@ impl NfNetlinkObject for Chain { self.inner.serialize(writer); writer.finalize_writing_object(); } - - fn decode_attribute(attr_type: u16, buf: &[u8]) -> Result<AttributeType, DecodeError> { - match attr_type { - NFTA_TABLE_NAME => Ok(AttributeType::String(String::from_utf8(buf.to_vec())?)), - NFTA_TABLE_FLAGS => { - let val = [buf[0], buf[1], buf[2], buf[3]]; - - Ok(AttributeType::U32(u32::from_ne_bytes(val))) - } - NFTA_TABLE_USERDATA => Ok(AttributeType::VecU8(buf.to_vec())), - _ => Err(DecodeError::UnsupportedAttributeType(attr_type)), - } - } - - fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { - let (hdr, msg) = parse_nlmsg(buf)?; - - let op = get_operation_from_nlmsghdr_type(hdr.nlmsg_type) as u32; - - if op != NFT_MSG_NEWTABLE && op != NFT_MSG_DELTABLE { - return Err(DecodeError::UnexpectedType(hdr.nlmsg_type)); - } - - let (nfgenmsg, attrs, remaining_data) = parse_object(hdr, msg, buf)?; - - let inner = attrs.decode::<Table>()?; - - Ok(( - Table { - inner, - family: ProtoFamily::try_from(nfgenmsg.family as i32)?, - }, - remaining_data, - )) - } -} -*/ - -/* -unsafe impl NlMsg for Chain { - unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType) { - let raw_msg_type = match msg_type { - MsgType::Add => libc::NFT_MSG_NEWCHAIN, - MsgType::Del => libc::NFT_MSG_DELCHAIN, - }; - let flags: u16 = match msg_type { - MsgType::Add => (libc::NLM_F_ACK | libc::NLM_F_CREATE) as u16, - MsgType::Del => libc::NLM_F_ACK as u16, - } | libc::NLM_F_ACK as u16; - let header = sys::nftnl_nlmsg_build_hdr( - buf as *mut c_char, - raw_msg_type as u16, - self.table.get_family() as u16, - flags, - seq, - ); - sys::nftnl_chain_nlmsg_build_payload(header, self.chain); - } } -impl Drop for Chain { - fn drop(&mut self) { - unsafe { sys::nftnl_chain_free(self.chain) }; - } -} - -pub fn get_chains_cb<'a>( - header: &libc::nlmsghdr, - _genmsg: &Nfgenmsg, - _data: &[u8], - (table, chains): &mut (&Rc<Table>, &mut Vec<Chain>), -) -> Result<(), crate::query::Error> { - unsafe { - let chain = sys::nftnl_chain_alloc(); - if chain == std::ptr::null_mut() { - return Err(ParseError::Custom(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "Chain allocation failed", - ))) - .into()); - } - let err = sys::nftnl_chain_nlmsg_parse(header, chain); - if err < 0 { - sys::nftnl_chain_free(chain); - return Err(ParseError::Custom(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "The netlink chain couldn't be parsed !?", - ))) - .into()); - } - - let table_name = CStr::from_ptr(sys::nftnl_chain_get_str( - chain, - sys::NFTNL_CHAIN_TABLE as u16, - )); - let family = sys::nftnl_chain_get_u32(chain, sys::NFTNL_CHAIN_FAMILY as u16); - let family = match crate::ProtoFamily::try_from(family as i32) { - Ok(family) => family, - Err(crate::InvalidProtocolFamily) => { - error!("The netlink table didn't have a valid protocol family !?"); - sys::nftnl_chain_free(chain); - return Err(ParseError::Custom(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "The netlink table didn't have a valid protocol family !?", - ))) - .into()); - } - }; - - if table_name != table.get_name() { - sys::nftnl_chain_free(chain); - return Ok(()); - } - - if family != crate::ProtoFamily::Unspec && family != table.get_family() { - sys::nftnl_chain_free(chain); - return Ok(()); - } +impl NfNetlinkDeserializable for Chain { + fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let (inner, _nfgenmsg, remaining_data) = + parse_object::<Self>(buf, NFT_MSG_NEWCHAIN, NFT_MSG_DELCHAIN)?; - chains.push(Chain::from_raw(chain, table.clone())); + Ok((Self { inner }, remaining_data)) } - - Ok(()) } -*/ impl_attr_getters_and_setters!( Chain, [ (get_flags, set_flags, with_flags, sys::NFTA_CHAIN_FLAGS, U32, u32), (get_name, set_name, with_name, sys::NFTA_CHAIN_NAME, String, String), + // Sets the hook and priority for this chain. Without calling this method the chain will + // become a "regular chain" without any hook and will thus not receive any traffic unless + // some rule forward packets to it via goto or jump verdicts. + // + // By calling `set_hook` with a hook the chain that is created will be registered with that + // hook and is thus a "base chain". A "base chain" is an entry point for packets from the + // networking stack. (set_hook, get_hook, with_hook, sys::NFTA_CHAIN_HOOK, ChainHook, Hook), (get_policy, set_policy, with_policy, sys::NFTA_CHAIN_POLICY, U32, u32), (get_table, set_table, with_table, sys::NFTA_CHAIN_TABLE, String, String), @@ -362,15 +266,24 @@ impl_attr_getters_and_setters!( ] ); -/* pub fn list_chains_for_table(table: &Table) -> Result<Vec<Chain>, crate::query::Error> { let mut result = Vec::new(); crate::query::list_objects_with_data( libc::NFT_MSG_GETCHAIN as u16, - &get_chains_cb, + &|chain: Chain, (table, chains): &mut (&Table, &mut Vec<Chain>)| { + if chain.get_table() == table.get_name() { + chains.push(chain); + } else { + info!( + "Ignoring chain {:?} because it doesn't map the table {:?}", + chain.get_name(), + table.get_name() + ); + } + Ok(()) + }, None, &mut (&table, &mut result), )?; Ok(result) } -*/ @@ -97,12 +97,12 @@ pub use batch::{default_batch_page_size, Batch}; //pub mod expr; pub mod table; +pub use table::list_tables; pub use table::Table; -//pub use table::{get_tables_cb, list_tables}; -// -mod chain; -//pub use chain::{get_chains_cb, list_chains_for_table}; -//pub use chain::{Chain, ChainType, Hook, Policy, Priority}; + +pub mod chain; +pub use chain::list_chains_for_table; +pub use chain::{Chain, ChainType, Hook, Policy, Priority}; //mod chain_methods; //pub use chain_methods::ChainMethods; diff --git a/src/nlmsg.rs b/src/nlmsg.rs index 435fed3..a1bb200 100644 --- a/src/nlmsg.rs +++ b/src/nlmsg.rs @@ -8,26 +8,22 @@ use std::{ use crate::{ parser::{ pad_netlink_object, pad_netlink_object_with_variable_size, AttributeType, DecodeError, - Nfgenmsg, }, sys::{ - nlattr, nlmsghdr, NFNETLINK_V0, NFNL_MSG_BATCH_BEGIN, NFNL_MSG_BATCH_END, - NFNL_SUBSYS_NFTABLES, NLA_TYPE_MASK, + nfgenmsg, nlattr, nlmsghdr, NFNETLINK_V0, NFNL_MSG_BATCH_BEGIN, NFNL_MSG_BATCH_END, + NFNL_SUBSYS_NFTABLES, }, MsgType, ProtoFamily, }; pub struct NfNetlinkWriter<'a> { buf: &'a mut Vec<u8>, - headers: HeaderStack<'a>, + headers: Option<(usize, usize)>, } impl<'a> NfNetlinkWriter<'a> { pub fn new(buf: &'a mut Vec<u8>) -> NfNetlinkWriter<'a> { - NfNetlinkWriter { - buf, - headers: HeaderStack::new(), - } + NfNetlinkWriter { buf, headers: None } } pub fn add_data_zeroed<'b>(&'b mut self, size: usize) -> &'b mut [u8] { @@ -35,7 +31,12 @@ impl<'a> NfNetlinkWriter<'a> { let start = self.buf.len(); self.buf.resize(start + padded_size, 0); - self.headers.add_size(padded_size as u32); + if let Some((msghdr_idx, _nfgenmsg_idx)) = self.headers { + let mut hdr: &mut nlmsghdr = unsafe { + std::mem::transmute(self.buf[msghdr_idx..].as_mut_ptr() as *mut nlmsghdr) + }; + hdr.nlmsg_len += padded_size as u32; + } &mut self.buf[start..start + size] } @@ -53,14 +54,16 @@ impl<'a> NfNetlinkWriter<'a> { seq: u32, ressource_id: Option<u16>, ) { + if self.headers.is_some() { + error!("Calling write_header while still holding headers open!?"); + } + let nlmsghdr_len = pad_netlink_object::<nlmsghdr>(); - let nfgenmsg_len = pad_netlink_object::<Nfgenmsg>(); - let nlmsghdr_buf = self.add_data_zeroed(nlmsghdr_len); + let nfgenmsg_len = pad_netlink_object::<nfgenmsg>(); + let nlmsghdr_buf = self.add_data_zeroed(nlmsghdr_len); let mut hdr: &mut nlmsghdr = unsafe { std::mem::transmute(nlmsghdr_buf.as_mut_ptr() as *mut nlmsghdr) }; - //let mut hdr = &mut unsafe { *(nlmsghdr_buf.as_mut_ptr() as *mut nlmsghdr) }; - hdr.nlmsg_len = (nlmsghdr_len + nfgenmsg_len) as u32; hdr.nlmsg_type = msg_type; // batch messages are not specific to the nftables subsystem @@ -71,58 +74,20 @@ impl<'a> NfNetlinkWriter<'a> { hdr.nlmsg_seq = seq; let nfgenmsg_buf = self.add_data_zeroed(nfgenmsg_len); - - let mut nfgenmsg: &mut Nfgenmsg = - unsafe { std::mem::transmute(nfgenmsg_buf.as_mut_ptr() as *mut Nfgenmsg) }; - nfgenmsg.family = family as u8; + let mut nfgenmsg: &mut nfgenmsg = + unsafe { std::mem::transmute(nfgenmsg_buf.as_mut_ptr() as *mut nfgenmsg) }; + nfgenmsg.nfgen_family = family as u8; nfgenmsg.version = NFNETLINK_V0 as u8; nfgenmsg.res_id = ressource_id.unwrap_or(0); - self.headers.add_level(hdr, Some(nfgenmsg)); - } - - pub fn get_current_header(&mut self) -> Option<&mut nlmsghdr> { - let stack_size = self.headers.stack.len(); - if stack_size > 0 { - Some(unsafe { std::mem::transmute(self.headers.stack[stack_size - 1].0) }) - } else { - None - } + self.headers = Some(( + self.buf.len() - (nlmsghdr_len + nfgenmsg_len), + self.buf.len() - nfgenmsg_len, + )); } pub fn finalize_writing_object(&mut self) { - self.headers.pop_level(); - } -} - -struct HeaderStack<'a> { - stack: Vec<(*mut nlmsghdr, Option<*mut Nfgenmsg>)>, - lifetime: PhantomData<&'a ()>, -} - -impl<'a> HeaderStack<'a> { - fn new() -> HeaderStack<'a> { - HeaderStack { - stack: Vec::new(), - lifetime: PhantomData, - } - } - - /// resize all the stacked netlink containers to hold additional_size new bytes - fn add_size(&mut self, additional_size: u32) { - for (hdr, _) in &mut self.stack { - unsafe { - (**hdr).nlmsg_len = (**hdr).nlmsg_len + additional_size; - } - } - } - - fn add_level(&mut self, hdr: *mut nlmsghdr, nfgenmsg: Option<*mut Nfgenmsg>) { - self.stack.push((hdr, nfgenmsg)); - } - - fn pop_level(&mut self) { - self.stack.pop(); + self.headers = None; } } diff --git a/src/parser.rs b/src/parser.rs index 8e12c5e..2d05f4f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ use std::{ any::TypeId, collections::HashMap, - fmt::Debug, + fmt::{Debug, DebugStruct}, mem::{size_of, transmute}, string::FromUtf8Error, }; @@ -14,9 +14,9 @@ use crate::{ NfNetlinkDeserializable, NfNetlinkObject, NfNetlinkSerializable, NfNetlinkWriter, }, sys::{ - nlattr, nlmsgerr, nlmsghdr, NFNETLINK_V0, NFNL_MSG_BATCH_BEGIN, NFNL_MSG_BATCH_END, - NFNL_SUBSYS_NFTABLES, NLA_TYPE_MASK, NLMSG_ALIGNTO, NLMSG_DONE, NLMSG_ERROR, - NLMSG_MIN_TYPE, NLMSG_NOOP, NLM_F_DUMP_INTR, + nfgenmsg, nlattr, nlmsgerr, nlmsghdr, NFNETLINK_V0, NFNL_MSG_BATCH_BEGIN, + NFNL_MSG_BATCH_END, NFNL_SUBSYS_NFTABLES, NLA_TYPE_MASK, NLMSG_ALIGNTO, NLMSG_DONE, + NLMSG_ERROR, NLMSG_MIN_TYPE, NLMSG_NOOP, NLM_F_DUMP_INTR, }, InvalidProtocolFamily, ProtoFamily, }; @@ -53,6 +53,9 @@ pub enum DecodeError { #[error("Invalid attribute type")] InvalidAttributeType, + #[error("Invalid type for a chain")] + UnknownChainType, + #[error("Unsupported attribute type")] UnsupportedAttributeType(u16), @@ -89,14 +92,6 @@ pub const fn pad_netlink_object<T>() -> usize { pad_netlink_object_with_variable_size(size) } -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct Nfgenmsg { - pub family: u8, /* AF_xxx */ - pub version: u8, /* nfnetlink version */ - pub res_id: u16, /* resource id */ -} - pub fn get_subsystem_from_nlmsghdr_type(x: u16) -> u8 { ((x & 0xff00) >> 8) as u8 } @@ -126,12 +121,12 @@ pub fn get_nlmsghdr(buf: &[u8]) -> Result<nlmsghdr, DecodeError> { Ok(nlmsghdr) } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq)] pub enum NlMsg<'a> { Done, Noop, Error(nlmsgerr), - NfGenMsg(Nfgenmsg, &'a [u8]), + NfGenMsg(nfgenmsg, &'a [u8]), } pub fn parse_nlmsg<'a>(buf: &'a [u8]) -> Result<(nlmsghdr, NlMsg<'a>), DecodeError> { @@ -173,14 +168,14 @@ pub fn parse_nlmsg<'a>(buf: &'a [u8]) -> Result<(nlmsghdr, NlMsg<'a>), DecodeErr } } - let size_of_nfgenmsg = pad_netlink_object::<Nfgenmsg>(); + let size_of_nfgenmsg = pad_netlink_object::<nfgenmsg>(); if hdr.nlmsg_len as usize > buf.len() || (hdr.nlmsg_len as usize) < size_of_hdr + size_of_nfgenmsg { return Err(DecodeError::NlMsgTooSmall); } - let nfgenmsg_ptr = buf[size_of_hdr..size_of_hdr + size_of_nfgenmsg].as_ptr() as *const Nfgenmsg; + let nfgenmsg_ptr = buf[size_of_hdr..size_of_hdr + size_of_nfgenmsg].as_ptr() as *const nfgenmsg; let nfgenmsg = unsafe { *nfgenmsg_ptr }; if nfgenmsg.version != NFNETLINK_V0 as u8 { @@ -302,7 +297,11 @@ impl NfNetlinkAttribute for String { } impl NfNetlinkDeserializable for String { - fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + fn deserialize(mut buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + // ignore the NULL byte terminator, if any + if buf.len() > 0 && buf[buf.len() - 1] == 0 { + buf = &buf[..buf.len() - 1]; + } Ok((String::from_utf8(buf.to_vec())?, &[])) } } @@ -394,7 +393,7 @@ impl<'a> NfNetlinkAttributeReader<'a> { ) { Ok(x) => self.attrs.set_attr(nla_type, x), Err(DecodeError::UnsupportedAttributeType(t)) => info!( - "Ignore attribute type {} for type id {:?}", + "Ignore attribute type {} for type identified by {:?}", t, TypeId::of::<T>() ), @@ -413,26 +412,6 @@ impl<'a> NfNetlinkAttributeReader<'a> { } } -pub fn parse_object<'a>( - hdr: nlmsghdr, - msg: NlMsg<'a>, - buf: &'a [u8], -) -> Result<(Nfgenmsg, NfNetlinkAttributeReader<'a>, &'a [u8]), DecodeError> { - let remaining_size = hdr.nlmsg_len as usize - - pad_netlink_object_with_variable_size(size_of::<nlmsghdr>() + size_of::<Nfgenmsg>()); - - let remaining_data = &buf[pad_netlink_object_with_variable_size(hdr.nlmsg_len as usize)..]; - - match msg { - NlMsg::NfGenMsg(nfgenmsg, content) => Ok(( - nfgenmsg, - NfNetlinkAttributeReader::new(content, remaining_size)?, - remaining_data, - )), - _ => Err(DecodeError::UnexpectedType(hdr.nlmsg_type)), - } -} - impl NfNetlinkSerializable for NfNetlinkAttributes { fn serialize<'a>(&self, writer: &mut NfNetlinkWriter<'a>) { // TODO: improve performance by not sorting this @@ -485,6 +464,13 @@ macro_rules! impl_attribute_holder { }; } +pub trait InnerFormat { + fn inner_format_struct<'a, 'b: 'a>( + &'a self, + s: DebugStruct<'a, 'b>, + ) -> Result<DebugStruct<'a, 'b>, std::fmt::Error>; +} + impl_attribute_holder!( AttributeType, [String, String], @@ -539,5 +525,58 @@ macro_rules! impl_attr_getters_and_setters { } } } + + + impl $crate::parser::InnerFormat for $struct { + fn inner_format_struct<'a, 'b: 'a>(&'a self, mut s: std::fmt::DebugStruct<'a, 'b>) -> Result<std::fmt::DebugStruct<'a, 'b>, std::fmt::Error> { + $( + // Rewrite attributes names to be readable: 'sys::NFTA_CHAIN_NAME' -> 'name' + // Performance must be terrible, but this is the Debug impl anyway, so that + // must mean we can afford to be slow, right? ;) + if let Some(val) = self.$getter_name() { + let mut attr = stringify!($attr_name); + if let Some((nfta_idx, _match )) = attr.rmatch_indices("NFTA_").next() { + if let Some(underscore_idx) = &attr[nfta_idx+5..].find('_') { + attr = &attr[nfta_idx+underscore_idx+6..]; + } + } + let attr = attr.to_lowercase(); + s.field(&attr, val); + } + )+ + Ok(s) + } + } }; } + +pub fn parse_object<T: AttributeDecoder + 'static>( + buf: &[u8], + add_obj: u32, + del_obj: u32, +) -> Result<(NfNetlinkAttributes, nfgenmsg, &[u8]), DecodeError> { + let (hdr, msg) = parse_nlmsg(buf)?; + + let op = get_operation_from_nlmsghdr_type(hdr.nlmsg_type) as u32; + + if op != add_obj && op != del_obj { + return Err(DecodeError::UnexpectedType(hdr.nlmsg_type)); + } + + let obj_size = hdr.nlmsg_len as usize + - pad_netlink_object_with_variable_size(size_of::<nlmsghdr>() + size_of::<nfgenmsg>()); + + let remaining_data_offset = pad_netlink_object_with_variable_size(hdr.nlmsg_len as usize); + let remaining_data = &buf[remaining_data_offset..]; + + let (nfgenmsg, attrs) = match msg { + NlMsg::NfGenMsg(nfgenmsg, content) => { + (nfgenmsg, NfNetlinkAttributeReader::new(content, obj_size)?) + } + _ => return Err(DecodeError::UnexpectedType(hdr.nlmsg_type)), + }; + + let inner = attrs.decode::<T>()?; + + Ok((inner, nfgenmsg, remaining_data)) +} diff --git a/src/table.rs b/src/table.rs index 66dc667..a21f3f2 100644 --- a/src/table.rs +++ b/src/table.rs @@ -5,10 +5,7 @@ use crate::nlmsg::{ AttributeDecoder, NfNetlinkAttributes, NfNetlinkDeserializable, NfNetlinkObject, NfNetlinkSerializable, NfNetlinkWriter, }; -use crate::parser::{ - get_operation_from_nlmsghdr_type, parse_nlmsg, parse_object, DecodeError, - NfNetlinkAttributeReader, -}; +use crate::parser::{parse_object, DecodeError, InnerFormat}; use crate::sys::{ self, NFTA_OBJ_TABLE, NFTA_TABLE_FLAGS, NFTA_TABLE_NAME, NFT_MSG_DELTABLE, NFT_MSG_GETTABLE, NFT_MSG_NEWTABLE, NLM_F_ACK, @@ -19,7 +16,7 @@ use crate::{impl_attr_getters_and_setters, MsgType, ProtoFamily}; /// family and contains [`Chain`]s that in turn hold the rules. /// /// [`Chain`]: struct.Chain.html -#[derive(Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct Table { inner: NfNetlinkAttributes, family: ProtoFamily, @@ -58,6 +55,14 @@ impl PartialEq for Table { } */ +impl Debug for Table { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut res = f.debug_struct("Table"); + res.field("family", &self.family); + self.inner_format_struct(res)?.finish() + } +} + impl NfNetlinkObject for Table { fn add_or_remove<'a>(&self, writer: &mut NfNetlinkWriter<'a>, msg_type: MsgType, seq: u32) { let raw_msg_type = match msg_type { @@ -72,45 +77,19 @@ impl NfNetlinkObject for Table { impl NfNetlinkDeserializable for Table { fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { - let (hdr, msg) = parse_nlmsg(buf)?; - - let op = get_operation_from_nlmsghdr_type(hdr.nlmsg_type) as u32; - - if op != NFT_MSG_NEWTABLE && op != NFT_MSG_DELTABLE { - return Err(DecodeError::UnexpectedType(hdr.nlmsg_type)); - } - - let (nfgenmsg, attrs, remaining_data) = parse_object(hdr, msg, buf)?; - - let inner = attrs.decode::<Table>()?; + let (inner, nfgenmsg, remaining_data) = + parse_object::<Self>(buf, NFT_MSG_NEWTABLE, NFT_MSG_DELTABLE)?; Ok(( - Table { + Self { inner, - family: ProtoFamily::try_from(nfgenmsg.family as i32)?, + family: ProtoFamily::try_from(nfgenmsg.nfgen_family as i32)?, }, remaining_data, )) } } -/* -impl AttributeDecoder for Table { - fn decode_attribute(attr_type: u16, buf: &[u8]) -> Result<AttributeType, DecodeError> { - match attr_type { - NFTA_TABLE_NAME => Ok(AttributeType::String(String::from_utf8(buf.to_vec())?)), - NFTA_TABLE_FLAGS => { - let val = [buf[0], buf[1], buf[2], buf[3]]; - - Ok(AttributeType::U32(u32::from_ne_bytes(val))) - } - NFTA_TABLE_USERDATA => Ok(AttributeType::VecU8(buf.to_vec())), - _ => Err(DecodeError::UnsupportedAttributeType(attr_type)), - } - } -} -*/ - impl_attr_getters_and_setters!( Table, [ |