From eb0639ac7f7f39a2cb6ebc9786214cfa161517fd Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 1 Feb 2025 12:23:01 -0300 Subject: [PATCH] isis: add support for IS reachability TE Sub-TLVs 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 --- holo-isis/src/northbound/state.rs | 39 ++- holo-isis/src/packet/consts.rs | 10 +- holo-isis/src/packet/mod.rs | 1 + holo-isis/src/packet/subtlvs/mod.rs | 10 + holo-isis/src/packet/subtlvs/neighbor/mod.rs | 254 +++++++++++++++++++ holo-isis/src/packet/tlv.rs | 119 +++++++-- holo-isis/tests/packet/mod.rs | 70 +++-- holo-northbound/build.rs | 33 +++ 8 files changed, 488 insertions(+), 48 deletions(-) create mode 100644 holo-isis/src/packet/subtlvs/mod.rs create mode 100644 holo-isis/src/packet/subtlvs/neighbor/mod.rs diff --git a/holo-isis/src/northbound/state.rs b/holo-isis/src/northbound/state.rs index c578a920..3fdf7876 100644 --- a/holo-isis/src/northbound/state.rs +++ b/holo-isis/src/northbound/state.rs @@ -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), @@ -346,36 +347,46 @@ fn load_callbacks() -> Callbacks { 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) diff --git a/holo-isis/src/packet/consts.rs b/holo-isis/src/packet/consts.rs index 0fab3661..24242018 100644 --- a/holo-isis/src/packet/consts.rs +++ b/holo-isis/src/packet/consts.rs @@ -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. // diff --git a/holo-isis/src/packet/mod.rs b/holo-isis/src/packet/mod.rs index 680637c4..cb2f8877 100644 --- a/holo-isis/src/packet/mod.rs +++ b/holo-isis/src/packet/mod.rs @@ -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}; diff --git a/holo-isis/src/packet/subtlvs/mod.rs b/holo-isis/src/packet/subtlvs/mod.rs new file mode 100644 index 00000000..4c48e54b --- /dev/null +++ b/holo-isis/src/packet/subtlvs/mod.rs @@ -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; diff --git a/holo-isis/src/packet/subtlvs/neighbor/mod.rs b/holo-isis/src/packet/subtlvs/neighbor/mod.rs new file mode 100644 index 00000000..4d2f4591 --- /dev/null +++ b/holo-isis/src/packet/subtlvs/neighbor/mod.rs @@ -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 { + // 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 { + // 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 { + // 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 { + // 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 { + // 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 { + // 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 { + self.0.iter().enumerate() + } +} + +// ===== impl TeDefaultMetricSubTlv ===== + +impl TeDefaultMetricSubTlv { + const SIZE: usize = 3; + + pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult { + // 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 + } +} diff --git a/holo-isis/src/packet/tlv.rs b/holo-isis/src/packet/tlv.rs index fab0a604..459d02f7 100644 --- a/holo-isis/src/packet/tlv.rs +++ b/holo-isis/src/packet/tlv.rs @@ -24,7 +24,7 @@ use crate::packet::consts::{ AuthenticationType, NeighborSubTlvType, PrefixSubTlvType, TlvType, }; use crate::packet::error::{DecodeError, DecodeResult}; -use crate::packet::{AreaAddr, LanId, LspId}; +use crate::packet::{AreaAddr, LanId, LspId, subtlvs}; // TLV header size. pub const TLV_HDR_SIZE: usize = 2; @@ -167,6 +167,13 @@ pub struct ExtIsReach { #[derive(Clone, Debug, Default, PartialEq)] #[derive(Deserialize, Serialize)] pub struct ExtIsReachSubTlvs { + pub admin_group: Option, + pub ipv4_interface_addr: Vec, + pub ipv4_neighbor_addr: Vec, + pub max_link_bw: Option, + pub max_resv_link_bw: Option, + pub unreserved_bw: Option, + pub te_default_metric: Option, pub unknown: Vec, } @@ -815,6 +822,12 @@ impl ExtIsReachTlv { const ENTRY_MIN_SIZE: usize = 11; pub(crate) fn decode(_tlv_len: u8, buf: &mut Bytes) -> DecodeResult { + use subtlvs::neighbor::{ + AdminGroupSubTlv, Ipv4InterfaceAddrSubTlv, Ipv4NeighborAddrSubTlv, + MaxLinkBwSubTlv, MaxResvLinkBwSubTlv, TeDefaultMetricSubTlv, + UnreservedBwSubTlv, + }; + let mut list = vec![]; while buf.remaining() >= Self::ENTRY_MIN_SIZE { @@ -837,13 +850,58 @@ impl ExtIsReachTlv { return Err(DecodeError::InvalidTlvLength(stlv_len)); } - // Parse TLV value. - let mut buf_tlv = buf.copy_to_bytes(stlv_len as usize); + // Parse Sub-TLV value. + let mut buf_stlv = buf.copy_to_bytes(stlv_len as usize); sub_tlvs_len -= stlv_len; match stlv_etype { + Some(NeighborSubTlvType::AdminGroup) => { + let stlv = + AdminGroupSubTlv::decode(stlv_len, &mut buf_stlv)?; + sub_tlvs.admin_group = Some(stlv); + } + Some(NeighborSubTlvType::Ipv4InterfaceAddress) => { + let stlv = Ipv4InterfaceAddrSubTlv::decode( + stlv_len, + &mut buf_stlv, + )?; + sub_tlvs.ipv4_interface_addr.push(stlv); + } + Some(NeighborSubTlvType::Ipv4NeighborAddress) => { + let stlv = Ipv4NeighborAddrSubTlv::decode( + stlv_len, + &mut buf_stlv, + )?; + sub_tlvs.ipv4_neighbor_addr.push(stlv); + } + Some(NeighborSubTlvType::MaxLinkBandwidth) => { + let stlv = + MaxLinkBwSubTlv::decode(stlv_len, &mut buf_stlv)?; + sub_tlvs.max_link_bw = Some(stlv); + } + Some(NeighborSubTlvType::MaxResvLinkBandwidth) => { + let stlv = MaxResvLinkBwSubTlv::decode( + stlv_len, + &mut buf_stlv, + )?; + sub_tlvs.max_resv_link_bw = Some(stlv); + } + Some(NeighborSubTlvType::UnreservedBandwidth) => { + let stlv = UnreservedBwSubTlv::decode( + stlv_len, + &mut buf_stlv, + )?; + sub_tlvs.unreserved_bw = Some(stlv); + } + Some(NeighborSubTlvType::TeDefaultMetric) => { + let stlv = TeDefaultMetricSubTlv::decode( + stlv_len, + &mut buf_stlv, + )?; + sub_tlvs.te_default_metric = Some(stlv); + } _ => { - // Save unknown top-level TLV. - let value = buf_tlv.copy_to_bytes(stlv_len as usize); + // Save unknown Sub-TLV. + let value = buf_stlv.copy_to_bytes(stlv_len as usize); sub_tlvs .unknown .push(UnknownTlv::new(stlv_type, stlv_len, value)); @@ -869,7 +927,31 @@ impl ExtIsReachTlv { // Encode metric. buf.put_u24(entry.metric); // Encode Sub-TLVs. + let subtlvs_len_pos = buf.len(); buf.put_u8(0); + if let Some(tlv) = &entry.sub_tlvs.admin_group { + tlv.encode(buf); + } + for tlv in &entry.sub_tlvs.ipv4_interface_addr { + tlv.encode(buf); + } + for tlv in &entry.sub_tlvs.ipv4_neighbor_addr { + tlv.encode(buf); + } + if let Some(tlv) = &entry.sub_tlvs.max_link_bw { + tlv.encode(buf); + } + if let Some(tlv) = &entry.sub_tlvs.max_resv_link_bw { + tlv.encode(buf); + } + if let Some(tlv) = &entry.sub_tlvs.unreserved_bw { + tlv.encode(buf); + } + if let Some(tlv) = &entry.sub_tlvs.te_default_metric { + tlv.encode(buf); + } + // Rewrite Sub-TLVs length field. + buf[subtlvs_len_pos] = (buf.len() - 1 - subtlvs_len_pos) as u8; } tlv_encode_end(buf, start_pos); } @@ -1053,14 +1135,14 @@ impl ExtIpv4ReachTlv { return Err(DecodeError::InvalidTlvLength(stlv_len)); } - // Parse TLV value. - let mut buf_tlv = buf.copy_to_bytes(stlv_len as usize); + // Parse Sub-TLV value. + let mut buf_stlv = buf.copy_to_bytes(stlv_len as usize); sub_tlvs_len -= stlv_len; match stlv_etype { _ => { - // Save unknown top-level TLV. + // Save unknown Sub-TLV. let value = - buf_tlv.copy_to_bytes(stlv_len as usize); + buf_stlv.copy_to_bytes(stlv_len as usize); sub_tlvs.unknown.push(UnknownTlv::new( stlv_type, stlv_len, value, )); @@ -1181,14 +1263,14 @@ impl Ipv6ReachTlv { return Err(DecodeError::InvalidTlvLength(stlv_len)); } - // Parse TLV value. - let mut buf_tlv = buf.copy_to_bytes(stlv_len as usize); + // Parse Sub-TLV value. + let mut buf_stlv = buf.copy_to_bytes(stlv_len as usize); sub_tlvs_len -= stlv_len; match stlv_etype { _ => { - // Save unknown top-level TLV. + // Save unknown Sub-TLV. let value = - buf_tlv.copy_to_bytes(stlv_len as usize); + buf_stlv.copy_to_bytes(stlv_len as usize); sub_tlvs.unknown.push(UnknownTlv::new( stlv_type, stlv_len, value, )); @@ -1349,7 +1431,12 @@ const fn prefix_wire_len(len: u8) -> usize { (len as usize).div_ceil(8) } -fn tlv_encode_start(buf: &mut BytesMut, tlv_type: impl ToPrimitive) -> usize { +// ===== global functions ===== + +pub(crate) fn tlv_encode_start( + buf: &mut BytesMut, + tlv_type: impl ToPrimitive, +) -> usize { let start_pos = buf.len(); buf.put_u8(tlv_type.to_u8().unwrap()); // The TLV length will be rewritten later. @@ -1357,13 +1444,11 @@ fn tlv_encode_start(buf: &mut BytesMut, tlv_type: impl ToPrimitive) -> usize { start_pos } -fn tlv_encode_end(buf: &mut BytesMut, start_pos: usize) { +pub(crate) fn tlv_encode_end(buf: &mut BytesMut, start_pos: usize) { // Rewrite TLV length. buf[start_pos + 1] = (buf.len() - start_pos - TLV_HDR_SIZE) as u8; } -// ===== global functions ===== - // Takes as many TLVs as will fit into the provided PDU remaining length. pub(crate) fn tlv_take_max( tlv_list: &mut Vec, diff --git a/holo-isis/tests/packet/mod.rs b/holo-isis/tests/packet/mod.rs index a83bf72e..d3076816 100644 --- a/holo-isis/tests/packet/mod.rs +++ b/holo-isis/tests/packet/mod.rs @@ -17,10 +17,15 @@ use holo_isis::packet::consts::LspFlags; use holo_isis::packet::pdu::{ Hello, HelloTlvs, HelloVariant, Lsp, LspTlvs, Pdu, Snp, SnpTlvs, }; +use holo_isis::packet::subtlvs::neighbor::{ + AdminGroupSubTlv, Ipv4InterfaceAddrSubTlv, Ipv4NeighborAddrSubTlv, + MaxLinkBwSubTlv, MaxResvLinkBwSubTlv, TeDefaultMetricSubTlv, + UnreservedBwSubTlv, +}; use holo_isis::packet::tlv::{ AreaAddressesTlv, DynamicHostnameTlv, ExtIpv4Reach, ExtIpv4ReachTlv, - ExtIsReach, ExtIsReachTlv, Ipv4AddressesTlv, Ipv4Reach, Ipv4ReachTlv, - Ipv4RouterIdTlv, Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, + ExtIsReach, ExtIsReachSubTlvs, ExtIsReachTlv, Ipv4AddressesTlv, Ipv4Reach, + Ipv4ReachTlv, Ipv4RouterIdTlv, Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, Ipv6RouterIdTlv, IsReach, IsReachTlv, LspBufferSizeTlv, LspEntriesTlv, LspEntry, NeighborsTlv, PaddingTlv, ProtocolsSupportedTlv, }; @@ -621,20 +626,27 @@ static PSNP1: Lazy<(Vec, Option<&Key>, Pdu)> = Lazy::new(|| { static LSP1: Lazy<(Vec, Option<&Key>, Pdu)> = Lazy::new(|| { ( vec![ - 0x83, 0x1b, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x9a, 0x04, + 0x83, 0x1b, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0xdf, 0x04, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0xb7, 0x38, 0x01, 0x81, 0x01, 0xcc, 0x01, 0x04, 0x03, - 0x49, 0x00, 0x00, 0x16, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x03, 0x00, 0x00, 0x0a, 0x00, 0x84, 0x04, 0x01, 0x01, 0x01, 0x01, - 0x87, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x18, 0x0a, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x0a, 0x20, 0x01, 0x01, 0x01, 0x01, 0x86, 0x04, 0x01, - 0x01, 0x01, 0x01, 0xe8, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xec, - 0x24, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x80, 0x20, 0x01, 0x0d, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x40, 0x20, 0x01, 0x0d, 0xb8, - 0x10, 0x00, 0x00, 0x00, 0x8c, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x04, 0x66, 0x91, 0x01, 0x81, 0x01, 0xcc, 0x01, 0x04, 0x03, + 0x49, 0x00, 0x00, 0x16, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x03, 0x00, 0x00, 0x0a, 0x45, 0x03, 0x04, 0x00, 0x00, 0x00, 0x0f, + 0x06, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x08, 0x04, 0x0a, 0x00, 0x01, + 0x02, 0x09, 0x04, 0x4c, 0xee, 0x6b, 0x28, 0x0a, 0x04, 0x4b, 0x3e, + 0xbc, 0x20, 0x0b, 0x20, 0x4b, 0x3e, 0xbc, 0x20, 0x4b, 0x3e, 0xbc, + 0x20, 0x4b, 0x3e, 0xbc, 0x20, 0x4b, 0x3e, 0xbc, 0x20, 0x4b, 0x3e, + 0xbc, 0x20, 0x4b, 0x3e, 0xbc, 0x20, 0x4b, 0x3e, 0xbc, 0x20, 0x4b, + 0x3e, 0xbc, 0x20, 0x12, 0x03, 0x00, 0x00, 0x64, 0x84, 0x04, 0x01, + 0x01, 0x01, 0x01, 0x87, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x18, 0x0a, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x20, 0x01, 0x01, 0x01, 0x01, + 0x86, 0x04, 0x01, 0x01, 0x01, 0x01, 0xe8, 0x10, 0x20, 0x01, 0x0d, + 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xec, 0x24, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x80, 0x20, + 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x40, 0x20, + 0x01, 0x0d, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8c, 0x10, 0x20, 0x01, + 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, ], None, Pdu::Lsp(Lsp::new( @@ -660,7 +672,33 @@ static LSP1: Lazy<(Vec, Option<&Key>, Pdu)> = Lazy::new(|| { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, ]), metric: 10, - sub_tlvs: Default::default(), + sub_tlvs: ExtIsReachSubTlvs { + admin_group: Some(AdminGroupSubTlv::new(0x0f)), + ipv4_interface_addr: vec![ + Ipv4InterfaceAddrSubTlv::new( + Ipv4Addr::from_str("10.0.1.1").unwrap(), + ), + ], + ipv4_neighbor_addr: vec![ + Ipv4NeighborAddrSubTlv::new( + Ipv4Addr::from_str("10.0.1.2").unwrap(), + ), + ], + max_link_bw: Some(MaxLinkBwSubTlv::new( + 125000000.0, + )), + max_resv_link_bw: Some(MaxResvLinkBwSubTlv::new( + 12500000.0, + )), + unreserved_bw: Some(UnreservedBwSubTlv::new([ + 12500000.0, 12500000.0, 12500000.0, 12500000.0, + 12500000.0, 12500000.0, 12500000.0, 12500000.0, + ])), + te_default_metric: Some( + TeDefaultMetricSubTlv::new(100), + ), + unknown: vec![], + }, }], }], ipv4_addrs: vec![Ipv4AddressesTlv { diff --git a/holo-northbound/build.rs b/holo-northbound/build.rs index a7069df8..59cdaade 100644 --- a/holo-northbound/build.rs +++ b/holo-northbound/build.rs @@ -77,6 +77,34 @@ fn timeticks64_to_yang(timeticks: Cow<'_, Instant>) -> String { uptime.to_string() } +fn bandwidth_ieee_float32_to_yang(value: &f32) -> String { + // Get the binary representation of the float value. + let bits = value.to_bits(); + + // Extract the sign bit, exponent, and fraction. + let sign = (bits >> 31) & 0x1; + let exponent = ((bits >> 23) & 0xFF) as i32 - 127; + let fraction = bits & 0x7FFFFF; + + // Normalize the fraction by adding the leading 1. + let mut fraction_hex = format!("{:x}", fraction); + + // Ensure 6 digits in hexadecimal. + while fraction_hex.len() < 6 { + fraction_hex = format!("0{}", fraction_hex); + } + + // Format the exponent as a signed decimal. + let exponent_str = if exponent >= 0 { + format!("p+{}", exponent) + } else { + format!("p{}", exponent) + }; + + // Build the final string. + format!("0x1.{}{}", fraction_hex, exponent_str) +} + fn fletcher_checksum16_to_yang(cksum: u16) -> String { format!("{:#06x}", cksum) } @@ -426,6 +454,7 @@ fn leaf_typedef_map(leaf_type: &SchemaLeafType<'_>) -> Option<&'static str> { Some("timeticks") => Some("Cow<'a, Instant>"), Some("timeticks64") => Some("Cow<'a, Instant>"), Some("hex-string") => Some("&'a [u8]"), + Some("bandwidth-ieee-float32") => Some("&'a f32"), // ietf-ospf Some("fletcher-checksum16-type") => Some("u16"), _ => None, @@ -462,6 +491,10 @@ fn leaf_typedef_value( Some("hex-string") => { Some(format!("Some(&hex_string_to_yang({}))", field_name)) } + Some("bandwidth-ieee-float32") => Some(format!( + "Some(&bandwidth_ieee_float32_to_yang({}))", + field_name + )), // ietf-ospf Some("fletcher-checksum16-type") => Some(format!( "Some(&fletcher_checksum16_to_yang({}))",