From 542dd7060276c90acb649b5c247409118ff7c669 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Tue, 14 Jan 2025 23:44:09 -0300 Subject: [PATCH] isis: implement TE Router ID TLVs Add support for IPv4/IPv6 Router ID TLVs and corresponding configuration options for their announcement. Signed-off-by: Renato Westphal --- holo-isis/src/lsdb.rs | 4 + holo-isis/src/northbound/configuration.rs | 37 +++++++++ holo-isis/src/northbound/state.rs | 4 +- holo-isis/src/packet/consts.rs | 2 + holo-isis/src/packet/pdu.rs | 44 ++++++++++- holo-isis/src/packet/tlv.rs | 76 +++++++++++++++++++ holo-isis/tests/packet/mod.rs | 32 +++++--- .../deviations/holo-ietf-isis-deviations.yang | 4 - holo-yang/src/lib.rs | 1 + 9 files changed, 183 insertions(+), 21 deletions(-) diff --git a/holo-isis/src/lsdb.rs b/holo-isis/src/lsdb.rs index 7f30d878..4f67a50b 100644 --- a/holo-isis/src/lsdb.rs +++ b/holo-isis/src/lsdb.rs @@ -306,8 +306,10 @@ fn lsp_build_tlvs( ipv4_internal_reach.into_values(), [], ext_ipv4_reach.into_values(), + instance.config.ipv4_router_id, ipv6_addrs, ipv6_reach.into_values(), + instance.config.ipv6_router_id, ) } @@ -363,8 +365,10 @@ fn lsp_build_tlvs_pseudo( [], [], [], + None, [], [], + None, ) } diff --git a/holo-isis/src/northbound/configuration.rs b/holo-isis/src/northbound/configuration.rs index 349a0bda..b6e6d84c 100644 --- a/holo-isis/src/northbound/configuration.rs +++ b/holo-isis/src/northbound/configuration.rs @@ -8,6 +8,7 @@ // use std::collections::{BTreeMap, BTreeSet}; +use std::net::{Ipv4Addr, Ipv6Addr}; use std::sync::LazyLock as Lazy; use async_trait::async_trait; @@ -81,6 +82,8 @@ pub struct InstanceCfg { pub metric_type: LevelsCfg, pub default_metric: LevelsCfg, pub auth: LevelsOptCfg, + pub ipv4_router_id: Option, + pub ipv6_router_id: Option, pub max_paths: u16, pub afs: BTreeMap, pub spf_initial_delay: u32, @@ -403,6 +406,38 @@ fn load_callbacks() -> Callbacks { event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1)); event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2)); }) + .path(isis::mpls::te_rid::ipv4_router_id::PATH) + .modify_apply(|instance, args| { + let addr = args.dnode.get_ipv4(); + instance.config.ipv4_router_id = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1)); + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2)); + }) + .delete_apply(|instance, args| { + instance.config.ipv4_router_id = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1)); + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2)); + }) + .path(isis::mpls::te_rid::ipv6_router_id::PATH) + .modify_apply(|instance, args| { + let addr = args.dnode.get_ipv6(); + instance.config.ipv6_router_id = Some(addr); + + let event_queue = args.event_queue; + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1)); + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2)); + }) + .delete_apply(|instance, args| { + instance.config.ipv6_router_id = None; + + let event_queue = args.event_queue; + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1)); + event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2)); + }) .path(isis::spf_control::paths::PATH) .modify_apply(|instance, args| { let max_paths = args.dnode.get_u16(); @@ -1242,6 +1277,8 @@ impl Default for InstanceCfg { default_metric, auth: Default::default(), max_paths, + ipv4_router_id: None, + ipv6_router_id: None, afs: Default::default(), spf_initial_delay, spf_short_delay, diff --git a/holo-isis/src/northbound/state.rs b/holo-isis/src/northbound/state.rs index becf1d44..b29f3839 100644 --- a/holo-isis/src/northbound/state.rs +++ b/holo-isis/src/northbound/state.rs @@ -191,8 +191,8 @@ fn load_callbacks() -> Callbacks { sequence: Some(lsp.seqno), ipv4_addresses: Some(Box::new(ipv4_addresses)), ipv6_addresses: Some(Box::new(ipv6_addresses)), - ipv4_te_routerid: None, - ipv6_te_routerid: None, + ipv4_te_routerid: lsp.tlvs.ipv4_router_id.as_ref().map(|tlv| Cow::Borrowed(tlv.get())), + ipv6_te_routerid: lsp.tlvs.ipv6_router_id.as_ref().map(|tlv| Cow::Borrowed(tlv.get())), protocol_supported: Some(Box::new(protocol_supported)), dynamic_hostname: lsp.tlvs.hostname().map(Cow::Borrowed), lsp_buffer_size: lsp.tlvs.lsp_buf_size(), diff --git a/holo-isis/src/packet/consts.rs b/holo-isis/src/packet/consts.rs index 03fbbafb..279b0b7a 100644 --- a/holo-isis/src/packet/consts.rs +++ b/holo-isis/src/packet/consts.rs @@ -54,8 +54,10 @@ pub enum TlvType { ProtocolsSupported = 129, Ipv4ExternalReach = 130, Ipv4Addresses = 132, + Ipv4RouterId = 134, ExtIpv4Reach = 135, DynamicHostname = 137, + Ipv6RouterId = 140, Ipv6Addresses = 232, Ipv6Reach = 236, } diff --git a/holo-isis/src/packet/pdu.rs b/holo-isis/src/packet/pdu.rs index cbe7d39c..a3e9247a 100644 --- a/holo-isis/src/packet/pdu.rs +++ b/holo-isis/src/packet/pdu.rs @@ -24,10 +24,10 @@ use crate::packet::error::{DecodeError, DecodeResult}; use crate::packet::tlv::{ AreaAddressesTlv, DynamicHostnameTlv, ExtIpv4Reach, ExtIpv4ReachTlv, ExtIsReach, ExtIsReachTlv, Ipv4AddressesTlv, Ipv4Reach, Ipv4ReachTlv, - Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, IsReach, IsReachTlv, - LspBufferSizeTlv, LspEntriesTlv, LspEntry, NeighborsTlv, PaddingTlv, - ProtocolsSupportedTlv, TLV_HDR_SIZE, TLV_MAX_LEN, Tlv, UnknownTlv, - tlv_entries_split, tlv_take_max, + Ipv4RouterIdTlv, Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, + Ipv6RouterIdTlv, IsReach, IsReachTlv, LspBufferSizeTlv, LspEntriesTlv, + LspEntry, NeighborsTlv, PaddingTlv, ProtocolsSupportedTlv, TLV_HDR_SIZE, + TLV_MAX_LEN, Tlv, UnknownTlv, tlv_entries_split, tlv_take_max, }; use crate::packet::{AreaAddr, LanId, LevelNumber, LevelType, LspId, SystemId}; @@ -111,8 +111,10 @@ pub struct LspTlvs { pub ipv4_internal_reach: Vec, pub ipv4_external_reach: Vec, pub ext_ipv4_reach: Vec, + pub ipv4_router_id: Option, pub ipv6_addrs: Vec, pub ipv6_reach: Vec, + pub ipv6_router_id: Option, pub unknown: Vec, } @@ -679,6 +681,13 @@ impl Lsp { let tlv = ExtIpv4ReachTlv::decode(tlv_len, &mut buf_tlv)?; tlvs.ext_ipv4_reach.push(tlv); } + Some(TlvType::Ipv4RouterId) => { + if tlvs.ipv4_router_id.is_some() { + continue; + } + let tlv = Ipv4RouterIdTlv::decode(tlv_len, &mut buf_tlv)?; + tlvs.ipv4_router_id = Some(tlv); + } Some(TlvType::Ipv6Addresses) => { let tlv = Ipv6AddressesTlv::decode(tlv_len, &mut buf_tlv)?; tlvs.ipv6_addrs.push(tlv); @@ -687,6 +696,13 @@ impl Lsp { let tlv = Ipv6ReachTlv::decode(tlv_len, &mut buf_tlv)?; tlvs.ipv6_reach.push(tlv); } + Some(TlvType::Ipv6RouterId) => { + if tlvs.ipv6_router_id.is_some() { + continue; + } + let tlv = Ipv6RouterIdTlv::decode(tlv_len, &mut buf_tlv)?; + tlvs.ipv6_router_id = Some(tlv); + } _ => { // Save unknown top-level TLV. let value = buf_tlv.copy_to_bytes(tlv_len as usize); @@ -754,12 +770,18 @@ impl Lsp { for tlv in &self.tlvs.ext_ipv4_reach { tlv.encode(&mut buf); } + if let Some(tlv) = &self.tlvs.ipv4_router_id { + tlv.encode(&mut buf); + } for tlv in &self.tlvs.ipv6_addrs { tlv.encode(&mut buf); } for tlv in &self.tlvs.ipv6_reach { tlv.encode(&mut buf); } + if let Some(tlv) = &self.tlvs.ipv6_router_id { + tlv.encode(&mut buf); + } // Compute LSP checksum. let cksum = Self::checksum(&buf[12..]); @@ -868,8 +890,10 @@ impl LspTlvs { ipv4_internal_reach: impl IntoIterator, ipv4_external_reach: impl IntoIterator, ext_ipv4_reach: impl IntoIterator, + ipv4_router_id: Option, ipv6_addrs: impl IntoIterator, ipv6_reach: impl IntoIterator, + ipv6_router_id: Option, ) -> Self { LspTlvs { protocols_supported: Some(ProtocolsSupportedTlv::from( @@ -884,8 +908,10 @@ impl LspTlvs { ipv4_internal_reach: tlv_entries_split(ipv4_internal_reach), ipv4_external_reach: tlv_entries_split(ipv4_external_reach), ext_ipv4_reach: tlv_entries_split(ext_ipv4_reach), + ipv4_router_id: ipv4_router_id.map(Ipv4RouterIdTlv::new), ipv6_addrs: tlv_entries_split(ipv6_addrs), ipv6_reach: tlv_entries_split(ipv6_reach), + ipv6_router_id: ipv6_router_id.map(Ipv6RouterIdTlv::new), unknown: Default::default(), } } @@ -905,6 +931,14 @@ impl LspTlvs { if let Some(lsp_buf_size) = &lsp_buf_size { rem_len -= lsp_buf_size.len(); } + let ipv4_router_id = self.ipv4_router_id.take(); + if let Some(ipv4_router_id) = &ipv4_router_id { + rem_len -= ipv4_router_id.len(); + } + let ipv6_router_id = self.ipv6_router_id.take(); + if let Some(ipv6_router_id) = &ipv6_router_id { + rem_len -= ipv6_router_id.len(); + } let is_reach = tlv_take_max(&mut self.is_reach, &mut rem_len); let ext_is_reach = tlv_take_max(&mut self.ext_is_reach, &mut rem_len); let ipv4_addrs = tlv_take_max(&mut self.ipv4_addrs, &mut rem_len); @@ -931,8 +965,10 @@ impl LspTlvs { ipv4_internal_reach, ipv4_external_reach, ext_ipv4_reach, + ipv4_router_id, ipv6_addrs, ipv6_reach, + ipv6_router_id, unknown: Default::default(), }) } diff --git a/holo-isis/src/packet/tlv.rs b/holo-isis/src/packet/tlv.rs index 4c6bd8f3..14ba05a8 100644 --- a/holo-isis/src/packet/tlv.rs +++ b/holo-isis/src/packet/tlv.rs @@ -220,6 +220,16 @@ pub struct Ipv6ReachSubTlvs { pub unknown: Vec, } +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(new)] +#[derive(Deserialize, Serialize)] +pub struct Ipv4RouterIdTlv(Ipv4Addr); + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(new)] +#[derive(Deserialize, Serialize)] +pub struct Ipv6RouterIdTlv(Ipv6Addr); + #[derive(Clone, Debug, Eq, PartialEq)] #[derive(new)] #[derive(Deserialize, Serialize)] @@ -1188,6 +1198,72 @@ where } } +// ===== impl Ipv4RouterIdTlv ===== + +impl Ipv4RouterIdTlv { + 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(Ipv4RouterIdTlv(addr)) + } + + pub(crate) fn encode(&self, buf: &mut BytesMut) { + let start_pos = tlv_encode_start(buf, TlvType::Ipv4RouterId); + buf.put_ipv4(&self.0); + tlv_encode_end(buf, start_pos); + } + + pub(crate) fn get(&self) -> &Ipv4Addr { + &self.0 + } +} + +impl Tlv for Ipv4RouterIdTlv { + fn len(&self) -> usize { + TLV_HDR_SIZE + Self::SIZE + } +} + +// ===== impl Ipv6RouterIdTlv ===== + +impl Ipv6RouterIdTlv { + const SIZE: usize = 16; + + 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_ipv6(); + + Ok(Ipv6RouterIdTlv(addr)) + } + + pub(crate) fn encode(&self, buf: &mut BytesMut) { + let start_pos = tlv_encode_start(buf, TlvType::Ipv6RouterId); + buf.put_ipv6(&self.0); + tlv_encode_end(buf, start_pos); + } + + pub(crate) fn get(&self) -> &Ipv6Addr { + &self.0 + } +} + +impl Tlv for Ipv6RouterIdTlv { + fn len(&self) -> usize { + TLV_HDR_SIZE + Self::SIZE + } +} + // ===== blanket implementations ===== impl Tlv for T { diff --git a/holo-isis/tests/packet/mod.rs b/holo-isis/tests/packet/mod.rs index 30af6534..2d7fe3d8 100644 --- a/holo-isis/tests/packet/mod.rs +++ b/holo-isis/tests/packet/mod.rs @@ -19,9 +19,9 @@ use holo_isis::packet::pdu::{ use holo_isis::packet::tlv::{ AreaAddressesTlv, DynamicHostnameTlv, ExtIpv4Reach, ExtIpv4ReachTlv, ExtIsReach, ExtIsReachTlv, Ipv4AddressesTlv, Ipv4Reach, Ipv4ReachTlv, - Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, IsReach, IsReachTlv, - LspBufferSizeTlv, LspEntriesTlv, LspEntry, NeighborsTlv, PaddingTlv, - ProtocolsSupportedTlv, + Ipv4RouterIdTlv, Ipv6AddressesTlv, Ipv6Reach, Ipv6ReachTlv, + Ipv6RouterIdTlv, IsReach, IsReachTlv, LspBufferSizeTlv, LspEntriesTlv, + LspEntry, NeighborsTlv, PaddingTlv, ProtocolsSupportedTlv, }; use holo_isis::packet::{ AreaAddr, LanId, LevelNumber, LevelType, LspId, SystemId, @@ -521,18 +521,20 @@ static PSNP1: Lazy<(Vec, Pdu)> = Lazy::new(|| { static LSP1: Lazy<(Vec, Pdu)> = Lazy::new(|| { ( vec![ - 0x83, 0x1b, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x82, 0x04, + 0x83, 0x1b, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x9a, 0x04, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0xd0, 0x32, 0x01, 0x81, 0x01, 0xcc, 0x01, 0x04, 0x03, + 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, 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, + 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, ], Pdu::Lsp(Lsp::new( LevelNumber::L1, @@ -582,6 +584,9 @@ static LSP1: Lazy<(Vec, Pdu)> = Lazy::new(|| { }, ], }], + ipv4_router_id: Some(Ipv4RouterIdTlv::new( + Ipv4Addr::from_str("1.1.1.1").unwrap(), + )), ipv6_addrs: vec![Ipv6AddressesTlv { list: vec![Ipv6Addr::from_str("2001:db8::1").unwrap()], }], @@ -607,6 +612,9 @@ static LSP1: Lazy<(Vec, Pdu)> = Lazy::new(|| { }, ], }], + ipv6_router_id: Some(Ipv6RouterIdTlv::new( + Ipv6Addr::from_str("2001:db8::1").unwrap(), + )), unknown: vec![], }, )), @@ -704,8 +712,10 @@ static LSP2: Lazy<(Vec, Pdu)> = Lazy::new(|| { }], ipv4_external_reach: vec![], ext_ipv4_reach: vec![], + ipv4_router_id: None, ipv6_addrs: vec![], ipv6_reach: vec![], + ipv6_router_id: None, unknown: vec![], }, )), diff --git a/holo-yang/modules/deviations/holo-ietf-isis-deviations.yang b/holo-yang/modules/deviations/holo-ietf-isis-deviations.yang index 39c87346..c44d7f6f 100644 --- a/holo-yang/modules/deviations/holo-ietf-isis-deviations.yang +++ b/holo-yang/modules/deviations/holo-ietf-isis-deviations.yang @@ -402,10 +402,6 @@ module holo-ietf-isis-deviations { } */ - deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/isis:isis/isis:mpls" { - deviate not-supported; - } - /* deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/isis:isis/isis:mpls/isis:ldp" { deviate not-supported; diff --git a/holo-yang/src/lib.rs b/holo-yang/src/lib.rs index d3163ee7..d34028c2 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -265,6 +265,7 @@ pub static YANG_FEATURES: Lazy>> = "lsp-refresh", "max-ecmp", "nlpid-control", + "te-rid", ], "ietf-ospf" => vec![ "bfd",