diff --git a/holo-ospf/src/instance.rs b/holo-ospf/src/instance.rs index c715fb2b..70bddc6b 100644 --- a/holo-ospf/src/instance.rs +++ b/holo-ospf/src/instance.rs @@ -780,6 +780,9 @@ where IbusMsg::SrCfgUpd(sr_config) => { instance.shared.sr_config = sr_config; } + IbusMsg::BierCfgUpd(bier_config) => { + instance.shared.bier_config = bier_config.clone(); + } // SR configuration event. IbusMsg::SrCfgEvent(event) => { events::process_sr_cfg_change(instance, event)? diff --git a/holo-ospf/src/northbound/configuration.rs b/holo-ospf/src/northbound/configuration.rs index 641e05b3..0817b309 100644 --- a/holo-ospf/src/northbound/configuration.rs +++ b/holo-ospf/src/northbound/configuration.rs @@ -102,6 +102,15 @@ pub struct InstanceCfg { pub extended_lsa: bool, pub sr_enabled: bool, pub instance_id: u8, + pub bier: BierOspfCfg, +} + +#[derive(Debug)] +pub struct BierOspfCfg { + pub mt_id: u16, + pub enabled: bool, + pub advertise: bool, + pub receive: bool, } #[derive(Debug)] @@ -778,6 +787,30 @@ where let mtu_ignore = args.dnode.get_bool(); iface.config.mtu_ignore = mtu_ignore; }) + .path(ospf::bier_ospf_cfg::mt_id::PATH) + .modify_apply(|instance, args| { + let mt_id = args.dnode.get_u16(); + instance.config.bier.mt_id = mt_id; + }) + .delete_apply(|instance, _args| { + let mt_id = 0; + instance.config.bier.mt_id = mt_id; + }) + .path(ospf::bier_ospf_cfg::bier::enable::PATH) + .modify_apply(|instance, args| { + let enable = args.dnode.get_bool(); + instance.config.bier.enabled = enable; + }) + .path(ospf::bier_ospf_cfg::bier::advertise::PATH) + .modify_apply(|instance, args| { + let advertise = args.dnode.get_bool(); + instance.config.bier.advertise = advertise; + }) + .path(ospf::bier_ospf_cfg::bier::receive::PATH) + .modify_apply(|instance, args| { + let receive = args.dnode.get_bool(); + instance.config.bier.receive = receive; + }) .build() } @@ -1559,6 +1592,21 @@ impl Default for InstanceCfg { extended_lsa, sr_enabled, instance_id, + bier: Default::default(), + } + } +} + +impl Default for BierOspfCfg { + fn default() -> Self { + let enabled = ospf::bier_ospf_cfg::bier::enable::DFLT; + let advertise = ospf::bier_ospf_cfg::bier::advertise::DFLT; + let receive = ospf::bier_ospf_cfg::bier::receive::DFLT; + Self { + mt_id: 0, + enabled, + advertise, + receive, } } } diff --git a/holo-ospf/src/ospfv3/lsdb.rs b/holo-ospf/src/ospfv3/lsdb.rs index 192a3e45..433fabef 100644 --- a/holo-ospf/src/ospfv3/lsdb.rs +++ b/holo-ospf/src/ospfv3/lsdb.rs @@ -36,7 +36,7 @@ use crate::packet::lsa::{ }; use crate::packet::tlv::{ PrefixSidFlags, RouterInfoCaps, RouterInfoCapsTlv, SidLabelRangeTlv, - SrAlgoTlv, SrLocalBlockTlv, + SrAlgoTlv, SrLocalBlockTlv, BierSubTlv, }; use crate::route::{SummaryNet, SummaryNetFlags, SummaryRtr}; use crate::version::Ospfv3; @@ -717,6 +717,7 @@ fn lsa_orig_intra_area_prefix( arenas: &InstanceArenas, ) { let sr_config = &instance.shared.sr_config; + let bier_config = &instance.shared.bier_config; let lsdb_id = LsdbId::Area(area.id); let extended_lsa = instance.config.extended_lsa; let adv_rtr = instance.state.router_id; @@ -817,6 +818,16 @@ fn lsa_orig_intra_area_prefix( } } + // Add BIER Sub-TLV(s) + if instance.config.bier.enabled { + bier_config.sd_cfg.iter().for_each(|((sd_id, addr_family), sd_cfg)|{ + if addr_family == &AddressFamily::Ipv6 && sd_cfg.bfr_prefix == prefix { + let bier = BierSubTlv::new(*sd_id, sd_cfg.mt_id, sd_cfg.bfr_id, sd_cfg.bar, sd_cfg.ipa); + entry.bier.push(bier); + } + }); + } + prefixes.push(entry); } let ref_lsa = LsaKey::new( diff --git a/holo-ospf/src/ospfv3/packet/lsa.rs b/holo-ospf/src/ospfv3/packet/lsa.rs index 0d780f9c..30c0de9a 100644 --- a/holo-ospf/src/ospfv3/packet/lsa.rs +++ b/holo-ospf/src/ospfv3/packet/lsa.rs @@ -33,7 +33,7 @@ use crate::packet::tlv::{ tlv_encode_end, tlv_encode_start, tlv_wire_len, AdjSidFlags, GrReason, GrReasonTlv, GracePeriodTlv, MsdTlv, PrefixSidFlags, RouterFuncCapsTlv, RouterInfoCapsTlv, RouterInfoTlvType, SidLabelRangeTlv, SrAlgoTlv, - SrLocalBlockTlv, SrmsPrefTlv, UnknownTlv, TLV_HDR_SIZE, + SrLocalBlockTlv, SrmsPrefTlv, UnknownTlv, TLV_HDR_SIZE, BierSubTlv, }; use crate::version::Ospfv3; @@ -188,6 +188,7 @@ pub enum ExtLsaSubTlv { LanAdjSid = 6, SidLabel = 7, LinkMsd = 9, + Bier = 42, } // OSPFv3 Extended-LSA Sub-TLVs. @@ -242,6 +243,7 @@ pub struct ExtLsaSubTlvs { pub route_tag: Option, pub prefix_sids: BTreeMap, pub adj_sids: Vec, + pub bier: Vec, pub unknown: Vec, } @@ -886,6 +888,8 @@ pub struct LsaIntraAreaPrefixEntry { #[new(default)] pub prefix_sids: BTreeMap, #[new(default)] + pub bier: Vec, + #[new(default)] pub unknown_stlvs: Vec, } @@ -2449,6 +2453,7 @@ impl LsaIntraAreaPrefixEntry { fn sub_tlvs(&self) -> ExtLsaSubTlvs { ExtLsaSubTlvs { prefix_sids: self.prefix_sids.clone(), + bier: self.bier.clone(), ..Default::default() } } @@ -2732,6 +2737,10 @@ impl ExtLsaSubTlvs { AdjSid::new(flags, weight, nbr_router_id, sid); stlvs.adj_sids.push(adj_sid); } + Some(ExtLsaSubTlv::Bier) => { + let bier = BierSubTlv::decode(tlv_len, &mut buf_value)?; + stlvs.bier.push(bier); + } _ => { // Save unknown Sub-TLV. let value = buf_value.copy_to_bytes(tlv_len as usize); @@ -2790,6 +2799,9 @@ impl ExtLsaSubTlvs { } tlv_encode_end(buf, start_pos); } + for bier in &self.bier { + BierSubTlv::encode(bier, buf, ExtLsaSubTlv::Bier); + } } } diff --git a/holo-ospf/src/packet/tlv.rs b/holo-ospf/src/packet/tlv.rs index 0a74b687..23337d28 100644 --- a/holo-ospf/src/packet/tlv.rs +++ b/holo-ospf/src/packet/tlv.rs @@ -267,6 +267,57 @@ pub enum GrReason { ControlProcessorSwitchover = 3, } +// +// BIER Sub-TLV. +// +// Encoding format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Sub-domain-ID | MT-ID | BFR-id | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BAR | IPA | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Sub-TLVs (variable) | +// +- -+ +// | | +// +#[derive(Clone, Debug, Eq, new, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct BierSubTlv { + pub sub_domain_id: u8, + pub mt_id: u8, + pub bfr_id: u16, + pub bar: u8, + pub ipa: u8, +} + +// +// Bier MPLS Encapsulation Sub-Tlv +// +// Encoding format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Max SI | Label | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |BS Len | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +#[derive(Clone, Debug, Eq, new, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct BierMplsEncapSubTlv { + pub max_si: u8, + pub label: Label, + pub bs_len: u8, +} + #[derive(Clone, Debug, Eq, new, PartialEq)] #[derive(Deserialize, Serialize)] pub struct UnknownTlv { @@ -275,6 +326,63 @@ pub struct UnknownTlv { pub value: Bytes, } +// ===== impl BierSubTlv ===== + +impl BierSubTlv { + pub(crate) fn decode(_tlv_len: u16, buf: &mut Bytes) -> DecodeResult { + let sub_domain_id = buf.get_u8(); + let mt_id = buf.get_u8(); + let bfr_id = buf.get_u16(); + let bar = buf.get_u8(); + let ipa = buf.get_u8(); + let _reserved = buf.get_u16(); + + while buf.remaining() >= TLV_HDR_SIZE as usize { + // Parse Sub-TLV type. + let _stlv_type = buf.get_u16(); + + // Parse and validate Sub-TLV length. + let stlv_len = buf.get_u16(); + let stlv_wlen = tlv_wire_len(stlv_len); + if stlv_wlen as usize > buf.remaining() { + return Err(DecodeError::InvalidTlvLength(stlv_len)); + } + + // TODO + // Parse Sub-TLV value. + /*let mut buf_stlv = buf.copy_to_bytes(stlv_wlen as usize); + match stlv_type { + SUBTLV_BIER_MPLS_ENCAP => { + }, + _ => { + // Ignore unknown Sub-TLV + } + }*/ + + } + + Ok(BierSubTlv { + sub_domain_id, + mt_id, + bfr_id, + bar, + ipa, + }) + } + + pub(crate) fn encode(&self, buf: &mut BytesMut, stlv_type: impl ToPrimitive) { + let start_pos = tlv_encode_start(buf, stlv_type); + buf.put_u8(self.sub_domain_id); + buf.put_u8(self.mt_id); + buf.put_u16(self.bfr_id); + buf.put_u8(self.bar); + buf.put_u8(self.ipa); + buf.put_u16(0); + // TODO: encode sub-tlvs + tlv_encode_end(buf, start_pos); + } +} + // ===== impl RouterInfoCapsTlv ===== impl RouterInfoCapsTlv { diff --git a/holo-yang/modules/augmentations/holo-ospf.yang b/holo-yang/modules/augmentations/holo-ospf.yang index d417654e..5c2ca285 100644 --- a/holo-yang/modules/augmentations/holo-ospf.yang +++ b/holo-yang/modules/augmentations/holo-ospf.yang @@ -66,6 +66,41 @@ module holo-ospf { "Describes the reason for the router restart."; } + /* + * Groupings. + */ + grouping bier-protocol-extensions{ + description + "Defines protocol extensions."; + leaf mt-id{ + type uint16 ; + description + "Multi-topology associated with bier sub-domain."; + } + container bier { + leaf enable { + type boolean; + default false; + description + "Enables bier protocol extensions."; + } + leaf advertise { + type boolean; + default true; + description + "Enable to advertise the parameters associated with bier."; + } + leaf receive { + type boolean; + default true; + description + "Enable to receive the parameters associated with bier."; + } + description + "BIER global config."; + } + } + /* * Augmentations. */ @@ -175,6 +210,22 @@ module holo-ospf { } } + augment "/rt:routing/rt:control-plane-protocols/" + + "rt:control-plane-protocol/ospf:ospf" { + when "../rt:type = 'ospf:ospfv2' or + ../rt:type = 'ospf:ospfv3'" { + description + "This augments the ospf routing protocol when used"; + } + description + "This augments ospf protocol configuration with bier."; + container bier-ospf-cfg{ + uses bier-protocol-extensions; + description + "Control of bier advertisement and reception."; + } + } + /* * Notifications. */