diff --git a/Cargo.toml b/Cargo.toml index a25aa39e..ea6cce15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,8 @@ byteorder = "1.3.2" libc = "0.2.66" log = { version = "0.4.20", features = ["std"] } netlink-packet-core = { version = "0.7.0" } -netlink-packet-utils = { version = "0.5.2" } +netlink-packet-utils = { path = "../netlink-packet-utils" } +thiserror = "1.0.58" [[example]] name = "dump_packet_links" diff --git a/src/address/attribute.rs b/src/address/attribute.rs index 3c72062d..03075597 100644 --- a/src/address/attribute.rs +++ b/src/address/attribute.rs @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT -use std::mem::size_of; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -use anyhow::Context; +use crate::address::{AddressError, AddressFlags, CacheInfo, CacheInfoBuffer}; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32}, - DecodeError, Emitable, Parseable, + Emitable, Parseable, }; - -use crate::address::{AddressFlags, CacheInfo, CacheInfoBuffer}; +use std::mem::size_of; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; const IFA_ADDRESS: u16 = 1; const IFA_LOCAL: u16 = 2; @@ -110,7 +107,9 @@ impl Nla for AddressAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AddressAttribute { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = AddressError; + + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { IFA_ADDRESS => { @@ -123,11 +122,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Address(IpAddr::from(data)) } else { - return Err(DecodeError::from(format!( - "Invalid IFA_LOCAL, got unexpected length \ - of payload {:?}", - payload - ))); + return Err(AddressError::ParseAttributeInvalidPayload { + kind: "IFA_ADDRESS", + payload_length: payload.len(), + }); } } IFA_LOCAL => { @@ -140,27 +138,28 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Local(IpAddr::from(data)) } else { - return Err(DecodeError::from(format!( - "Invalid IFA_LOCAL, got unexpected length \ - of payload {:?}", - payload - ))); + return Err(AddressError::ParseAttributeInvalidPayload { + kind: "IFA_LOCAL", + payload_length: payload.len(), + }); } } - IFA_LABEL => Self::Label( - parse_string(payload).context("invalid IFA_LABEL value")?, - ), + IFA_LABEL => Self::Label(parse_string(payload).map_err(|err| { + AddressError::ParseAttribute { + kind: "IFA_LABEL", + err, + } + })?), IFA_BROADCAST => { if payload.len() == IPV4_ADDR_LEN { let mut data = [0u8; IPV4_ADDR_LEN]; data.copy_from_slice(&payload[0..IPV4_ADDR_LEN]); Self::Broadcast(Ipv4Addr::from(data)) } else { - return Err(DecodeError::from(format!( - "Invalid IFA_BROADCAST, got unexpected length \ - of IPv4 address payload {:?}", - payload - ))); + return Err(AddressError::ParseAttributeInvalidPayload { + kind: "IFA_BROADCAST", + payload_length: payload.len(), + }); } } IFA_ANYCAST => { @@ -169,16 +168,19 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Anycast(Ipv6Addr::from(data)) } else { - return Err(DecodeError::from(format!( - "Invalid IFA_ANYCAST, got unexpected length \ - of IPv6 address payload {:?}", - payload - ))); + return Err(AddressError::ParseAttributeInvalidPayload { + kind: "IFA_ANYCAST", + payload_length: payload.len(), + }); } } IFA_CACHEINFO => Self::CacheInfo( - CacheInfo::parse(&CacheInfoBuffer::new(payload)) - .context(format!("Invalid IFA_CACHEINFO {:?}", payload))?, + CacheInfo::parse(&CacheInfoBuffer::new(payload)).map_err( + |err| AddressError::ParseAttribute { + kind: "IFA_CACHEINFO", + err, + }, + )?, ), IFA_MULTICAST => { if payload.len() == IPV6_ADDR_LEN { @@ -186,20 +188,25 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> data.copy_from_slice(&payload[0..IPV6_ADDR_LEN]); Self::Multicast(Ipv6Addr::from(data)) } else { - return Err(DecodeError::from(format!( - "Invalid IFA_MULTICAST, got unexpected length \ - of IPv6 address payload {:?}", - payload - ))); + return Err(AddressError::ParseAttributeInvalidPayload { + kind: "IFA_MULTICAST", + payload_length: payload.len(), + }); } } IFA_FLAGS => Self::Flags(AddressFlags::from_bits_retain( - parse_u32(payload).context("invalid IFA_FLAGS value")?, + parse_u32(payload).map_err(|err| { + AddressError::ParseAttribute { + kind: "IFA_FLAGS", + err, + } + })?, )), - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), + kind => { + Self::Other(DefaultNla::parse(buf).map_err(|err| { + AddressError::ParseUnknownNLA { kind, err } + })?) + } }) } } diff --git a/src/address/cache_info.rs b/src/address/cache_info.rs index b23edde1..4c94082e 100644 --- a/src/address/cache_info.rs +++ b/src/address/cache_info.rs @@ -24,6 +24,8 @@ buffer!(CacheInfoBuffer(ADDRESSS_CACHE_INFO_LEN) { }); impl> Parseable> for CacheInfo { + type Error = DecodeError; + fn parse(buf: &CacheInfoBuffer) -> Result { Ok(CacheInfo { ifa_preferred: buf.ifa_preferred(), diff --git a/src/address/error.rs b/src/address/error.rs new file mode 100644 index 00000000..211f14e9 --- /dev/null +++ b/src/address/error.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum AddressError { + #[error( + "Invalid {kind}, got unexpected length of payload: {payload_length}" + )] + ParseAttributeInvalidPayload { + kind: &'static str, + payload_length: usize, + }, + + #[error("Invalid {kind} value")] + ParseAttribute { + kind: &'static str, + err: DecodeError, + }, + + #[error("unknown NLA {kind}")] + ParseUnknownNLA { kind: u16, err: DecodeError }, + + #[error(transparent)] + NlaAttribute(#[from] NlaError), + + #[error("Faield to parse address buffer: {0:?}")] + FailedBufferInit(DecodeError), +} diff --git a/src/address/message.rs b/src/address/message.rs index d8d9bd0e..78864889 100644 --- a/src/address/message.rs +++ b/src/address/message.rs @@ -1,17 +1,17 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use crate::{ + address::{ + AddressAttribute, AddressError, AddressHeaderFlags, AddressScope, + }, + AddressFamily, +}; use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -use crate::{ - address::{AddressAttribute, AddressHeaderFlags, AddressScope}, - AddressFamily, -}; - const ADDRESS_HEADER_LEN: usize = 8; buffer!(AddressMessageBuffer(ADDRESS_HEADER_LEN) { @@ -26,7 +26,7 @@ buffer!(AddressMessageBuffer(ADDRESS_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> AddressMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -76,7 +76,8 @@ impl Emitable for AddressMessage { } impl> Parseable> for AddressHeader { - fn parse(buf: &AddressMessageBuffer) -> Result { + type Error = (); + fn parse(buf: &AddressMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), prefix_len: buf.prefix_len(), @@ -90,12 +91,12 @@ impl> Parseable> for AddressHeader { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for AddressMessage { - fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { + type Error = AddressError; + fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { Ok(AddressMessage { - header: AddressHeader::parse(buf) - .context("failed to parse address message header")?, - attributes: Vec::::parse(buf) - .context("failed to parse address message NLAs")?, + // ok to unwrap, we never fail parsing the header. + header: AddressHeader::parse(buf).unwrap(), + attributes: Vec::::parse(buf)?, }) } } @@ -103,7 +104,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { - fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { + type Error = AddressError; + fn parse(buf: &AddressMessageBuffer<&'a T>) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(AddressAttribute::parse(&nla_buf?)?); diff --git a/src/address/mod.rs b/src/address/mod.rs index 5c9119f0..3ea23d3e 100644 --- a/src/address/mod.rs +++ b/src/address/mod.rs @@ -4,6 +4,7 @@ mod addr_flags; mod addr_scope; mod attribute; mod cache_info; +mod error; mod message; #[cfg(test)] @@ -13,4 +14,5 @@ pub use self::addr_flags::{AddressFlags, AddressHeaderFlags}; pub use self::addr_scope::AddressScope; pub use self::attribute::AddressAttribute; pub use self::cache_info::{CacheInfo, CacheInfoBuffer}; +pub use self::error::AddressError; pub use self::message::{AddressHeader, AddressMessage, AddressMessageBuffer}; diff --git a/src/link/af_spec/bridge.rs b/src/link/af_spec/bridge.rs index 6a5352dd..66907420 100644 --- a/src/link/af_spec/bridge.rs +++ b/src/link/af_spec/bridge.rs @@ -52,6 +52,7 @@ impl nla::Nla for AfSpecBridge { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecBridge { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecBridge::*; @@ -80,6 +81,7 @@ pub(crate) struct VecAfSpecBridge(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecBridge { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_BRIDGE)"; diff --git a/src/link/af_spec/inet.rs b/src/link/af_spec/inet.rs index 27a5a0da..b07b4f2c 100644 --- a/src/link/af_spec/inet.rs +++ b/src/link/af_spec/inet.rs @@ -27,6 +27,7 @@ pub(crate) struct VecAfSpecInet(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecInet { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_UNSPEC)"; @@ -65,6 +66,7 @@ impl nla::Nla for AfSpecInet { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecInet::*; @@ -162,6 +164,7 @@ pub struct InetDevConf { } impl> Parseable> for InetDevConf { + type Error = DecodeError; fn parse(buf: &InetDevConfBuffer) -> Result { Ok(Self { forwarding: buf.forwarding(), diff --git a/src/link/af_spec/inet6.rs b/src/link/af_spec/inet6.rs index b4034888..4e053d91 100644 --- a/src/link/af_spec/inet6.rs +++ b/src/link/af_spec/inet6.rs @@ -53,6 +53,7 @@ pub(crate) struct VecAfSpecInet6(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecInet6 { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid AF_INET6 NLA for IFLA_AF_SPEC(AF_UNSPEC)"; @@ -111,6 +112,7 @@ impl Nla for AfSpecInet6 { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet6 { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::AfSpecInet6::*; diff --git a/src/link/af_spec/inet6_cache.rs b/src/link/af_spec/inet6_cache.rs index 8d28710d..9fc761d3 100644 --- a/src/link/af_spec/inet6_cache.rs +++ b/src/link/af_spec/inet6_cache.rs @@ -24,6 +24,7 @@ buffer!(Inet6CacheInfoBuffer(LINK_INET6_CACHE_INFO_LEN) { }); impl> Parseable> for Inet6CacheInfo { + type Error = DecodeError; fn parse(buf: &Inet6CacheInfoBuffer) -> Result { Ok(Self { max_reasm_len: buf.max_reasm_len(), diff --git a/src/link/af_spec/inet6_devconf.rs b/src/link/af_spec/inet6_devconf.rs index 740ce3da..38ad7622 100644 --- a/src/link/af_spec/inet6_devconf.rs +++ b/src/link/af_spec/inet6_devconf.rs @@ -72,6 +72,7 @@ buffer!(Inet6DevConfBuffer(LINK_INET6_DEV_CONF_LEN) { }); impl> Parseable> for Inet6DevConf { + type Error = DecodeError; fn parse(buf: &Inet6DevConfBuffer) -> Result { Ok(Self { forwarding: buf.forwarding(), diff --git a/src/link/af_spec/inet6_icmp.rs b/src/link/af_spec/inet6_icmp.rs index 238e3245..eb6a71ac 100644 --- a/src/link/af_spec/inet6_icmp.rs +++ b/src/link/af_spec/inet6_icmp.rs @@ -28,6 +28,7 @@ buffer!(Icmp6StatsBuffer(ICMP6_STATS_LEN) { }); impl> Parseable> for Icmp6Stats { + type Error = DecodeError; fn parse(buf: &Icmp6StatsBuffer) -> Result { Ok(Self { num: buf.num(), diff --git a/src/link/af_spec/inet6_stats.rs b/src/link/af_spec/inet6_stats.rs index 685d9693..8377a73a 100644 --- a/src/link/af_spec/inet6_stats.rs +++ b/src/link/af_spec/inet6_stats.rs @@ -88,6 +88,7 @@ pub struct Inet6Stats { } impl> Parseable> for Inet6Stats { + type Error = DecodeError; fn parse(buf: &Inet6StatsBuffer) -> Result { Ok(Self { num: buf.num(), diff --git a/src/link/af_spec/unspec.rs b/src/link/af_spec/unspec.rs index 439d3626..306874a0 100644 --- a/src/link/af_spec/unspec.rs +++ b/src/link/af_spec/unspec.rs @@ -48,6 +48,7 @@ pub(crate) struct VecAfSpecUnspec(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecAfSpecUnspec { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; let err = "Invalid NLA for IFLA_AF_SPEC(AF_UNSPEC)"; diff --git a/src/link/attribute.rs b/src/link/attribute.rs index 1a7665a3..06bc9be0 100644 --- a/src/link/attribute.rs +++ b/src/link/attribute.rs @@ -14,7 +14,7 @@ use netlink_packet_utils::{ #[cfg(any(target_os = "linux", target_os = "fuchsia",))] use super::af_spec::VecAfSpecBridge; #[cfg(any(target_os = "linux", target_os = "fuchsia",))] -use super::proto_info::VecLinkProtoInfoBridge; +use super::proto_info::VecanhowLinkProtoInfoBridge; use super::{ af_spec::VecAfSpecUnspec, buffer_tool::expand_buffer_if_small, @@ -367,6 +367,7 @@ impl Nla for LinkAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, AddressFamily> for LinkAttribute { + type Error = DecodeError; fn parse_with_param( buf: &NlaBuffer<&'a T>, interface_family: AddressFamily, diff --git a/src/link/down_reason.rs b/src/link/down_reason.rs index a542e8fb..d64f8655 100644 --- a/src/link/down_reason.rs +++ b/src/link/down_reason.rs @@ -22,6 +22,7 @@ pub enum LinkProtocolDownReason { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtocolDownReason { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/event.rs b/src/link/event.rs index 634ff91e..5c9754f1 100644 --- a/src/link/event.rs +++ b/src/link/event.rs @@ -59,6 +59,7 @@ impl From for u32 { } impl + ?Sized> Parseable for LinkEvent { + type Error = DecodeError; fn parse(buf: &T) -> Result { Ok(LinkEvent::from( parse_u32(buf.as_ref()).context("invalid IFLA_EVENT value")?, diff --git a/src/link/header.rs b/src/link/header.rs index d92a642f..87ea5b93 100644 --- a/src/link/header.rs +++ b/src/link/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; @@ -25,7 +25,7 @@ buffer!(LinkMessageBuffer(LINK_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> LinkMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -86,6 +86,7 @@ impl Emitable for LinkHeader { } impl> Parseable> for LinkHeader { + type Error = DecodeError; fn parse(buf: &LinkMessageBuffer) -> Result { Ok(Self { interface_family: buf.interface_family().into(), diff --git a/src/link/link_info/bond.rs b/src/link/link_info/bond.rs index dd9e1d5e..14b4c188 100644 --- a/src/link/link_info/bond.rs +++ b/src/link/link_info/bond.rs @@ -107,6 +107,7 @@ impl Nla for BondAdInfo { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BondAdInfo { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { @@ -426,6 +427,7 @@ impl Nla for InfoBond { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBond { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/link_info/bond_port.rs b/src/link/link_info/bond_port.rs index 62206818..4b849401 100644 --- a/src/link/link_info/bond_port.rs +++ b/src/link/link_info/bond_port.rs @@ -158,6 +158,7 @@ impl Nla for InfoBondPort { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBondPort { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoBondPort::*; let payload = buf.value(); diff --git a/src/link/link_info/bridge.rs b/src/link/link_info/bridge.rs index 13d2a894..ed1a83bc 100644 --- a/src/link/link_info/bridge.rs +++ b/src/link/link_info/bridge.rs @@ -305,6 +305,7 @@ impl Nla for InfoBridge { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridge { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { @@ -530,6 +531,7 @@ buffer!(BridgeIdBuffer(BRIDGE_ID_LEN) { }); impl + ?Sized> Parseable> for BridgeId { + type Error = DecodeError; fn parse(buf: &BridgeIdBuffer<&T>) -> Result { // Priority is encoded in big endian. From kernel's // net/bridge/br_netlink.c br_fill_info(): @@ -616,6 +618,7 @@ impl Nla for BridgeQuerierState { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for BridgeQuerierState { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::BridgeQuerierState::*; let payload = buf.value(); diff --git a/src/link/link_info/bridge_port.rs b/src/link/link_info/bridge_port.rs index 35c818cf..76ca4d8a 100644 --- a/src/link/link_info/bridge_port.rs +++ b/src/link/link_info/bridge_port.rs @@ -274,6 +274,7 @@ impl Nla for InfoBridgePort { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridgePort { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); diff --git a/src/link/link_info/gre.rs b/src/link/link_info/gre.rs index d1ff4a1a..a1299759 100644 --- a/src/link/link_info/gre.rs +++ b/src/link/link_info/gre.rs @@ -33,6 +33,7 @@ impl Nla for InfoGreTun { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/gre6.rs b/src/link/link_info/gre6.rs index ceef4161..e3813b7b 100644 --- a/src/link/link_info/gre6.rs +++ b/src/link/link_info/gre6.rs @@ -33,6 +33,7 @@ impl Nla for InfoGreTun6 { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun6 { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/gre_tap.rs b/src/link/link_info/gre_tap.rs index 16293dd2..87198849 100644 --- a/src/link/link_info/gre_tap.rs +++ b/src/link/link_info/gre_tap.rs @@ -33,6 +33,7 @@ impl Nla for InfoGreTap { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/gre_tap6.rs b/src/link/link_info/gre_tap6.rs index 491e9f2b..9fe4617f 100644 --- a/src/link/link_info/gre_tap6.rs +++ b/src/link/link_info/gre_tap6.rs @@ -33,6 +33,8 @@ impl Nla for InfoGreTap6 { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap6 { + type Error = DecodeError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/gtp.rs b/src/link/link_info/gtp.rs index 4a87d314..804a78b3 100644 --- a/src/link/link_info/gtp.rs +++ b/src/link/link_info/gtp.rs @@ -33,6 +33,7 @@ impl Nla for InfoGtp { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGtp { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/hsr.rs b/src/link/link_info/hsr.rs index 52884ac5..ceb903c1 100644 --- a/src/link/link_info/hsr.rs +++ b/src/link/link_info/hsr.rs @@ -74,6 +74,7 @@ impl Nla for InfoHsr { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoHsr { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoHsr::*; let payload = buf.value(); diff --git a/src/link/link_info/info_port.rs b/src/link/link_info/info_port.rs index 67f3f966..870fc58b 100644 --- a/src/link/link_info/info_port.rs +++ b/src/link/link_info/info_port.rs @@ -63,6 +63,7 @@ impl Nla for InfoPortKind { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoPortKind { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { if buf.kind() != IFLA_INFO_PORT_KIND { return Err(format!( @@ -118,11 +119,17 @@ impl InfoPortData { ) -> Result { let port_data = match kind { InfoPortKind::Bond => NlasIterator::new(payload) - .map(|nla| nla.and_then(|nla| InfoBondPort::parse(&nla))) + .map(|nla| { + let nla = nla?; + InfoBondPort::parse(&nla) + }) .collect::, _>>() .map(InfoPortData::BondPort), InfoPortKind::Bridge => NlasIterator::new(payload) - .map(|nla| nla.and_then(|nla| InfoBridgePort::parse(&nla))) + .map(|nla| { + let nla = nla?; + InfoBridgePort::parse(&nla) + }) .collect::, _>>() .map(InfoPortData::BridgePort), InfoPortKind::Other(_) => Ok(InfoPortData::Other(payload.to_vec())), diff --git a/src/link/link_info/infos.rs b/src/link/link_info/infos.rs index 87c0afd2..8f78398c 100644 --- a/src/link/link_info/infos.rs +++ b/src/link/link_info/infos.rs @@ -103,6 +103,7 @@ pub(crate) struct VecLinkInfo(pub(crate) Vec); // The downside is that this impl will not be exposed. impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkInfo { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = Vec::new(); let mut link_info_kind: Option = None; @@ -290,6 +291,7 @@ impl Nla for InfoKind { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoKind { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { if buf.kind() != IFLA_INFO_KIND { return Err(format!( diff --git a/src/link/link_info/ipoib.rs b/src/link/link_info/ipoib.rs index e68c57b6..0800f002 100644 --- a/src/link/link_info/ipoib.rs +++ b/src/link/link_info/ipoib.rs @@ -53,6 +53,7 @@ impl Nla for InfoIpoib { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpoib { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpoib::*; let payload = buf.value(); diff --git a/src/link/link_info/ipvlan.rs b/src/link/link_info/ipvlan.rs index a08cc2f2..125ecb32 100644 --- a/src/link/link_info/ipvlan.rs +++ b/src/link/link_info/ipvlan.rs @@ -49,6 +49,7 @@ impl Nla for InfoIpVlan { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVlan { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpVlan::*; let payload = buf.value(); @@ -106,6 +107,7 @@ impl Nla for InfoIpVtap { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVtap { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoIpVtap::*; let payload = buf.value(); diff --git a/src/link/link_info/mac_vlan.rs b/src/link/link_info/mac_vlan.rs index 88d454a1..ad6d7a0f 100644 --- a/src/link/link_info/mac_vlan.rs +++ b/src/link/link_info/mac_vlan.rs @@ -90,6 +90,7 @@ impl Nla for InfoMacVlan { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVlan { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacVlan::*; let payload = buf.value(); @@ -210,6 +211,7 @@ impl Nla for InfoMacVtap { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVtap { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacVtap::*; let payload = buf.value(); diff --git a/src/link/link_info/macsec.rs b/src/link/link_info/macsec.rs index c8d24111..b16b437e 100644 --- a/src/link/link_info/macsec.rs +++ b/src/link/link_info/macsec.rs @@ -213,6 +213,7 @@ impl Nla for InfoMacSec { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacSec { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoMacSec::*; let payload = buf.value(); diff --git a/src/link/link_info/sit.rs b/src/link/link_info/sit.rs index f4977f63..18c16ee2 100644 --- a/src/link/link_info/sit.rs +++ b/src/link/link_info/sit.rs @@ -33,6 +33,7 @@ impl Nla for InfoSitTun { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoSitTun { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/tun.rs b/src/link/link_info/tun.rs index e7326c21..2960c4a2 100644 --- a/src/link/link_info/tun.rs +++ b/src/link/link_info/tun.rs @@ -33,6 +33,7 @@ impl Nla for InfoTun { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoTun { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/veth.rs b/src/link/link_info/veth.rs index e36f698d..1f2b1622 100644 --- a/src/link/link_info/veth.rs +++ b/src/link/link_info/veth.rs @@ -48,6 +48,7 @@ impl Nla for InfoVeth { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVeth { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVeth::*; let payload = buf.value(); diff --git a/src/link/link_info/vlan.rs b/src/link/link_info/vlan.rs index d4c1ab4a..5638e03c 100644 --- a/src/link/link_info/vlan.rs +++ b/src/link/link_info/vlan.rs @@ -109,6 +109,7 @@ impl Nla for VlanQosMapping { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VlanQosMapping { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use VlanQosMapping::*; let payload = buf.value(); @@ -142,6 +143,7 @@ fn parse_mappings(payload: &[u8]) -> Result, DecodeError> { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVlan { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVlan::*; let payload = buf.value(); diff --git a/src/link/link_info/vrf.rs b/src/link/link_info/vrf.rs index a8524e12..bb911f52 100644 --- a/src/link/link_info/vrf.rs +++ b/src/link/link_info/vrf.rs @@ -45,6 +45,7 @@ impl Nla for InfoVrf { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVrf { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoVrf::*; let payload = buf.value(); diff --git a/src/link/link_info/vti.rs b/src/link/link_info/vti.rs index bfe52608..2adadb94 100644 --- a/src/link/link_info/vti.rs +++ b/src/link/link_info/vti.rs @@ -33,6 +33,7 @@ impl Nla for InfoVti { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVti { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { #[allow(clippy::match_single_binding)] Ok(match buf.kind() { diff --git a/src/link/link_info/vxlan.rs b/src/link/link_info/vxlan.rs index 03861717..6bfe598b 100644 --- a/src/link/link_info/vxlan.rs +++ b/src/link/link_info/vxlan.rs @@ -193,6 +193,7 @@ impl Nla for InfoVxlan { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/link_info/xfrm.rs b/src/link/link_info/xfrm.rs index d76ffc42..2dca8c66 100644 --- a/src/link/link_info/xfrm.rs +++ b/src/link/link_info/xfrm.rs @@ -50,6 +50,7 @@ impl Nla for InfoXfrm { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoXfrm { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { use self::InfoXfrm::*; let payload = buf.value(); diff --git a/src/link/link_info/xstats.rs b/src/link/link_info/xstats.rs index 9e5f5f1a..49b3a80f 100644 --- a/src/link/link_info/xstats.rs +++ b/src/link/link_info/xstats.rs @@ -31,6 +31,7 @@ impl Emitable for LinkXstats { impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, &InfoKind> for LinkXstats { + type Error = DecodeError; fn parse_with_param( buf: &NlaBuffer<&'a T>, _kind: &InfoKind, diff --git a/src/link/map.rs b/src/link/map.rs index 425243ae..7b43970d 100644 --- a/src/link/map.rs +++ b/src/link/map.rs @@ -28,6 +28,7 @@ pub struct Map { } impl> Parseable> for Map { + type Error = DecodeError; fn parse(buf: &MapBuffer) -> Result { Ok(Self { memory_start: buf.memory_start(), diff --git a/src/link/message.rs b/src/link/message.rs index b6922a01..4296c9f0 100644 --- a/src/link/message.rs +++ b/src/link/message.rs @@ -32,6 +32,7 @@ impl Emitable for LinkMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for LinkMessage { + type Error = DecodeError; fn parse(buf: &LinkMessageBuffer<&'a T>) -> Result { let header = LinkHeader::parse(buf) .context("failed to parse link message header")?; @@ -47,6 +48,7 @@ impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, AddressFamily> for Vec { + type Error = DecodeError; fn parse_with_param( buf: &LinkMessageBuffer<&'a T>, family: AddressFamily, diff --git a/src/link/phys_id.rs b/src/link/phys_id.rs index 19d8fac2..a9f64716 100644 --- a/src/link/phys_id.rs +++ b/src/link/phys_id.rs @@ -15,6 +15,7 @@ pub struct LinkPhysId { } impl Parseable<[u8]> for LinkPhysId { + type Error = DecodeError; fn parse(buf: &[u8]) -> Result { let len = buf.len() % MAX_PHYS_ITEM_ID_LEN; let mut id = [0; MAX_PHYS_ITEM_ID_LEN]; diff --git a/src/link/prop_list.rs b/src/link/prop_list.rs index 7ba4061b..a9bc64db 100644 --- a/src/link/prop_list.rs +++ b/src/link/prop_list.rs @@ -49,6 +49,7 @@ impl Nla for Prop { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Prop { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/proto_info/bridge.rs b/src/link/proto_info/bridge.rs index 8645cc15..02cafcde 100644 --- a/src/link/proto_info/bridge.rs +++ b/src/link/proto_info/bridge.rs @@ -18,6 +18,7 @@ pub(crate) struct VecLinkProtoInfoBridge(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkProtoInfoBridge { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -54,6 +55,7 @@ impl Nla for LinkProtoInfoBridge { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtoInfoBridge { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(Self::Other(DefaultNla::parse(buf).context(format!( "invalid bridge IFLA_PROTINFO {:?}", diff --git a/src/link/proto_info/inet6.rs b/src/link/proto_info/inet6.rs index e7cad4d0..bbb5c019 100644 --- a/src/link/proto_info/inet6.rs +++ b/src/link/proto_info/inet6.rs @@ -18,6 +18,7 @@ pub(crate) struct VecLinkProtoInfoInet6(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkProtoInfoInet6 { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -54,6 +55,7 @@ impl Nla for LinkProtoInfoInet6 { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkProtoInfoInet6 { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { Ok(Self::Other(DefaultNla::parse(buf).context(format!( "invalid inet6 IFLA_PROTINFO {:?}", diff --git a/src/link/sriov/broadcast.rs b/src/link/sriov/broadcast.rs index e23a1234..9b09245f 100644 --- a/src/link/sriov/broadcast.rs +++ b/src/link/sriov/broadcast.rs @@ -29,6 +29,7 @@ buffer!(VfInfoBroadcastBuffer(VF_INFO_BROADCAST_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoBroadcast { + type Error = DecodeError; fn parse(buf: &VfInfoBroadcastBuffer<&T>) -> Result { Ok(Self::new(buf.addr())) } diff --git a/src/link/sriov/guid.rs b/src/link/sriov/guid.rs index a57af829..07623599 100644 --- a/src/link/sriov/guid.rs +++ b/src/link/sriov/guid.rs @@ -25,6 +25,7 @@ buffer!(VfInfoGuidBuffer(VF_INFO_GUID_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoGuid { + type Error = DecodeError; fn parse(buf: &VfInfoGuidBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.guid())) } diff --git a/src/link/sriov/link_state.rs b/src/link/sriov/link_state.rs index baf3144d..0c0839e5 100644 --- a/src/link/sriov/link_state.rs +++ b/src/link/sriov/link_state.rs @@ -25,6 +25,7 @@ buffer!(VfInfoLinkStateBuffer(VF_INFO_LINK_STATE_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoLinkState { + type Error = DecodeError; fn parse(buf: &VfInfoLinkStateBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.state().into())) } diff --git a/src/link/sriov/mac.rs b/src/link/sriov/mac.rs index 5be6dbce..285f6059 100644 --- a/src/link/sriov/mac.rs +++ b/src/link/sriov/mac.rs @@ -36,6 +36,7 @@ buffer!(VfInfoMacBuffer(VF_INFO_MAC_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoMac { + type Error = DecodeError; fn parse(buf: &VfInfoMacBuffer<&T>) -> Result { Ok(Self::new(buf.vf_id(), buf.mac())) } diff --git a/src/link/sriov/rate.rs b/src/link/sriov/rate.rs index 111e482a..8db89223 100644 --- a/src/link/sriov/rate.rs +++ b/src/link/sriov/rate.rs @@ -31,6 +31,7 @@ buffer!(VfInfoRateBuffer(VF_INFO_RATE_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoRate { + type Error = DecodeError; fn parse(buf: &VfInfoRateBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), diff --git a/src/link/sriov/rss_query.rs b/src/link/sriov/rss_query.rs index d595487c..acfa2ed5 100644 --- a/src/link/sriov/rss_query.rs +++ b/src/link/sriov/rss_query.rs @@ -25,6 +25,7 @@ buffer!(VfInfoRssQueryEnBuffer(VF_INFO_RSS_QUERY_EN_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoRssQueryEn { + type Error = DecodeError; fn parse(buf: &VfInfoRssQueryEnBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), diff --git a/src/link/sriov/spoofchk.rs b/src/link/sriov/spoofchk.rs index d45ae38f..c4fe7417 100644 --- a/src/link/sriov/spoofchk.rs +++ b/src/link/sriov/spoofchk.rs @@ -25,6 +25,7 @@ buffer!(VfInfoSpoofCheckBuffer(VF_INFO_SPOOFCHK_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoSpoofCheck { + type Error = DecodeError; fn parse(buf: &VfInfoSpoofCheckBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), diff --git a/src/link/sriov/stats.rs b/src/link/sriov/stats.rs index 30a14dac..9608b1a1 100644 --- a/src/link/sriov/stats.rs +++ b/src/link/sriov/stats.rs @@ -70,6 +70,7 @@ impl Nla for VfStats { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfStats { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/sriov/trust.rs b/src/link/sriov/trust.rs index 57478d44..299a7890 100644 --- a/src/link/sriov/trust.rs +++ b/src/link/sriov/trust.rs @@ -25,6 +25,7 @@ buffer!(VfInfoTrustBuffer(VF_INFO_TRUST_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoTrust { + type Error = DecodeError; fn parse(buf: &VfInfoTrustBuffer<&T>) -> Result { Ok(Self::new( buf.vf_id(), diff --git a/src/link/sriov/tx_rate.rs b/src/link/sriov/tx_rate.rs index 302f4851..6fb319dd 100644 --- a/src/link/sriov/tx_rate.rs +++ b/src/link/sriov/tx_rate.rs @@ -25,6 +25,7 @@ buffer!(VfInfoTxRateBuffer(VF_INFO_TX_RATE_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoTxRate { + type Error = DecodeError; fn parse(buf: &VfInfoTxRateBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), diff --git a/src/link/sriov/vf_list.rs b/src/link/sriov/vf_list.rs index cd5c631e..24a85223 100644 --- a/src/link/sriov/vf_list.rs +++ b/src/link/sriov/vf_list.rs @@ -23,6 +23,7 @@ pub(crate) struct VecLinkVfInfo(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkVfInfo { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -62,6 +63,7 @@ impl Nla for LinkVfInfo { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkVfInfo { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -168,6 +170,7 @@ impl Nla for VfInfo { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfo { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { diff --git a/src/link/sriov/vf_port.rs b/src/link/sriov/vf_port.rs index ac759e2f..5b63d50b 100644 --- a/src/link/sriov/vf_port.rs +++ b/src/link/sriov/vf_port.rs @@ -12,6 +12,7 @@ pub(crate) struct VecLinkVfPort(pub(crate) Vec); impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkVfPort { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -53,6 +54,7 @@ impl Nla for LinkVfPort { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkVfPort { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(buf.into_inner()) { @@ -111,6 +113,7 @@ impl Nla for VfPort { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfPort { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); #[allow(clippy::match_single_binding)] diff --git a/src/link/sriov/vf_vlan.rs b/src/link/sriov/vf_vlan.rs index 62b3954b..54f84211 100644 --- a/src/link/sriov/vf_vlan.rs +++ b/src/link/sriov/vf_vlan.rs @@ -41,6 +41,7 @@ impl Nla for VfVlan { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfVlan { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { @@ -93,6 +94,7 @@ buffer!(VfVlanInfoBuffer(VF_VLAN_INFO_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfVlanInfo { + type Error = DecodeError; fn parse(buf: &VfVlanInfoBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), diff --git a/src/link/sriov/vlan.rs b/src/link/sriov/vlan.rs index e2f87d5a..284033e7 100644 --- a/src/link/sriov/vlan.rs +++ b/src/link/sriov/vlan.rs @@ -31,6 +31,7 @@ buffer!(VfInfoVlanBuffer(VF_INFO_VLAN_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VfInfoVlan { + type Error = DecodeError; fn parse(buf: &VfInfoVlanBuffer<&T>) -> Result { Ok(Self { vf_id: buf.vf_id(), diff --git a/src/link/stats.rs b/src/link/stats.rs index 7ab476d2..687f6d29 100644 --- a/src/link/stats.rs +++ b/src/link/stats.rs @@ -86,6 +86,7 @@ buffer!(StatsBuffer(LINK_STATS_LEN) { }); impl> Parseable> for Stats { + type Error = DecodeError; fn parse(buf: &StatsBuffer) -> Result { Ok(Self { rx_packets: buf.rx_packets(), diff --git a/src/link/stats64.rs b/src/link/stats64.rs index 5bece142..b9aae4c4 100644 --- a/src/link/stats64.rs +++ b/src/link/stats64.rs @@ -89,6 +89,7 @@ pub struct Stats64 { } impl> Parseable> for Stats64 { + type Error = DecodeError; fn parse(buf: &Stats64Buffer) -> Result { Ok(Self { rx_packets: buf.rx_packets(), diff --git a/src/link/wireless.rs b/src/link/wireless.rs index 65fa628c..8045f01c 100644 --- a/src/link/wireless.rs +++ b/src/link/wireless.rs @@ -8,6 +8,7 @@ use netlink_packet_utils::{DecodeError, Emitable, Parseable}; pub struct LinkWirelessEvent(Vec); impl + ?Sized> Parseable for LinkWirelessEvent { + type Error = DecodeError; fn parse(buf: &T) -> Result { Ok(LinkWirelessEvent(buf.as_ref().to_vec())) } diff --git a/src/link/xdp.rs b/src/link/xdp.rs index 99182a2d..ea717445 100644 --- a/src/link/xdp.rs +++ b/src/link/xdp.rs @@ -92,6 +92,7 @@ impl Nla for LinkXdp { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for LinkXdp { + type Error = DecodeError; fn parse(nla: &NlaBuffer<&'a T>) -> Result { let payload = nla.value(); Ok(match nla.kind() as u32 { @@ -138,6 +139,7 @@ pub(crate) struct VecLinkXdp(pub(crate) Vec); // nla->data[0].type <- nla.kind() // nla->data[0].len impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkXdp { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut res = Vec::new(); let nlas = NlasIterator::new(buf.into_inner()); diff --git a/src/message.rs b/src/message.rs index 3529f07a..caaf41d7 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,25 +1,27 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - DecodeError, Emitable, Parseable, ParseableParametrized, +use crate::{ + address::{ + AddressError, AddressHeader, AddressMessage, AddressMessageBuffer, + }, + link::{LinkMessage, LinkMessageBuffer}, + neighbour::{NeighbourError, NeighbourMessage, NeighbourMessageBuffer}, + neighbour_table::{ + NeighbourTableError, NeighbourTableMessage, NeighbourTableMessageBuffer, + }, + nsid::{NsidError, NsidMessage, NsidMessageBuffer}, + prefix::{PrefixError, PrefixMessage, PrefixMessageBuffer}, + route::{RouteError, RouteHeader, RouteMessage, RouteMessageBuffer}, + rule::{RuleError, RuleMessage, RuleMessageBuffer}, + tc::{TcError, TcMessage, TcMessageBuffer}, }; - use netlink_packet_core::{ NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable, }; - -use crate::{ - address::{AddressHeader, AddressMessage, AddressMessageBuffer}, - link::{LinkMessage, LinkMessageBuffer}, - neighbour::{NeighbourMessage, NeighbourMessageBuffer}, - neighbour_table::{NeighbourTableMessage, NeighbourTableMessageBuffer}, - nsid::{NsidMessage, NsidMessageBuffer}, - prefix::{PrefixMessage, PrefixMessageBuffer}, - route::{RouteHeader, RouteMessage, RouteMessageBuffer}, - rule::{RuleMessage, RuleMessageBuffer}, - tc::{TcMessage, TcMessageBuffer}, +use netlink_packet_utils::{ + DecodeError, Emitable, Parseable, ParseableParametrized, }; +use thiserror::Error; const RTM_NEWLINK: u16 = 16; const RTM_DELLINK: u16 = 17; @@ -81,20 +83,59 @@ const RTM_DELLINKPROP: u16 = 109; buffer!(RouteNetlinkMessageBuffer); +#[derive(Debug, Error)] +pub enum RouteNetlinkMessageParseError { + #[error("Invalid link message")] + InvalidLinkMessage(#[source] DecodeError), + + #[error(transparent)] + InvalidRouteMessage(#[from] RouteError), + + #[error(transparent)] + InvalidAddrMessage(#[from] AddressError), + + #[error(transparent)] + InvalidPrefixMessage(#[from] PrefixError), + + #[error(transparent)] + InvalidFibRuleMessage(#[from] RuleError), + + #[error(transparent)] + InvalidTcMessage(#[from] TcError), + + #[error(transparent)] + InvalidNsidMessage(#[from] NsidError), + + #[error(transparent)] + InvalidNeighbourMessage(#[from] NeighbourError), + + #[error(transparent)] + InvalidNeighbourTableMessage(#[from] NeighbourTableError), + + #[error("Unknown message type: {0}")] + UnknownMessageType(u16), + + #[error("Parse buffer: {0}")] + ParseBuffer(#[source] DecodeError), +} + impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> for RouteNetlinkMessage { + type Error = RouteNetlinkMessageParseError; + fn parse_with_param( buf: &RouteNetlinkMessageBuffer<&'a T>, message_type: u16, - ) -> Result { + ) -> Result { let message = match message_type { // Link messages RTM_NEWLINK | RTM_GETLINK | RTM_DELLINK | RTM_SETLINK => { let msg = match LinkMessageBuffer::new_checked(&buf.inner()) { - Ok(buf) => LinkMessage::parse(&buf) - .context("invalid link message")?, + Ok(buf) => LinkMessage::parse(&buf).map_err( + RouteNetlinkMessageParseError::InvalidLinkMessage, + )?, // HACK: iproute2 sends invalid RTM_GETLINK message, where // the header is limited to the // interface family (1 byte) and 3 bytes of padding. @@ -105,7 +146,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> msg.header.interface_family = buf.inner()[0].into(); msg } else { - return Err(e); + return Err(RouteNetlinkMessageParseError::InvalidLinkMessage(e)); } } }; @@ -122,8 +163,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> RTM_NEWADDR | RTM_GETADDR | RTM_DELADDR => { let msg = match AddressMessageBuffer::new_checked(&buf.inner()) { - Ok(buf) => AddressMessage::parse(&buf) - .context("invalid link message")?, + Ok(buf) => AddressMessage::parse(&buf)?, // HACK: iproute2 sends invalid RTM_GETADDR message, where // the header is limited to the // interface family (1 byte) and 3 bytes of padding. @@ -137,7 +177,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> msg.header.family = buf.inner()[0].into(); msg } else { - return Err(e); + return Err(RouteNetlinkMessageParseError::InvalidAddrMessage(AddressError::FailedBufferInit(e))); } } }; @@ -151,12 +191,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> // Neighbour messages RTM_NEWNEIGH | RTM_GETNEIGH | RTM_DELNEIGH => { - let err = "invalid neighbour message"; - let msg = NeighbourMessage::parse( - &NeighbourMessageBuffer::new_checked(&buf.inner()) - .context(err)?, - ) - .context(err)?; + let buf_inner = buf.inner(); + let buffer = NeighbourMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + let msg = NeighbourMessage::parse(&buffer)?; match message_type { RTM_GETNEIGH => RouteNetlinkMessage::GetNeighbour(msg), RTM_NEWNEIGH => RouteNetlinkMessage::NewNeighbour(msg), @@ -167,12 +205,13 @@ impl<'a, T: AsRef<[u8]> + ?Sized> // Neighbour table messages RTM_NEWNEIGHTBL | RTM_GETNEIGHTBL | RTM_SETNEIGHTBL => { - let err = "invalid neighbour table message"; - let msg = NeighbourTableMessage::parse( - &NeighbourTableMessageBuffer::new_checked(&buf.inner()) - .context(err)?, - ) - .context(err)?; + let buf_inner = buf.inner(); + let buffer = + NeighbourTableMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + let msg = NeighbourTableMessage::parse(&buffer).map_err( + RouteNetlinkMessageParseError::InvalidNeighbourTableMessage, + )?; match message_type { RTM_GETNEIGHTBL => { RouteNetlinkMessage::GetNeighbourTable(msg) @@ -190,8 +229,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> // Route messages RTM_NEWROUTE | RTM_GETROUTE | RTM_DELROUTE => { let msg = match RouteMessageBuffer::new_checked(&buf.inner()) { - Ok(buf) => RouteMessage::parse(&buf) - .context("invalid route message")?, + Ok(buf) => RouteMessage::parse(&buf)?, // HACK: iproute2 sends invalid RTM_GETROUTE message, where // the header is limited to the // interface family (1 byte) and 3 bytes of padding. @@ -214,7 +252,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> msg.header.address_family = buf.inner()[0].into(); msg } else { - return Err(e); + return Err( + RouteNetlinkMessageParseError::ParseBuffer(e), + ); } } }; @@ -228,23 +268,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> // Prefix messages RTM_NEWPREFIX => { - let err = "invalid prefix message"; - RouteNetlinkMessage::NewPrefix( - PrefixMessage::parse( - &PrefixMessageBuffer::new_checked(&buf.inner()) - .context(err)?, - ) - .context(err)?, - ) + let buf_inner = buf.inner(); + let buffer = PrefixMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + RouteNetlinkMessage::NewPrefix(PrefixMessage::parse(&buffer)?) } - RTM_NEWRULE | RTM_GETRULE | RTM_DELRULE => { - let err = "invalid fib rule message"; - let msg = RuleMessage::parse( - &RuleMessageBuffer::new_checked(&buf.inner()) - .context(err)?, - ) - .context(err)?; + let buf_inner = buf.inner(); + let buffer = RuleMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + let msg = RuleMessage::parse(&buffer)?; match message_type { RTM_NEWRULE => RouteNetlinkMessage::NewRule(msg), RTM_DELRULE => RouteNetlinkMessage::DelRule(msg), @@ -257,11 +290,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> | RTM_DELTCLASS | RTM_GETTCLASS | RTM_NEWTFILTER | RTM_DELTFILTER | RTM_GETTFILTER | RTM_NEWCHAIN | RTM_DELCHAIN | RTM_GETCHAIN => { - let err = "invalid tc message"; - let msg = TcMessage::parse( - &TcMessageBuffer::new_checked(&buf.inner()).context(err)?, - ) - .context(err)?; + let buf_inner = buf.inner(); + let buffer = TcMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + let msg = TcMessage::parse(&buffer)?; match message_type { RTM_NEWQDISC => { RouteNetlinkMessage::NewQueueDiscipline(msg) @@ -293,12 +325,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> // ND ID Messages RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => { - let err = "invalid nsid message"; - let msg = NsidMessage::parse( - &NsidMessageBuffer::new_checked(&buf.inner()) - .context(err)?, - ) - .context(err)?; + let buf_inner = buf.inner(); + let buffer = NsidMessageBuffer::new_checked(&buf_inner) + .map_err(RouteNetlinkMessageParseError::ParseBuffer)?; + let msg = NsidMessage::parse(&buffer)?; match message_type { RTM_NEWNSID => RouteNetlinkMessage::NewNsId(msg), RTM_DELNSID => RouteNetlinkMessage::DelNsId(msg), @@ -308,9 +338,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> } _ => { - return Err( - format!("Unknown message type: {message_type}").into() - ) + return Err(RouteNetlinkMessageParseError::UnknownMessageType( + message_type, + )) } }; Ok(message) @@ -678,7 +708,8 @@ impl NetlinkSerializable for RouteNetlinkMessage { } impl NetlinkDeserializable for RouteNetlinkMessage { - type Error = DecodeError; + type Error = RouteNetlinkMessageParseError; + fn deserialize( header: &NetlinkHeader, payload: &[u8], diff --git a/src/neighbour/attribute.rs b/src/neighbour/attribute.rs index ec871eda..48d7f50c 100644 --- a/src/neighbour/attribute.rs +++ b/src/neighbour/attribute.rs @@ -1,16 +1,17 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + NeighbourAddress, NeighbourCacheInfo, NeighbourCacheInfoBuffer, + NeighbourError, +}; +use crate::{route::RouteProtocol, AddressFamily}; use byteorder::{BigEndian, ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u16_be, parse_u32}, - DecodeError, Emitable, Parseable, ParseableParametrized, + Emitable, Parseable, ParseableParametrized, }; -use super::{NeighbourAddress, NeighbourCacheInfo, NeighbourCacheInfoBuffer}; -use crate::{route::RouteProtocol, AddressFamily}; - const NDA_DST: u16 = 1; const NDA_LLADDR: u16 = 2; const NDA_CACHEINFO: u16 = 3; @@ -106,57 +107,93 @@ impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, AddressFamily> for NeighbourAttribute { + type Error = NeighbourError; fn parse_with_param( buf: &NlaBuffer<&'a T>, address_family: AddressFamily, - ) -> Result { + ) -> Result { let payload = buf.value(); Ok(match buf.kind() { NDA_DST => Self::Destination( NeighbourAddress::parse_with_param(address_family, payload) - .context(format!("invalid NDA_DST value {:?}", payload))?, + .map_err(|error| NeighbourError::InvalidValue { + kind: "NDA_DST", + error, + })?, ), NDA_LLADDR => Self::LinkLocalAddress(payload.to_vec()), NDA_CACHEINFO => Self::CacheInfo( - NeighbourCacheInfo::parse( - &NeighbourCacheInfoBuffer::new_checked(payload).context( - format!("invalid NDA_CACHEINFO value {:?}", payload), - )?, - ) - .context(format!( - "invalid NDA_CACHEINFO value {:?}", - payload - ))?, + NeighbourCacheInfoBuffer::new_checked(payload) + .and_then(|buffer| NeighbourCacheInfo::parse(&buffer)) + .map_err(|error| NeighbourError::InvalidValue { + kind: "NDA_CACHEINFO", + error, + })?, ), NDA_PROBES => { - Self::Probes(parse_u32(payload).context(format!( - "invalid NDA_PROBES value {:?}", - payload - ))?) + Self::Probes(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_PROBES", + error, + } + })?) } - NDA_VLAN => Self::Vlan(parse_u16(payload)?), - NDA_PORT => Self::Port( - parse_u16_be(payload) - .context(format!("invalid NDA_PORT value {payload:?}"))?, - ), - NDA_VNI => Self::Vni(parse_u32(payload)?), - NDA_IFINDEX => Self::IfIndex(parse_u32(payload)?), - NDA_CONTROLLER => Self::Controller(parse_u32(payload).context( - format!("invalid NDA_CONTROLLER value {payload:?}"), - )?), - NDA_LINK_NETNSID => Self::LinkNetNsId(parse_u32(payload).context( - format!("invalid NDA_LINK_NETNSID value {payload:?}"), - )?), - NDA_SRC_VNI => Self::SourceVni(parse_u32(payload)?), - NDA_PROTOCOL => { - Self::Protocol(RouteProtocol::parse(payload).context( - format!("invalid NDA_PROTOCOL value {:?}", payload), - )?) + NDA_VLAN => Self::Vlan(parse_u16(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_VLAN", + error, + } + })?), + NDA_PORT => Self::Port(parse_u16_be(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_PORT", + error, + } + })?), + NDA_VNI => Self::Vni(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_VNI", + error, + } + })?), + NDA_IFINDEX => { + Self::IfIndex(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_IFINDEX", + error, + } + })?) + } + NDA_CONTROLLER => { + Self::Controller(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_CONTROLLER", + error, + } + })?) + } + NDA_LINK_NETNSID => { + Self::LinkNetNsId(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_LINK_NETNSID", + error, + } + })?) + } + NDA_SRC_VNI => { + Self::SourceVni(parse_u32(payload).map_err(|error| { + NeighbourError::InvalidValue { + kind: "NDA_SRC_VNI", + error, + } + })?) + } + NDA_PROTOCOL => Self::Protocol(RouteProtocol::parse(payload)?), + kind => { + Self::Other(DefaultNla::parse(buf).map_err(|error| { + NeighbourError::UnknownNLA { kind, error } + })?) } - _ => Self::Other( - DefaultNla::parse(buf) - .context("invalid link NLA value (unknown type)")?, - ), }) } } diff --git a/src/neighbour/cache_info.rs b/src/neighbour/cache_info.rs index 86c61865..75066c41 100644 --- a/src/neighbour/cache_info.rs +++ b/src/neighbour/cache_info.rs @@ -26,6 +26,7 @@ buffer!(NeighbourCacheInfoBuffer(NEIGHBOUR_CACHE_INFO_LEN) { impl> Parseable> for NeighbourCacheInfo { + type Error = DecodeError; fn parse(buf: &NeighbourCacheInfoBuffer) -> Result { Ok(Self { confirmed: buf.confirmed(), diff --git a/src/neighbour/error.rs b/src/neighbour/error.rs new file mode 100644 index 00000000..3decd82c --- /dev/null +++ b/src/neighbour/error.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +use crate::route::RouteError; +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NeighbourError { + #[error("Invalid {kind}")] + InvalidValue { + kind: &'static str, + error: DecodeError, + }, + + #[error("Unknown NLA type: {kind}")] + UnknownNLA { kind: u16, error: DecodeError }, + + #[error(transparent)] + ParseNdaProtocol(#[from] RouteError), + + #[error(transparent)] + ParseNla(#[from] NlaError), +} diff --git a/src/neighbour/header.rs b/src/neighbour/header.rs index f12fab69..c915143a 100644 --- a/src/neighbour/header.rs +++ b/src/neighbour/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; @@ -23,7 +23,7 @@ buffer!(NeighbourMessageBuffer(NEIGHBOUR_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -58,7 +58,8 @@ pub struct NeighbourHeader { } impl> Parseable> for NeighbourHeader { - fn parse(buf: &NeighbourMessageBuffer) -> Result { + type Error = (); + fn parse(buf: &NeighbourMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), ifindex: buf.ifindex(), diff --git a/src/neighbour/message.rs b/src/neighbour/message.rs index f5908584..617a723d 100644 --- a/src/neighbour/message.rs +++ b/src/neighbour/message.rs @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, -}; - use super::{ - super::AddressFamily, NeighbourAttribute, NeighbourHeader, + super::AddressFamily, NeighbourAttribute, NeighbourError, NeighbourHeader, NeighbourMessageBuffer, }; +use netlink_packet_utils::traits::{ + Emitable, Parseable, ParseableParametrized, +}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] @@ -34,17 +31,19 @@ impl Emitable for NeighbourMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NeighbourMessage { - fn parse(buf: &NeighbourMessageBuffer<&'a T>) -> Result { - let header = NeighbourHeader::parse(buf) - .context("failed to parse neighbour message header")?; + type Error = NeighbourError; + fn parse( + buf: &NeighbourMessageBuffer<&'a T>, + ) -> Result { + // unwrap: parsing the header is always ok. + let header = NeighbourHeader::parse(buf).unwrap(); let address_family = header.family; Ok(NeighbourMessage { header, attributes: Vec::::parse_with_param( buf, address_family, - ) - .context("failed to parse neighbour message NLAs")?, + )?, }) } } @@ -53,10 +52,11 @@ impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, AddressFamily> for Vec { + type Error = NeighbourError; fn parse_with_param( buf: &NeighbourMessageBuffer<&'a T>, address_family: AddressFamily, - ) -> Result { + ) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NeighbourAttribute::parse_with_param( diff --git a/src/neighbour/mod.rs b/src/neighbour/mod.rs index 9b8c105e..88366970 100644 --- a/src/neighbour/mod.rs +++ b/src/neighbour/mod.rs @@ -3,6 +3,7 @@ mod address; mod attribute; mod cache_info; +mod error; mod flags; mod header; mod message; @@ -14,6 +15,7 @@ mod tests; pub use self::address::NeighbourAddress; pub use self::attribute::NeighbourAttribute; pub use self::cache_info::{NeighbourCacheInfo, NeighbourCacheInfoBuffer}; +pub use self::error::NeighbourError; pub use self::flags::NeighbourFlags; pub use self::header::{NeighbourHeader, NeighbourMessageBuffer}; pub use self::message::NeighbourMessage; diff --git a/src/neighbour_table/attribute.rs b/src/neighbour_table/attribute.rs index 45326e26..1b9ce6ae 100644 --- a/src/neighbour_table/attribute.rs +++ b/src/neighbour_table/attribute.rs @@ -1,17 +1,15 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + param::VecNeighbourTableParameter, NeighbourTableConfig, + NeighbourTableConfigBuffer, NeighbourTableError, NeighbourTableParameter, + NeighbourTableStats, NeighbourTableStatsBuffer, +}; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u64}, - DecodeError, Emitable, Parseable, -}; - -use super::{ - param::VecNeighbourTableParameter, NeighbourTableConfig, - NeighbourTableConfigBuffer, NeighbourTableParameter, NeighbourTableStats, - NeighbourTableStatsBuffer, + Emitable, Parseable, }; const NDTA_NAME: u16 = 1; @@ -90,47 +88,88 @@ impl Nla for NeighbourTableAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NeighbourTableAttribute { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = NeighbourTableError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - NDTA_NAME => Self::Name( - parse_string(payload).context("invalid NDTA_NAME value")?, - ), + NDTA_NAME => { + Self::Name(parse_string(payload).map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_NAME", + error, + } + })?) + } NDTA_CONFIG => Self::Config( NeighbourTableConfig::parse( - &NeighbourTableConfigBuffer::new_checked(payload) - .context(format!("invalid NDTA_CONFIG {payload:?}"))?, + &NeighbourTableConfigBuffer::new_checked(payload).map_err( + |error| NeighbourTableError::InvalidValue { + kind: "NDTA_CONFIG", + error, + }, + )?, ) - .context(format!("invalid NDTA_CONFIG {payload:?}"))?, + .map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_CONFIG", + error, + } + })?, ), NDTA_STATS => Self::Stats( NeighbourTableStats::parse( - &NeighbourTableStatsBuffer::new_checked(payload) - .context(format!("invalid NDTA_STATS {payload:?}"))?, + &NeighbourTableStatsBuffer::new_checked(payload).map_err( + |error| NeighbourTableError::InvalidValue { + kind: "NDTA_STATS", + error, + }, + )?, ) - .context(format!("invalid NDTA_STATS {payload:?}"))?, + .map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_STATS", + error, + } + })?, ), NDTA_PARMS => Self::Parms( - VecNeighbourTableParameter::parse(&NlaBuffer::new(payload)) - .context(format!("invalid NDTA_PARMS {payload:?}"))? - .0, - ), - NDTA_GC_INTERVAL => Self::GcInterval( - parse_u64(payload).context("invalid NDTA_GC_INTERVAL value")?, - ), - NDTA_THRESH1 => Self::Threshold1( - parse_u32(payload).context("invalid NDTA_THRESH1 value")?, - ), - NDTA_THRESH2 => Self::Threshold2( - parse_u32(payload).context("invalid NDTA_THRESH2 value")?, - ), - NDTA_THRESH3 => Self::Threshold3( - parse_u32(payload).context("invalid NDTA_THRESH3 value")?, - ), - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, + VecNeighbourTableParameter::parse(&NlaBuffer::new(payload))?.0, ), + NDTA_GC_INTERVAL => { + Self::GcInterval(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_GC_INTERVAL", + error, + } + })?) + } + NDTA_THRESH1 => { + Self::Threshold1(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_THRESH1", + error, + } + })?) + } + NDTA_THRESH2 => { + Self::Threshold2(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_THRESH2", + error, + } + })?) + } + NDTA_THRESH3 => { + Self::Threshold3(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidValue { + kind: "NDTA_THRESH3", + error, + } + })?) + } + kind => Self::Other(DefaultNla::parse(buf).map_err(|error| { + NeighbourTableError::UnknownNla { kind, error } + })?), }) } } diff --git a/src/neighbour_table/config.rs b/src/neighbour_table/config.rs index 6e625333..6e137e63 100644 --- a/src/neighbour_table/config.rs +++ b/src/neighbour_table/config.rs @@ -36,6 +36,7 @@ buffer!(NeighbourTableConfigBuffer(CONFIG_LEN) { impl> Parseable> for NeighbourTableConfig { + type Error = DecodeError; fn parse(buf: &NeighbourTableConfigBuffer) -> Result { Ok(Self { key_len: buf.key_len(), diff --git a/src/neighbour_table/error.rs b/src/neighbour_table/error.rs new file mode 100644 index 00000000..3b0b3843 --- /dev/null +++ b/src/neighbour_table/error.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NeighbourTableError { + #[error("Invalid {kind}")] + InvalidValue { + kind: &'static str, + error: DecodeError, + }, + + #[error("Invalid {kind} value")] + InvalidParameter { + kind: &'static str, + error: DecodeError, + }, + + #[error("Unknown NLA type: {kind}")] + UnknownNla { kind: u16, error: DecodeError }, + + #[error(transparent)] + ParseNla(#[from] NlaError), +} diff --git a/src/neighbour_table/header.rs b/src/neighbour_table/header.rs index 1f41d838..1387ca27 100644 --- a/src/neighbour_table/header.rs +++ b/src/neighbour_table/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; @@ -18,7 +18,7 @@ buffer!(NeighbourTableMessageBuffer(NEIGHBOUR_TABLE_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourTableMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -32,9 +32,8 @@ pub struct NeighbourTableHeader { impl> Parseable> for NeighbourTableHeader { - fn parse( - buf: &NeighbourTableMessageBuffer, - ) -> Result { + type Error = (); + fn parse(buf: &NeighbourTableMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), }) diff --git a/src/neighbour_table/message.rs b/src/neighbour_table/message.rs index 9dbdb11d..3796d6c3 100644 --- a/src/neighbour_table/message.rs +++ b/src/neighbour_table/message.rs @@ -1,14 +1,10 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable}, - DecodeError, -}; - use super::{ - NeighbourTableAttribute, NeighbourTableHeader, NeighbourTableMessageBuffer, + NeighbourTableAttribute, NeighbourTableError, NeighbourTableHeader, + NeighbourTableMessageBuffer, }; +use netlink_packet_utils::traits::{Emitable, Parseable}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] @@ -33,14 +29,14 @@ impl Emitable for NeighbourTableMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NeighbourTableMessage { + type Error = NeighbourTableError; fn parse( buf: &NeighbourTableMessageBuffer<&'a T>, - ) -> Result { + ) -> Result { Ok(NeighbourTableMessage { - header: NeighbourTableHeader::parse(buf) - .context("failed to parse neighbour table message header")?, - attributes: Vec::::parse(buf) - .context("failed to parse neighbour table message NLAs")?, + // unwrap: we always succeed at parsing the header + header: NeighbourTableHeader::parse(buf).unwrap(), + attributes: Vec::::parse(buf)?, }) } } @@ -48,9 +44,10 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { + type Error = NeighbourTableError; fn parse( buf: &NeighbourTableMessageBuffer<&'a T>, - ) -> Result { + ) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NeighbourTableAttribute::parse(&nla_buf?)?); diff --git a/src/neighbour_table/mod.rs b/src/neighbour_table/mod.rs index 51f6f8c2..26bfb6a6 100644 --- a/src/neighbour_table/mod.rs +++ b/src/neighbour_table/mod.rs @@ -2,6 +2,7 @@ mod attribute; mod config; +mod error; mod header; mod message; pub(crate) mod param; @@ -11,6 +12,7 @@ mod tests; pub use self::attribute::NeighbourTableAttribute; pub use self::config::{NeighbourTableConfig, NeighbourTableConfigBuffer}; +pub use self::error::NeighbourTableError; pub use self::header::{NeighbourTableHeader, NeighbourTableMessageBuffer}; pub use self::message::NeighbourTableMessage; pub use self::param::NeighbourTableParameter; diff --git a/src/neighbour_table/param.rs b/src/neighbour_table/param.rs index 91cd791c..6008c59a 100644 --- a/src/neighbour_table/param.rs +++ b/src/neighbour_table/param.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::NeighbourTableError; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_u32, parse_u64}, - DecodeError, Parseable, + Parseable, }; const NDTPA_IFINDEX: u16 = 1; @@ -135,92 +135,160 @@ impl Nla for NeighbourTableParameter { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NeighbourTableParameter { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = NeighbourTableError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { NDTPA_IFINDEX => { - Self::Ifindex(parse_u32(payload).context(format!( - "invalid NDTPA_IFINDEX value {payload:?}" - ))?) + Self::Ifindex(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_IFINDEX", + error, + } + })?) } NDTPA_REFCNT => { - Self::ReferenceCount(parse_u32(payload).context(format!( - "invalid NDTPA_REFCNT value {payload:?}" - ))?) + Self::ReferenceCount(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_REFCNT", + error, + } + })?) } NDTPA_REACHABLE_TIME => { - Self::ReachableTime(parse_u64(payload).context(format!( - "invalid NDTPA_REACHABLE_TIME value {payload:?}" - ))?) + Self::ReachableTime(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_REACHABLE_TIME", + error, + } + })?) } NDTPA_BASE_REACHABLE_TIME => { - Self::BaseReachableTime(parse_u64(payload).context(format!( - "invalid NDTPA_BASE_REACHABLE_TIME value {payload:?}" - ))?) + Self::BaseReachableTime(parse_u64(payload).map_err( + |error| NeighbourTableError::InvalidParameter { + kind: "NDTPA_BASE_REACHABLE_TIME", + error, + }, + )?) } NDTPA_RETRANS_TIME => { - Self::RetransTime(parse_u64(payload).context(format!( - "invalid NDTPA_RETRANS_TIME value {payload:?}" - ))?) + Self::RetransTime(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_RETRANS_TIME", + error, + } + })?) } NDTPA_GC_STALETIME => { - Self::GcStaletime(parse_u64(payload).context(format!( - "invalid NDTPA_GC_STALE_TIME value {payload:?}" - ))?) + Self::GcStaletime(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_GC_STALETIME", + error, + } + })?) } NDTPA_DELAY_PROBE_TIME => { - Self::DelayProbeTime(parse_u64(payload).context(format!( - "invalid NDTPA_DELAY_PROBE_TIME value {payload:?}" - ))?) - } - NDTPA_QUEUE_LEN => Self::QueueLen(parse_u32(payload).context( - format!("invalid NDTPA_QUEUE_LEN value {payload:?}"), - )?), - NDTPA_APP_PROBES => Self::AppProbes(parse_u32(payload).context( - format!("invalid NDTPA_APP_PROBES value {payload:?}"), - )?), + Self::DelayProbeTime(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_DELAY_PROBE_TIME", + error, + } + })?) + } + NDTPA_QUEUE_LEN => { + Self::QueueLen(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_QUEUE_LEN", + error, + } + })?) + } + NDTPA_APP_PROBES => { + Self::AppProbes(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_APP_PROBES", + error, + } + })?) + } NDTPA_UCAST_PROBES => { - Self::UcastProbes(parse_u32(payload).context(format!( - "invalid NDTPA_UCAST_PROBES value {payload:?}" - ))?) + Self::UcastProbes(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_UCAST_PROBES", + error, + } + })?) } NDTPA_MCAST_PROBES => { - Self::McastProbes(parse_u32(payload).context(format!( - "invalid NDTPA_MCAST_PROBES value {payload:?}" - ))?) + Self::McastProbes(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_MCAST_PROBES", + error, + } + })?) } NDTPA_ANYCAST_DELAY => { - Self::AnycastDelay(parse_u64(payload).context(format!( - "invalid NDTPA_ANYCAST_DELAY value {payload:?}" - ))?) - } - NDTPA_PROXY_DELAY => Self::ProxyDelay(parse_u64(payload).context( - format!("invalid NDTPA_PROXY_DELAY value {payload:?}"), - )?), - NDTPA_PROXY_QLEN => Self::ProxyQlen(parse_u32(payload).context( - format!("invalid NDTPA_PROXY_QLEN value {payload:?}"), - )?), - NDTPA_LOCKTIME => Self::Locktime(parse_u64(payload).context( - format!("invalid NDTPA_LOCKTIME value {payload:?}"), - )?), + Self::AnycastDelay(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_ANYCAST_DELAY", + error, + } + })?) + } + NDTPA_PROXY_DELAY => { + Self::ProxyDelay(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_PROXY_DELAY", + error, + } + })?) + } + NDTPA_PROXY_QLEN => { + Self::ProxyQlen(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_PROXY_QLEN", + error, + } + })?) + } + NDTPA_LOCKTIME => { + Self::Locktime(parse_u64(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_LOCKTIME", + error, + } + })?) + } NDTPA_QUEUE_LENBYTES => { - Self::QueueLenbytes(parse_u32(payload).context(format!( - "invalid NDTPA_QUEUE_LENBYTES value {payload:?}" - ))?) + Self::QueueLenbytes(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_QUEUE_LENBYTES", + error, + } + })?) } NDTPA_MCAST_REPROBES => { - Self::McastReprobes(parse_u32(payload).context(format!( - "invalid NDTPA_MCAST_PROBES value {payload:?}" - ))?) - } - NDTPA_INTERVAL_PROBE_TIME_MS => Self::IntervalProbeTimeMs( - parse_u64(payload).context(format!( - "invalid NDTPA_INTERVAL_PROBE_TIME_MS value {payload:?}" - ))?, - ), - _ => Self::Other(DefaultNla::parse(buf).context(format!( - "invalid NDTA_PARMS attribute {payload:?}" - ))?), + Self::McastReprobes(parse_u32(payload).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTPA_MCAST_REPROBES", + error, + } + })?) + } + NDTPA_INTERVAL_PROBE_TIME_MS => { + Self::IntervalProbeTimeMs(parse_u64(payload).map_err( + |error| NeighbourTableError::InvalidParameter { + kind: "NDTPA_INTERVAL_PROBE_TIME_MS", + error, + }, + )?) + } + _ => Self::Other(DefaultNla::parse(buf).map_err(|error| { + NeighbourTableError::InvalidParameter { + kind: "NDTA_PARMS", + error, + } + })?), }) } } @@ -233,12 +301,12 @@ pub(crate) struct VecNeighbourTableParameter( impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecNeighbourTableParameter { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = NeighbourTableError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut nlas = vec![]; - let err = "invalid NDTA_PARMS attribute"; for nla in NlasIterator::new(buf.into_inner()) { - let nla = nla.context(err)?; - nlas.push(NeighbourTableParameter::parse(&nla).context(err)?); + let nla = nla?; + nlas.push(NeighbourTableParameter::parse(&nla)?); } Ok(Self(nlas)) } diff --git a/src/neighbour_table/stats.rs b/src/neighbour_table/stats.rs index a99bf6ff..ab08d96b 100644 --- a/src/neighbour_table/stats.rs +++ b/src/neighbour_table/stats.rs @@ -39,6 +39,7 @@ buffer!(NeighbourTableStatsBuffer(STATS_LEN) { impl> Parseable> for NeighbourTableStats { + type Error = DecodeError; fn parse(buf: &NeighbourTableStatsBuffer) -> Result { Ok(Self { allocs: buf.allocs(), diff --git a/src/nsid/attribute.rs b/src/nsid/attribute.rs index 487e6f3e..b04f88c2 100644 --- a/src/nsid/attribute.rs +++ b/src/nsid/attribute.rs @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::NsidError; use byteorder::{ByteOrder, NativeEndian}; - use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_i32, parse_u32}, traits::Parseable, - DecodeError, }; const NETNSA_NSID: u16 = 1; @@ -65,27 +63,47 @@ impl Nla for NsidAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for NsidAttribute { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = NsidError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - NETNSA_NSID => { - Self::Id(parse_i32(payload).context("invalid NETNSA_NSID")?) - } - NETNSA_PID => { - Self::Pid(parse_u32(payload).context("invalid NETNSA_PID")?) + NETNSA_NSID => Self::Id(parse_i32(payload).map_err(|error| { + NsidError::InvalidValue { + kind: "NETNSA_NSID", + error, + } + })?), + NETNSA_PID => Self::Pid(parse_u32(payload).map_err(|error| { + NsidError::InvalidValue { + kind: "NETNSA_PID", + error, + } + })?), + NETNSA_FD => Self::Fd(parse_u32(payload).map_err(|error| { + NsidError::InvalidValue { + kind: "NETNSA_FD", + error, + } + })?), + NETNSA_TARGET_NSID => { + Self::TargetNsid(parse_i32(payload).map_err(|error| { + NsidError::InvalidValue { + kind: "NETNSA_TARGET_NSID", + error, + } + })?) } - NETNSA_FD => { - Self::Fd(parse_u32(payload).context("invalid NETNSA_FD")?) + NETNSA_CURRENT_NSID => { + Self::CurrentNsid(parse_i32(payload).map_err(|error| { + NsidError::InvalidValue { + kind: "NETNSA_CURRENT_NSID", + error, + } + })?) } - NETNSA_TARGET_NSID => Self::TargetNsid( - parse_i32(payload).context("invalid NETNSA_TARGET_NSID")?, - ), - NETNSA_CURRENT_NSID => Self::CurrentNsid( - parse_i32(payload).context("invalid NETNSA_CURRENT_NSID")?, - ), kind => Self::Other( DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, + .map_err(|error| NsidError::UnknownNLA { kind, error })?, ), }) } diff --git a/src/nsid/error.rs b/src/nsid/error.rs new file mode 100644 index 00000000..d6608b33 --- /dev/null +++ b/src/nsid/error.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NsidError { + #[error("Invalid {kind}")] + InvalidValue { + kind: &'static str, + error: DecodeError, + }, + + #[error("Unknown NLA type: {kind}")] + UnknownNLA { kind: u16, error: DecodeError }, + + #[error(transparent)] + ParseNla(#[from] NlaError), +} diff --git a/src/nsid/header.rs b/src/nsid/header.rs index 7b43b736..f8b6e923 100644 --- a/src/nsid/header.rs +++ b/src/nsid/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, DecodeError, Emitable, Parseable, }; @@ -17,7 +17,7 @@ buffer!(NsidMessageBuffer(NSID_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> NsidMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -39,7 +39,8 @@ impl Emitable for NsidHeader { } impl> Parseable> for NsidHeader { - fn parse(buf: &NsidMessageBuffer) -> Result { + type Error = (); + fn parse(buf: &NsidMessageBuffer) -> Result { Ok(NsidHeader { family: buf.family().into(), }) diff --git a/src/nsid/message.rs b/src/nsid/message.rs index e6269f76..855541b4 100644 --- a/src/nsid/message.rs +++ b/src/nsid/message.rs @@ -1,12 +1,7 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable}, - DecodeError, -}; - -use crate::nsid::{NsidAttribute, NsidHeader, NsidMessageBuffer}; +use crate::nsid::{NsidAttribute, NsidError, NsidHeader, NsidMessageBuffer}; +use netlink_packet_utils::traits::{Emitable, Parseable}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] @@ -18,12 +13,12 @@ pub struct NsidMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for NsidMessage { - fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { + type Error = NsidError; + fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { Ok(Self { - header: NsidHeader::parse(buf) - .context("failed to parse nsid message header")?, - attributes: Vec::::parse(buf) - .context("failed to parse nsid message NLAs")?, + // unwrap: parsing the header can't fail + header: NsidHeader::parse(buf).unwrap(), + attributes: Vec::::parse(buf)?, }) } } @@ -31,7 +26,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { - fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { + type Error = NsidError; + fn parse(buf: &NsidMessageBuffer<&'a T>) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(NsidAttribute::parse(&nla_buf?)?); diff --git a/src/nsid/mod.rs b/src/nsid/mod.rs index 1b7f7b8e..0c07d217 100644 --- a/src/nsid/mod.rs +++ b/src/nsid/mod.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: MIT mod attribute; +mod error; mod header; mod message; #[cfg(test)] mod tests; pub use self::attribute::NsidAttribute; +pub use self::error::NsidError; pub use self::header::{NsidHeader, NsidMessageBuffer}; pub use self::message::NsidMessage; diff --git a/src/prefix/attribute.rs b/src/prefix/attribute.rs index 1c129ca4..dc3e2017 100644 --- a/src/prefix/attribute.rs +++ b/src/prefix/attribute.rs @@ -1,15 +1,15 @@ // SPDX-License-Identifier: MIT -use std::net::Ipv6Addr; - -use anyhow::Context; +use super::{ + cache_info::{CacheInfo, CacheInfoBuffer}, + error::PrefixError, +}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, traits::Parseable, - DecodeError, Emitable, + Emitable, }; - -use super::cache_info::{CacheInfo, CacheInfoBuffer}; +use std::net::Ipv6Addr; const PREFIX_ADDRESS: u16 = 1; const PREFIX_CACHEINFO: u16 = 2; @@ -50,22 +50,26 @@ impl nla::Nla for PrefixAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for PrefixAttribute { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = PrefixError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); match buf.kind() { PREFIX_ADDRESS => { if let Ok(payload) = TryInto::<[u8; 16]>::try_into(payload) { Ok(Self::Address(Ipv6Addr::from(payload))) } else { - Err(DecodeError::from(format!("Invalid PREFIX_ADDRESS, unexpected payload length: {:?}", payload))) + Err(PrefixError::InvalidPrefixAddress { + payload_length: payload.len(), + }) } } PREFIX_CACHEINFO => Ok(Self::CacheInfo( - CacheInfo::parse(&CacheInfoBuffer::new(payload)).context( - format!("Invalid PREFIX_CACHEINFO: {:?}", payload), - )?, + CacheInfo::parse(&CacheInfoBuffer::new(payload)) + .map_err(PrefixError::InvalidPrefixCacheInfo)?, + )), + _ => Ok(Self::Other( + DefaultNla::parse(buf).map_err(PrefixError::Other)?, )), - _ => Ok(Self::Other(DefaultNla::parse(buf)?)), } } } diff --git a/src/prefix/cache_info.rs b/src/prefix/cache_info.rs index b2dc2bb9..4e090909 100644 --- a/src/prefix/cache_info.rs +++ b/src/prefix/cache_info.rs @@ -17,6 +17,7 @@ buffer!(CacheInfoBuffer(CACHE_INFO_LEN) { }); impl> Parseable> for CacheInfo { + type Error = DecodeError; fn parse(buf: &CacheInfoBuffer) -> Result { Ok(CacheInfo { preferred_time: buf.preferred_time(), diff --git a/src/prefix/error.rs b/src/prefix/error.rs new file mode 100644 index 00000000..c3107338 --- /dev/null +++ b/src/prefix/error.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum PrefixError { + #[error( + "Invalid PREFIX_ADDRESS, unexpected payload length: {payload_length}" + )] + InvalidPrefixAddress { payload_length: usize }, + + #[error("Invalid PREFIX_CACHEINFO: {0:?}")] + InvalidPrefixCacheInfo(DecodeError), + + #[error(transparent)] + ParseNla(#[from] NlaError), + + #[error(transparent)] + Other(#[from] DecodeError), +} diff --git a/src/prefix/header.rs b/src/prefix/header.rs index aead250f..9faa4076 100644 --- a/src/prefix/header.rs +++ b/src/prefix/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, DecodeError, Emitable, }; @@ -22,7 +22,7 @@ buffer!(PrefixMessageBuffer(PREFIX_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> PrefixMessageBuffer<&'a T> { pub fn nlas( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } diff --git a/src/prefix/message.rs b/src/prefix/message.rs index fc4e9bcf..05c3fa47 100644 --- a/src/prefix/message.rs +++ b/src/prefix/message.rs @@ -1,16 +1,11 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; - -use netlink_packet_utils::{ - traits::{Emitable, Parseable}, - DecodeError, -}; - use super::{ attribute::PrefixAttribute, + error::PrefixError, header::{PrefixHeader, PrefixMessageBuffer}, }; +use netlink_packet_utils::traits::{Emitable, Parseable}; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct PrefixMessage { @@ -32,7 +27,8 @@ impl Emitable for PrefixMessage { } impl> Parseable> for PrefixHeader { - fn parse(buf: &PrefixMessageBuffer) -> Result { + type Error = (); + fn parse(buf: &PrefixMessageBuffer) -> Result { Ok(Self { prefix_family: buf.prefix_family(), ifindex: buf.ifindex(), @@ -46,12 +42,12 @@ impl> Parseable> for PrefixHeader { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for PrefixMessage { - fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { + type Error = PrefixError; + fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { Ok(Self { - header: PrefixHeader::parse(buf) - .context("failed to parse prefix message header")?, - attributes: Vec::::parse(buf) - .context("failed to parse prefix message attributes")?, + // Unwrap: ok, we never return an error above. + header: PrefixHeader::parse(buf).unwrap(), + attributes: Vec::::parse(buf)?, }) } } @@ -59,7 +55,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { - fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { + type Error = PrefixError; + fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result { let mut nlas = vec![]; for nla_buf in buf.nlas() { nlas.push(PrefixAttribute::parse(&nla_buf?)?); diff --git a/src/prefix/mod.rs b/src/prefix/mod.rs index be4fc1dd..c3c6e3b5 100644 --- a/src/prefix/mod.rs +++ b/src/prefix/mod.rs @@ -2,10 +2,12 @@ mod attribute; mod cache_info; +mod error; mod header; mod message; #[cfg(test)] mod tests; +pub use error::PrefixError; pub use header::PrefixMessageBuffer; pub use message::PrefixMessage; diff --git a/src/route/attribute.rs b/src/route/attribute.rs index 344fc745..d4b09642 100644 --- a/src/route/attribute.rs +++ b/src/route/attribute.rs @@ -1,21 +1,18 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + super::AddressFamily, lwtunnel::VecRouteLwTunnelEncap, + metrics::VecRouteMetric, mpls::VecMplsLabel, MplsLabel, RouteAddress, + RouteCacheInfo, RouteCacheInfoBuffer, RouteError, RouteLwEnCapType, + RouteLwTunnelEncap, RouteMetric, RouteMfcStats, RouteMfcStatsBuffer, + RouteMplsTtlPropagation, RouteNextHop, RouteNextHopBuffer, RoutePreference, + RouteRealm, RouteType, RouteVia, RouteViaBuffer, +}; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_u16, parse_u32, parse_u64, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, -}; - -use super::{ - super::AddressFamily, lwtunnel::VecRouteLwTunnelEncap, - metrics::VecRouteMetric, mpls::VecMplsLabel, MplsLabel, RouteAddress, - RouteCacheInfo, RouteCacheInfoBuffer, RouteLwEnCapType, RouteLwTunnelEncap, - RouteMetric, RouteMfcStats, RouteMfcStatsBuffer, RouteMplsTtlPropagation, - RouteNextHop, RouteNextHopBuffer, RoutePreference, RouteRealm, RouteType, - RouteVia, RouteViaBuffer, }; const RTA_DST: u16 = 1; @@ -194,6 +191,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> (AddressFamily, RouteType, RouteLwEnCapType), > for RouteAttribute { + type Error = RouteError; fn parse_with_param( buf: &NlaBuffer<&'a T>, (address_family, route_type, encap_type): ( @@ -201,117 +199,184 @@ impl<'a, T: AsRef<[u8]> + ?Sized> RouteType, RouteLwEnCapType, ), - ) -> Result { + ) -> Result { let payload = buf.value(); Ok(match buf.kind() { - RTA_DST => { - Self::Destination(RouteAddress::parse(address_family, payload)?) - } - RTA_SRC => { - Self::Source(RouteAddress::parse(address_family, payload)?) - } - RTA_GATEWAY => { - Self::Gateway(RouteAddress::parse(address_family, payload)?) - } - RTA_PREFSRC => { - Self::PrefSource(RouteAddress::parse(address_family, payload)?) - } + RTA_DST => Self::Destination( + RouteAddress::parse(address_family, payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_DST", + error, + }, + )?, + ), + RTA_SRC => Self::Source( + RouteAddress::parse(address_family, payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_SRC", + error, + }, + )?, + ), + RTA_GATEWAY => Self::Gateway( + RouteAddress::parse(address_family, payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_GATEWAY", + error, + }, + )?, + ), + RTA_PREFSRC => Self::PrefSource( + RouteAddress::parse(address_family, payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_PREFSRC", + error, + }, + )?, + ), RTA_VIA => Self::Via( RouteVia::parse( - &RouteViaBuffer::new_checked(payload).context(format!( - "Invalid RTA_VIA value {:?}", - payload - ))?, + &RouteViaBuffer::new_checked(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_VIA", + error, + } + })?, ) - .context(format!("Invalid RTA_VIA value {:?}", payload))?, - ), - RTA_NEWDST => Self::NewDestination( - VecMplsLabel::parse(payload) - .context(format!("Invalid RTA_NEWDST value {:?}", payload))? - .0, + .map_err(|error| RouteError::InvalidValue { + kind: "RTA_VIA", + error, + })?, ), + RTA_NEWDST => Self::NewDestination(VecMplsLabel::parse(payload)?.0), - RTA_PREF => Self::Preference(parse_u8(payload)?.into()), + RTA_PREF => Self::Preference( + parse_u8(payload) + .map_err(|error| RouteError::InvalidValue { + kind: "RTA_PREF", + error, + })? + .into(), + ), RTA_ENCAP => Self::Encap( VecRouteLwTunnelEncap::parse_with_param(buf, encap_type)?.0, ), RTA_EXPIRES => { if route_type == RouteType::Multicast { - Self::MulticastExpires(parse_u64(payload).context( - format!( - "invalid RTA_EXPIRES (multicast) value {:?}", - payload - ), + Self::MulticastExpires(parse_u64(payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_EXPIRES (multicast)", + error, + }, )?) } else { - Self::Expires(parse_u32(payload).context(format!( - "invalid RTA_EXPIRES value {:?}", - payload - ))?) + Self::Expires(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_EXPIRES", + error, + } + })?) } } - RTA_UID => Self::Uid( - parse_u32(payload) - .context(format!("invalid RTA_UID value {:?}", payload))?, - ), + RTA_UID => Self::Uid(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_UID", + error, + } + })?), RTA_TTL_PROPAGATE => Self::TtlPropagate( - RouteMplsTtlPropagation::from(parse_u8(payload).context( - format!("invalid RTA_TTL_PROPAGATE {:?}", payload), + RouteMplsTtlPropagation::from(parse_u8(payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_TTL_PROPAGATE", + error, + }, )?), ), RTA_ENCAP_TYPE => Self::EncapType(RouteLwEnCapType::from( - parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?, + parse_u16(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_ENCAP_TYPE", + error, + } + })?, )), - RTA_IIF => { - Self::Iif(parse_u32(payload).context("invalid RTA_IIF value")?) - } - RTA_OIF => { - Self::Oif(parse_u32(payload).context("invalid RTA_OIF value")?) + RTA_IIF => Self::Iif(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_IIF", + error, + } + })?), + RTA_OIF => Self::Oif(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_OIF", + error, + } + })?), + RTA_PRIORITY => { + Self::Priority(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_PRIORITY", + error, + } + })?) } - RTA_PRIORITY => Self::Priority( - parse_u32(payload).context("invalid RTA_PRIORITY value")?, - ), - RTA_FLOW => Self::Realm( - RouteRealm::parse(payload).context("invalid RTA_FLOW value")?, - ), - RTA_TABLE => Self::Table( - parse_u32(payload).context("invalid RTA_TABLE value")?, - ), - RTA_MARK => Self::Mark( - parse_u32(payload).context("invalid RTA_MARK value")?, - ), + RTA_FLOW => Self::Realm(RouteRealm::parse(payload)?), + RTA_TABLE => Self::Table(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_TABLE", + error, + } + })?), + RTA_MARK => Self::Mark(parse_u32(payload).map_err(|error| { + RouteError::InvalidValue { + kind: "RTA_MARK", + error, + } + })?), RTA_CACHEINFO => Self::CacheInfo( RouteCacheInfo::parse( - &RouteCacheInfoBuffer::new_checked(payload) - .context("invalid RTA_CACHEINFO value")?, + &RouteCacheInfoBuffer::new_checked(payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_CACHEINFO", + error, + }, + )?, ) - .context("invalid RTA_CACHEINFO value")?, + .map_err(|error| RouteError::InvalidValue { + kind: "RTA_CACHEINFO", + error, + })?, ), RTA_MFC_STATS => Self::MfcStats( RouteMfcStats::parse( - &RouteMfcStatsBuffer::new_checked(payload) - .context("invalid RTA_MFC_STATS value")?, + &RouteMfcStatsBuffer::new_checked(payload).map_err( + |error| RouteError::InvalidValue { + kind: "RTA_MFC_STATS", + error, + }, + )?, ) - .context("invalid RTA_MFC_STATS value")?, - ), - RTA_METRICS => Self::Metrics( - VecRouteMetric::parse(payload) - .context("invalid RTA_METRICS value")? - .0, + .map_err(|error| RouteError::InvalidValue { + kind: "RTA_MFC_STATS", + error, + })?, ), + RTA_METRICS => Self::Metrics(VecRouteMetric::parse(payload)?.0), RTA_MULTIPATH => { let mut next_hops = vec![]; let mut buf = payload; loop { let nh_buf = RouteNextHopBuffer::new_checked(&buf) - .context("invalid RTA_MULTIPATH value")?; + .map_err(|error| RouteError::InvalidValue { + kind: "RTA_MULTIPATH", + error, + })?; let len = nh_buf.length() as usize; let nh = RouteNextHop::parse_with_param( &nh_buf, (address_family, route_type, encap_type), - ) - .context("invalid RTA_MULTIPATH value")?; + )?; next_hops.push(nh); if buf.len() == len { break; @@ -320,9 +385,12 @@ impl<'a, T: AsRef<[u8]> + ?Sized> } Self::MultiPath(next_hops) } - _ => Self::Other( - DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, - ), + _ => Self::Other(DefaultNla::parse(buf).map_err(|error| { + RouteError::InvalidValue { + kind: "NLA (uknown kind)", + error, + } + })?), }) } } diff --git a/src/route/cache_info.rs b/src/route/cache_info.rs index 42484c24..e725779b 100644 --- a/src/route/cache_info.rs +++ b/src/route/cache_info.rs @@ -32,6 +32,7 @@ buffer!(RouteCacheInfoBuffer(CACHE_INFO_LEN) { }); impl> Parseable> for RouteCacheInfo { + type Error = DecodeError; fn parse(buf: &RouteCacheInfoBuffer) -> Result { Ok(Self { clntref: buf.clntref(), diff --git a/src/route/error.rs b/src/route/error.rs new file mode 100644 index 00000000..0bdcf3a0 --- /dev/null +++ b/src/route/error.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +use super::RouteLwEnCapType; +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum RouteError { + #[error("Invalid {kind} value")] + InvalidValue { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("cannot parse route attributes in next-hop")] + ParseNextHopAttributes(#[source] DecodeError), + + #[error("Invalid RTA_ENCAP for kind: {kind}")] + InvalidRtaEncap { + kind: RouteLwEnCapType, + error: NlaError, + }, + + #[error("invalid MPLS_IPTUNNEL_DST value")] + InvalidMplsIpTunnelTtl(#[source] DecodeError), + + #[error("Invalid {kind} value")] + InvalidRouteMetric { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("Invalid array length. Expected={expected}, got={got}")] + ParseMplsLabel { expected: usize, got: usize }, + + #[error("Expected single u8 for route protocol")] + ParseRouteProtocol, + + #[error("Invalid rule port range data, expecting {expected} u8 array, but got {got}")] + InvalidRulePortRange { expected: usize, got: usize }, + + #[error(transparent)] + ParseNla(#[from] NlaError), + + #[error(transparent)] + Other(#[from] DecodeError), +} diff --git a/src/route/header.rs b/src/route/header.rs index c33af0f6..ca419d92 100644 --- a/src/route/header.rs +++ b/src/route/header.rs @@ -1,13 +1,12 @@ // SPDX-License-Identifier: MIT +use super::{super::AddressFamily, flags::RouteFlags, RouteError}; use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -use super::{super::AddressFamily, flags::RouteFlags}; - const ROUTE_HEADER_LEN: usize = 12; buffer!(RouteMessageBuffer(ROUTE_HEADER_LEN) { @@ -26,7 +25,7 @@ buffer!(RouteMessageBuffer(ROUTE_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> RouteMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -64,7 +63,8 @@ impl RouteHeader { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteHeader { - fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { + type Error = (); + fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { Ok(RouteHeader { address_family: buf.address_family().into(), destination_prefix_length: buf.destination_prefix_length(), @@ -246,14 +246,12 @@ impl Default for RouteProtocol { } impl Parseable<[u8]> for RouteProtocol { - fn parse(buf: &[u8]) -> Result { + type Error = RouteError; + fn parse(buf: &[u8]) -> Result { if buf.len() == 1 { Ok(Self::from(buf[0])) } else { - Err(DecodeError::from(format!( - "Expecting single u8 for route protocol, but got {:?}", - buf - ))) + Err(RouteError::ParseRouteProtocol) } } } diff --git a/src/route/lwtunnel.rs b/src/route/lwtunnel.rs index 8e1619f1..93656380 100644 --- a/src/route/lwtunnel.rs +++ b/src/route/lwtunnel.rs @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{RouteError, RouteMplsIpTunnel}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, }; -use super::RouteMplsIpTunnel; - const LWTUNNEL_ENCAP_NONE: u16 = 0; const LWTUNNEL_ENCAP_MPLS: u16 = 1; const LWTUNNEL_ENCAP_IP: u16 = 2; @@ -141,10 +138,11 @@ impl<'a, T> ParseableParametrized, RouteLwEnCapType> where T: AsRef<[u8]> + ?Sized, { + type Error = RouteError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: RouteLwEnCapType, - ) -> Result { + ) -> Result { Ok(match kind { RouteLwEnCapType::Mpls => { Self::Mpls(RouteMplsIpTunnel::parse(buf)?) @@ -163,17 +161,16 @@ impl<'a, T> ParseableParametrized, RouteLwEnCapType> where T: AsRef<[u8]> + ?Sized, { + type Error = RouteError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: RouteLwEnCapType, - ) -> Result { + ) -> Result { let mut ret = Vec::new(); for nla in NlasIterator::new(buf.value()) { - let nla = - nla.context(format!("Invalid RTA_ENCAP for kind: {kind}"))?; - ret.push(RouteLwTunnelEncap::parse_with_param(&nla, kind).context( - format!("Failed to parse RTA_ENCAP for kind: {kind}",), - )?) + let nla = nla + .map_err(|error| RouteError::InvalidRtaEncap { error, kind })?; + ret.push(RouteLwTunnelEncap::parse_with_param(&nla, kind)?); } Ok(Self(ret)) } diff --git a/src/route/message.rs b/src/route/message.rs index 072776cc..5687c99b 100644 --- a/src/route/message.rs +++ b/src/route/message.rs @@ -1,14 +1,12 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, -}; - use super::{ - super::AddressFamily, attribute::RTA_ENCAP_TYPE, RouteAttribute, - RouteHeader, RouteLwEnCapType, RouteMessageBuffer, RouteType, + super::AddressFamily, attribute::RTA_ENCAP_TYPE, error::RouteError, + RouteAttribute, RouteHeader, RouteLwEnCapType, RouteMessageBuffer, + RouteType, +}; +use netlink_packet_utils::traits::{ + Emitable, Parseable, ParseableParametrized, }; #[derive(Debug, PartialEq, Eq, Clone, Default)] @@ -34,9 +32,10 @@ impl Emitable for RouteMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for RouteMessage { - fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { - let header = RouteHeader::parse(buf) - .context("failed to parse route message header")?; + type Error = RouteError; + fn parse(buf: &RouteMessageBuffer<&'a T>) -> Result { + // unwrap: RouteHeader can't fail. + let header = RouteHeader::parse(buf).unwrap(); let address_family = header.address_family; let route_type = header.kind; Ok(RouteMessage { @@ -44,8 +43,7 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> attributes: Vec::::parse_with_param( buf, (address_family, route_type), - ) - .context("failed to parse route message NLAs")?, + )?, }) } } @@ -54,10 +52,11 @@ impl<'a, T: AsRef<[u8]> + 'a> ParseableParametrized, (AddressFamily, RouteType)> for Vec { + type Error = RouteError; fn parse_with_param( buf: &RouteMessageBuffer<&'a T>, (address_family, route_type): (AddressFamily, RouteType), - ) -> Result { + ) -> Result { let mut attributes = vec![]; let mut encap_type = RouteLwEnCapType::None; // The RTA_ENCAP_TYPE is provided __after__ RTA_ENCAP, we should find diff --git a/src/route/metrics.rs b/src/route/metrics.rs index 6bb24ea5..a3b0c9b0 100644 --- a/src/route/metrics.rs +++ b/src/route/metrics.rs @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::error::RouteError; use byteorder::{ByteOrder, NativeEndian}; -use std::mem::size_of; - use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::Parseable, - DecodeError, }; +use std::mem::size_of; const RTAX_LOCK: u16 = 1; const RTAX_MTU: u16 = 2; @@ -127,65 +125,144 @@ impl Nla for RouteMetric { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteMetric { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = RouteError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - RTAX_LOCK => Self::Lock( - parse_u32(payload).context("invalid RTAX_LOCK value")?, - ), - RTAX_MTU => { - Self::Mtu(parse_u32(payload).context("invalid RTAX_MTU value")?) + RTAX_LOCK => Self::Lock(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_LOCK", + error, + } + })?), + RTAX_MTU => Self::Mtu(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_MTU", + error, + } + })?), + RTAX_WINDOW => { + Self::Window(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_WINDOW", + error, + } + })?) + } + RTAX_RTT => Self::Rtt(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_RTT", + error, + } + })?), + RTAX_RTTVAR => { + Self::RttVar(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_RTTVAR", + error, + } + })?) + } + RTAX_SSTHRESH => { + Self::SsThresh(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_SSHTHRESH", + error, + } + })?) + } + RTAX_CWND => Self::Cwnd(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_CWND", + error, + } + })?), + RTAX_ADVMSS => { + Self::Advmss(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_ADVMSS", + error, + } + })?) + } + RTAX_REORDERING => { + Self::Reordering(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_REORDERING", + error, + } + })?) + } + RTAX_HOPLIMIT => { + Self::Hoplimit(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_HOPLIMIT", + error, + } + })?) + } + RTAX_INITCWND => { + Self::InitCwnd(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_INITCWND", + error, + } + })?) + } + RTAX_FEATURES => { + Self::Features(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_FEATURES", + error, + } + })?) + } + RTAX_RTO_MIN => { + Self::RtoMin(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_RTO_MIN", + error, + } + })?) + } + RTAX_INITRWND => { + Self::InitRwnd(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_INITRWND", + error, + } + })?) + } + RTAX_QUICKACK => { + Self::QuickAck(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_QUICKACK", + error, + } + })?) + } + RTAX_CC_ALGO => { + Self::CcAlgo(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_CC_ALGO", + error, + } + })?) } - RTAX_WINDOW => Self::Window( - parse_u32(payload).context("invalid RTAX_WINDOW value")?, - ), - RTAX_RTT => { - Self::Rtt(parse_u32(payload).context("invalid RTAX_RTT value")?) + RTAX_FASTOPEN_NO_COOKIE => { + Self::FastopenNoCookie(parse_u32(payload).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "RTAX_FASTOPEN_NO_COOKIE", + error, + } + })?) } - RTAX_RTTVAR => Self::RttVar( - parse_u32(payload).context("invalid RTAX_RTTVAR value")?, - ), - RTAX_SSTHRESH => Self::SsThresh( - parse_u32(payload).context("invalid RTAX_SSTHRESH value")?, - ), - RTAX_CWND => Self::Cwnd( - parse_u32(payload).context("invalid RTAX_CWND value")?, - ), - RTAX_ADVMSS => Self::Advmss( - parse_u32(payload).context("invalid RTAX_ADVMSS value")?, - ), - RTAX_REORDERING => Self::Reordering( - parse_u32(payload).context("invalid RTAX_REORDERING value")?, - ), - RTAX_HOPLIMIT => Self::Hoplimit( - parse_u32(payload).context("invalid RTAX_HOPLIMIT value")?, - ), - RTAX_INITCWND => Self::InitCwnd( - parse_u32(payload).context("invalid RTAX_INITCWND value")?, - ), - RTAX_FEATURES => Self::Features( - parse_u32(payload).context("invalid RTAX_FEATURES value")?, - ), - RTAX_RTO_MIN => Self::RtoMin( - parse_u32(payload).context("invalid RTAX_RTO_MIN value")?, - ), - RTAX_INITRWND => Self::InitRwnd( - parse_u32(payload).context("invalid RTAX_INITRWND value")?, - ), - RTAX_QUICKACK => Self::QuickAck( - parse_u32(payload).context("invalid RTAX_QUICKACK value")?, - ), - RTAX_CC_ALGO => Self::CcAlgo( - parse_u32(payload).context("invalid RTAX_CC_ALGO value")?, - ), - RTAX_FASTOPEN_NO_COOKIE => Self::FastopenNoCookie( - parse_u32(payload) - .context("invalid RTAX_FASTOPEN_NO_COOKIE value")?, - ), - _ => Self::Other( - DefaultNla::parse(buf) - .context("invalid NLA value (unknown type) value")?, - ), + _ => Self::Other(DefaultNla::parse(buf).map_err(|error| { + RouteError::InvalidRouteMetric { + kind: "NLA unkwnon", + error, + } + })?), }) } } @@ -193,11 +270,11 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteMetric { pub(crate) struct VecRouteMetric(pub(crate) Vec); impl + ?Sized> Parseable for VecRouteMetric { - fn parse(payload: &T) -> Result { + type Error = RouteError; + fn parse(payload: &T) -> Result { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { - let nla = nla.context("Invalid RTA_METRICS")?; - nlas.push(RouteMetric::parse(&nla).context("Invalid RTA_METRICS")?); + nlas.push(RouteMetric::parse(&nla?)?); } Ok(Self(nlas)) } diff --git a/src/route/mfc_stats.rs b/src/route/mfc_stats.rs index a9f14455..b9391298 100644 --- a/src/route/mfc_stats.rs +++ b/src/route/mfc_stats.rs @@ -22,6 +22,7 @@ buffer!(RouteMfcStatsBuffer(MFC_STATS_LEN) { }); impl> Parseable> for RouteMfcStats { + type Error = DecodeError; fn parse( buf: &RouteMfcStatsBuffer, ) -> Result { diff --git a/src/route/mod.rs b/src/route/mod.rs index 497172db..e72f0f22 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -3,6 +3,7 @@ mod address; mod attribute; mod cache_info; +mod error; mod flags; mod header; mod lwtunnel; @@ -21,6 +22,7 @@ mod tests; pub use self::address::RouteAddress; pub use self::attribute::RouteAttribute; pub use self::cache_info::{RouteCacheInfo, RouteCacheInfoBuffer}; +pub use self::error::RouteError; pub use self::header::{ RouteHeader, RouteMessageBuffer, RouteProtocol, RouteScope, RouteType, }; diff --git a/src/route/mpls.rs b/src/route/mpls.rs index 5c4b4ece..f5c9c094 100644 --- a/src/route/mpls.rs +++ b/src/route/mpls.rs @@ -1,11 +1,10 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::error::RouteError; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::parse_u8, traits::{Emitable, Parseable}, - DecodeError, }; const MPLS_IPTUNNEL_DST: u16 = 1; @@ -50,23 +49,20 @@ impl Nla for RouteMplsIpTunnel { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteMplsIpTunnel { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = RouteError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - MPLS_IPTUNNEL_DST => Self::Destination( - VecMplsLabel::parse(payload) - .context(format!( - "invalid MPLS_IPTUNNEL_DST value {:?}", - payload - ))? - .0, - ), + MPLS_IPTUNNEL_DST => { + Self::Destination(VecMplsLabel::parse(payload)?.0) + } MPLS_IPTUNNEL_TTL => Self::Ttl( - parse_u8(payload).context("invalid MPLS_IPTUNNEL_TTL value")?, + parse_u8(payload) + .map_err(RouteError::InvalidMplsIpTunnelTtl)?, ), _ => Self::Other( DefaultNla::parse(buf) - .context("invalid NLA value (unknown type) value")?, + .map_err(RouteError::InvalidMplsIpTunnelTtl)?, ), }) } @@ -95,18 +91,16 @@ pub struct MplsLabel { } impl MplsLabel { - pub(crate) fn parse(payload: &[u8]) -> Result { + pub(crate) fn parse(payload: &[u8]) -> Result { if payload.len() == 4 { Ok(Self::from(u32::from_be_bytes([ payload[0], payload[1], payload[2], payload[3], ]))) } else { - Err(DecodeError::from(format!( - "Invalid u8 array length {}, expecting \ - 4 bytes for MPLS label, got {:?}", - payload.len(), - payload, - ))) + Err(RouteError::ParseMplsLabel { + expected: 4, + got: payload.len(), + }) } } } @@ -148,7 +142,7 @@ impl From for u32 { pub(crate) struct VecMplsLabel(pub(crate) Vec); impl VecMplsLabel { - pub(crate) fn parse(payload: &[u8]) -> Result { + pub(crate) fn parse(payload: &[u8]) -> Result { let mut labels = vec![]; let mut i: usize = 0; while i + 4 <= payload.len() { diff --git a/src/route/next_hops.rs b/src/route/next_hops.rs index 724c1c01..71f93634 100644 --- a/src/route/next_hops.rs +++ b/src/route/next_hops.rs @@ -1,16 +1,15 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + super::AddressFamily, RouteAttribute, RouteError, RouteLwEnCapType, + RouteType, +}; use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, ParseableParametrized}, DecodeError, }; -use super::{ - super::AddressFamily, RouteAttribute, RouteLwEnCapType, RouteType, -}; - pub(crate) const RTNH_F_DEAD: u8 = 1; pub(crate) const RTNH_F_PERVASIVE: u8 = 2; pub(crate) const RTNH_F_ONLINK: u8 = 4; @@ -54,18 +53,18 @@ impl> RouteNextHopBuffer { fn check_buffer_length(&self) -> Result<(), DecodeError> { let len = self.buffer.as_ref().len(); if len < PAYLOAD_OFFSET { - return Err(format!( - "invalid RouteNextHopBuffer: length {len} < {PAYLOAD_OFFSET}" - ) - .into()); + return Err(DecodeError::InvalidBufferLength { + name: "RouteNextHopBuffer", + len, + buffer_len: PAYLOAD_OFFSET, + }); } if len < self.length() as usize { - return Err(format!( - "invalid RouteNextHopBuffer: length {} < {}", + return Err(DecodeError::InvalidBufferLength { + name: "RouteNextHopBuffer", len, - 8 + self.length() - ) - .into()); + buffer_len: (8 + self.length()) as usize, + }); } Ok(()) } @@ -74,7 +73,7 @@ impl> RouteNextHopBuffer { impl<'a, T: AsRef<[u8]> + ?Sized> RouteNextHopBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new( &self.payload()[..(self.length() as usize - PAYLOAD_OFFSET)], ) @@ -100,6 +99,7 @@ impl<'a, T: AsRef<[u8]>> (AddressFamily, RouteType, RouteLwEnCapType), > for RouteNextHop { + type Error = RouteError; fn parse_with_param( buf: &RouteNextHopBuffer<&T>, (address_family, route_type, encap_type): ( @@ -107,13 +107,11 @@ impl<'a, T: AsRef<[u8]>> RouteType, RouteLwEnCapType, ), - ) -> Result { + ) -> Result { let attributes = Vec::::parse_with_param( - &RouteNextHopBuffer::new_checked(buf.buffer) - .context("cannot parse route attributes in next-hop")?, + &RouteNextHopBuffer::new_checked(buf.buffer)?, (address_family, route_type, encap_type), - ) - .context("cannot parse route attributes in next-hop")?; + )?; Ok(RouteNextHop { flags: RouteNextHopFlags::from_bits_retain(buf.flags()), hops: buf.hops(), @@ -129,6 +127,7 @@ impl<'a, T: AsRef<[u8]> + 'a> (AddressFamily, RouteType, RouteLwEnCapType), > for Vec { + type Error = RouteError; fn parse_with_param( buf: &RouteNextHopBuffer<&'a T>, (address_family, route_type, encap_type): ( @@ -136,7 +135,7 @@ impl<'a, T: AsRef<[u8]> + 'a> RouteType, RouteLwEnCapType, ), - ) -> Result { + ) -> Result { let mut nlas = vec![]; for nla_buf in buf.attributes() { nlas.push(RouteAttribute::parse_with_param( diff --git a/src/route/realm.rs b/src/route/realm.rs index 8b395ff5..7d743683 100644 --- a/src/route/realm.rs +++ b/src/route/realm.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -use netlink_packet_utils::{DecodeError, Emitable}; +use super::RouteError; +use netlink_packet_utils::Emitable; const RULE_REALM_LEN: usize = 4; @@ -11,7 +12,7 @@ pub struct RouteRealm { } impl RouteRealm { - pub(crate) fn parse(buf: &[u8]) -> Result { + pub(crate) fn parse(buf: &[u8]) -> Result { let all = u32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]); if buf.len() == RULE_REALM_LEN { Ok(Self { @@ -19,11 +20,10 @@ impl RouteRealm { destination: (all & 0xFFFF) as u16, }) } else { - Err(DecodeError::from(format!( - "Invalid rule port range data, expecting \ - {RULE_REALM_LEN} u8 array, but got {:?}", - buf - ))) + Err(RouteError::InvalidRulePortRange { + expected: RULE_REALM_LEN, + got: buf.len(), + }) } } } diff --git a/src/route/via.rs b/src/route/via.rs index e7da6924..53320e77 100644 --- a/src/route/via.rs +++ b/src/route/via.rs @@ -36,6 +36,7 @@ buffer!(RouteViaBuffer(RTVIA_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RouteVia { + type Error = DecodeError; fn parse(buf: &RouteViaBuffer<&'a T>) -> Result { let address_family: AddressFamily = (buf.address_family() as u8).into(); Ok(match address_family { diff --git a/src/rule/attribute.rs b/src/rule/attribute.rs index e56e554d..164ad14f 100644 --- a/src/rule/attribute.rs +++ b/src/rule/attribute.rs @@ -1,20 +1,17 @@ // SPDX-License-Identifier: MIT -use std::net::IpAddr; - -use anyhow::Context; +use crate::{ + ip::{emit_ip_addr, ip_addr_len, parse_ip_addr, IpProtocol}, + route::{RouteProtocol, RouteRealm}, + rule::{RuleError, RulePortRange, RuleUidRange}, +}; use netlink_packet_utils::{ byteorder::{ByteOrder, NativeEndian}, nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32, parse_u8}, - DecodeError, Emitable, Parseable, -}; - -use crate::{ - ip::{emit_ip_addr, ip_addr_len, parse_ip_addr, IpProtocol}, - route::{RouteProtocol, RouteRealm}, - rule::{RulePortRange, RuleUidRange}, + Emitable, Parseable, }; +use std::net::IpAddr; const FRA_DST: u16 = 1; const FRA_SRC: u16 = 2; @@ -152,78 +149,132 @@ impl Nla for RuleAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleAttribute { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = RuleError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - FRA_DST => Self::Destination( - parse_ip_addr(payload) - .context(format!("Invalid FRA_DST value {payload:?}"))?, - ), - FRA_SRC => Self::Source( - parse_ip_addr(payload) - .context(format!("Invalid FRA_SRC value {payload:?}"))?, - ), - FRA_IIFNAME => Self::Iifname( - parse_string(payload).context("invalid FRA_IIFNAME value")?, - ), - FRA_GOTO => Self::Goto( - parse_u32(payload).context("invalid FRA_GOTO value")?, - ), - FRA_PRIORITY => Self::Priority( - parse_u32(payload).context("invalid FRA_PRIORITY value")?, - ), - FRA_FWMARK => Self::FwMark( - parse_u32(payload).context("invalid FRA_FWMARK value")?, - ), - FRA_FLOW => Self::Realm( - RouteRealm::parse(payload).context("invalid FRA_FLOW value")?, - ), - FRA_TUN_ID => Self::TunId( - parse_u32(payload).context("invalid FRA_TUN_ID value")?, - ), - FRA_SUPPRESS_IFGROUP => Self::SuppressIfGroup( - parse_u32(payload) - .context("invalid FRA_SUPPRESS_IFGROUP value")?, - ), - FRA_SUPPRESS_PREFIXLEN => Self::SuppressPrefixLen( - parse_u32(payload) - .context("invalid FRA_SUPPRESS_PREFIXLEN value")?, - ), - FRA_TABLE => Self::Table( - parse_u32(payload).context("invalid FRA_TABLE value")?, - ), - FRA_FWMASK => Self::FwMask( - parse_u32(payload).context("invalid FRA_FWMASK value")?, - ), - FRA_OIFNAME => Self::Oifname( - parse_string(payload).context("invalid FRA_OIFNAME value")?, - ), + FRA_DST => { + Self::Destination(parse_ip_addr(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_DST", + error, + } + })?) + } + FRA_SRC => { + Self::Source(parse_ip_addr(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_DST", + error, + } + })?) + } + FRA_IIFNAME => { + Self::Iifname(parse_string(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_IIFNAME", + error, + } + })?) + } + FRA_GOTO => Self::Goto(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_GOTO", + error, + } + })?), + FRA_PRIORITY => { + Self::Priority(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_PRIORITY", + error, + } + })?) + } + FRA_FWMARK => { + Self::FwMark(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_FWMARK", + error, + } + })?) + } + FRA_FLOW => Self::Realm(RouteRealm::parse(payload)?), + FRA_TUN_ID => Self::TunId(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_TUN_ID", + error, + } + })?), + FRA_SUPPRESS_IFGROUP => { + Self::SuppressIfGroup(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_SUPPRESS_IFGROUP", + error, + } + })?) + } + FRA_SUPPRESS_PREFIXLEN => { + Self::SuppressPrefixLen(parse_u32(payload).map_err( + |error| RuleError::InvalidValue { + kind: "FRA_SUPPRESS_PREFIXLEN", + error, + }, + )?) + } + FRA_TABLE => Self::Table(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_TABLE", + error, + } + })?), + FRA_FWMASK => { + Self::FwMask(parse_u32(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_FWMASK", + error, + } + })?) + } + FRA_OIFNAME => { + Self::Oifname(parse_string(payload).map_err(|error| { + RuleError::InvalidValue { + kind: "FRA_OIFNAME", + error, + } + })?) + } FRA_L3MDEV => Self::L3MDev( - parse_u8(payload).context("invalid FRA_L3MDEV value")? > 0, - ), - FRA_UID_RANGE => Self::UidRange( - RuleUidRange::parse(payload) - .context("invalid FRA_UID_RANGE value")?, + parse_u8(payload).map_err(|error| RuleError::InvalidValue { + kind: "FRA_L3MDEV", + error, + })? > 0, ), + FRA_UID_RANGE => Self::UidRange(RuleUidRange::parse(payload)?), FRA_PROTOCOL => Self::Protocol( parse_u8(payload) - .context("invalid FRA_PROTOCOL value")? + .map_err(|error| RuleError::InvalidValue { + kind: "FRA_PROTOCOL", + error, + })? .into(), ), FRA_IP_PROTO => Self::IpProtocol(IpProtocol::from( - parse_u8(payload).context("invalid FRA_IP_PROTO value")? as i32, + parse_u8(payload).map_err(|error| RuleError::InvalidValue { + kind: "FRA_IP_PROTO", + error, + })? as i32, )), - FRA_SPORT_RANGE => Self::SourcePortRange( - RulePortRange::parse(payload) - .context("invalid FRA_SPORT_RANGE value")?, - ), - FRA_DPORT_RANGE => Self::DestinationPortRange( - RulePortRange::parse(payload) - .context("invalid FRA_DPORT_RANGE value")?, - ), - _ => Self::Other( - DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, + FRA_SPORT_RANGE => { + Self::SourcePortRange(RulePortRange::parse(payload)?) + } + FRA_DPORT_RANGE => { + Self::DestinationPortRange(RulePortRange::parse(payload)?) + } + kind => Self::Other( + DefaultNla::parse(buf) + .map_err(|error| RuleError::UnknownNLA { kind, error })?, ), }) } diff --git a/src/rule/error.rs b/src/rule/error.rs new file mode 100644 index 00000000..d37d1a8d --- /dev/null +++ b/src/rule/error.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +use crate::route::RouteError; +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum RuleError { + #[error("Invalid {kind}")] + InvalidValue { + kind: &'static str, + error: DecodeError, + }, + + #[error("Unknown NLA type: {kind}")] + UnknownNLA { kind: u16, error: DecodeError }, + + #[error(transparent)] + ParseNla(#[from] NlaError), + + #[error(transparent)] + ParseFraFlow(#[from] RouteError), + + #[error("Invalid rule uid range data, expecting {expected} u8 array, but got {got}")] + ParseUidRange { expected: usize, got: usize }, + + #[error("Invalid rule port range data, expecting {expected} u8 array, but got {got}")] + ParsePortRange { expected: usize, got: usize }, +} diff --git a/src/rule/header.rs b/src/rule/header.rs index 8451ec47..ab54288f 100644 --- a/src/rule/header.rs +++ b/src/rule/header.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; @@ -26,7 +26,7 @@ buffer!(RuleMessageBuffer(RULE_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> RuleMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -63,7 +63,8 @@ impl Emitable for RuleHeader { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for RuleHeader { - fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { + type Error = (); + fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { Ok(RuleHeader { family: buf.family().into(), dst_len: buf.dst_len(), diff --git a/src/rule/message.rs b/src/rule/message.rs index fa96cf01..427f155e 100644 --- a/src/rule/message.rs +++ b/src/rule/message.rs @@ -1,12 +1,7 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable}, - DecodeError, -}; - -use super::{RuleAttribute, RuleHeader, RuleMessageBuffer}; +use super::{RuleAttribute, RuleError, RuleHeader, RuleMessageBuffer}; +use netlink_packet_utils::traits::{Emitable, Parseable}; #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] @@ -31,11 +26,11 @@ impl Emitable for RuleMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for RuleMessage { - fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { - let header = RuleHeader::parse(buf) - .context("failed to parse link message header")?; - let attributes = Vec::::parse(buf) - .context("failed to parse link message NLAs")?; + type Error = RuleError; + fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { + // unwrap: RuleHeader never fails to parse. + let header = RuleHeader::parse(buf).unwrap(); + let attributes = Vec::::parse(buf)?; Ok(RuleMessage { header, attributes }) } } @@ -43,7 +38,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { - fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { + type Error = RuleError; + fn parse(buf: &RuleMessageBuffer<&'a T>) -> Result { let mut attributes = vec![]; for nla_buf in buf.attributes() { attributes.push(RuleAttribute::parse(&nla_buf?)?); diff --git a/src/rule/mod.rs b/src/rule/mod.rs index 8778dcd8..3ef9595a 100644 --- a/src/rule/mod.rs +++ b/src/rule/mod.rs @@ -2,6 +2,7 @@ mod action; mod attribute; +mod error; mod flags; mod header; mod message; @@ -12,6 +13,7 @@ mod uid_range; pub use self::action::RuleAction; pub use self::attribute::RuleAttribute; +pub use self::error::RuleError; pub use self::flags::RuleFlags; pub use self::header::{RuleHeader, RuleMessageBuffer}; pub use self::message::RuleMessage; diff --git a/src/rule/port_range.rs b/src/rule/port_range.rs index 04ae7fd9..5d1078c2 100644 --- a/src/rule/port_range.rs +++ b/src/rule/port_range.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -use netlink_packet_utils::{DecodeError, Emitable}; +use crate::rule::RuleError; +use netlink_packet_utils::Emitable; const RULE_PORT_RANGE_LEN: usize = 4; @@ -11,18 +12,17 @@ pub struct RulePortRange { } impl RulePortRange { - pub(crate) fn parse(buf: &[u8]) -> Result { + pub(crate) fn parse(buf: &[u8]) -> Result { if buf.len() == RULE_PORT_RANGE_LEN { Ok(Self { start: u16::from_ne_bytes([buf[0], buf[1]]), end: u16::from_ne_bytes([buf[2], buf[3]]), }) } else { - Err(DecodeError::from(format!( - "Invalid rule port range data, expecting \ - {RULE_PORT_RANGE_LEN} u8 array, but got {:?}", - buf - ))) + Err(RuleError::ParsePortRange { + expected: RULE_PORT_RANGE_LEN, + got: buf.len(), + }) } } } diff --git a/src/rule/uid_range.rs b/src/rule/uid_range.rs index f63249ca..2b5c86e3 100644 --- a/src/rule/uid_range.rs +++ b/src/rule/uid_range.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -use netlink_packet_utils::{DecodeError, Emitable}; +use crate::rule::RuleError; +use netlink_packet_utils::Emitable; const RULE_UID_RANGE_LEN: usize = 8; @@ -11,18 +12,17 @@ pub struct RuleUidRange { } impl RuleUidRange { - pub(crate) fn parse(buf: &[u8]) -> Result { + pub(crate) fn parse(buf: &[u8]) -> Result { if buf.len() == RULE_UID_RANGE_LEN { Ok(Self { start: u32::from_ne_bytes([buf[0], buf[1], buf[2], buf[3]]), end: u32::from_ne_bytes([buf[4], buf[5], buf[6], buf[7]]), }) } else { - Err(DecodeError::from(format!( - "Invalid rule port range data, expecting \ - {RULE_UID_RANGE_LEN} u8 array, but got {:?}", - buf - ))) + Err(RuleError::ParseUidRange { + expected: RULE_UID_RANGE_LEN, + got: buf.len(), + }) } } } diff --git a/src/tc/actions/action.rs b/src/tc/actions/action.rs index 153a0e62..faf87cf3 100644 --- a/src/tc/actions/action.rs +++ b/src/tc/actions/action.rs @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, +}; +use crate::tc::{TcError, TcStats2}; use byteorder::{ByteOrder, NativeEndian}; - use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_string, parse_u32}, @@ -10,11 +12,6 @@ use netlink_packet_utils::{ DecodeError, }; -use super::{ - TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption, -}; -use crate::tc::TcStats2; - const TCA_ACT_TAB: u16 = 1; #[derive(Debug, PartialEq, Eq, Clone)] @@ -48,60 +45,62 @@ impl Nla for TcAction { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcAction { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let mut attributes = vec![]; let mut kind = String::new(); for iter in NlasIterator::new(buf.value()) { - let buf = iter.context("invalid action nla")?; + let buf = iter?; let payload = buf.value(); attributes.push(match buf.kind() { TCA_ACT_KIND => { - kind = parse_string(payload) - .context("failed to parse TCA_ACT_KIND")?; + kind = parse_string(payload).map_err(|error| { + TcError::ParseAction { + kind: "TCA_ACT_KIND", + error, + } + })?; TcActionAttribute::Kind(kind.clone()) } TCA_ACT_OPTIONS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { - let nla = nla.context("invalid TCA_ACT_OPTIONS")?; - nlas.push( - TcActionOption::parse_with_param(&nla, &kind) - .context(format!( - "failed to parse TCA_ACT_OPTIONS \ - for kind {kind}" - ))?, - ) + let nla = nla?; + nlas.push(TcActionOption::parse_with_param( + &nla, &kind, + )?); } TcActionAttribute::Options(nlas) } - TCA_ACT_INDEX => TcActionAttribute::Index( - parse_u32(payload) - .context("failed to parse TCA_ACT_INDEX")?, - ), + TCA_ACT_INDEX => { + TcActionAttribute::Index(parse_u32(payload).map_err( + |error| TcError::ParseAction { + kind: "TCA_ACT_INDEX", + error, + }, + )?) + } TCA_ACT_STATS => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { - let nla = nla.context("invalid TCA_ACT_STATS")?; - nlas.push( - TcStats2::parse_with_param(&nla, &kind).context( - format!( - "failed to parse TCA_ACT_STATS for \ - kind {kind}", - ), - )?, - ); + let nla = nla?; + nlas.push(TcStats2::parse_with_param(&nla, &kind)?); } TcActionAttribute::Stats(nlas) } TCA_ACT_COOKIE => TcActionAttribute::Cookie(payload.to_vec()), - TCA_ACT_IN_HW_COUNT => TcActionAttribute::InHwCount( - parse_u32(payload) - .context("failed to parse TCA_ACT_IN_HW_COUNT")?, - ), - _ => TcActionAttribute::Other( + TCA_ACT_IN_HW_COUNT => { + TcActionAttribute::InHwCount(parse_u32(payload).map_err( + |error| TcError::ParseAction { + kind: "TCA_ACT_IN_HW_COUNT", + error, + }, + )?) + } + kind => TcActionAttribute::Other( DefaultNla::parse(&buf) - .context("failed to parse action nla")?, + .map_err(|error| TcError::UnknownNla { kind, error })?, ), }); } @@ -214,22 +213,22 @@ where T: AsRef<[u8]> + ?Sized, S: AsRef, { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: S, - ) -> Result { + ) -> Result { Ok(match kind.as_ref() { TcActionMirror::KIND => Self::Mirror( TcActionMirrorOption::parse(buf) - .context("failed to parse mirror action")?, + .map_err(TcError::ParseMirrorAction)?, ), TcActionNat::KIND => Self::Nat( TcActionNatOption::parse(buf) - .context("failed to parse nat action")?, + .map_err(TcError::ParseMirrorAction)?, ), _ => Self::Other( - DefaultNla::parse(buf) - .context("failed to parse action options")?, + DefaultNla::parse(buf).map_err(TcError::ParseMirrorAction)?, ), }) } @@ -274,6 +273,7 @@ impl Emitable for TcActionGeneric { } impl> Parseable> for TcActionGeneric { + type Error = DecodeError; fn parse(buf: &TcActionGenericBuffer) -> Result { Ok(Self { index: buf.index(), diff --git a/src/tc/actions/mirror.rs b/src/tc/actions/mirror.rs index c818da94..e72ca484 100644 --- a/src/tc/actions/mirror.rs +++ b/src/tc/actions/mirror.rs @@ -60,6 +60,7 @@ impl Nla for TcActionMirrorOption { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcActionMirrorOption { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { @@ -105,6 +106,7 @@ impl Emitable for TcMirror { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcMirror { + type Error = DecodeError; fn parse(buf: &TcMirrorBuffer<&T>) -> Result { Ok(Self { generic: TcActionGeneric::parse(&TcActionGenericBuffer::new( diff --git a/src/tc/actions/nat.rs b/src/tc/actions/nat.rs index a1d6a350..f1393049 100644 --- a/src/tc/actions/nat.rs +++ b/src/tc/actions/nat.rs @@ -60,6 +60,7 @@ impl Nla for TcActionNatOption { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcActionNatOption { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { @@ -124,6 +125,7 @@ impl Emitable for TcNat { } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcNat { + type Error = DecodeError; fn parse(buf: &TcNatBuffer<&T>) -> Result { Ok(Self { generic: TcActionGeneric::parse(&TcActionGenericBuffer::new( @@ -139,10 +141,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcNat { fn parse_ipv4(data: &[u8]) -> Result { if data.len() != 4 { - Err(DecodeError::from(format!( - "Invalid length of IPv4 Address, expecting 4 bytes, but got {:?}", - data - ))) + Err(DecodeError::InvalidIPAddress) } else { Ok(Ipv4Addr::new(data[0], data[1], data[2], data[3])) } diff --git a/src/tc/attribute.rs b/src/tc/attribute.rs index 992ec880..38d349ae 100644 --- a/src/tc/attribute.rs +++ b/src/tc/attribute.rs @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + TcError, TcOption, TcStats, TcStats2, TcStatsBuffer, TcXstats, VecTcOption, +}; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::{parse_string, parse_u32, parse_u8}, - DecodeError, Emitable, Parseable, ParseableParametrized, -}; - -use super::{ - TcOption, TcStats, TcStats2, TcStatsBuffer, TcXstats, VecTcOption, + Emitable, Parseable, ParseableParametrized, }; const TCA_KIND: u16 = 1; @@ -109,53 +107,70 @@ impl Nla for TcAttribute { impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, &str> for TcAttribute { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, - ) -> Result { + ) -> Result { let payload = buf.value(); Ok(match buf.kind() { - TCA_KIND => TcAttribute::Kind( - parse_string(payload).context("invalid TCA_KIND")?, - ), + TCA_KIND => { + TcAttribute::Kind(parse_string(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_KIND", + error, + } + })?) + } TCA_OPTIONS => TcAttribute::Options( - VecTcOption::parse_with_param(buf, kind) - .context(format!("Invalid TCA_OPTIONS for kind: {kind}"))? - .0, + VecTcOption::parse_with_param(buf, kind)?.0, ), TCA_STATS => TcAttribute::Stats( - TcStats::parse( - &TcStatsBuffer::new_checked(payload) - .context("invalid TCA_STATS")?, - ) - .context("failed to parse TCA_STATS")?, - ), - TCA_XSTATS => TcAttribute::Xstats( - TcXstats::parse_with_param(buf, kind) - .context("invalid TCA_XSTATS")?, + TcStats::parse(&TcStatsBuffer::new_checked(payload).map_err( + |error| TcError::InvalidValue { + kind: "TCA_STATS", + error, + }, + )?) + .map_err(|error| TcError::InvalidValue { + kind: "TCA_STATS", + error, + })?, ), + TCA_XSTATS => { + TcAttribute::Xstats(TcXstats::parse_with_param(buf, kind)?) + } TCA_RATE => TcAttribute::Rate(payload.to_vec()), TCA_FCNT => TcAttribute::Fcnt(payload.to_vec()), TCA_STATS2 => { let mut nlas = vec![]; for nla in NlasIterator::new(payload) { - let nla = nla.context("invalid TCA_STATS2")?; - nlas.push(TcStats2::parse_with_param(&nla, kind).context( - format!("failed to parse TCA_STATS2 for kind {kind}"), - )?); + let nla = nla?; + nlas.push(TcStats2::parse_with_param(&nla, kind)?); } TcAttribute::Stats2(nlas) } TCA_STAB => TcAttribute::Stab(payload.to_vec()), - TCA_CHAIN => TcAttribute::Chain( - parse_u32(payload).context("failed to parse TCA_CHAIN")?, - ), - TCA_HW_OFFLOAD => TcAttribute::HwOffload( - parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?, - ), + TCA_CHAIN => { + TcAttribute::Chain(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_CHAIN", + error, + } + })?) + } + TCA_HW_OFFLOAD => { + TcAttribute::HwOffload(parse_u8(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_HW_OFFLOAD", + error, + } + })?) + } TCA_DUMP_INVISIBLE => TcAttribute::DumpInvisible(true), - _ => TcAttribute::Other( - DefaultNla::parse(buf).context("failed to parse tc nla")?, + kind => TcAttribute::Other( + DefaultNla::parse(buf) + .map_err(|error| TcError::UnknownNla { kind, error })?, ), }) } diff --git a/src/tc/error.rs b/src/tc/error.rs new file mode 100644 index 00000000..c4c22411 --- /dev/null +++ b/src/tc/error.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::NlaError, DecodeError}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum TcError { + #[error("Invalid {kind}")] + InvalidValue { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("failed to parse {kind} TCA_OPTIONS attributes")] + ParseTcaOptionAttributes { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("failed to parse {kind}")] + ParseFilterMatchallOption { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("failed to parse {kind}")] + ParseAction { + kind: &'static str, + #[source] + error: DecodeError, + }, + + #[error("failed to parse TCA_ACT_OPTIONS for kind {kind}")] + ParseActOptions { + kind: String, + #[source] + error: DecodeError, + }, + + #[error("failed to parse mirror action")] + ParseMirrorAction(#[source] DecodeError), + + #[error("Unknown matchall option: {kind}")] + UnknownFilterMatchAllOption { + kind: String, + #[source] + error: DecodeError, + }, + + #[error("Unknown NLA type: {kind}")] + UnknownNla { + kind: u16, + #[source] + error: DecodeError, + }, + + #[error("Unknown TC_OPTIONS: {kind}")] + UnknownOption { + kind: String, + #[source] + error: DecodeError, + }, + + #[error(transparent)] + ParseNla(#[from] NlaError), + + #[error("failed to parse TCA_STATS2 for kind {kind}")] + ParseTcaStats2 { + kind: String, + #[source] + error: DecodeError, + }, + + #[error("Invalid u32 key")] + InvalidU32Key(#[source] DecodeError), + + #[error("Invalid TcFqCodelXstats length: {0}")] + InvalidXstatsLength(usize), +} diff --git a/src/tc/filters/cls_u32.rs b/src/tc/filters/cls_u32.rs index 06327f5b..99fda321 100644 --- a/src/tc/filters/cls_u32.rs +++ b/src/tc/filters/cls_u32.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +use super::u32_flags::{TcU32OptionFlags, TcU32SelectorFlags}; +use crate::tc::{TcAction, TcError, TcHandle}; /// U32 filter /// /// In its simplest form the U32 filter is a list of records, each @@ -7,7 +9,6 @@ /// described below, are compared with the currently processed IP packet /// until the first match occurs, and then the associated action is /// performed. -use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, @@ -16,9 +17,6 @@ use netlink_packet_utils::{ DecodeError, }; -use super::u32_flags::{TcU32OptionFlags, TcU32SelectorFlags}; -use crate::tc::{TcAction, TcHandle}; - const TC_U32_SEL_BUF_LEN: usize = 16; const TC_U32_KEY_BUF_LEN: usize = 16; @@ -115,38 +113,54 @@ impl Nla for TcFilterU32Option { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcFilterU32Option { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_U32_CLASSID => Self::ClassId(TcHandle::from( - parse_u32(payload).context("failed to parse TCA_U32_UNSPEC")?, + parse_u32(payload).map_err(|error| TcError::InvalidValue { + kind: "TCA_U32_UNSPEC", + error, + })?, )), - TCA_U32_HASH => Self::Hash( - parse_u32(payload).context("failed to parse TCA_U32_HASH")?, - ), - TCA_U32_LINK => Self::Link( - parse_u32(payload).context("failed to parse TCA_U32_LINK")?, - ), - TCA_U32_DIVISOR => Self::Divisor( - parse_u32(payload) - .context("failed to parse TCA_U32_DIVISOR")?, - ), - TCA_U32_SEL => Self::Selector( - TcU32Selector::parse( - &TcU32SelectorBuffer::new_checked(payload) - .context("invalid TCA_U32_SEL")?, - ) - .context("failed to parse TCA_U32_SEL")?, - ), + TCA_U32_HASH => { + Self::Hash(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_U32_HASH", + error, + } + })?) + } + TCA_U32_LINK => { + Self::Link(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_U32_LINK", + error, + } + })?) + } + TCA_U32_DIVISOR => { + Self::Divisor(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_U32_DIVISOR", + error, + } + })?) + } + TCA_U32_SEL => Self::Selector(TcU32Selector::parse( + &TcU32SelectorBuffer::new_checked(payload).map_err( + |error| TcError::InvalidValue { + kind: "TCA_U32_SEL", + error, + }, + )?, + )?), TCA_U32_POLICE => Self::Police(payload.to_vec()), TCA_U32_ACT => { let mut acts = vec![]; for act in NlasIterator::new(payload) { - let act = act.context("invalid TCA_U32_ACT")?; - acts.push( - TcAction::parse(&act) - .context("failed to parse TCA_U32_ACT")?, - ); + let act = act?; + acts.push(TcAction::parse(&act)?); } Self::Action(acts) } @@ -154,10 +168,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> TCA_U32_PCNT => Self::Pnct(payload.to_vec()), TCA_U32_MARK => Self::Mark(payload.to_vec()), TCA_U32_FLAGS => Self::Flags(TcU32OptionFlags::from_bits_retain( - parse_u32(payload).context("failed to parse TCA_U32_FLAGS")?, + parse_u32(payload).map_err(|error| TcError::InvalidValue { + kind: "TCA_U32_FLAGS", + error, + })?, )), - _ => Self::Other( - DefaultNla::parse(buf).context("failed to parse u32 nla")?, + kind => Self::Other( + DefaultNla::parse(buf) + .map_err(|error| TcError::UnknownNla { kind, error })?, ), }) } @@ -219,7 +237,8 @@ impl Emitable for TcU32Selector { impl + ?Sized> Parseable> for TcU32Selector { - fn parse(buf: &TcU32SelectorBuffer<&T>) -> Result { + type Error = TcError; + fn parse(buf: &TcU32SelectorBuffer<&T>) -> Result { let nkeys = buf.nkeys(); let mut keys = Vec::::with_capacity(nkeys.into()); let key_payload = buf.keys(); @@ -229,10 +248,9 @@ impl + ?Sized> Parseable> &key_payload [(i * TC_U32_KEY_BUF_LEN)..(i + 1) * TC_U32_KEY_BUF_LEN], ) - .context("invalid u32 key")?; - keys.push( - TcU32Key::parse(&keybuf).context("failed to parse u32 key")?, - ); + .map_err(|error| TcError::InvalidU32Key(error))?; + // unwrap: this never fails to parse. + keys.push(TcU32Key::parse(&keybuf).unwrap()); } Ok(Self { @@ -279,7 +297,8 @@ impl Emitable for TcU32Key { } impl> Parseable> for TcU32Key { - fn parse(buf: &TcU32KeyBuffer) -> Result { + type Error = (); + fn parse(buf: &TcU32KeyBuffer) -> Result { Ok(Self { mask: buf.mask(), val: buf.val(), diff --git a/src/tc/filters/matchall.rs b/src/tc/filters/matchall.rs index 280f8a24..016c59db 100644 --- a/src/tc/filters/matchall.rs +++ b/src/tc/filters/matchall.rs @@ -1,19 +1,16 @@ // SPDX-License-Identifier: MIT +use crate::tc::{TcAction, TcError, TcHandle}; /// Matchall filter /// /// Matches all packets and performs an action on them. -use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, parsers::parse_u32, traits::{Emitable, Parseable}, - DecodeError, }; -use crate::tc::{TcAction, TcHandle}; - const TCA_MATCHALL_CLASSID: u16 = 1; const TCA_MATCHALL_ACT: u16 = 2; const TCA_MATCHALL_FLAGS: u16 = 3; @@ -71,33 +68,41 @@ impl Nla for TcFilterMatchAllOption { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcFilterMatchAllOption { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_MATCHALL_CLASSID => Self::ClassId( parse_u32(payload) - .context("failed to parse TCA_MATCHALL_UNSPEC")? + .map_err(|error| TcError::ParseFilterMatchallOption { + kind: "TCA_MATCHALL_UNSPEC", + error, + })? .into(), ), TCA_MATCHALL_ACT => { let mut acts = vec![]; for act in NlasIterator::new(payload) { - let act = act.context("invalid TCA_MATCHALL_ACT")?; - acts.push( - TcAction::parse(&act) - .context("failed to parse TCA_MATCHALL_ACT")?, - ); + let act = act?; + acts.push(TcAction::parse(&act)?); } Self::Action(acts) } TCA_MATCHALL_PCNT => Self::Pnct(payload.to_vec()), - TCA_MATCHALL_FLAGS => Self::Flags( - parse_u32(payload) - .context("failed to parse TCA_MATCHALL_FLAGS")?, - ), - _ => Self::Other( - DefaultNla::parse(buf).context("failed to parse u32 nla")?, - ), + TCA_MATCHALL_FLAGS => { + Self::Flags(parse_u32(payload).map_err(|error| { + TcError::ParseFilterMatchallOption { + kind: "TCA_MATCHALL_FLAGS", + error, + } + })?) + } + kind => Self::Other(DefaultNla::parse(buf).map_err(|error| { + TcError::UnknownFilterMatchAllOption { + kind: kind.to_string(), + error, + } + })?), }) } } diff --git a/src/tc/header.rs b/src/tc/header.rs index 1b4dc824..13765f6d 100644 --- a/src/tc/header.rs +++ b/src/tc/header.rs @@ -1,13 +1,12 @@ // SPDX-License-Identifier: MIT +use crate::AddressFamily; use netlink_packet_utils::{ - nla::{NlaBuffer, NlasIterator}, + nla::{NlaBuffer, NlaError, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -use crate::AddressFamily; - const TC_HEADER_LEN: usize = 20; buffer!(TcMessageBuffer(TC_HEADER_LEN) { @@ -24,7 +23,7 @@ buffer!(TcMessageBuffer(TC_HEADER_LEN) { impl<'a, T: AsRef<[u8]> + ?Sized> TcMessageBuffer<&'a T> { pub fn attributes( &self, - ) -> impl Iterator, DecodeError>> { + ) -> impl Iterator, NlaError>> { NlasIterator::new(self.payload()) } } @@ -61,7 +60,8 @@ impl Emitable for TcHeader { } impl> Parseable> for TcHeader { - fn parse(buf: &TcMessageBuffer) -> Result { + type Error = (); + fn parse(buf: &TcMessageBuffer) -> Result { Ok(Self { family: buf.family().into(), index: buf.index(), diff --git a/src/tc/message.rs b/src/tc/message.rs index 00a6d7bf..740b2088 100644 --- a/src/tc/message.rs +++ b/src/tc/message.rs @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, +use super::{TcAttribute, TcError, TcHeader, TcMessageBuffer}; +use netlink_packet_utils::traits::{ + Emitable, Parseable, ParseableParametrized, }; -use super::{TcAttribute, TcHeader, TcMessageBuffer}; - #[derive(Debug, PartialEq, Eq, Clone, Default)] #[non_exhaustive] pub struct TcMessage { @@ -37,12 +34,11 @@ impl TcMessage { } impl<'a, T: AsRef<[u8]> + 'a> Parseable> for TcMessage { - fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { Ok(Self { - header: TcHeader::parse(buf) - .context("failed to parse tc message header")?, - attributes: Vec::::parse(buf) - .context("failed to parse tc message NLAs")?, + header: TcHeader::parse(buf).unwrap(), + attributes: Vec::::parse(buf)?, }) } } @@ -50,7 +46,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> for TcMessage { impl<'a, T: AsRef<[u8]> + 'a> Parseable> for Vec { - fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &TcMessageBuffer<&'a T>) -> Result { let mut attributes = vec![]; let mut kind = String::new(); diff --git a/src/tc/mod.rs b/src/tc/mod.rs index 56608bd2..b67485fc 100644 --- a/src/tc/mod.rs +++ b/src/tc/mod.rs @@ -2,6 +2,7 @@ mod actions; mod attribute; +mod error; mod filters; mod header; mod message; @@ -16,6 +17,7 @@ pub use self::actions::{ TcNat, TcNatBuffer, TcNatFlags, }; pub use self::attribute::TcAttribute; +pub use self::error::TcError; pub use self::filters::{ TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, TcU32Key, TcU32OptionFlags, TcU32Selector, TcU32SelectorFlags, diff --git a/src/tc/options.rs b/src/tc/options.rs index 1b180f4c..c6a9e1ce 100644 --- a/src/tc/options.rs +++ b/src/tc/options.rs @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use super::{ + TcError, TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, + TcFilterU32Option, TcQdiscFqCodel, TcQdiscFqCodelOption, TcQdiscIngress, + TcQdiscIngressOption, +}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, traits::{Parseable, ParseableParametrized}, - DecodeError, -}; - -use super::{ - TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, - TcQdiscFqCodel, TcQdiscFqCodelOption, TcQdiscIngress, TcQdiscIngressOption, }; #[derive(Debug, PartialEq, Eq, Clone)] @@ -62,31 +60,33 @@ impl<'a, T> ParseableParametrized, &str> for TcOption where T: AsRef<[u8]> + ?Sized, { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, - ) -> Result { + ) -> Result { Ok(match kind { TcQdiscIngress::KIND => { - Self::Ingress(TcQdiscIngressOption::parse(buf).context( - "failed to parse ingress TCA_OPTIONS attributes", + Self::Ingress(TcQdiscIngressOption::parse(buf).map_err( + |error| TcError::ParseTcaOptionAttributes { + kind: "ingress", + error, + }, )?) } TcQdiscFqCodel::KIND => { - Self::FqCodel(TcQdiscFqCodelOption::parse(buf).context( - "failed to parse fq_codel TCA_OPTIONS attributes", - )?) + Self::FqCodel(TcQdiscFqCodelOption::parse(buf)?) } - TcFilterU32::KIND => Self::U32( - TcFilterU32Option::parse(buf) - .context("failed to parse u32 TCA_OPTIONS attributes")?, - ), + TcFilterU32::KIND => Self::U32(TcFilterU32Option::parse(buf)?), TcFilterMatchAll::KIND => { - Self::MatchAll(TcFilterMatchAllOption::parse(buf).context( - "failed to parse matchall TCA_OPTIONS attributes", - )?) + Self::MatchAll(TcFilterMatchAllOption::parse(buf)?) } - _ => Self::Other(DefaultNla::parse(buf)?), + kind => Self::Other(DefaultNla::parse(buf).map_err(|error| { + TcError::UnknownOption { + kind: kind.to_string(), + error, + } + })?), }) } } @@ -97,10 +97,11 @@ impl<'a, T> ParseableParametrized, &str> for VecTcOption where T: AsRef<[u8]> + ?Sized, { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, - ) -> Result { + ) -> Result { Ok(match kind { TcFilterU32::KIND | TcFilterMatchAll::KIND @@ -108,16 +109,8 @@ where | TcQdiscFqCodel::KIND => { let mut nlas = vec![]; for nla in NlasIterator::new(buf.value()) { - let nla = nla.context(format!( - "Invalid TCA_OPTIONS for kind: {kind}", - ))?; - nlas.push( - TcOption::parse_with_param(&nla, kind).context( - format!( - "Failed to parse TCA_OPTIONS for kind: {kind}", - ), - )?, - ) + let nla = nla?; + nlas.push(TcOption::parse_with_param(&nla, kind)?) } Self(nlas) } @@ -125,7 +118,14 @@ where // should place a nla_nest here. The `sfq` qdisc kernel code is // using single NLA instead nested ones. Hence we are storing // unknown Nla as Vec with single item. - _ => Self(vec![TcOption::Other(DefaultNla::parse(buf)?)]), + kind => { + Self(vec![TcOption::Other(DefaultNla::parse(buf).map_err( + |error| TcError::UnknownOption { + kind: kind.to_string(), + error, + }, + )?)]) + } }) } } diff --git a/src/tc/qdiscs/fq_codel.rs b/src/tc/qdiscs/fq_codel.rs index a19484b4..11d5e378 100644 --- a/src/tc/qdiscs/fq_codel.rs +++ b/src/tc/qdiscs/fq_codel.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -use anyhow::Context; +use crate::tc::TcError; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, @@ -32,12 +32,10 @@ pub enum TcFqCodelXstats { } impl + ?Sized> Parseable for TcFqCodelXstats { - fn parse(buf: &T) -> Result { + type Error = TcError; + fn parse(buf: &T) -> Result { if buf.as_ref().len() < 4 { - return Err(DecodeError::from(format!( - "Invalid TcFqCodelXstats {:?}", - buf.as_ref() - ))); + return Err(TcError::InvalidXstatsLength(buf.as_ref().len())); } let mut buf_type_bytes = [0; 4]; buf_type_bytes.copy_from_slice(&buf.as_ref()[0..4]); @@ -46,14 +44,22 @@ impl + ?Sized> Parseable for TcFqCodelXstats { match buf_type { TCA_FQ_CODEL_XSTATS_QDISC => { - Ok(Self::Qdisc(TcFqCodelQdStats::parse( - &TcFqCodelQdStatsBuffer::new(&buf.as_ref()[4..]), - )?)) + // unwrap: we never fail below to parse TcFqCodelQdStats. + Ok(Self::Qdisc( + TcFqCodelQdStats::parse(&TcFqCodelQdStatsBuffer::new( + &buf.as_ref()[4..], + )) + .unwrap(), + )) } TCA_FQ_CODEL_XSTATS_CLASS => { - Ok(Self::Class(TcFqCodelClStats::parse( - &TcFqCodelClStatsBuffer::new(&buf.as_ref()[4..]), - )?)) + // unwrap: we never fail below to parse TcFqCodelQdStats. + Ok(Self::Class( + TcFqCodelClStats::parse(&TcFqCodelClStatsBuffer::new( + &buf.as_ref()[4..], + )) + .unwrap(), + )) } _ => Ok(Self::Other(buf.as_ref().to_vec())), } @@ -117,7 +123,8 @@ buffer!(TcFqCodelQdStatsBuffer(TC_FQ_CODEL_QD_STATS_LEN) { }); impl> Parseable> for TcFqCodelQdStats { - fn parse(buf: &TcFqCodelQdStatsBuffer) -> Result { + type Error = (); + fn parse(buf: &TcFqCodelQdStatsBuffer) -> Result { Ok(Self { maxpacket: buf.maxpacket(), drop_overlimit: buf.drop_overlimit(), @@ -172,6 +179,7 @@ buffer!(TcFqCodelClStatsBuffer(TC_FQ_CODEL_CL_STATS_LEN) { }); impl> Parseable> for TcFqCodelClStats { + type Error = DecodeError; fn parse(buf: &TcFqCodelClStatsBuffer) -> Result { Ok(Self { deficit: buf.deficit(), @@ -285,57 +293,99 @@ impl Nla for TcQdiscFqCodelOption { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcQdiscFqCodelOption { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { + type Error = TcError; + fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { - TCA_FQ_CODEL_TARGET => Self::Target( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_TARGET")?, - ), - TCA_FQ_CODEL_LIMIT => Self::Limit( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_LIMIT")?, - ), - TCA_FQ_CODEL_INTERVAL => Self::Interval( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_INTERVAL")?, - ), - TCA_FQ_CODEL_ECN => Self::Ecn( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_ECN")?, - ), - TCA_FQ_CODEL_FLOWS => Self::Flows( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_FLOWS")?, - ), - TCA_FQ_CODEL_QUANTUM => Self::Quantum( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_QUANTUM")?, - ), - TCA_FQ_CODEL_CE_THRESHOLD => Self::CeThreshold( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_CETHRESHOLD")?, - ), - TCA_FQ_CODEL_DROP_BATCH_SIZE => Self::DropBatchSize( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_DROP_BATCH_SIZE")?, - ), - TCA_FQ_CODEL_MEMORY_LIMIT => Self::MemoryLimit( - parse_u32(payload) - .context("failed to parse TCA_FQ_CODEL_MEMORY_LIMIT")?, - ), - TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR => { - Self::CeThresholdSelector(parse_u8(payload).context( - "failed to parse TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR", - )?) + TCA_FQ_CODEL_TARGET => { + Self::Target(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_TARGET", + error, + } + })?) } + TCA_FQ_CODEL_LIMIT => { + Self::Limit(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_LIMIT", + error, + } + })?) + } + TCA_FQ_CODEL_INTERVAL => { + Self::Interval(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_INTERVAL", + error, + } + })?) + } + TCA_FQ_CODEL_ECN => { + Self::Ecn(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_ECN", + error, + } + })?) + } + TCA_FQ_CODEL_FLOWS => { + Self::Flows(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_FLOWS", + error, + } + })?) + } + TCA_FQ_CODEL_QUANTUM => { + Self::Quantum(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_QUANTUM", + error, + } + })?) + } + TCA_FQ_CODEL_CE_THRESHOLD => { + Self::CeThreshold(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_CE_THRESHOLD", + error, + } + })?) + } + TCA_FQ_CODEL_DROP_BATCH_SIZE => { + Self::DropBatchSize(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_DROP_BATCH_SIZE", + error, + } + })?) + } + TCA_FQ_CODEL_MEMORY_LIMIT => { + Self::MemoryLimit(parse_u32(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_MEMORY_LIMIT", + error, + } + })?) + } + TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR => Self::CeThresholdSelector( + parse_u8(payload).map_err(|error| TcError::InvalidValue { + kind: "TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR", + error, + })?, + ), TCA_FQ_CODEL_CE_THRESHOLD_MASK => { - Self::CeThresholdMask(parse_u8(payload).context( - "failed to parse TCA_FQ_CODEL_CE_THRESHOLD_MASK", - )?) + Self::CeThresholdMask(parse_u8(payload).map_err(|error| { + TcError::InvalidValue { + kind: "TCA_FQ_CODEL_CE_THRESHOLD_MASK", + error, + } + })?) } - _ => Self::Other( - DefaultNla::parse(buf).context("failed to parse u32 nla")?, + kind => Self::Other( + DefaultNla::parse(buf) + .map_err(|error| TcError::UnknownNla { kind, error })?, ), }) } diff --git a/src/tc/qdiscs/ingress.rs b/src/tc/qdiscs/ingress.rs index c87a29e0..a369ef70 100644 --- a/src/tc/qdiscs/ingress.rs +++ b/src/tc/qdiscs/ingress.rs @@ -3,7 +3,6 @@ // Currently, the qdisc ingress does not have any attribute, kernel // just start a empty nla_nest. This is just a place holder -use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, DecodeError, Parseable, @@ -46,9 +45,8 @@ impl Nla for TcQdiscIngressOption { impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for TcQdiscIngressOption { + type Error = DecodeError; fn parse(buf: &NlaBuffer<&'a T>) -> Result { - Ok(Self::Other( - DefaultNla::parse(buf).context("failed to parse ingress nla")?, - )) + Ok(Self::Other(DefaultNla::parse(buf)?)) } } diff --git a/src/tc/stats/basic.rs b/src/tc/stats/basic.rs index 786fee9c..bb5ecca8 100644 --- a/src/tc/stats/basic.rs +++ b/src/tc/stats/basic.rs @@ -24,7 +24,8 @@ buffer!(TcStatsBasicBuffer(STATS_BASIC_LEN) { }); impl> Parseable> for TcStatsBasic { - fn parse(buf: &TcStatsBasicBuffer) -> Result { + type Error = (); + fn parse(buf: &TcStatsBasicBuffer) -> Result { Ok(TcStatsBasic { bytes: buf.bytes(), packets: buf.packets(), diff --git a/src/tc/stats/compat.rs b/src/tc/stats/compat.rs index 5d8cd397..f193fc28 100644 --- a/src/tc/stats/compat.rs +++ b/src/tc/stats/compat.rs @@ -41,6 +41,7 @@ buffer!(TcStatsBuffer(STATS_LEN) { }); impl> Parseable> for TcStats { + type Error = DecodeError; fn parse(buf: &TcStatsBuffer) -> Result { Ok(Self { bytes: buf.bytes(), diff --git a/src/tc/stats/queue.rs b/src/tc/stats/queue.rs index 9909e819..fff0186f 100644 --- a/src/tc/stats/queue.rs +++ b/src/tc/stats/queue.rs @@ -32,7 +32,8 @@ buffer!(TcStatsQueueBuffer( STATS_QUEUE_LEN) { }); impl> Parseable> for TcStatsQueue { - fn parse(buf: &TcStatsQueueBuffer) -> Result { + type Error = (); + fn parse(buf: &TcStatsQueueBuffer) -> Result { Ok(Self { qlen: buf.qlen(), backlog: buf.backlog(), diff --git a/src/tc/stats/stats2.rs b/src/tc/stats/stats2.rs index 9cab4f1e..8239a34b 100644 --- a/src/tc/stats/stats2.rs +++ b/src/tc/stats/stats2.rs @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT -use netlink_packet_utils::{ - nla::{DefaultNla, Nla, NlaBuffer}, - traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, -}; - use super::{ TcStatsBasic, TcStatsBasicBuffer, TcStatsQueue, TcStatsQueueBuffer, TcXstats, }; +use crate::tc::TcError; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + traits::{Emitable, Parseable, ParseableParametrized}, +}; const TCA_STATS_BASIC: u16 = 1; // const TCA_STATS_RATE_EST: u16 = 2; // TODO @@ -65,23 +64,30 @@ impl<'a, T> ParseableParametrized, &str> for TcStats2 where T: AsRef<[u8]> + ?Sized, { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, - ) -> Result { + ) -> Result { let payload = buf.value(); Ok(match buf.kind() { TCA_STATS_APP => Self::App(TcXstats::parse_with_param(buf, kind)?), - TCA_STATS_BASIC => Self::Basic(TcStatsBasic::parse( - &TcStatsBasicBuffer::new(payload), - )?), - TCA_STATS_QUEUE => Self::Queue(TcStatsQueue::parse( - &TcStatsQueueBuffer::new(payload), - )?), - TCA_STATS_BASIC_HW => Self::BasicHw(TcStatsBasic::parse( - &TcStatsBasicBuffer::new(payload), - )?), - _ => Self::Other(DefaultNla::parse(buf)?), + TCA_STATS_BASIC => Self::Basic( + // unwrap: TCStatsBasic doesn't fail to parse. + TcStatsBasic::parse(&TcStatsBasicBuffer::new(payload)).unwrap(), + ), + TCA_STATS_QUEUE => Self::Queue( + // unwrap: TCStatsQueue doesn't fail to parse. + TcStatsQueue::parse(&TcStatsQueueBuffer::new(payload)).unwrap(), + ), + TCA_STATS_BASIC_HW => Self::BasicHw( + // unwrap: TCStatsBasic doesn't fail to parse. + TcStatsBasic::parse(&TcStatsBasicBuffer::new(payload)).unwrap(), + ), + kind => Self::Other( + DefaultNla::parse(buf) + .map_err(|error| TcError::UnknownNla { kind, error })?, + ), }) } } diff --git a/src/tc/stats/xstats.rs b/src/tc/stats/xstats.rs index 38522e2a..bea137c8 100644 --- a/src/tc/stats/xstats.rs +++ b/src/tc/stats/xstats.rs @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT +use crate::tc::{TcError, TcFqCodelXstats, TcQdiscFqCodel}; use netlink_packet_utils::{ nla::NlaBuffer, traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, }; -use crate::tc::{TcFqCodelXstats, TcQdiscFqCodel}; - #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum TcXstats { @@ -35,10 +33,11 @@ impl<'a, T> ParseableParametrized, &str> for TcXstats where T: AsRef<[u8]> + ?Sized, { + type Error = TcError; fn parse_with_param( buf: &NlaBuffer<&'a T>, kind: &str, - ) -> Result { + ) -> Result { Ok(match kind { TcQdiscFqCodel::KIND => { TcXstats::FqCodel(TcFqCodelXstats::parse(buf.value())?)