diff --git a/holo-ospf/src/ospfv3/lsdb.rs b/holo-ospf/src/ospfv3/lsdb.rs index 433fabef..c7ab8d49 100644 --- a/holo-ospf/src/ospfv3/lsdb.rs +++ b/holo-ospf/src/ospfv3/lsdb.rs @@ -7,6 +7,7 @@ use std::collections::{hash_map, BTreeMap, HashMap}; use std::net::{IpAddr, Ipv4Addr}; +use holo_utils::bier::{BierEncapsulationType, BierInBiftId, BiftId}; use holo_utils::ibus::SrCfgEvent; use holo_utils::ip::{AddressFamily, IpNetworkKind}; use holo_utils::mpls::Label; @@ -35,8 +36,9 @@ use crate::packet::lsa::{ Lsa, LsaHdrVersion, LsaKey, LsaScope, LsaTypeVersion, PrefixSidVersion, }; use crate::packet::tlv::{ - PrefixSidFlags, RouterInfoCaps, RouterInfoCapsTlv, SidLabelRangeTlv, - SrAlgoTlv, SrLocalBlockTlv, BierSubTlv, + BierEncapId, BierEncapSubSubTlv, BierSubSubTlv, BierSubTlv, PrefixSidFlags, + RouterInfoCaps, RouterInfoCapsTlv, SidLabelRangeTlv, SrAlgoTlv, + SrLocalBlockTlv, }; use crate::route::{SummaryNet, SummaryNetFlags, SummaryRtr}; use crate::version::Ospfv3; @@ -820,12 +822,62 @@ 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); + bier_config + .sd_cfg + .iter() + // Search for subdomain configuration(s) for current prefix + .filter(|((_, af), sd_cfg)| { + af == &AddressFamily::Ipv6 && sd_cfg.bfr_prefix == prefix + }) + .for_each(|((sd_id, _), sd_cfg)| { + // BIER prefix has configured encap ? + let bier_encaps = sd_cfg + .encap + .iter() + .filter_map(|((bsl, encap_type), encap)| { + if let Some(id) = match encap_type { + BierEncapsulationType::Mpls => { + // TODO: where is the label defined? + Some(BierEncapId::Mpls(Label::new(0))) + } + _ => { + if let Some(id) = match encap.in_bift_id { + BierInBiftId::Base(id) => Some(id), + BierInBiftId::Encoding(true) => Some(0), + _ => None, + } { + Some(BierEncapId::NonMpls(BiftId::new( + id, + ))) + } else { + None + } + } + } { + Some(BierSubSubTlv::BierEncapSubSubTlv( + BierEncapSubSubTlv::new( + encap.max_si, + id, + (*bsl).into(), + ), + )) + } else { + None + } + }) + .collect::>(); + + let bier = BierSubTlv::new( + *sd_id, + sd_cfg.mt_id, + sd_cfg.bfr_id, + sd_cfg.bar, + sd_cfg.ipa, + bier_encaps, + ); + entry.bier.push(bier); - } - }); + }); } prefixes.push(entry); diff --git a/holo-ospf/src/packet/tlv.rs b/holo-ospf/src/packet/tlv.rs index 23337d28..f9914652 100644 --- a/holo-ospf/src/packet/tlv.rs +++ b/holo-ospf/src/packet/tlv.rs @@ -9,6 +9,7 @@ use std::collections::{BTreeMap, BTreeSet}; use bitflags::bitflags; use bytes::{Buf, BufMut, Bytes, BytesMut}; use derive_new::new; +use holo_utils::bier::BiftId; use holo_utils::bytes::{BytesExt, BytesMutExt}; use holo_utils::mpls::Label; use holo_utils::sr::{IgpAlgoType, Sid}; @@ -293,6 +294,13 @@ pub struct BierSubTlv { pub bfr_id: u16, pub bar: u8, pub ipa: u8, + pub subtlvs: Vec, +} + +#[derive(Clone, Debug, Eq, new, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum BierSubSubTlv { + BierEncapSubSubTlv(BierEncapSubSubTlv), } // @@ -310,14 +318,50 @@ pub struct BierSubTlv { // |BS Len | Reserved | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +// Bier Non-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 | BIFT-id | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |BS Len | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// #[derive(Clone, Debug, Eq, new, PartialEq)] #[derive(Deserialize, Serialize)] -pub struct BierMplsEncapSubTlv { +pub struct BierEncapSubSubTlv { pub max_si: u8, - pub label: Label, + pub id: BierEncapId, pub bs_len: u8, } +#[derive(Clone, Debug, Eq, new, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum BierEncapId { + Mpls(Label), + NonMpls(BiftId), +} + +impl BierEncapId { + fn get(self) -> u32 { + match self { + Self::Mpls(label) => label.get(), + Self::NonMpls(bift_id) => bift_id.get(), + } + } +} + +#[derive(FromPrimitive, ToPrimitive)] +pub enum BierSubTlvType { + MplsEncap = 41, + NonMplsEncap = 42, +} + #[derive(Clone, Debug, Eq, new, PartialEq)] #[derive(Deserialize, Serialize)] pub struct UnknownTlv { @@ -336,10 +380,11 @@ impl BierSubTlv { let bar = buf.get_u8(); let ipa = buf.get_u8(); let _reserved = buf.get_u16(); + let mut subtlvs: Vec = Vec::new(); while buf.remaining() >= TLV_HDR_SIZE as usize { // Parse Sub-TLV type. - let _stlv_type = buf.get_u16(); + let stlv_type = buf.get_u16(); // Parse and validate Sub-TLV length. let stlv_len = buf.get_u16(); @@ -348,17 +393,36 @@ impl BierSubTlv { 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 => { - }, - _ => { + let mut buf_stlv = buf.copy_to_bytes(stlv_wlen as usize); + match BierSubTlvType::from_u16(stlv_type) { + Some(stlv_type) => { + match stlv_type { + BierSubTlvType::MplsEncap + | BierSubTlvType::NonMplsEncap => { + let max_si = buf_stlv.get_u8(); + let id = buf_stlv.get_u24(); + let bs_len = (buf_stlv.get_u8() & 0xf0) >> 4; + + let id = match stlv_type { + BierSubTlvType::MplsEncap => { + BierEncapId::Mpls(Label::new(id)) + } + BierSubTlvType::NonMplsEncap => { + BierEncapId::NonMpls(BiftId::new(id)) + } + }; + subtlvs.push(BierSubSubTlv::BierEncapSubSubTlv( + BierEncapSubSubTlv { max_si, id, bs_len }, + )); + } + }; + } + None => { // Ignore unknown Sub-TLV + continue; } - }*/ - + } } Ok(BierSubTlv { @@ -367,10 +431,15 @@ impl BierSubTlv { bfr_id, bar, ipa, + subtlvs, }) } - pub(crate) fn encode(&self, buf: &mut BytesMut, stlv_type: impl ToPrimitive) { + 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); @@ -378,8 +447,20 @@ impl BierSubTlv { buf.put_u8(self.bar); buf.put_u8(self.ipa); buf.put_u16(0); - // TODO: encode sub-tlvs tlv_encode_end(buf, start_pos); + for subtlv in &self.subtlvs { + match subtlv { + BierSubSubTlv::BierEncapSubSubTlv(encap) => { + let start_pos = + tlv_encode_start(buf, BierSubTlvType::NonMplsEncap); + buf.put_u8(encap.max_si); + buf.put_u24(encap.id.clone().get()); + buf.put_u8(encap.bs_len << 4); + tlv_encode_end(buf, start_pos); + } + _ => {} + } + } } } diff --git a/holo-utils/src/bier.rs b/holo-utils/src/bier.rs index 3d6c2d4b..4ae33fac 100644 --- a/holo-utils/src/bier.rs +++ b/holo-utils/src/bier.rs @@ -17,6 +17,22 @@ use crate::ip::AddressFamily; pub type SubDomainId = u8; pub type BfrId = u16; +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct BiftId(u32); + +impl BiftId { + pub const VALUE_MASK: u32 = 0x000FFFFF; + + pub fn new(bift_id: u32) -> Self { + Self(bift_id) + } + + pub fn get(&self) -> u32 { + self.0 & Self::VALUE_MASK + } +} + #[derive(Clone, Debug, Default)] #[derive(Deserialize, Serialize)] pub struct BierCfg { @@ -125,6 +141,37 @@ impl TryFromYang for Bsl { } } +impl Into for Bsl { + // Mapping defined in RFC8296, Section 2.1.2 + fn into(self) -> u8 { + match self { + Self::_64 => 1, + Self::_128 => 2, + Self::_256 => 3, + Self::_512 => 4, + Self::_1024 => 5, + Self::_2048 => 6, + Self::_4096 => 7, + } + } +} + +impl TryFrom for Bsl { + type Error = &'static str; + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(Self::_64), + 2 => Ok(Self::_128), + 3 => Ok(Self::_256), + 4 => Ok(Self::_512), + 5 => Ok(Self::_1024), + 6 => Ok(Self::_2048), + 7 => Ok(Self::_4096), + _ => Err("Not Supported"), + } + } +} + impl TryFromYang for BierEncapsulationType { fn try_from_yang(value: &str) -> Option { match value {