-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[API break] neighbour_table: Reorganize the code base
* This patch is first preparation on stable release 1.0 for submodule neighbour_table. The detailed checklist for developer is stated in README.md file. * Stored constants into where it been used. Will remove `constants.rs` once all components finished. * Replace `pub use self::*` to explicit expose. * Renamed `neighbour_table::Nla` to `NeighbourTableAttribute` and all its entry changed to detailed data structure instead of `Vec<u8>` * Unit test case included. There is no `ip` command for `neighbour_table`. Hence this patch introduce example `dump_neighbour_tables.rs`, and use it to generate netlink query to kernel. The integers are verified by `sysctl -a|grep neigh`. Signed-off-by: Gris Ge <[email protected]>
- Loading branch information
Showing
17 changed files
with
747 additions
and
218 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
use netlink_packet_core::{ | ||
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, | ||
}; | ||
use netlink_packet_route::{ | ||
neighbour_table::NeighbourTableMessage, RtnlMessage, | ||
}; | ||
use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; | ||
|
||
fn main() { | ||
let mut socket = Socket::new(NETLINK_ROUTE).unwrap(); | ||
let _port_number = socket.bind_auto().unwrap().port_number(); | ||
socket.connect(&SocketAddr::new(0, 0)).unwrap(); | ||
|
||
let mut nl_hdr = NetlinkHeader::default(); | ||
nl_hdr.flags = NLM_F_DUMP | NLM_F_REQUEST; | ||
|
||
let mut req = NetlinkMessage::new( | ||
nl_hdr, | ||
NetlinkPayload::from(RtnlMessage::GetNeighbourTable( | ||
NeighbourTableMessage::default(), | ||
)), | ||
); | ||
// IMPORTANT: call `finalize()` to automatically set the | ||
// `message_type` and `length` fields to the appropriate values in | ||
// the netlink header. | ||
req.finalize(); | ||
|
||
let mut buf = vec![0; req.header.length as usize]; | ||
req.serialize(&mut buf[..]); | ||
|
||
println!(">>> {req:?}"); | ||
socket.send(&buf[..], 0).unwrap(); | ||
|
||
let mut receive_buffer = vec![0; 4096]; | ||
let mut offset = 0; | ||
|
||
'outer: loop { | ||
let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); | ||
|
||
loop { | ||
let bytes = &receive_buffer[offset..]; | ||
// Parse the message | ||
let msg: NetlinkMessage<RtnlMessage> = | ||
NetlinkMessage::deserialize(bytes).unwrap(); | ||
|
||
match msg.payload { | ||
NetlinkPayload::Done(_) => break 'outer, | ||
NetlinkPayload::InnerMessage( | ||
RtnlMessage::NewNeighbourTable(entry), | ||
) => { | ||
println!("HAHA {:?}", entry); | ||
} | ||
NetlinkPayload::Error(err) => { | ||
eprintln!("Received a netlink error message: {err:?}"); | ||
return; | ||
} | ||
_ => {} | ||
} | ||
|
||
offset += msg.header.length as usize; | ||
if offset == size || msg.header.length == 0 { | ||
offset = 0; | ||
break; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
use anyhow::Context; | ||
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, | ||
}; | ||
|
||
const NDTA_NAME: u16 = 1; | ||
const NDTA_THRESH1: u16 = 2; | ||
const NDTA_THRESH2: u16 = 3; | ||
const NDTA_THRESH3: u16 = 4; | ||
const NDTA_CONFIG: u16 = 5; | ||
const NDTA_PARMS: u16 = 6; | ||
const NDTA_STATS: u16 = 7; | ||
const NDTA_GC_INTERVAL: u16 = 8; | ||
|
||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
#[non_exhaustive] | ||
pub enum NeighbourTableAttribute { | ||
Parms(Vec<NeighbourTableParameter>), | ||
Name(String), | ||
Threshold1(u32), | ||
Threshold2(u32), | ||
Threshold3(u32), | ||
Config(NeighbourTableConfig), | ||
Stats(NeighbourTableStats), | ||
GcInterval(u64), | ||
Other(DefaultNla), | ||
} | ||
|
||
impl Nla for NeighbourTableAttribute { | ||
fn value_len(&self) -> usize { | ||
match self { | ||
Self::Parms(v) => v.as_slice().buffer_len(), | ||
Self::Stats(v) => v.buffer_len(), | ||
Self::Config(v) => v.buffer_len(), | ||
// strings: +1 because we need to append a nul byte | ||
Self::Name(s) => s.len() + 1, | ||
Self::Threshold1(_) | Self::Threshold2(_) | Self::Threshold3(_) => { | ||
4 | ||
} | ||
Self::GcInterval(_) => 8, | ||
Self::Other(attr) => attr.value_len(), | ||
} | ||
} | ||
|
||
fn emit_value(&self, buffer: &mut [u8]) { | ||
match self { | ||
Self::Parms(v) => v.as_slice().emit(buffer), | ||
Self::Stats(v) => v.emit(buffer), | ||
Self::Config(v) => v.emit(buffer), | ||
Self::Name(string) => { | ||
buffer[..string.len()].copy_from_slice(string.as_bytes()); | ||
buffer[string.len()] = 0; | ||
} | ||
Self::GcInterval(value) => NativeEndian::write_u64(buffer, *value), | ||
Self::Threshold1(value) | ||
| Self::Threshold2(value) | ||
| Self::Threshold3(value) => { | ||
NativeEndian::write_u32(buffer, *value) | ||
} | ||
Self::Other(attr) => attr.emit_value(buffer), | ||
} | ||
} | ||
|
||
fn kind(&self) -> u16 { | ||
match self { | ||
Self::Name(_) => NDTA_NAME, | ||
Self::Config(_) => NDTA_CONFIG, | ||
Self::Stats(_) => NDTA_STATS, | ||
Self::Parms(_) => NDTA_PARMS, | ||
Self::GcInterval(_) => NDTA_GC_INTERVAL, | ||
Self::Threshold1(_) => NDTA_THRESH1, | ||
Self::Threshold2(_) => NDTA_THRESH2, | ||
Self::Threshold3(_) => NDTA_THRESH3, | ||
Self::Other(attr) => attr.kind(), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> | ||
for NeighbourTableAttribute | ||
{ | ||
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> { | ||
let payload = buf.value(); | ||
Ok(match buf.kind() { | ||
NDTA_NAME => Self::Name( | ||
parse_string(payload).context("invalid NDTA_NAME value")?, | ||
), | ||
NDTA_CONFIG => Self::Config( | ||
NeighbourTableConfig::parse( | ||
&NeighbourTableConfigBuffer::new_checked(payload) | ||
.context(format!("invalid NDTA_CONFIG {payload:?}"))?, | ||
) | ||
.context(format!("invalid NDTA_CONFIG {payload:?}"))?, | ||
), | ||
NDTA_STATS => Self::Stats( | ||
NeighbourTableStats::parse( | ||
&NeighbourTableStatsBuffer::new_checked(payload) | ||
.context(format!("invalid NDTA_STATS {payload:?}"))?, | ||
) | ||
.context(format!("invalid NDTA_STATS {payload:?}"))?, | ||
), | ||
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}"))?, | ||
), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
use netlink_packet_utils::{ | ||
nla::{NlaBuffer, NlasIterator}, | ||
traits::{Emitable, Parseable}, | ||
DecodeError, | ||
}; | ||
|
||
use crate::AddressFamily; | ||
|
||
const NEIGHBOUR_TABLE_HEADER_LEN: usize = 4; | ||
|
||
buffer!(NeighbourTableMessageBuffer(NEIGHBOUR_TABLE_HEADER_LEN) { | ||
family: (u8, 0), | ||
payload: (slice, NEIGHBOUR_TABLE_HEADER_LEN..), | ||
}); | ||
|
||
impl<'a, T: AsRef<[u8]> + ?Sized> NeighbourTableMessageBuffer<&'a T> { | ||
pub fn attributes( | ||
&self, | ||
) -> impl Iterator<Item = Result<NlaBuffer<&'a [u8]>, DecodeError>> { | ||
NlasIterator::new(self.payload()) | ||
} | ||
} | ||
|
||
// kernel code is `struct rtgenmsg` | ||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] | ||
pub struct NeighbourTableHeader { | ||
pub family: AddressFamily, | ||
} | ||
|
||
impl<T: AsRef<[u8]>> Parseable<NeighbourTableMessageBuffer<T>> | ||
for NeighbourTableHeader | ||
{ | ||
fn parse( | ||
buf: &NeighbourTableMessageBuffer<T>, | ||
) -> Result<Self, DecodeError> { | ||
Ok(Self { | ||
family: buf.family().into(), | ||
}) | ||
} | ||
} | ||
|
||
impl Emitable for NeighbourTableHeader { | ||
fn buffer_len(&self) -> usize { | ||
NEIGHBOUR_TABLE_HEADER_LEN | ||
} | ||
|
||
fn emit(&self, buffer: &mut [u8]) { | ||
let mut packet = NeighbourTableMessageBuffer::new(buffer); | ||
packet.set_family(self.family.into()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
mod attribute; | ||
mod config; | ||
mod header; | ||
mod message; | ||
pub(crate) mod param; | ||
mod stats; | ||
#[cfg(test)] | ||
mod tests; | ||
|
||
pub use self::attribute::NeighbourTableAttribute; | ||
pub use self::config::{NeighbourTableConfig, NeighbourTableConfigBuffer}; | ||
pub use self::header::{NeighbourTableHeader, NeighbourTableMessageBuffer}; | ||
pub use self::message::NeighbourTableMessage; | ||
pub use self::param::NeighbourTableParameter; | ||
pub use self::stats::{NeighbourTableStats, NeighbourTableStatsBuffer}; |
Oops, something went wrong.