Skip to content

Commit 2a43e1c

Browse files
deastoecathay4t
authored andcommitted
prefix: add support for RTM_NEWPREFIX
Signed-off-by: Duncan Eastoe <[email protected]>
1 parent 0219628 commit 2a43e1c

File tree

8 files changed

+321
-1
lines changed

8 files changed

+321
-1
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod link;
55
pub mod neighbour;
66
pub mod neighbour_table;
77
pub mod nsid;
8+
pub mod prefix;
89
pub mod route;
910
pub mod rule;
1011
pub mod tc;

src/message.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{
1515
neighbour::{NeighbourMessage, NeighbourMessageBuffer},
1616
neighbour_table::{NeighbourTableMessage, NeighbourTableMessageBuffer},
1717
nsid::{NsidMessage, NsidMessageBuffer},
18+
prefix::{PrefixMessage, PrefixMessageBuffer},
1819
route::{RouteHeader, RouteMessage, RouteMessageBuffer},
1920
rule::{RuleMessage, RuleMessageBuffer},
2021
tc::{TcMessage, TcMessageBuffer},
@@ -48,7 +49,7 @@ const RTM_GETTFILTER: u16 = 46;
4849
// const RTM_NEWACTION: u16 = 48;
4950
// const RTM_DELACTION: u16 = 49;
5051
// const RTM_GETACTION: u16 = 50;
51-
// const RTM_NEWPREFIX: u16 = 52;
52+
const RTM_NEWPREFIX: u16 = 52;
5253
// const RTM_GETMULTICAST: u16 = 58;
5354
// const RTM_GETANYCAST: u16 = 62;
5455
const RTM_NEWNEIGHTBL: u16 = 64;
@@ -225,6 +226,18 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
225226
}
226227
}
227228

229+
// Prefix messages
230+
RTM_NEWPREFIX => {
231+
let err = "invalid prefix message";
232+
RouteNetlinkMessage::NewPrefix(
233+
PrefixMessage::parse(
234+
&PrefixMessageBuffer::new_checked(&buf.inner())
235+
.context(err)?,
236+
)
237+
.context(err)?,
238+
)
239+
}
240+
228241
RTM_NEWRULE | RTM_GETRULE | RTM_DELRULE => {
229242
let err = "invalid fib rule message";
230243
let msg = RuleMessage::parse(
@@ -325,6 +338,7 @@ pub enum RouteNetlinkMessage {
325338
NewRoute(RouteMessage),
326339
DelRoute(RouteMessage),
327340
GetRoute(RouteMessage),
341+
NewPrefix(PrefixMessage),
328342
NewQueueDiscipline(TcMessage),
329343
DelQueueDiscipline(TcMessage),
330344
GetQueueDiscipline(TcMessage),
@@ -504,6 +518,7 @@ impl RouteNetlinkMessage {
504518
NewRoute(_) => RTM_NEWROUTE,
505519
DelRoute(_) => RTM_DELROUTE,
506520
GetRoute(_) => RTM_GETROUTE,
521+
NewPrefix(_) => RTM_NEWPREFIX,
507522
NewQueueDiscipline(_) => RTM_NEWQDISC,
508523
DelQueueDiscipline(_) => RTM_DELQDISC,
509524
GetQueueDiscipline(_) => RTM_GETQDISC,
@@ -559,6 +574,8 @@ impl Emitable for RouteNetlinkMessage {
559574
| GetRoute(ref msg)
560575
=> msg.buffer_len(),
561576

577+
NewPrefix(ref msg) => msg.buffer_len(),
578+
562579
| NewQueueDiscipline(ref msg)
563580
| DelQueueDiscipline(ref msg)
564581
| GetQueueDiscipline(ref msg)
@@ -617,6 +634,8 @@ impl Emitable for RouteNetlinkMessage {
617634
| GetRoute(ref msg)
618635
=> msg.emit(buffer),
619636

637+
| NewPrefix(ref msg) => msg.emit(buffer),
638+
620639
| NewQueueDiscipline(ref msg)
621640
| DelQueueDiscipline(ref msg)
622641
| GetQueueDiscipline(ref msg)

src/prefix/attribute.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use std::net::Ipv6Addr;
4+
5+
use anyhow::Context;
6+
use netlink_packet_utils::{
7+
nla::{self, DefaultNla, NlaBuffer},
8+
traits::Parseable,
9+
DecodeError, Emitable,
10+
};
11+
12+
use super::cache_info::{CacheInfo, CacheInfoBuffer};
13+
14+
const PREFIX_ADDRESS: u16 = 1;
15+
const PREFIX_CACHEINFO: u16 = 2;
16+
17+
#[derive(Debug, PartialEq, Eq, Clone)]
18+
pub enum PrefixAttribute {
19+
Address(Ipv6Addr),
20+
CacheInfo(CacheInfo),
21+
Other(DefaultNla),
22+
}
23+
24+
impl nla::Nla for PrefixAttribute {
25+
fn value_len(&self) -> usize {
26+
match *self {
27+
Self::Address(_) => 16,
28+
Self::CacheInfo(ref cache_info) => cache_info.buffer_len(),
29+
Self::Other(ref attr) => attr.value_len(),
30+
}
31+
}
32+
33+
fn emit_value(&self, buffer: &mut [u8]) {
34+
match *self {
35+
Self::Address(ref addr) => buffer.copy_from_slice(&addr.octets()),
36+
Self::CacheInfo(ref cache_info) => cache_info.emit(buffer),
37+
Self::Other(ref attr) => attr.emit_value(buffer),
38+
}
39+
}
40+
41+
fn kind(&self) -> u16 {
42+
match *self {
43+
Self::Address(_) => PREFIX_ADDRESS,
44+
Self::CacheInfo(_) => PREFIX_CACHEINFO,
45+
Self::Other(ref nla) => nla.kind(),
46+
}
47+
}
48+
}
49+
50+
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
51+
for PrefixAttribute
52+
{
53+
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
54+
let payload = buf.value();
55+
match buf.kind() {
56+
PREFIX_ADDRESS => {
57+
if let Ok(payload) = TryInto::<[u8; 16]>::try_into(payload) {
58+
Ok(Self::Address(Ipv6Addr::from(payload)))
59+
} else {
60+
Err(DecodeError::from(format!("Invalid PREFIX_ADDRESS, unexpected payload length: {:?}", payload)))
61+
}
62+
}
63+
PREFIX_CACHEINFO => Ok(Self::CacheInfo(
64+
CacheInfo::parse(&CacheInfoBuffer::new(payload)).context(
65+
format!("Invalid PREFIX_CACHEINFO: {:?}", payload),
66+
)?,
67+
)),
68+
_ => Ok(Self::Other(DefaultNla::parse(buf)?)),
69+
}
70+
}
71+
}

src/prefix/cache_info.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_utils::{traits::Parseable, DecodeError, Emitable};
4+
5+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
6+
#[non_exhaustive]
7+
pub struct CacheInfo {
8+
pub preferred_time: u32,
9+
pub valid_time: u32,
10+
}
11+
12+
const CACHE_INFO_LEN: usize = 8;
13+
14+
buffer!(CacheInfoBuffer(CACHE_INFO_LEN) {
15+
preferred_time: (u32, 0..4),
16+
valid_time: (u32, 4..8),
17+
});
18+
19+
impl<T: AsRef<[u8]>> Parseable<CacheInfoBuffer<T>> for CacheInfo {
20+
fn parse(buf: &CacheInfoBuffer<T>) -> Result<Self, DecodeError> {
21+
Ok(CacheInfo {
22+
preferred_time: buf.preferred_time(),
23+
valid_time: buf.valid_time(),
24+
})
25+
}
26+
}
27+
28+
impl Emitable for CacheInfo {
29+
fn buffer_len(&self) -> usize {
30+
CACHE_INFO_LEN
31+
}
32+
33+
fn emit(&self, buffer: &mut [u8]) {
34+
let mut buffer = CacheInfoBuffer::new(buffer);
35+
buffer.set_preferred_time(self.preferred_time);
36+
buffer.set_valid_time(self.valid_time);
37+
}
38+
}

src/prefix/header.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_utils::{
4+
nla::{NlaBuffer, NlasIterator},
5+
DecodeError, Emitable,
6+
};
7+
8+
const PREFIX_HEADER_LEN: usize = 12;
9+
10+
buffer!(PrefixMessageBuffer(PREFIX_HEADER_LEN) {
11+
prefix_family: (u8, 0),
12+
pad1: (u8, 1),
13+
pad2: (u16, 2..4),
14+
ifindex: (i32, 4..8),
15+
prefix_type: (u8, 8),
16+
prefix_len: (u8, 9),
17+
flags: (u8, 10),
18+
pad3: (u8, 11),
19+
payload: (slice, PREFIX_HEADER_LEN..),
20+
});
21+
22+
impl<'a, T: AsRef<[u8]> + ?Sized> PrefixMessageBuffer<&'a T> {
23+
pub fn nlas(
24+
&self,
25+
) -> impl Iterator<Item = Result<NlaBuffer<&'a [u8]>, DecodeError>> {
26+
NlasIterator::new(self.payload())
27+
}
28+
}
29+
30+
#[derive(Debug, PartialEq, Eq, Clone, Default)]
31+
pub struct PrefixHeader {
32+
pub prefix_family: u8,
33+
pub ifindex: i32,
34+
pub prefix_type: u8,
35+
pub prefix_len: u8,
36+
pub flags: u8,
37+
}
38+
39+
impl Emitable for PrefixHeader {
40+
fn buffer_len(&self) -> usize {
41+
PREFIX_HEADER_LEN
42+
}
43+
44+
fn emit(&self, buffer: &mut [u8]) {
45+
let mut packet = PrefixMessageBuffer::new(buffer);
46+
packet.set_prefix_family(self.prefix_family);
47+
packet.set_ifindex(self.ifindex);
48+
packet.set_prefix_type(self.prefix_type);
49+
packet.set_prefix_len(self.prefix_len);
50+
packet.set_flags(self.flags);
51+
}
52+
}

src/prefix/message.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use anyhow::Context;
4+
5+
use netlink_packet_utils::{
6+
traits::{Emitable, Parseable},
7+
DecodeError,
8+
};
9+
10+
use super::{
11+
attribute::PrefixAttribute,
12+
header::{PrefixHeader, PrefixMessageBuffer},
13+
};
14+
15+
#[derive(Debug, PartialEq, Eq, Clone, Default)]
16+
pub struct PrefixMessage {
17+
pub header: PrefixHeader,
18+
pub attributes: Vec<PrefixAttribute>,
19+
}
20+
21+
impl Emitable for PrefixMessage {
22+
fn buffer_len(&self) -> usize {
23+
self.header.buffer_len() + self.attributes.as_slice().buffer_len()
24+
}
25+
26+
fn emit(&self, buffer: &mut [u8]) {
27+
self.header.emit(buffer);
28+
self.attributes
29+
.as_slice()
30+
.emit(&mut buffer[self.header.buffer_len()..]);
31+
}
32+
}
33+
34+
impl<T: AsRef<[u8]>> Parseable<PrefixMessageBuffer<T>> for PrefixHeader {
35+
fn parse(buf: &PrefixMessageBuffer<T>) -> Result<Self, DecodeError> {
36+
Ok(Self {
37+
prefix_family: buf.prefix_family(),
38+
ifindex: buf.ifindex(),
39+
prefix_type: buf.prefix_type(),
40+
prefix_len: buf.prefix_len(),
41+
flags: buf.flags(),
42+
})
43+
}
44+
}
45+
46+
impl<'a, T: AsRef<[u8]> + 'a> Parseable<PrefixMessageBuffer<&'a T>>
47+
for PrefixMessage
48+
{
49+
fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
50+
Ok(Self {
51+
header: PrefixHeader::parse(buf)
52+
.context("failed to parse prefix message header")?,
53+
attributes: Vec::<PrefixAttribute>::parse(buf)
54+
.context("failed to parse prefix message attributes")?,
55+
})
56+
}
57+
}
58+
59+
impl<'a, T: AsRef<[u8]> + 'a> Parseable<PrefixMessageBuffer<&'a T>>
60+
for Vec<PrefixAttribute>
61+
{
62+
fn parse(buf: &PrefixMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
63+
let mut nlas = vec![];
64+
for nla_buf in buf.nlas() {
65+
nlas.push(PrefixAttribute::parse(&nla_buf?)?);
66+
}
67+
Ok(nlas)
68+
}
69+
}

src/prefix/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod attribute;
4+
mod cache_info;
5+
mod header;
6+
mod message;
7+
#[cfg(test)]
8+
mod tests;
9+
10+
pub use header::PrefixMessageBuffer;
11+
pub use message::PrefixMessage;

src/prefix/tests.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use std::{net::Ipv6Addr, str::FromStr};
4+
5+
use netlink_packet_utils::{Emitable, Parseable};
6+
7+
use crate::prefix::{
8+
attribute::PrefixAttribute, cache_info::CacheInfo, header::PrefixHeader,
9+
PrefixMessage, PrefixMessageBuffer,
10+
};
11+
12+
#[test]
13+
fn test_new_prefix() {
14+
#[rustfmt::skip]
15+
let data = vec![
16+
// prefixmsg
17+
// AF_INET6 + padding
18+
0x0a, 0x00, 0x00, 0x00,
19+
// ifindex
20+
0x02, 0x00, 0x00, 0x00,
21+
// type, prefix length, flags, padding
22+
0x03, 0x40, 0x01, 0x00,
23+
// PREFIX_ADDRESS attribute
24+
0x14, 0x00, 0x01, 0x00,
25+
0xfc, 0x00, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00,
26+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
27+
// PREFIX_CACHEINFO attribute
28+
0x0c, 0x00, 0x02, 0x00,
29+
0xff, 0xff, 0xff, 0xfa,
30+
0xff, 0xff, 0xff, 0xff,
31+
];
32+
let actual = PrefixMessage::parse(&PrefixMessageBuffer::new(&data))
33+
.expect("Generated PrefixMessage");
34+
35+
let expected = PrefixMessage {
36+
header: PrefixHeader {
37+
prefix_family: libc::AF_INET6 as u8,
38+
ifindex: 2,
39+
prefix_type: 3,
40+
prefix_len: 64,
41+
flags: 1,
42+
},
43+
attributes: vec![
44+
PrefixAttribute::Address(
45+
Ipv6Addr::from_str("fc00:a0a::1").expect("Ipv6Addr"),
46+
),
47+
PrefixAttribute::CacheInfo(CacheInfo {
48+
preferred_time: 0xfaffffff,
49+
valid_time: 0xffffffff,
50+
}),
51+
],
52+
};
53+
54+
assert_eq!(expected, actual);
55+
56+
let mut buf = vec![0; 44];
57+
expected.emit(&mut buf);
58+
assert_eq!(data, buf);
59+
}

0 commit comments

Comments
 (0)