Skip to content

SRv6 and Nexthop Group support #139

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod address;
pub mod link;
pub mod neighbour;
pub mod neighbour_table;
pub mod nexthop;
pub mod nsid;
pub mod prefix;
pub mod route;
Expand Down
2 changes: 1 addition & 1 deletion src/link/af_spec/inet6_icmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use netlink_packet_utils::{
DecodeError,
};

pub(crate) const ICMP6_STATS_LEN: usize = 48;
pub(crate) const ICMP6_STATS_LEN: usize = 56;

#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
#[non_exhaustive]
Expand Down
2 changes: 1 addition & 1 deletion src/link/af_spec/inet6_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use netlink_packet_utils::{
DecodeError,
};

pub(crate) const INET6_STATS_LEN: usize = 288;
pub(crate) const INET6_STATS_LEN: usize = 304;

buffer!(Inet6StatsBuffer(INET6_STATS_LEN) {
num: (i64, 0..8),
Expand Down
52 changes: 50 additions & 2 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use netlink_packet_utils::{
DecodeError, Emitable, Parseable, ParseableParametrized,
};

use crate::nexthop::{NexthopMessage, NexthopMessageBuffer};
use crate::tc::{TcActionMessage, TcActionMessageBuffer};
use crate::{
address::{AddressHeader, AddressMessage, AddressMessageBuffer},
Expand Down Expand Up @@ -76,6 +77,9 @@ const RTM_GETNSID: u16 = 90;
const RTM_NEWCHAIN: u16 = 100;
const RTM_DELCHAIN: u16 = 101;
const RTM_GETCHAIN: u16 = 102;
const RTM_NEWNEXTHOP: u16 = 104;
const RTM_DELNEXTHOP: u16 = 105;
const RTM_GETNEXTHOP: u16 = 106;
const RTM_NEWLINKPROP: u16 = 108;
const RTM_DELLINKPROP: u16 = 109;

Expand Down Expand Up @@ -322,6 +326,22 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
}
}

// Nexthop Messages
RTM_NEWNEXTHOP | RTM_DELNEXTHOP | RTM_GETNEXTHOP => {
let err = "invalid nexthop message";
let msg = NexthopMessage::parse(
&NexthopMessageBuffer::new_checked(&buf.inner())
.context(err)?,
)
.context(err)?;
match message_type {
RTM_NEWNEXTHOP => RouteNetlinkMessage::NewNexthop(msg),
RTM_DELNEXTHOP => RouteNetlinkMessage::DelNexthop(msg),
RTM_GETNEXTHOP => RouteNetlinkMessage::GetNexthop(msg),
_ => unreachable!(),
}
}

_ => {
return Err(
format!("Unknown message type: {message_type}").into()
Expand Down Expand Up @@ -369,6 +389,9 @@ pub enum RouteNetlinkMessage {
NewTrafficChain(TcMessage),
DelTrafficChain(TcMessage),
GetTrafficChain(TcMessage),
NewNexthop(NexthopMessage),
DelNexthop(NexthopMessage),
GetNexthop(NexthopMessage),
NewNsId(NsidMessage),
DelNsId(NsidMessage),
GetNsId(NsidMessage),
Expand Down Expand Up @@ -526,6 +549,18 @@ impl RouteNetlinkMessage {
matches!(self, RouteNetlinkMessage::DelRule(_))
}

pub fn is_get_nexthop(&self) -> bool {
matches!(self, RouteNetlinkMessage::GetNexthop(_))
}

pub fn is_new_nexthop(&self) -> bool {
matches!(self, RouteNetlinkMessage::NewNexthop(_))
}

pub fn is_del_nexthop(&self) -> bool {
matches!(self, RouteNetlinkMessage::DelNexthop(_))
}

pub fn message_type(&self) -> u16 {
use self::RouteNetlinkMessage::*;

Expand Down Expand Up @@ -570,6 +605,9 @@ impl RouteNetlinkMessage {
GetRule(_) => RTM_GETRULE,
NewRule(_) => RTM_NEWRULE,
DelRule(_) => RTM_DELRULE,
NewNexthop(_) => RTM_NEWNEXTHOP,
DelNexthop(_) => RTM_DELNEXTHOP,
GetNexthop(_) => RTM_GETNEXTHOP,
}
}
}
Expand Down Expand Up @@ -637,7 +675,12 @@ impl Emitable for RouteNetlinkMessage {
| DelTrafficAction(ref msg)
| GetTrafficAction(ref msg)
=> msg.buffer_len(),
}

| NewNexthop(ref msg)
| DelNexthop(ref msg)
| GetNexthop(ref msg)
=> msg.buffer_len(),
}
}

#[rustfmt::skip]
Expand Down Expand Up @@ -702,7 +745,12 @@ impl Emitable for RouteNetlinkMessage {
| DelTrafficAction(ref msg)
| GetTrafficAction(ref msg)
=> msg.emit(buffer),
}

| NewNexthop(ref msg)
| DelNexthop(ref msg)
| GetNexthop(ref msg)
=> msg.emit(buffer),
}
}
}

Expand Down
154 changes: 154 additions & 0 deletions src/nexthop/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: MIT

use anyhow::Context;
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
nla::{DefaultNla, Nla, NlaBuffer, NLA_F_NESTED},
parsers::{parse_u16, parse_u32},
DecodeError, Emitable, Parseable, ParseableParametrized,
};

use crate::{
route::{RouteAddress, RouteLwTunnelEncap},
AddressFamily,
};

use super::NexthopGroup;

const NHA_ID: u16 = 1;
const NHA_GROUP: u16 = 2;
const NHA_GROUP_TYPE: u16 = 3;
const NHA_BLACKHOLE: u16 = 4;
const NHA_OIF: u16 = 5;
const NHA_GATEWAY: u16 = 6;
const NHA_ENCAP_TYPE: u16 = 7;
const NHA_ENCAP: u16 = 8;
const NHA_GROUPS: u16 = 9;
const NHA_MASTER: u16 = 10;
const NHA_FDB: u16 = 11;
// const NHA_RES_GROUP: u16 = 12;
// const NHA_RES_BUCKET: u16 = 13;

#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum NexthopAttribute {
Id(u32),
Group(Vec<NexthopGroup>),
GroupType(u16),
Blackhole,
Oif(u32),
Gateway(RouteAddress),
EncapType(u16),
Encap(Vec<RouteLwTunnelEncap>),
Groups,
Master(u32),
Fdb,
Other(DefaultNla),
}

impl Nla for NexthopAttribute {
fn value_len(&self) -> usize {
match self {
Self::Id(_) | Self::Oif(_) | Self::Master(_) => 4,
Self::Group(groups) => {
groups.iter().map(|grp| grp.buffer_len()).sum()
}
Self::GroupType(_) | Self::EncapType(_) => 2,
Self::Blackhole | Self::Groups | Self::Fdb => 0,
Self::Encap(v) => v.as_slice().buffer_len(),
Self::Gateway(addr) => addr.buffer_len(),
Self::Other(attr) => attr.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Id(value) | Self::Oif(value) | Self::Master(value) => {
NativeEndian::write_u32(buffer, *value)
}
Self::Group(groups) => {
let mut offset = 0;
for grp in groups {
let len = grp.buffer_len();
grp.emit(&mut buffer[offset..offset + len]);
offset += len
}
}
Self::GroupType(value) | Self::EncapType(value) => {
NativeEndian::write_u16(buffer, *value);
}
Self::Blackhole | Self::Groups | Self::Fdb => {}
Self::Encap(nlas) => nlas.as_slice().emit(buffer),
Self::Gateway(addr) => addr.emit(buffer),
Self::Other(attr) => attr.emit_value(buffer),
}
}

fn kind(&self) -> u16 {
match self {
Self::Id(_) => NHA_ID,
Self::Group(_) => NHA_GROUP,
Self::GroupType(_) => NHA_GROUP_TYPE,
Self::Blackhole => NHA_BLACKHOLE,
Self::Oif(_) => NHA_OIF,
Self::Gateway(_) => NHA_GATEWAY,
Self::EncapType(_) => NHA_ENCAP_TYPE,
Self::Encap(_) => NHA_ENCAP | NLA_F_NESTED,
Self::Groups => NHA_GROUPS,
Self::Master(_) => NHA_MASTER,
Self::Fdb => NHA_FDB,
Self::Other(nla) => nla.kind(),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized>
ParseableParametrized<NlaBuffer<&'a T>, AddressFamily>
for NexthopAttribute
{
fn parse_with_param(
buf: &NlaBuffer<&'a T>,
address_family: AddressFamily,
) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
NHA_ID => Self::Id(
parse_u32(payload).context(format!("invalid NHA_ID value"))?,
),
NHA_GROUP => {
let mut groups = vec![];
let mut i: usize = 0;
while i + 8 <= payload.len() {
groups.push(NexthopGroup::parse(&payload[i..i + 8])?);
i += 8;
}
Self::Group(groups)
}
NHA_GROUP_TYPE => Self::GroupType(
parse_u16(payload)
.context(format!("invalid NHA_GROUP_TYPE value"))?,
),
NHA_BLACKHOLE => Self::Blackhole,
NHA_OIF => Self::Oif(
parse_u32(payload).context(format!("invalid NHA_OIF value"))?,
),
NHA_GATEWAY => {
Self::Gateway(RouteAddress::parse(address_family, payload)?)
}
NHA_ENCAP_TYPE => Self::EncapType(
parse_u16(payload)
.context(format!("invalid NHA_ENCAP_TYPE value"))?,
),
NHA_GROUPS => Self::Groups,
NHA_MASTER => Self::Master(
parse_u32(payload)
.context(format!("invalid NHA_MASTER value"))?,
),
NHA_FDB => Self::Fdb,
_ => Self::Other(
DefaultNla::parse(buf)
.context("invalid link NLA value (unknown type)")?,
),
})
}
}
26 changes: 26 additions & 0 deletions src/nexthop/flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT

const RTNH_F_DEAD: u32 = 1 << 0;
const RTNH_F_PERVASIVE: u32 = 1 << 1;
const RTNH_F_ONLINK: u32 = 1 << 2;
const RTNH_F_OFFLOAD: u32 = 1 << 3;
const RTNH_F_LINKDOWN: u32 = 1 << 4;
const RTNH_F_UNRESOLVED: u32 = 1 << 5;
const RTNH_F_TRAP: u32 = 1 << 6;
// const RTNH_COMPARE_MASK: u32 =
// RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD | RTNH_F_TRAP;

bitflags! {
#[derive(Clone, Eq, PartialEq, Debug, Copy, Default)]
#[non_exhaustive]
pub struct NexthopFlags: u32 {
const Dead = RTNH_F_DEAD;
const Pervasive = RTNH_F_PERVASIVE;
const Onlink = RTNH_F_ONLINK;
const Offload = RTNH_F_OFFLOAD;
const Linkdown = RTNH_F_LINKDOWN;
const Unresolved = RTNH_F_UNRESOLVED;
const Trap= RTNH_F_TRAP;
const _ = !0;
}
}
43 changes: 43 additions & 0 deletions src/nexthop/group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
parsers::{parse_u16, parse_u32},
DecodeError, Emitable,
};

#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
/// Nexthop Group
pub struct NexthopGroup {
/// Nexthop id
pub id: u32,
/// Weight of this nexthop
pub weight: u8,
/// High order bits of weight
pub weight_high: u8,
/// Reserved
pub resvd2: u16,
}

impl Emitable for NexthopGroup {
fn buffer_len(&self) -> usize {
8
}

fn emit(&self, buffer: &mut [u8]) {
NativeEndian::write_u32(buffer, self.id);
buffer[4] = self.weight;
buffer[5] = self.weight_high;
NativeEndian::write_u16(&mut buffer[6..8], self.resvd2);
}
}

impl NexthopGroup {
pub fn parse(payload: &[u8]) -> Result<Self, DecodeError> {
let grp = Self {
id: parse_u32(payload)?,
weight: payload[4],
weight_high: payload[5],
resvd2: parse_u16(&payload[6..8])?,
};
Ok(grp)
}
}
Loading