Skip to content

Commit

Permalink
isis: add support for IS reachability TE Sub-TLVs
Browse files Browse the repository at this point in the history
This commit implements the encoding and decoding routines for the
IS reachability TE Sub-TLVs as defined in section 3 of RFC 5305.
Additionally, these TE Sub-TLVs are visible as part of YANG-modeled
state data.

Configuration and announcement of these attributes are not yet
supported.

Example operational data (JSON output):
```
{
  "extended-is-neighbor": {
    "neighbor": [
      {
        "neighbor-id": "0000.0001.0003.01",
        "instances": {
          "instance": [
            {
              "id": 0,
              "metric": 10,
              "admin-group": 171,
              "local-if-ipv4-addrs": {
                "local-if-ipv4-addr": [
                  "10.0.1.1"
                ]
              },
              "remote-if-ipv4-addrs": {
                "remote-if-ipv4-addr": [
                  "10.0.1.2"
                ]
              },
              "te-metric": 100,
              "max-bandwidth": "0x1.3ebc20p+23",
              "max-reservable-bandwidth": "0x1.189680p+20",
              "unreserved-bandwidths": {
                "unreserved-bandwidth": [
                  {
                    "priority": 0,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 1,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 2,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 3,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 4,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 5,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 6,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  },
                  {
                    "priority": 7,
                    "unreserved-bandwidth": "0x1.189680p+20"
                  }
                ]
              }
            }
          ]
        }
      }
    ]
  }
}
```

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
  • Loading branch information
rwestphal committed Feb 2, 2025
1 parent 91fc626 commit eb0639a
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 48 deletions.
39 changes: 25 additions & 14 deletions holo-isis/src/northbound/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub enum ListEntry<'a> {
IsReach(&'a LspEntry, LanId),
IsReachInstance(u32, &'a IsReach),
ExtIsReach(u32, &'a ExtIsReach),
ExtIsReachUnreservedBw(usize, &'a f32),
Ipv4Reach(&'a Ipv4Reach),
ExtIpv4Reach(&'a ExtIpv4Reach),
Ipv6Reach(&'a Ipv6Reach),
Expand Down Expand Up @@ -346,36 +347,46 @@ fn load_callbacks() -> Callbacks<Instance> {
Box::new(Instance {
id: *id,
metric: Some(reach.metric),
admin_group: None,
te_metric: None,
max_bandwidth: None,
max_reservable_bandwidth: None,
admin_group: reach.sub_tlvs.admin_group.as_ref().map(|tlv| tlv.get()),
te_metric: reach.sub_tlvs.te_default_metric.as_ref().map(|tlv| tlv.get()),
max_bandwidth: reach.sub_tlvs.max_link_bw.as_ref().map(|tlv| tlv.get()),
max_reservable_bandwidth: reach.sub_tlvs.max_resv_link_bw.as_ref().map(|tlv| tlv.get()),
})
})
.path(isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::local_if_ipv4_addrs::PATH)
.get_object(|_instance, _args| {
.get_object(|_instance, args| {
use isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::local_if_ipv4_addrs::LocalIfIpv4Addrs;
let (_, reach) = args.list_entry.as_ext_is_reach().unwrap();
let iter = reach.sub_tlvs.ipv4_interface_addr.iter().map(|tlv| tlv.get()).map(Cow::Borrowed);
Box::new(LocalIfIpv4Addrs {
local_if_ipv4_addr: None,
local_if_ipv4_addr: Some(Box::new(iter)),
})
})
.path(isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::remote_if_ipv4_addrs::PATH)
.get_object(|_instance, _args| {
.get_object(|_instance, args| {
use isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::remote_if_ipv4_addrs::RemoteIfIpv4Addrs;
let (_, reach) = args.list_entry.as_ext_is_reach().unwrap();
let iter = reach.sub_tlvs.ipv4_neighbor_addr.iter().map(|tlv| tlv.get()).map(Cow::Borrowed);
Box::new(RemoteIfIpv4Addrs {
remote_if_ipv4_addr: None,
remote_if_ipv4_addr: Some(Box::new(iter)),
})
})
.path(isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::unreserved_bandwidths::unreserved_bandwidth::PATH)
.get_iterate(|_instance, _args| {
// TODO: implement me!
None
.get_iterate(|_instance, args| {
let (_, reach) = args.parent_list_entry.as_ext_is_reach().unwrap();
if let Some(unreserved_bw) = &reach.sub_tlvs.unreserved_bw {
let iter = unreserved_bw.iter().map(|(prio, bw)| ListEntry::ExtIsReachUnreservedBw(prio, bw));
Some(Box::new(iter))
} else {
None
}
})
.get_object(|_instance, _args| {
.get_object(|_instance, args| {
use isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::unreserved_bandwidths::unreserved_bandwidth::UnreservedBandwidth;
let (priority, unreserved_bandwidth) = args.list_entry.as_ext_is_reach_unreserved_bw().unwrap();
Box::new(UnreservedBandwidth {
priority: None,
unreserved_bandwidth: None,
priority: Some(*priority as u8),
unreserved_bandwidth: Some(unreserved_bandwidth),
})
})
.path(isis::database::levels::lsp::extended_is_neighbor::neighbor::instances::instance::unknown_tlvs::unknown_tlv::PATH)
Expand Down
10 changes: 9 additions & 1 deletion holo-isis/src/packet/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,15 @@ pub enum AuthenticationType {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(FromPrimitive, ToPrimitive)]
#[derive(Deserialize, Serialize)]
pub enum NeighborSubTlvType {}
pub enum NeighborSubTlvType {
AdminGroup = 3,
Ipv4InterfaceAddress = 6,
Ipv4NeighborAddress = 8,
MaxLinkBandwidth = 9,
MaxResvLinkBandwidth = 10,
UnreservedBandwidth = 11,
TeDefaultMetric = 18,
}

// IS-IS Sub-TLVs for TLVs Advertising Prefix Reachability.
//
Expand Down
1 change: 1 addition & 0 deletions holo-isis/src/packet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod auth;
pub mod consts;
pub mod error;
pub mod pdu;
pub mod subtlvs;
pub mod tlv;

use bytes::{Buf, BufMut, Bytes, BytesMut};
Expand Down
10 changes: 10 additions & 0 deletions holo-isis/src/packet/subtlvs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// Copyright (c) The Holo Core Contributors
//
// SPDX-License-Identifier: MIT
//
// Sponsored by NLnet as part of the Next Generation Internet initiative.
// See: https://nlnet.nl/NGI0
//

pub mod neighbor;
254 changes: 254 additions & 0 deletions holo-isis/src/packet/subtlvs/neighbor/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
//
// Copyright (c) The Holo Core Contributors
//
// SPDX-License-Identifier: MIT
//
// Sponsored by NLnet as part of the Next Generation Internet initiative.
// See: https://nlnet.nl/NGI0
//

use std::net::Ipv4Addr;

use bytes::{Buf, BufMut, Bytes, BytesMut};
use derive_new::new;
use holo_utils::bytes::{BytesExt, BytesMutExt};
use serde::{Deserialize, Serialize};

use crate::packet::consts::NeighborSubTlvType;
use crate::packet::error::{DecodeError, DecodeResult};
use crate::packet::tlv::{tlv_encode_end, tlv_encode_start};

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct AdminGroupSubTlv(u32);

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct Ipv4InterfaceAddrSubTlv(Ipv4Addr);

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct Ipv4NeighborAddrSubTlv(Ipv4Addr);

#[derive(Clone, Debug, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct MaxLinkBwSubTlv(f32);

#[derive(Clone, Debug, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct MaxResvLinkBwSubTlv(f32);

#[derive(Clone, Debug, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct UnreservedBwSubTlv([f32; 8]);

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(new)]
#[derive(Deserialize, Serialize)]
pub struct TeDefaultMetricSubTlv(u32);

// ===== impl AdminGroupSubTlv =====

impl AdminGroupSubTlv {
const SIZE: usize = 4;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let groups = buf.get_u32();

Ok(AdminGroupSubTlv(groups))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos = tlv_encode_start(buf, NeighborSubTlvType::AdminGroup);
buf.put_u32(self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> u32 {
self.0
}
}

// ===== impl Ipv4InterfaceAddrSubTlv =====

impl Ipv4InterfaceAddrSubTlv {
const SIZE: usize = 4;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let addr = buf.get_ipv4();

Ok(Ipv4InterfaceAddrSubTlv(addr))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::Ipv4InterfaceAddress);
buf.put_ipv4(&self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> &Ipv4Addr {
&self.0
}
}

// ===== impl Ipv4NeighborAddrSubTlv =====

impl Ipv4NeighborAddrSubTlv {
const SIZE: usize = 4;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let addr = buf.get_ipv4();

Ok(Ipv4NeighborAddrSubTlv(addr))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::Ipv4NeighborAddress);
buf.put_ipv4(&self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> &Ipv4Addr {
&self.0
}
}

// ===== impl MaxLinkBwSubTlv =====

impl MaxLinkBwSubTlv {
const SIZE: usize = 4;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let bw = buf.get_f32();

Ok(MaxLinkBwSubTlv(bw))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::MaxLinkBandwidth);
buf.put_f32(self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> &f32 {
&self.0
}
}

// ===== impl MaxResvLinkBwSubTlv =====

impl MaxResvLinkBwSubTlv {
const SIZE: usize = 4;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let bw = buf.get_f32();

Ok(MaxResvLinkBwSubTlv(bw))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::MaxResvLinkBandwidth);
buf.put_f32(self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> &f32 {
&self.0
}
}

// ===== impl UnreservedBwSubTlv =====

impl UnreservedBwSubTlv {
const SIZE: usize = 32;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let mut bws = [0f32; 8];
for bw in &mut bws {
*bw = buf.get_f32();
}

Ok(UnreservedBwSubTlv(bws))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::UnreservedBandwidth);
for bw in &self.0 {
buf.put_f32(*bw);
}
tlv_encode_end(buf, start_pos);
}

pub(crate) fn iter(&self) -> impl Iterator<Item = (usize, &f32)> {
self.0.iter().enumerate()
}
}

// ===== impl TeDefaultMetricSubTlv =====

impl TeDefaultMetricSubTlv {
const SIZE: usize = 3;

pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
// Validate the TLV length.
if tlv_len as usize != Self::SIZE {
return Err(DecodeError::InvalidTlvLength(tlv_len));
}

let metric = buf.get_u24();

Ok(TeDefaultMetricSubTlv(metric))
}

pub(crate) fn encode(&self, buf: &mut BytesMut) {
let start_pos =
tlv_encode_start(buf, NeighborSubTlvType::TeDefaultMetric);
buf.put_u24(self.0);
tlv_encode_end(buf, start_pos);
}

pub(crate) fn get(&self) -> u32 {
self.0
}
}
Loading

0 comments on commit eb0639a

Please sign in to comment.