Skip to content

Commit d327d20

Browse files
committed
Add nexthop message.
1 parent a55ede9 commit d327d20

File tree

9 files changed

+432
-5
lines changed

9 files changed

+432
-5
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod address;
44
pub mod link;
55
pub mod neighbour;
66
pub mod neighbour_table;
7+
pub mod nexthop;
78
pub mod nsid;
89
pub mod prefix;
910
pub mod route;

src/message.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use netlink_packet_utils::{
88
DecodeError, Emitable, Parseable, ParseableParametrized,
99
};
1010

11+
use crate::nexthop::{NexthopMessage, NexthopMessageBuffer};
1112
use crate::tc::{TcActionMessage, TcActionMessageBuffer};
1213
use crate::{
1314
address::{AddressHeader, AddressMessage, AddressMessageBuffer},
@@ -76,6 +77,9 @@ const RTM_GETNSID: u16 = 90;
7677
const RTM_NEWCHAIN: u16 = 100;
7778
const RTM_DELCHAIN: u16 = 101;
7879
const RTM_GETCHAIN: u16 = 102;
80+
const RTM_NEWNEXTHOP: u16 = 104;
81+
const RTM_DELNEXTHOP: u16 = 105;
82+
const RTM_GETNEXTHOP: u16 = 106;
7983
const RTM_NEWLINKPROP: u16 = 108;
8084
const RTM_DELLINKPROP: u16 = 109;
8185

@@ -322,6 +326,22 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
322326
}
323327
}
324328

329+
// Nexthop Messages
330+
RTM_NEWNEXTHOP | RTM_DELNEXTHOP | RTM_GETNEXTHOP => {
331+
let err = "invalid nexthop message";
332+
let msg = NexthopMessage::parse(
333+
&NexthopMessageBuffer::new_checked(&buf.inner())
334+
.context(err)?,
335+
)
336+
.context(err)?;
337+
match message_type {
338+
RTM_NEWNEXTHOP => RouteNetlinkMessage::NewNexthop(msg),
339+
RTM_DELNEXTHOP => RouteNetlinkMessage::DelNexthop(msg),
340+
RTM_GETNEXTHOP => RouteNetlinkMessage::GetNexthop(msg),
341+
_ => unreachable!(),
342+
}
343+
}
344+
325345
_ => {
326346
return Err(
327347
format!("Unknown message type: {message_type}").into()
@@ -369,6 +389,9 @@ pub enum RouteNetlinkMessage {
369389
NewTrafficChain(TcMessage),
370390
DelTrafficChain(TcMessage),
371391
GetTrafficChain(TcMessage),
392+
NewNexthop(NexthopMessage),
393+
DelNexthop(NexthopMessage),
394+
GetNexthop(NexthopMessage),
372395
NewNsId(NsidMessage),
373396
DelNsId(NsidMessage),
374397
GetNsId(NsidMessage),
@@ -526,6 +549,18 @@ impl RouteNetlinkMessage {
526549
matches!(self, RouteNetlinkMessage::DelRule(_))
527550
}
528551

552+
pub fn is_get_nexthop(&self) -> bool {
553+
matches!(self, RouteNetlinkMessage::GetNexthop(_))
554+
}
555+
556+
pub fn is_new_nexthop(&self) -> bool {
557+
matches!(self, RouteNetlinkMessage::NewNexthop(_))
558+
}
559+
560+
pub fn is_del_nexthop(&self) -> bool {
561+
matches!(self, RouteNetlinkMessage::DelNexthop(_))
562+
}
563+
529564
pub fn message_type(&self) -> u16 {
530565
use self::RouteNetlinkMessage::*;
531566

@@ -570,6 +605,9 @@ impl RouteNetlinkMessage {
570605
GetRule(_) => RTM_GETRULE,
571606
NewRule(_) => RTM_NEWRULE,
572607
DelRule(_) => RTM_DELRULE,
608+
NewNexthop(_) => RTM_NEWNEXTHOP,
609+
DelNexthop(_) => RTM_DELNEXTHOP,
610+
GetNexthop(_) => RTM_GETNEXTHOP,
573611
}
574612
}
575613
}
@@ -637,7 +675,12 @@ impl Emitable for RouteNetlinkMessage {
637675
| DelTrafficAction(ref msg)
638676
| GetTrafficAction(ref msg)
639677
=> msg.buffer_len(),
640-
}
678+
679+
| NewNexthop(ref msg)
680+
| DelNexthop(ref msg)
681+
| GetNexthop(ref msg)
682+
=> msg.buffer_len(),
683+
}
641684
}
642685

643686
#[rustfmt::skip]
@@ -702,7 +745,12 @@ impl Emitable for RouteNetlinkMessage {
702745
| DelTrafficAction(ref msg)
703746
| GetTrafficAction(ref msg)
704747
=> msg.emit(buffer),
705-
}
748+
749+
| NewNexthop(ref msg)
750+
| DelNexthop(ref msg)
751+
| GetNexthop(ref msg)
752+
=> msg.emit(buffer),
753+
}
706754
}
707755
}
708756

src/nexthop/attribute.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use anyhow::Context;
4+
use byteorder::{ByteOrder, NativeEndian};
5+
use netlink_packet_utils::{
6+
nla::{DefaultNla, Nla, NlaBuffer},
7+
parsers::{parse_u16, parse_u32},
8+
DecodeError, Emitable, Parseable, ParseableParametrized,
9+
};
10+
11+
use crate::{
12+
route::{RouteAddress, RouteLwTunnelEncap},
13+
AddressFamily,
14+
};
15+
16+
use super::NexthopGroup;
17+
18+
const NHA_ID: u16 = 1;
19+
const NHA_GROUP: u16 = 2;
20+
const NHA_GROUP_TYPE: u16 = 3;
21+
const NHA_BLACKHOLE: u16 = 4;
22+
const NHA_OIF: u16 = 5;
23+
const NHA_GATEWAY: u16 = 6;
24+
const NHA_ENCAP_TYPE: u16 = 7;
25+
const NHA_ENCAP: u16 = 8;
26+
const NHA_GROUPS: u16 = 9;
27+
const NHA_MASTER: u16 = 10;
28+
const NHA_FDB: u16 = 11;
29+
// const NHA_RES_GROUP: u16 = 12;
30+
// const NHA_RES_BUCKET: u16 = 13;
31+
32+
#[derive(Debug, PartialEq, Eq, Clone)]
33+
#[non_exhaustive]
34+
pub enum NexthopAttribute {
35+
Id(u32),
36+
Group(Vec<NexthopGroup>),
37+
GroupType(u16),
38+
Blackhole,
39+
Oif(u32),
40+
Gateway(RouteAddress),
41+
EncapType(u16),
42+
Encap(Vec<RouteLwTunnelEncap>),
43+
Groups,
44+
Master(u32),
45+
Fdb,
46+
Other(DefaultNla),
47+
}
48+
49+
impl Nla for NexthopAttribute {
50+
fn value_len(&self) -> usize {
51+
match self {
52+
Self::Id(_) | Self::Oif(_) | Self::Master(_) => 4,
53+
Self::Group(groups) => {
54+
groups.iter().map(|grp| grp.buffer_len()).sum()
55+
}
56+
Self::GroupType(_) | Self::EncapType(_) => 2,
57+
Self::Blackhole | Self::Groups | Self::Fdb => 0,
58+
Self::Encap(v) => v.as_slice().buffer_len(),
59+
Self::Gateway(addr) => addr.buffer_len(),
60+
Self::Other(attr) => attr.value_len(),
61+
}
62+
}
63+
64+
fn emit_value(&self, buffer: &mut [u8]) {
65+
match self {
66+
Self::Id(value) | Self::Oif(value) | Self::Master(value) => {
67+
NativeEndian::write_u32(buffer, *value)
68+
}
69+
Self::Group(groups) => {
70+
let mut offset = 0;
71+
for grp in groups {
72+
let len = grp.buffer_len();
73+
grp.emit(&mut buffer[offset..offset + len]);
74+
offset += len
75+
}
76+
}
77+
Self::GroupType(value) | Self::EncapType(value) => {
78+
NativeEndian::write_u16(buffer, *value);
79+
}
80+
Self::Blackhole | Self::Groups | Self::Fdb => {}
81+
Self::Encap(nlas) => nlas.as_slice().emit(buffer),
82+
Self::Gateway(addr) => addr.emit(buffer),
83+
Self::Other(attr) => attr.emit_value(buffer),
84+
}
85+
}
86+
87+
fn kind(&self) -> u16 {
88+
match self {
89+
Self::Id(_) => NHA_ID,
90+
Self::Group(_) => NHA_GROUP,
91+
Self::GroupType(_) => NHA_GROUP_TYPE,
92+
Self::Blackhole => NHA_BLACKHOLE,
93+
Self::Oif(_) => NHA_OIF,
94+
Self::Gateway(_) => NHA_GATEWAY,
95+
Self::EncapType(_) => NHA_ENCAP_TYPE,
96+
Self::Encap(_) => NHA_ENCAP,
97+
Self::Groups => NHA_GROUPS,
98+
Self::Master(_) => NHA_MASTER,
99+
Self::Fdb => NHA_FDB,
100+
Self::Other(nla) => nla.kind(),
101+
}
102+
}
103+
}
104+
105+
impl<'a, T: AsRef<[u8]> + ?Sized>
106+
ParseableParametrized<NlaBuffer<&'a T>, AddressFamily>
107+
for NexthopAttribute
108+
{
109+
fn parse_with_param(
110+
buf: &NlaBuffer<&'a T>,
111+
address_family: AddressFamily,
112+
) -> Result<Self, DecodeError> {
113+
let payload = buf.value();
114+
Ok(match buf.kind() {
115+
NHA_ID => Self::Id(
116+
parse_u32(payload).context(format!("invalid NHA_ID value"))?,
117+
),
118+
NHA_GROUP => {
119+
let mut groups = vec![];
120+
let mut i: usize = 0;
121+
while i + 8 <= payload.len() {
122+
groups.push(NexthopGroup::parse(&payload[i..i + 8])?);
123+
i += 8;
124+
}
125+
Self::Group(groups)
126+
}
127+
NHA_GROUP_TYPE => Self::GroupType(
128+
parse_u16(payload)
129+
.context(format!("invalid NHA_GROUP_TYPE value"))?,
130+
),
131+
NHA_BLACKHOLE => Self::Blackhole,
132+
NHA_OIF => Self::Oif(
133+
parse_u32(payload).context(format!("invalid NHA_OIF value"))?,
134+
),
135+
NHA_GATEWAY => {
136+
Self::Gateway(RouteAddress::parse(address_family, payload)?)
137+
}
138+
NHA_ENCAP_TYPE => Self::EncapType(
139+
parse_u16(payload)
140+
.context(format!("invalid NHA_ENCAP_TYPE value"))?,
141+
),
142+
NHA_GROUPS => Self::Groups,
143+
NHA_MASTER => Self::Master(
144+
parse_u32(payload)
145+
.context(format!("invalid NHA_MASTER value"))?,
146+
),
147+
NHA_FDB => Self::Fdb,
148+
_ => Self::Other(
149+
DefaultNla::parse(buf)
150+
.context("invalid link NLA value (unknown type)")?,
151+
),
152+
})
153+
}
154+
}

src/nexthop/flags.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
const RTNH_F_DEAD: u32 = 1 << 0;
4+
const RTNH_F_PERVASIVE: u32 = 1 << 1;
5+
const RTNH_F_ONLINK: u32 = 1 << 2;
6+
const RTNH_F_OFFLOAD: u32 = 1 << 3;
7+
const RTNH_F_LINKDOWN: u32 = 1 << 4;
8+
const RTNH_F_UNRESOLVED: u32 = 1 << 5;
9+
const RTNH_F_TRAP: u32 = 1 << 6;
10+
// const RTNH_COMPARE_MASK: u32 =
11+
// RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD | RTNH_F_TRAP;
12+
13+
bitflags! {
14+
#[derive(Clone, Eq, PartialEq, Debug, Copy, Default)]
15+
#[non_exhaustive]
16+
pub struct NexthopFlags: u32 {
17+
const Dead = RTNH_F_DEAD;
18+
const Pervasive = RTNH_F_PERVASIVE;
19+
const Onlink =RTNH_F_ONLINK;
20+
const Offload = RTNH_F_OFFLOAD;
21+
const Linkdown = RTNH_F_LINKDOWN;
22+
const Unresolved = RTNH_F_UNRESOLVED;
23+
const Trap= RTNH_F_TRAP;
24+
const _ = !0;
25+
}
26+
}

src/nexthop/group.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use byteorder::{ByteOrder, NativeEndian};
2+
use netlink_packet_utils::{
3+
parsers::{parse_u16, parse_u32},
4+
DecodeError, Emitable,
5+
};
6+
7+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8+
/// Nexthop Group
9+
pub struct NexthopGroup {
10+
/// Nexthop id
11+
pub id: u32,
12+
/// Weight of this nexthop
13+
pub weight: u8,
14+
/// Reserved
15+
pub resvd1: u8,
16+
/// Reserved
17+
pub resvd2: u16,
18+
}
19+
20+
impl Emitable for NexthopGroup {
21+
fn buffer_len(&self) -> usize {
22+
8
23+
}
24+
25+
fn emit(&self, buffer: &mut [u8]) {
26+
NativeEndian::write_u32(buffer, self.id);
27+
buffer[4] = self.weight;
28+
buffer[5] = self.resvd1;
29+
NativeEndian::write_u16(&mut buffer[6..8], self.resvd2);
30+
}
31+
}
32+
33+
impl NexthopGroup {
34+
pub fn parse(payload: &[u8]) -> Result<Self, DecodeError> {
35+
let grp = Self {
36+
id: parse_u32(payload)?,
37+
weight: payload[4],
38+
resvd1: payload[5],
39+
resvd2: parse_u16(&payload[6..8])?,
40+
};
41+
Ok(grp)
42+
}
43+
}

0 commit comments

Comments
 (0)