diff --git a/examples/dump_neighbours.rs b/examples/dump_neighbours.rs index d2beb72d..8b21c0b1 100644 --- a/examples/dump_neighbours.rs +++ b/examples/dump_neighbours.rs @@ -1,17 +1,13 @@ // SPDX-License-Identifier: MIT -use std::{convert::TryFrom, net::IpAddr, string::ToString}; +use std::string::ToString; use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ - constants::{ - NUD_DELAY, NUD_FAILED, NUD_INCOMPLETE, NUD_NOARP, NUD_NONE, - NUD_PERMANENT, NUD_PROBE, NUD_REACHABLE, NUD_STALE, - }, - nlas::neighbour::Nla, - AddressFamily, NeighbourMessage, RtnlMessage, + neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourMessage}, + AddressFamily, RtnlMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; @@ -57,10 +53,9 @@ fn main() { NetlinkPayload::InnerMessage(RtnlMessage::NewNeighbour( entry, )) => { - let address_family = entry.header.family as u16; - if address_family == u8::from(AddressFamily::Inet) as u16 - || address_family - == u8::from(AddressFamily::Inet6) as u16 + let address_family = entry.header.family; + if address_family == AddressFamily::Inet + || address_family == AddressFamily::Inet6 { print_entry(entry); } @@ -81,63 +76,45 @@ fn main() { } } -fn format_ip(buf: &[u8]) -> String { - if let Ok(bytes) = <&[u8; 4]>::try_from(buf) { - IpAddr::from(*bytes).to_string() - } else if let Ok(bytes) = <&[u8; 16]>::try_from(buf) { - IpAddr::from(*bytes).to_string() +fn format_ip(addr: &NeighbourAddress) -> String { + if let NeighbourAddress::Inet(ip) = addr { + ip.to_string() + } else if let NeighbourAddress::Inet6(ip) = addr { + ip.to_string() } else { panic!("Invalid IP Address"); } } fn format_mac(buf: &[u8]) -> String { - assert_eq!(buf.len(), 6); - format!( - "{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] - ) -} - -fn state_str(value: u16) -> &'static str { - match value { - NUD_INCOMPLETE => "INCOMPLETE", - NUD_REACHABLE => "REACHABLE", - NUD_STALE => "STALE", - NUD_DELAY => "DELAY", - NUD_PROBE => "PROBE", - NUD_FAILED => "FAILED", - NUD_NOARP => "NOARP", - NUD_PERMANENT => "PERMANENT", - NUD_NONE => "NONE", - _ => "UNKNOWN", + if buf.len() == 6 { + format!( + "{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] + ) + } else { + "00:00:00:00:00:00".into() } } fn print_entry(entry: NeighbourMessage) { - let state = state_str(entry.header.state); - let dest = entry - .nlas - .iter() - .find_map(|nla| { - if let Nla::Destination(addr) = nla { - Some(format_ip(&addr[..])) + let state = entry.header.state; + if let (Some(dest), Some(lladdr)) = ( + entry.attributes.iter().find_map(|nla| { + if let NeighbourAttribute::Destination(addr) = nla { + Some(format_ip(addr)) } else { None } - }) - .unwrap(); - let lladdr = entry - .nlas - .iter() - .find_map(|nla| { - if let Nla::LinkLocalAddress(addr) = nla { - Some(format_mac(&addr[..])) + }), + entry.attributes.iter().find_map(|nla| { + if let NeighbourAttribute::LinkLocalAddress(addr) = nla { + Some(format_mac(addr)) } else { None } - }) - .unwrap(); - - println!("{dest:<30} {lladdr:<20} ({state})"); + }), + ) { + println!("{dest:<30} {lladdr:<20} ({state})"); + } } diff --git a/src/lib.rs b/src/lib.rs index 4bff18db..95607ad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub use self::rtnl::*; pub mod address; pub mod link; +pub mod neighbour; pub mod route; pub mod rule; pub mod tc; diff --git a/src/neighbour/address.rs b/src/neighbour/address.rs new file mode 100644 index 00000000..31df8cf0 --- /dev/null +++ b/src/neighbour/address.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +use std::net::{Ipv4Addr, Ipv6Addr}; + +use netlink_packet_utils::{DecodeError, Emitable}; + +use crate::{ + ip::{ + emit_ip_to_buffer, parse_ipv4_addr, parse_ipv6_addr, IPV4_ADDR_LEN, + IPV6_ADDR_LEN, + }, + AddressFamily, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum NeighbourAddress { + Inet(Ipv4Addr), + Inet6(Ipv6Addr), + Other(Vec), +} + +impl NeighbourAddress { + pub(crate) fn parse_with_param( + address_family: AddressFamily, + payload: &[u8], + ) -> Result { + Ok(match address_family { + AddressFamily::Inet => Self::Inet(parse_ipv4_addr(payload)?), + AddressFamily::Inet6 => Self::Inet6(parse_ipv6_addr(payload)?), + _ => Self::Other(payload.to_vec()), + }) + } +} + +impl Emitable for NeighbourAddress { + fn buffer_len(&self) -> usize { + match self { + Self::Inet(_) => IPV4_ADDR_LEN, + Self::Inet6(_) => IPV6_ADDR_LEN, + Self::Other(v) => v.len(), + } + } + + fn emit(&self, buffer: &mut [u8]) { + match self { + Self::Inet(v) => emit_ip_to_buffer(&((*v).into()), buffer), + Self::Inet6(v) => emit_ip_to_buffer(&((*v).into()), buffer), + Self::Other(v) => buffer.copy_from_slice(v.as_slice()), + } + } +} + +impl From for NeighbourAddress { + fn from(v: Ipv4Addr) -> Self { + Self::Inet(v) + } +} + +impl From for NeighbourAddress { + fn from(v: Ipv6Addr) -> Self { + Self::Inet6(v) + } +} diff --git a/src/neighbour/attribute.rs b/src/neighbour/attribute.rs new file mode 100644 index 00000000..ec871eda --- /dev/null +++ b/src/neighbour/attribute.rs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +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, +}; + +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; +const NDA_PROBES: u16 = 4; +const NDA_VLAN: u16 = 5; +const NDA_PORT: u16 = 6; +const NDA_VNI: u16 = 7; +const NDA_IFINDEX: u16 = 8; +// Kernel constant name is NDA_MASTER +const NDA_CONTROLLER: u16 = 9; +const NDA_LINK_NETNSID: u16 = 10; +const NDA_SRC_VNI: u16 = 11; +const NDA_PROTOCOL: u16 = 12; +// const NDA_NH_ID: u16 = 13; +// const NDA_FDB_EXT_ATTRS: u16 = 14; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum NeighbourAttribute { + Destination(NeighbourAddress), + LinkLocalAddress(Vec), + CacheInfo(NeighbourCacheInfo), + Probes(u32), + Vlan(u16), + Port(u16), + Vni(u32), + IfIndex(u32), + Controller(u32), + LinkNetNsId(u32), + SourceVni(u32), + Protocol(RouteProtocol), + Other(DefaultNla), +} + +impl Nla for NeighbourAttribute { + fn value_len(&self) -> usize { + match self { + Self::LinkLocalAddress(bytes) => bytes.len(), + Self::Destination(v) => v.buffer_len(), + Self::CacheInfo(v) => v.buffer_len(), + Self::Vlan(_) | Self::Port(_) => 2, + Self::Protocol(v) => v.buffer_len(), + Self::Probes(_) + | Self::LinkNetNsId(_) + | Self::Controller(_) + | Self::Vni(_) + | Self::IfIndex(_) + | Self::SourceVni(_) => 4, + Self::Other(attr) => attr.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Destination(v) => v.emit(buffer), + Self::LinkLocalAddress(bytes) => { + buffer.copy_from_slice(bytes.as_slice()) + } + Self::CacheInfo(v) => v.emit(buffer), + Self::Vlan(value) => NativeEndian::write_u16(buffer, *value), + Self::Port(value) => BigEndian::write_u16(buffer, *value), + Self::Probes(value) + | Self::LinkNetNsId(value) + | Self::Controller(value) + | Self::Vni(value) + | Self::IfIndex(value) + | Self::SourceVni(value) => NativeEndian::write_u32(buffer, *value), + Self::Protocol(v) => v.emit(buffer), + Self::Other(attr) => attr.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Destination(_) => NDA_DST, + Self::LinkLocalAddress(_) => NDA_LLADDR, + Self::CacheInfo(_) => NDA_CACHEINFO, + Self::Probes(_) => NDA_PROBES, + Self::Vlan(_) => NDA_VLAN, + Self::Port(_) => NDA_PORT, + Self::Vni(_) => NDA_VNI, + Self::IfIndex(_) => NDA_IFINDEX, + Self::Controller(_) => NDA_CONTROLLER, + Self::LinkNetNsId(_) => NDA_LINK_NETNSID, + Self::SourceVni(_) => NDA_SRC_VNI, + Self::Protocol(_) => NDA_PROTOCOL, + Self::Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> + ParseableParametrized, AddressFamily> + for NeighbourAttribute +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + address_family: AddressFamily, + ) -> 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))?, + ), + 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 + ))?, + ), + NDA_PROBES => { + Self::Probes(parse_u32(payload).context(format!( + "invalid NDA_PROBES value {:?}", + payload + ))?) + } + 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), + )?) + } + _ => Self::Other( + DefaultNla::parse(buf) + .context("invalid link NLA value (unknown type)")?, + ), + }) + } +} diff --git a/src/rtnl/neighbour/nlas/cache_info.rs b/src/neighbour/cache_info.rs similarity index 67% rename from src/rtnl/neighbour/nlas/cache_info.rs rename to src/neighbour/cache_info.rs index be098d28..86c61865 100644 --- a/src/rtnl/neighbour/nlas/cache_info.rs +++ b/src/neighbour/cache_info.rs @@ -7,24 +7,26 @@ use netlink_packet_utils::{ #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] -pub struct CacheInfo { +pub struct NeighbourCacheInfo { pub confirmed: u32, pub used: u32, pub updated: u32, pub refcnt: u32, } -pub const NEIGHBOUR_CACHE_INFO_LEN: usize = 16; +const NEIGHBOUR_CACHE_INFO_LEN: usize = 16; -buffer!(CacheInfoBuffer(NEIGHBOUR_CACHE_INFO_LEN) { +buffer!(NeighbourCacheInfoBuffer(NEIGHBOUR_CACHE_INFO_LEN) { confirmed: (u32, 0..4), used: (u32, 4..8), updated: (u32, 8..12), refcnt: (u32, 12..16), }); -impl> Parseable> for CacheInfo { - fn parse(buf: &CacheInfoBuffer) -> Result { +impl> Parseable> + for NeighbourCacheInfo +{ + fn parse(buf: &NeighbourCacheInfoBuffer) -> Result { Ok(Self { confirmed: buf.confirmed(), used: buf.used(), @@ -34,13 +36,13 @@ impl> Parseable> for CacheInfo { } } -impl Emitable for CacheInfo { +impl Emitable for NeighbourCacheInfo { fn buffer_len(&self) -> usize { NEIGHBOUR_CACHE_INFO_LEN } fn emit(&self, buffer: &mut [u8]) { - let mut buffer = CacheInfoBuffer::new(buffer); + let mut buffer = NeighbourCacheInfoBuffer::new(buffer); buffer.set_confirmed(self.confirmed); buffer.set_used(self.used); buffer.set_updated(self.updated); diff --git a/src/neighbour/flags.rs b/src/neighbour/flags.rs new file mode 100644 index 00000000..b7e554e7 --- /dev/null +++ b/src/neighbour/flags.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT + +const NTF_USE: u8 = 1 << 0; +const NTF_SELF: u8 = 1 << 1; +// Kernel constant name is NTF_MASTER +const NTF_CONTROLLER: u8 = 1 << 2; +const NTF_PROXY: u8 = 1 << 3; +const NTF_EXT_LEARNED: u8 = 1 << 4; +const NTF_OFFLOADED: u8 = 1 << 5; +const NTF_STICKY: u8 = 1 << 6; +const NTF_ROUTER: u8 = 1 << 7; + +#[derive(Clone, Eq, PartialEq, Debug, Copy)] +#[non_exhaustive] +pub enum NeighbourFlag { + Use, + // Hold NTF_SELF as Self is not rust reserved keyword + Own, + Controller, + Proxy, + ExtLearned, + Offloaded, + Sticky, + Router, + // No Other required as these are all 8 bits. +} + +const ALL_RULE_FLAGS: [NeighbourFlag; 8] = [ + NeighbourFlag::Use, + NeighbourFlag::Own, + NeighbourFlag::Controller, + NeighbourFlag::Proxy, + NeighbourFlag::ExtLearned, + NeighbourFlag::Offloaded, + NeighbourFlag::Sticky, + NeighbourFlag::Router, +]; + +impl From for u8 { + fn from(v: NeighbourFlag) -> u8 { + match v { + NeighbourFlag::Use => NTF_USE, + NeighbourFlag::Own => NTF_SELF, + NeighbourFlag::Controller => NTF_CONTROLLER, + NeighbourFlag::Proxy => NTF_PROXY, + NeighbourFlag::ExtLearned => NTF_EXT_LEARNED, + NeighbourFlag::Offloaded => NTF_OFFLOADED, + NeighbourFlag::Sticky => NTF_STICKY, + NeighbourFlag::Router => NTF_ROUTER, + } + } +} + +#[derive(Clone, Eq, PartialEq, Debug, Default)] +pub(crate) struct VecNeighbourFlag(pub(crate) Vec); + +impl From for VecNeighbourFlag { + fn from(d: u8) -> Self { + let mut ret = Vec::new(); + for flag in ALL_RULE_FLAGS { + if (d & (u8::from(flag))) > 0 { + ret.push(flag); + } + } + Self(ret) + } +} + +impl From<&VecNeighbourFlag> for u8 { + fn from(v: &VecNeighbourFlag) -> u8 { + let mut d: u8 = 0; + for flag in &v.0 { + d += u8::from(*flag); + } + d + } +} diff --git a/src/rtnl/neighbour/header.rs b/src/neighbour/header.rs similarity index 57% rename from src/rtnl/neighbour/header.rs rename to src/neighbour/header.rs index 89520106..5de35e80 100644 --- a/src/rtnl/neighbour/header.rs +++ b/src/neighbour/header.rs @@ -1,11 +1,32 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ + nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -use crate::{NeighbourMessageBuffer, NEIGHBOUR_HEADER_LEN}; +use super::{flags::VecNeighbourFlag, NeighbourFlag, NeighbourState}; +use crate::{route::RouteType, AddressFamily}; + +const NEIGHBOUR_HEADER_LEN: usize = 12; + +buffer!(NeighbourMessageBuffer(NEIGHBOUR_HEADER_LEN) { + family: (u8, 0), + ifindex: (u32, 4..8), + state: (u16, 8..10), + flags: (u8, 10), + kind: (u8, 11), + payload:(slice, NEIGHBOUR_HEADER_LEN..), +}); + +impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourMessageBuffer<&'a T> { + pub fn attributes( + &self, + ) -> impl Iterator, DecodeError>> { + NlasIterator::new(self.payload()) + } +} /// Neighbour headers have the following structure: /// @@ -21,29 +42,29 @@ use crate::{NeighbourMessageBuffer, NEIGHBOUR_HEADER_LEN}; /// ``` /// /// `NeighbourHeader` exposes all these fields. +// Linux kernel struct `struct ndmsg` #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct NeighbourHeader { - pub family: u8, + pub family: AddressFamily, pub ifindex: u32, - /// Neighbour cache entry state. It should be set to one of the - /// `NUD_*` constants - pub state: u16, + /// Neighbour cache entry state. + pub state: NeighbourState, /// Neighbour cache entry flags. It should be set to a combination /// of the `NTF_*` constants - pub flags: u8, + pub flags: Vec, /// Neighbour cache entry type. It should be set to one of the /// `NDA_*` constants. - pub ntype: u8, + pub kind: RouteType, } impl> Parseable> for NeighbourHeader { fn parse(buf: &NeighbourMessageBuffer) -> Result { Ok(Self { - family: buf.family(), + family: buf.family().into(), ifindex: buf.ifindex(), - state: buf.state(), - flags: buf.flags(), - ntype: buf.ntype(), + state: buf.state().into(), + flags: VecNeighbourFlag::from(buf.flags()).0, + kind: buf.kind().into(), }) } } @@ -55,10 +76,10 @@ impl Emitable for NeighbourHeader { fn emit(&self, buffer: &mut [u8]) { let mut packet = NeighbourMessageBuffer::new(buffer); - packet.set_family(self.family); + packet.set_family(self.family.into()); packet.set_ifindex(self.ifindex); - packet.set_state(self.state); - packet.set_flags(self.flags); - packet.set_ntype(self.ntype); + packet.set_state(self.state.into()); + packet.set_flags(u8::from(&VecNeighbourFlag(self.flags.to_vec()))); + packet.set_kind(self.kind.into()); } } diff --git a/src/neighbour/kind.rs b/src/neighbour/kind.rs new file mode 100644 index 00000000..9e570aae --- /dev/null +++ b/src/neighbour/kind.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT + +const NUD_INCOMPLETE: u16 = 0x01; +const NUD_REACHABLE: u16 = 0x02; +const NUD_STALE: u16 = 0x04; +const NUD_DELAY: u16 = 0x08; +const NUD_PROBE: u16 = 0x10; +const NUD_FAILED: u16 = 0x20; +const NUD_NOARP: u16 = 0x40; +const NUD_PERMANENT: u16 = 0x80; +const NUD_NONE: u16 = 0x00; + +/// Types that can be set in a `RTM_GETROUTE` +/// ([`NeightbourNetlinkMessage::GetNeightbour`]) message. +#[derive(Clone, Eq, PartialEq, Debug, Copy)] +#[non_exhaustive] +pub enum NeightbourType { + Incomplete, + Reachable, + Stale, + Delay, + Probe, + Failed, + Noarp, + Permanent, + None, + Other(u16), +} + +impl From for u16 { + fn from(v: NeightbourType) -> u16 { + match v { + NeightbourType::Incomplete => NUD_INCOMPLETE, + NeightbourType::Reachable => NUD_REACHABLE, + NeightbourType::Stale => NUD_STALE, + NeightbourType::Delay => NUD_DELAY, + NeightbourType::Probe => NUD_PROBE, + NeightbourType::Failed => NUD_FAILED, + NeightbourType::Noarp => NUD_NOARP, + NeightbourType::Permanent => NUD_PERMANENT, + NeightbourType::None => NUD_NONE, + NeightbourType::Other(d) => d, + } + } +} + +impl From for NeightbourType { + fn from(d: u16) -> Self { + match d { + NUD_INCOMPLETE => Self::Incomplete, + NUD_REACHABLE => Self::Reachable, + NUD_STALE => Self::Stale, + NUD_DELAY => Self::Delay, + NUD_PROBE => Self::Probe, + NUD_FAILED => Self::Failed, + NUD_NOARP => Self::Noarp, + NUD_PERMANENT => Self::Permanent, + NUD_NONE => Self::None, + _ => Self::Other(d), + } + } +} diff --git a/src/neighbour/message.rs b/src/neighbour/message.rs new file mode 100644 index 00000000..f5908584 --- /dev/null +++ b/src/neighbour/message.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + traits::{Emitable, Parseable, ParseableParametrized}, + DecodeError, +}; + +use super::{ + super::AddressFamily, NeighbourAttribute, NeighbourHeader, + NeighbourMessageBuffer, +}; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +#[non_exhaustive] +pub struct NeighbourMessage { + pub header: NeighbourHeader, + pub attributes: Vec, +} + +impl Emitable for NeighbourMessage { + fn buffer_len(&self) -> usize { + self.header.buffer_len() + self.attributes.as_slice().buffer_len() + } + + fn emit(&self, buffer: &mut [u8]) { + self.header.emit(buffer); + self.attributes + .as_slice() + .emit(&mut buffer[self.header.buffer_len()..]); + } +} + +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")?; + let address_family = header.family; + Ok(NeighbourMessage { + header, + attributes: Vec::::parse_with_param( + buf, + address_family, + ) + .context("failed to parse neighbour message NLAs")?, + }) + } +} + +impl<'a, T: AsRef<[u8]> + 'a> + ParseableParametrized, AddressFamily> + for Vec +{ + fn parse_with_param( + buf: &NeighbourMessageBuffer<&'a T>, + address_family: AddressFamily, + ) -> Result { + let mut attributes = vec![]; + for nla_buf in buf.attributes() { + attributes.push(NeighbourAttribute::parse_with_param( + &nla_buf?, + address_family, + )?); + } + Ok(attributes) + } +} diff --git a/src/neighbour/mod.rs b/src/neighbour/mod.rs new file mode 100644 index 00000000..23b3c7fa --- /dev/null +++ b/src/neighbour/mod.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +mod address; +mod attribute; +mod cache_info; +pub(crate) mod flags; +mod header; +mod message; +mod state; + +#[cfg(test)] +mod tests; + +pub use self::address::NeighbourAddress; +pub use self::attribute::NeighbourAttribute; +pub use self::cache_info::{NeighbourCacheInfo, NeighbourCacheInfoBuffer}; +pub use self::flags::NeighbourFlag; +pub use self::header::{NeighbourHeader, NeighbourMessageBuffer}; +pub use self::message::NeighbourMessage; +pub use self::state::NeighbourState; diff --git a/src/neighbour/state.rs b/src/neighbour/state.rs new file mode 100644 index 00000000..1e7c3710 --- /dev/null +++ b/src/neighbour/state.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +const NUD_INCOMPLETE: u16 = 0x01; +const NUD_REACHABLE: u16 = 0x02; +const NUD_STALE: u16 = 0x04; +const NUD_DELAY: u16 = 0x08; +const NUD_PROBE: u16 = 0x10; +const NUD_FAILED: u16 = 0x20; +const NUD_NOARP: u16 = 0x40; +const NUD_PERMANENT: u16 = 0x80; +const NUD_NONE: u16 = 0x00; + +#[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] +#[non_exhaustive] +pub enum NeighbourState { + Incomplete, + Reachable, + Stale, + Delay, + Probe, + Failed, + Noarp, + Permanent, + #[default] + None, + Other(u16), +} + +impl From for u16 { + fn from(v: NeighbourState) -> u16 { + match v { + NeighbourState::Incomplete => NUD_INCOMPLETE, + NeighbourState::Reachable => NUD_REACHABLE, + NeighbourState::Stale => NUD_STALE, + NeighbourState::Delay => NUD_DELAY, + NeighbourState::Probe => NUD_PROBE, + NeighbourState::Failed => NUD_FAILED, + NeighbourState::Noarp => NUD_NOARP, + NeighbourState::Permanent => NUD_PERMANENT, + NeighbourState::None => NUD_NONE, + NeighbourState::Other(d) => d, + } + } +} + +impl From for NeighbourState { + fn from(d: u16) -> Self { + match d { + NUD_INCOMPLETE => Self::Incomplete, + NUD_REACHABLE => Self::Reachable, + NUD_STALE => Self::Stale, + NUD_DELAY => Self::Delay, + NUD_PROBE => Self::Probe, + NUD_FAILED => Self::Failed, + NUD_NOARP => Self::Noarp, + NUD_PERMANENT => Self::Permanent, + NUD_NONE => Self::None, + _ => Self::Other(d), + } + } +} + +impl std::fmt::Display for NeighbourState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Incomplete => write!(f, "incomplete"), + Self::Reachable => write!(f, "reachable"), + Self::Stale => write!(f, "stale"), + Self::Delay => write!(f, "delay"), + Self::Probe => write!(f, "probe"), + Self::Failed => write!(f, "failed"), + Self::Noarp => write!(f, "noarp"), + Self::Permanent => write!(f, "permanent"), + Self::None => write!(f, "none"), + Self::Other(d) => write!(f, "other({d}"), + } + } +} diff --git a/src/neighbour/tests/bridge.rs b/src/neighbour/tests/bridge.rs new file mode 100644 index 00000000..c7e5eae8 --- /dev/null +++ b/src/neighbour/tests/bridge.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::{ + neighbour::{ + NeighbourAttribute, NeighbourFlag, NeighbourHeader, NeighbourMessage, + NeighbourMessageBuffer, NeighbourState, + }, + route::RouteType, + AddressFamily, +}; + +// wireshark capture(netlink message header removed) of nlmon against command: +// ip -f bridge neighbour show +#[test] +fn test_bridge_neighbour_show() { + let raw = vec![ + 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x00, + 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x00, + ]; + + let expected = NeighbourMessage { + header: NeighbourHeader { + family: AddressFamily::Bridge, + ifindex: 3, + state: NeighbourState::Permanent, + flags: vec![NeighbourFlag::Own], + kind: RouteType::Unspec, + }, + attributes: vec![NeighbourAttribute::LinkLocalAddress(vec![ + 1, 0, 94, 0, 0, 1, + ])], + }; + + assert_eq!( + expected, + NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/neighbour/tests/ip.rs b/src/neighbour/tests/ip.rs new file mode 100644 index 00000000..5bacfed4 --- /dev/null +++ b/src/neighbour/tests/ip.rs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: MIT + +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::{ + neighbour::{ + NeighbourAttribute, NeighbourCacheInfo, NeighbourFlag, NeighbourHeader, + NeighbourMessage, NeighbourMessageBuffer, NeighbourState, + }, + route::{RouteProtocol, RouteType}, + AddressFamily, +}; + +// wireshark capture(netlink message header removed) of nlmon against command: +// ip -4 neighbour show +#[test] +fn test_ipv4_neighbour_show() { + let raw = vec![ + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x01, 0x00, 0xac, 0x11, 0x02, 0x01, 0x0a, 0x00, 0x02, 0x00, + 0x1c, 0x69, 0x7a, 0x07, 0xc3, 0x36, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x71, 0x01, 0x00, 0x00, + 0xcb, 0x0d, 0x1f, 0x00, 0xcb, 0x0d, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, + ]; + + let expected = NeighbourMessage { + header: NeighbourHeader { + family: AddressFamily::Inet, + ifindex: 3, + state: NeighbourState::Reachable, + flags: vec![], + kind: RouteType::Unicast, + }, + attributes: vec![ + NeighbourAttribute::Destination( + Ipv4Addr::from_str("172.17.2.1").unwrap().into(), + ), + NeighbourAttribute::LinkLocalAddress(vec![ + 28, 105, 122, 7, 195, 54, + ]), + NeighbourAttribute::Probes(1), + NeighbourAttribute::CacheInfo(NeighbourCacheInfo { + confirmed: 369, + used: 2035147, + updated: 2035147, + refcnt: 1, + }), + ], + }; + + assert_eq!( + expected, + NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} + +// wireshark capture(netlink message header removed) of nlmon against command: +// ip -6 neighbour show +#[test] +fn test_ipv6_neighbour_show() { + let raw = vec![ + 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x01, + 0x14, 0x00, 0x01, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x69, 0x7a, 0xff, 0xfe, 0x07, 0xc3, 0x36, 0x0a, 0x00, 0x02, 0x00, + 0x1c, 0x69, 0x7a, 0x07, 0xc3, 0x36, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x61, 0x76, 0x00, 0x00, + 0x61, 0x76, 0x00, 0x00, 0x8c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = NeighbourMessage { + header: NeighbourHeader { + family: AddressFamily::Inet6, + ifindex: 3, + state: NeighbourState::Stale, + flags: vec![NeighbourFlag::Router], + kind: RouteType::Unicast, + }, + attributes: vec![ + NeighbourAttribute::Destination( + Ipv6Addr::from_str("fe80::1e69:7aff:fe07:c336") + .unwrap() + .into(), + ), + NeighbourAttribute::LinkLocalAddress(vec![ + 28, 105, 122, 7, 195, 54, + ]), + NeighbourAttribute::Probes(1), + NeighbourAttribute::CacheInfo(NeighbourCacheInfo { + confirmed: 30305, + used: 30305, + updated: 25996, + refcnt: 0, + }), + ], + }; + + assert_eq!( + expected, + NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} + +// Setup +// ip neighbo add 172.17.2.99 dev wlan0 lladdr 00:11:22:33:44:55 \ +// protocol dhcp +// wireshark capture(netlink message header removed) of nlmon against command: +// ip -4 neighbour show +#[test] +fn test_ipv4_neighbour_protocol_show() { + let raw = vec![ + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, + 0x08, 0x00, 0x01, 0x00, 0xac, 0x11, 0x02, 0x63, 0x0a, 0x00, 0x02, 0x00, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x03, 0x00, 0x4d, 0x0b, 0x00, 0x00, + 0x4d, 0x0b, 0x00, 0x00, 0x4d, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x00, + ]; + + let expected = NeighbourMessage { + header: NeighbourHeader { + family: AddressFamily::Inet, + ifindex: 3, + state: NeighbourState::Permanent, + flags: vec![], + kind: RouteType::Unicast, + }, + attributes: vec![ + NeighbourAttribute::Destination( + Ipv4Addr::from_str("172.17.2.99").unwrap().into(), + ), + NeighbourAttribute::LinkLocalAddress(vec![ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, + ]), + NeighbourAttribute::Probes(0), + NeighbourAttribute::CacheInfo(NeighbourCacheInfo { + confirmed: 2893, + used: 2893, + updated: 2893, + refcnt: 0, + }), + NeighbourAttribute::Protocol(RouteProtocol::Dhcp), + ], + }; + + assert_eq!( + expected, + NeighbourMessage::parse(&NeighbourMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/neighbour/tests/mod.rs b/src/neighbour/tests/mod.rs new file mode 100644 index 00000000..2b942af0 --- /dev/null +++ b/src/neighbour/tests/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT + +#[cfg(test)] +mod bridge; +#[cfg(test)] +mod ip; diff --git a/src/route/header.rs b/src/route/header.rs index f783b6f3..0d6a1d4c 100644 --- a/src/route/header.rs +++ b/src/route/header.rs @@ -240,6 +240,29 @@ impl Default for RouteProtocol { } } +impl Parseable<[u8]> for RouteProtocol { + 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 + ))) + } + } +} + +impl Emitable for RouteProtocol { + fn buffer_len(&self) -> usize { + 1 + } + + fn emit(&self, buffer: &mut [u8]) { + buffer[0] = u8::from(*self); + } +} + const RT_SCOPE_UNIVERSE: u8 = 0; const RT_SCOPE_SITE: u8 = 200; const RT_SCOPE_LINK: u8 = 253; diff --git a/src/rtnl/buffer.rs b/src/rtnl/buffer.rs index 125d3621..94885fb4 100644 --- a/src/rtnl/buffer.rs +++ b/src/rtnl/buffer.rs @@ -10,11 +10,12 @@ use crate::{ address::{AddressHeader, AddressMessage, AddressMessageBuffer}, constants::*, link::{LinkMessage, LinkMessageBuffer}, + neighbour::{NeighbourMessage, NeighbourMessageBuffer}, route::{RouteHeader, RouteMessage, RouteMessageBuffer}, rule::{RuleMessage, RuleMessageBuffer}, tc::{TcMessage, TcMessageBuffer}, - NeighbourMessage, NeighbourMessageBuffer, NeighbourTableMessage, - NeighbourTableMessageBuffer, NsidMessage, NsidMessageBuffer, RtnlMessage, + NeighbourTableMessage, NeighbourTableMessageBuffer, NsidMessage, + NsidMessageBuffer, RtnlMessage, }; buffer!(RtnlMessageBuffer); diff --git a/src/rtnl/message.rs b/src/rtnl/message.rs index a4201f6d..e4dab626 100644 --- a/src/rtnl/message.rs +++ b/src/rtnl/message.rs @@ -11,8 +11,8 @@ use netlink_packet_core::{ use crate::{ address::AddressMessage, constants::*, link::LinkMessage, - route::RouteMessage, rule::RuleMessage, tc::TcMessage, NeighbourMessage, - NeighbourTableMessage, NsidMessage, RtnlMessageBuffer, + neighbour::NeighbourMessage, route::RouteMessage, rule::RuleMessage, + tc::TcMessage, NeighbourTableMessage, NsidMessage, RtnlMessageBuffer, }; #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/src/rtnl/mod.rs b/src/rtnl/mod.rs index b9a69a79..d148a880 100644 --- a/src/rtnl/mod.rs +++ b/src/rtnl/mod.rs @@ -1,11 +1,5 @@ // SPDX-License-Identifier: MIT -pub mod neighbour; -pub use neighbour::{ - NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, - NEIGHBOUR_HEADER_LEN, -}; - pub mod neighbour_table; pub use neighbour_table::{ NeighbourTableHeader, NeighbourTableMessage, NeighbourTableMessageBuffer, @@ -26,7 +20,6 @@ pub use self::message::*; pub mod nlas { pub use super::{ - neighbour::nlas as neighbour, neighbour_table::nlas as neighbour_table, - nsid::nlas as nsid, + neighbour_table::nlas as neighbour_table, nsid::nlas as nsid, }; } diff --git a/src/rtnl/neighbour/buffer.rs b/src/rtnl/neighbour/buffer.rs deleted file mode 100644 index e182dd92..00000000 --- a/src/rtnl/neighbour/buffer.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_utils::DecodeError; - -use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; - -pub const NEIGHBOUR_HEADER_LEN: usize = 12; -buffer!(NeighbourMessageBuffer(NEIGHBOUR_HEADER_LEN) { - family: (u8, 0), - ifindex: (u32, 4..8), - state: (u16, 8..10), - flags: (u8, 10), - ntype: (u8, 11), - payload:(slice, NEIGHBOUR_HEADER_LEN..), -}); - -impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourMessageBuffer<&'a T> { - pub fn nlas( - &self, - ) -> impl Iterator, DecodeError>> { - NlasIterator::new(self.payload()) - } -} diff --git a/src/rtnl/neighbour/message.rs b/src/rtnl/neighbour/message.rs deleted file mode 100644 index 2b2143b0..00000000 --- a/src/rtnl/neighbour/message.rs +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable}, - DecodeError, -}; - -use crate::{nlas::neighbour::Nla, NeighbourHeader, NeighbourMessageBuffer}; - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -#[non_exhaustive] -pub struct NeighbourMessage { - pub header: NeighbourHeader, - pub nlas: Vec, -} - -impl Emitable for NeighbourMessage { - fn buffer_len(&self) -> usize { - self.header.buffer_len() + self.nlas.as_slice().buffer_len() - } - - fn emit(&self, buffer: &mut [u8]) { - self.header.emit(buffer); - self.nlas - .as_slice() - .emit(&mut buffer[self.header.buffer_len()..]); - } -} - -impl<'a, T: AsRef<[u8]> + 'a> Parseable> - for NeighbourMessage -{ - fn parse(buf: &NeighbourMessageBuffer<&'a T>) -> Result { - Ok(NeighbourMessage { - header: NeighbourHeader::parse(buf) - .context("failed to parse neighbour message header")?, - nlas: Vec::::parse(buf) - .context("failed to parse neighbour message NLAs")?, - }) - } -} - -impl<'a, T: AsRef<[u8]> + 'a> Parseable> - for Vec -{ - fn parse(buf: &NeighbourMessageBuffer<&'a T>) -> Result { - let mut nlas = vec![]; - for nla_buf in buf.nlas() { - nlas.push(Nla::parse(&nla_buf?)?); - } - Ok(nlas) - } -} - -#[cfg(test)] -mod test { - use crate::{ - constants::*, AddressFamily, NeighbourHeader, NeighbourMessage, - NeighbourMessageBuffer, - }; - use netlink_packet_utils::traits::Emitable; - - // 0020 0a 00 00 00 02 00 00 00 02 00 80 01 14 00 01 00 - // 0030 2a 02 80 10 66 d5 00 00 f6 90 ea ff fe 00 2d 83 - // 0040 0a 00 02 00 f4 90 ea 00 2d 83 00 00 08 00 04 00 - // 0050 01 00 00 00 14 00 03 00 00 00 00 00 00 00 00 00 - // 0060 00 00 00 00 02 00 00 00 - - #[rustfmt::skip] - static HEADER: [u8; 12] = [ - 0x0a, // interface family (inet6) - 0xff, 0xff, 0xff, // padding - 0x01, 0x00, 0x00, 0x00, // interface index = 1 - 0x02, 0x00, // state NUD_REACHABLE - 0x80, // flags NTF_PROXY - 0x01 // ntype - - // nlas - // will add some once I've got them parsed out. - ]; - - #[test] - fn packet_header_read() { - let packet = NeighbourMessageBuffer::new(&HEADER[0..12]); - assert_eq!(packet.family(), u8::from(AddressFamily::Inet6)); - assert_eq!(packet.ifindex(), 1); - assert_eq!(packet.state(), NUD_REACHABLE); - assert_eq!(packet.flags(), NTF_ROUTER); - assert_eq!(packet.ntype(), NDA_DST as u8); - } - - #[test] - fn packet_header_build() { - let mut buf = vec![0xff; 12]; - { - let mut packet = NeighbourMessageBuffer::new(&mut buf); - packet.set_family(u8::from(AddressFamily::Inet6)); - packet.set_ifindex(1); - packet.set_state(NUD_REACHABLE); - packet.set_flags(NTF_ROUTER); - packet.set_ntype(NDA_DST as u8); - } - assert_eq!(&buf[..], &HEADER[0..12]); - } - - #[test] - fn emit() { - let header = NeighbourHeader { - family: u8::from(AddressFamily::Inet6), - ifindex: 1, - state: NUD_REACHABLE, - flags: NTF_ROUTER, - ntype: NDA_DST as u8, - }; - - let nlas = vec![]; - let packet = NeighbourMessage { header, nlas }; - let mut buf = [0; 12]; - - assert_eq!(packet.buffer_len(), 12); - packet.emit(&mut buf[..]); - } -} diff --git a/src/rtnl/neighbour/mod.rs b/src/rtnl/neighbour/mod.rs deleted file mode 100644 index 9fdbab9b..00000000 --- a/src/rtnl/neighbour/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT - -mod buffer; -mod header; -mod message; -pub mod nlas; - -pub use self::{buffer::*, header::*, message::*, nlas::*}; diff --git a/src/rtnl/neighbour/nlas/mod.rs b/src/rtnl/neighbour/nlas/mod.rs deleted file mode 100644 index d87722cf..00000000 --- a/src/rtnl/neighbour/nlas/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: MIT - -mod cache_info; -pub use self::cache_info::*; - -use anyhow::Context; -use byteorder::{ByteOrder, NativeEndian}; -use netlink_packet_utils::{ - nla::{self, DefaultNla, NlaBuffer}, - parsers::{parse_u16, parse_u32}, - traits::Parseable, - DecodeError, -}; - -use crate::constants::*; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum Nla { - Unspec(Vec), - Destination(Vec), - LinkLocalAddress(Vec), - CacheInfo(Vec), - Probes(Vec), - Vlan(u16), - Port(Vec), - Vni(u32), - IfIndex(u32), - Master(Vec), - LinkNetNsId(Vec), - SourceVni(u32), - Other(DefaultNla), -} - -impl nla::Nla for Nla { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::Nla::*; - match *self { - Unspec(ref bytes) - | Destination(ref bytes) - | LinkLocalAddress(ref bytes) - | Probes(ref bytes) - | Port(ref bytes) - | Master(ref bytes) - | CacheInfo(ref bytes) - | LinkNetNsId(ref bytes) => bytes.len(), - Vlan(_) => 2, - Vni(_) - | IfIndex(_) - | SourceVni(_) => 4, - Other(ref attr) => attr.value_len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::Nla::*; - match *self { - Unspec(ref bytes) - | Destination(ref bytes) - | LinkLocalAddress(ref bytes) - | Probes(ref bytes) - | Port(ref bytes) - | Master(ref bytes) - | CacheInfo(ref bytes) - | LinkNetNsId(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), - Vlan(ref value) => NativeEndian::write_u16(buffer, *value), - Vni(ref value) - | IfIndex(ref value) - | SourceVni(ref value) => NativeEndian::write_u32(buffer, *value), - Other(ref attr) => attr.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::Nla::*; - match *self { - Unspec(_) => NDA_UNSPEC, - Destination(_) => NDA_DST, - LinkLocalAddress(_) => NDA_LLADDR, - CacheInfo(_) => NDA_CACHEINFO, - Probes(_) => NDA_PROBES, - Vlan(_) => NDA_VLAN, - Port(_) => NDA_PORT, - Vni(_) => NDA_VNI, - IfIndex(_) => NDA_IFINDEX, - Master(_) => NDA_MASTER, - LinkNetNsId(_) => NDA_LINK_NETNSID, - SourceVni(_) => NDA_SRC_VNI, - Other(ref nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Nla { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::Nla::*; - - let payload = buf.value(); - Ok(match buf.kind() { - NDA_UNSPEC => Unspec(payload.to_vec()), - NDA_DST => Destination(payload.to_vec()), - NDA_LLADDR => LinkLocalAddress(payload.to_vec()), - NDA_CACHEINFO => CacheInfo(payload.to_vec()), - NDA_PROBES => Probes(payload.to_vec()), - NDA_VLAN => Vlan(parse_u16(payload)?), - NDA_PORT => Port(payload.to_vec()), - NDA_VNI => Vni(parse_u32(payload)?), - NDA_IFINDEX => IfIndex(parse_u32(payload)?), - NDA_MASTER => Master(payload.to_vec()), - NDA_LINK_NETNSID => LinkNetNsId(payload.to_vec()), - NDA_SRC_VNI => SourceVni(parse_u32(payload)?), - _ => Other( - DefaultNla::parse(buf) - .context("invalid link NLA value (unknown type)")?, - ), - }) - } -}