Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API break] neighbour: Reorganize the code base #66

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 31 additions & 54 deletions examples/dump_neighbours.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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);
}
Expand All @@ -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})");
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
64 changes: 64 additions & 0 deletions src/neighbour/address.rs
Original file line number Diff line number Diff line change
@@ -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<u8>),
}

impl NeighbourAddress {
pub(crate) fn parse_with_param(
address_family: AddressFamily,
payload: &[u8],
) -> Result<Self, DecodeError> {
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()),

Check warning on line 31 in src/neighbour/address.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/address.rs#L31

Added line #L31 was not covered by tests
})
}
}

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(),

Check warning on line 41 in src/neighbour/address.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/address.rs#L41

Added line #L41 was not covered by tests
}
}

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()),

Check warning on line 49 in src/neighbour/address.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/address.rs#L49

Added line #L49 was not covered by tests
}
}
}

impl From<Ipv4Addr> for NeighbourAddress {
fn from(v: Ipv4Addr) -> Self {
Self::Inet(v)
}
}

impl From<Ipv6Addr> for NeighbourAddress {
fn from(v: Ipv6Addr) -> Self {
Self::Inet6(v)
}
}
162 changes: 162 additions & 0 deletions src/neighbour/attribute.rs
Original file line number Diff line number Diff line change
@@ -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<u8>),
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,

Check warning on line 54 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L54

Added line #L54 was not covered by tests
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(),

Check warning on line 62 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L62

Added line #L62 was not covered by tests
}
}

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),

Check warning on line 74 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L73-L74

Added lines #L73 - L74 were not covered by tests
Self::Probes(value)
| Self::LinkNetNsId(value)
| Self::Controller(value)
| Self::Vni(value)
| Self::IfIndex(value)

Check warning on line 79 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L76-L79

Added lines #L76 - L79 were not covered by tests
| Self::SourceVni(value) => NativeEndian::write_u32(buffer, *value),
Self::Protocol(v) => v.emit(buffer),
Self::Other(attr) => attr.emit_value(buffer),

Check warning on line 82 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L82

Added line #L82 was not covered by tests
}
}

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,

Check warning on line 98 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L92-L98

Added lines #L92 - L98 were not covered by tests
Self::Protocol(_) => NDA_PROTOCOL,
Self::Other(nla) => nla.kind(),

Check warning on line 100 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L100

Added line #L100 was not covered by tests
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized>
ParseableParametrized<NlaBuffer<&'a T>, AddressFamily>
for NeighbourAttribute
{
fn parse_with_param(
buf: &NlaBuffer<&'a T>,
address_family: AddressFamily,
) -> Result<Self, DecodeError> {
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)?),

Check warning on line 137 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L137

Added line #L137 was not covered by tests
NDA_PORT => Self::Port(
parse_u16_be(payload)
.context(format!("invalid NDA_PORT value {payload:?}"))?,

Check warning on line 140 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L139-L140

Added lines #L139 - L140 were not covered by tests
),
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)?),

Check warning on line 150 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L142-L150

Added lines #L142 - L150 were not covered by tests
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)")?,

Check warning on line 158 in src/neighbour/attribute.rs

View check run for this annotation

Codecov / codecov/patch

src/neighbour/attribute.rs#L157-L158

Added lines #L157 - L158 were not covered by tests
),
})
}
}
Loading