diff --git a/README.md b/README.md index 677079f2..c1e83392 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ Holo supports the following IETF RFCs and Internet drafts: | ietf-bfd@2022-09-22 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-bfd.html) | | ietf-bgp-policy@2023-07-05 | 100.00% | - | - | - | [100.00%](http://westphal.com.br/holo/ietf-bgp-policy.html) | | ietf-bgp@2023-07-05 | 32.38% | 85.95% | - | - | [60.40%](http://westphal.com.br/holo/ietf-bgp.html) | +| ietf-bier@2023-09-12 | 65.52% | - | - | 0.00% | [47.50%](http://westphal.com.br/holo/ietf-bier@2023-09-12.coverage.md) | | ietf-if-extensions@2023-01-26 | 100.00% | 0.00% | - | - | [50.00%](http://westphal.com.br/holo/ietf-if-extensions.html) | | ietf-if-vlan-encapsulation@2023-01-26 | 42.86% | - | - | - | [42.86%](http://westphal.com.br/holo/ietf-if-vlan-encapsulation.html) | | ietf-interfaces@2018-01-09 | 100.00% | 0.00% | - | - | [22.22%](http://westphal.com.br/holo/ietf-interfaces.html) | diff --git a/holo-protocol/src/lib.rs b/holo-protocol/src/lib.rs index f730ff42..a3f40a65 100644 --- a/holo-protocol/src/lib.rs +++ b/holo-protocol/src/lib.rs @@ -16,6 +16,7 @@ use holo_northbound as northbound; use holo_northbound::{ process_northbound_msg, NbDaemonReceiver, NbDaemonSender, NbProviderSender, }; +use holo_utils::bier::BierCfg; use holo_utils::ibus::{IbusMsg, IbusReceiver, IbusSender}; use holo_utils::keychain::Keychains; use holo_utils::mpls::LabelManager; @@ -96,6 +97,8 @@ pub struct InstanceShared { pub policies: Policies, // Global Segment Routing configuration. pub sr_config: Arc, + // Global BIER configuration. + pub bier_config: Arc, // Event recorder configuration. pub event_recorder_config: Option, } @@ -155,6 +158,7 @@ impl std::fmt::Debug for InstanceShared { .field("policy_match_sets", &self.policy_match_sets) .field("policies", &self.policies) .field("sr_config", &self.sr_config) + .field("bier_config", &self.bier_config) .finish() } } diff --git a/holo-routing/src/lib.rs b/holo-routing/src/lib.rs index 6e4be6d0..b7d769b3 100644 --- a/holo-routing/src/lib.rs +++ b/holo-routing/src/lib.rs @@ -19,6 +19,7 @@ use holo_northbound::{ ProviderBase, }; use holo_protocol::{event_recorder, InstanceShared}; +use holo_utils::bier::BierCfg; use holo_utils::ibus::{IbusReceiver, IbusSender}; use holo_utils::protocol::Protocol; use holo_utils::southbound::InterfaceFlags; @@ -48,6 +49,8 @@ pub struct Master { pub static_routes: BTreeMap, // SR configuration data. pub sr_config: SrCfg, + // BIER configuration data. + pub bier_config: BierCfg, // Protocol instances. pub instances: BTreeMap, } @@ -129,6 +132,7 @@ pub fn start( rib: Default::default(), static_routes: Default::default(), sr_config: Default::default(), + bier_config: Default::default(), instances: Default::default(), }; diff --git a/holo-routing/src/northbound/configuration.rs b/holo-routing/src/northbound/configuration.rs index 4442e2dd..fb87784d 100644 --- a/holo-routing/src/northbound/configuration.rs +++ b/holo-routing/src/northbound/configuration.rs @@ -15,10 +15,14 @@ use holo_northbound::configuration::{ ValidationCallbacks, ValidationCallbacksBuilder, }; use holo_northbound::yang::control_plane_protocol; -use holo_northbound::yang::routing::ribs; use holo_northbound::yang::routing::segment_routing::sr_mpls; +use holo_northbound::yang::routing::{bier, ribs}; use holo_northbound::{CallbackKey, NbDaemonSender}; -use holo_utils::ibus::{IbusMsg, SrCfgEvent}; +use holo_utils::bier::{ + BierEncapsulation, BierEncapsulationType, BierInBiftId, BierSubDomainCfg, + Bsl, SubDomainId, UnderlayProtocolType, +}; +use holo_utils::ibus::{BierCfgEvent, IbusMsg, SrCfgEvent}; use holo_utils::ip::{AddressFamily, IpNetworkKind}; use holo_utils::mpls::LabelRange; use holo_utils::protocol::Protocol; @@ -46,6 +50,13 @@ pub enum ListEntry { StaticRoute(IpNetwork), StaticRouteNexthop(IpNetwork, String), SrCfgPrefixSid(IpNetwork, IgpAlgoType), + BierCfgSubDomain(SubDomainId, AddressFamily), + BierCfgEncapsulation( + SubDomainId, + AddressFamily, + Bsl, + BierEncapsulationType, + ), } #[derive(Debug, EnumAsInner)] @@ -61,6 +72,8 @@ pub enum Event { SrCfgUpdate, SrCfgLabelRangeUpdate, SrCfgPrefixSidUpdate(AddressFamily), + BierCfgUpdate, + BierCfgEncapUpdate(SubDomainId, AddressFamily, Bsl, BierEncapsulationType), } // ===== configuration structs ===== @@ -625,6 +638,227 @@ fn load_callbacks() -> Callbacks { .delete_apply(|_master, _args| { // Nothing to do. }) + .path(bier::sub_domain::PATH) + .create_apply(|master, args| { + let sd_id = args.dnode.get_u8_relative("./sub-domain-id").unwrap(); + let af = args.dnode.get_af_relative("./address-family").unwrap(); + let bfr_prefix = args.dnode.get_prefix_relative("./bfr-prefix").unwrap(); + let underlay_protocol = args.dnode.get_string_relative("./underlay-protocol-type").unwrap(); + let underlay_protocol = UnderlayProtocolType::try_from_yang(&underlay_protocol).unwrap(); + let bfr_id = args.dnode.get_u16_relative("./bfr-id").unwrap(); + let bsl = args.dnode.get_string_relative("./bsl").unwrap(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + let sd_cfg = BierSubDomainCfg { + sd_id, + af, + bfr_prefix, + underlay_protocol, + mt_id: bier::sub_domain::mt_id::DFLT, + bfr_id, + bsl, + ipa: bier::sub_domain::igp_algorithm::DFLT, + bar: bier::sub_domain::bier_algorithm::DFLT, + load_balance_num: bier::sub_domain::load_balance_num::DFLT, + encap: Default::default(), + }; + master.bier_config.sd_cfg.insert((sd_id, af), sd_cfg); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|master, args| { + let sd_id = args.dnode.get_u8_relative("./sub-domain-id").unwrap(); + let af = args.dnode.get_af_relative("./address-family").unwrap(); + master.bier_config.sd_cfg.remove(&(sd_id, af)); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .lookup(|_master, _list_entry, dnode| { + let sd_id = dnode.get_u8_relative("./sub-domain-id").unwrap(); + let af = dnode.get_af_relative("./address-family").unwrap(); + ListEntry::BierCfgSubDomain(sd_id, af) + }) + .path(bier::sub_domain::bfr_prefix::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bfr_prefix = args.dnode.get_prefix(); + sd_cfg.bfr_prefix = bfr_prefix; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::underlay_protocol_type::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let underlay_protocol = args.dnode.get_string(); + let underlay_protocol = UnderlayProtocolType::try_from_yang(&underlay_protocol).unwrap(); + sd_cfg.underlay_protocol = underlay_protocol; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::mt_id::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let mt_id = args.dnode.get_u8(); + sd_cfg.mt_id = mt_id; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::bfr_id::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bfr_id = args.dnode.get_u16(); + sd_cfg.bfr_id = bfr_id; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::bsl::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bsl = args.dnode.get_string(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + sd_cfg.bsl = bsl; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::igp_algorithm::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let ipa = args.dnode.get_u8(); + sd_cfg.ipa = ipa; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::bier_algorithm::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bar = args.dnode.get_u8(); + sd_cfg.bar = bar; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::load_balance_num::PATH) + .modify_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let load_balance_num = args.dnode.get_u8(); + sd_cfg.load_balance_num = load_balance_num; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .path(bier::sub_domain::encapsulation::PATH) + .create_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bsl = args.dnode.get_string_relative("./bsl").unwrap(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + let encap_type = args.dnode.get_string_relative("./encapsulation-type").unwrap(); + let encap_type = BierEncapsulationType::try_from_yang(&encap_type).unwrap(); + let max_si = args.dnode.get_u8_relative("./max-si").unwrap(); + let in_bift_id_base = args.dnode.get_u32_relative("./in-bift-id/in-bift-id-base"); + let in_bift_id_encoding = args.dnode.get_bool_relative("./in-bift-id/in-bift-id-encoding"); + let in_bift_id = in_bift_id_base + .map_or(in_bift_id_encoding.map(BierInBiftId::Encoding), |v| { + Some(BierInBiftId::Base(v)) + }) + .unwrap(); + let encap_cfg = BierEncapsulation::new(bsl, encap_type, max_si, in_bift_id); + sd_cfg.encap.insert((bsl, encap_type), encap_cfg); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + event_queue.insert(Event::BierCfgEncapUpdate(sd_id, af, bsl, encap_type)); + }) + .delete_apply(|context, args| { + let (sd_id, af) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + + let bsl = args.dnode.get_string_relative("./bsl").unwrap(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + let encap_type = args.dnode.get_string_relative("./encapsulation-type").unwrap(); + let encap_type = BierEncapsulationType::try_from_yang(&encap_type).unwrap(); + sd_cfg.encap.remove(&(bsl, encap_type)); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .lookup(|_context, list_entry, dnode| { + let (sd_id, af) = list_entry.into_bier_cfg_sub_domain().unwrap(); + let bsl = dnode.get_string_relative("./bsl").unwrap(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + let encap_type = dnode.get_string_relative("./encapsulation-type").unwrap(); + let encap_type = BierEncapsulationType::try_from_yang(&encap_type).unwrap(); + ListEntry::BierCfgEncapsulation(sd_id, af, bsl, encap_type) + }) + .path(bier::sub_domain::encapsulation::max_si::PATH) + .modify_apply(|context, args| { + let (sd_id, af, bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + + let max_si = args.dnode.get_u8(); + encap.max_si = max_si; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + event_queue.insert(Event::BierCfgEncapUpdate(sd_id, af, bsl, encap_type)); + }) + .path(bier::sub_domain::encapsulation::in_bift_id::in_bift_id_base::PATH) + .modify_apply(|context, args| { + let (sd_id, af, bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + + let in_bift_id_base = args.dnode.get_u32(); + encap.in_bift_id = BierInBiftId::Base(in_bift_id_base); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + event_queue.insert(Event::BierCfgEncapUpdate(sd_id, af, bsl, encap_type)); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::encapsulation::in_bift_id::in_bift_id_encoding::PATH) + .modify_apply(|context, args| { + let (sd_id, af, bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, af)).unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + + let in_bift_id_encoding = args.dnode.get_bool(); + encap.in_bift_id = BierInBiftId::Encoding(in_bift_id_encoding); + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + event_queue.insert(Event::BierCfgEncapUpdate(sd_id, af, bsl, encap_type)); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) .build() } @@ -652,6 +886,26 @@ fn load_validation_callbacks() -> ValidationCallbacks { Ok(()) }) + .path(bier::sub_domain::PATH) + .validate(|args| { + let af = args.dnode.get_af_relative("./address-family").unwrap(); + let mt_id = args.dnode.get_u8_relative("./mt-id"); + + // Enforce configured address family. + if let Some(bfr_prefix) = + args.dnode.get_prefix_relative("./bfr-prefix") + && bfr_prefix.address_family() != af + { + return Err("Configured address family differs from BFR prefix address family.".to_owned()); + } + + // Enforce MT-ID value per RFC4915. + if let Some(mt_id) = mt_id && mt_id > 128 { + return Err("Invalid MT-ID per RFC4915".to_owned()); + } + + Ok(()) + }) .build() } @@ -799,6 +1053,20 @@ impl Provider for Master { .ibus_tx .send(IbusMsg::SrCfgEvent(SrCfgEvent::PrefixSidUpdate(af))); } + Event::BierCfgUpdate => { + // Update the shared BIER configuration by creating a new reference-counted copy. + self.shared.bier_config = Arc::new(self.bier_config.clone()); + + // Notify protocol instances about the updated BIER configuration. + let _ = self + .ibus_tx + .send(IbusMsg::BierCfgUpd(self.shared.bier_config.clone())); + } + Event::BierCfgEncapUpdate(sd_id, af, bsl, encap_type) => { + let _ = self.ibus_tx.send(IbusMsg::BierCfgEvent( + BierCfgEvent::EncapUpdate(sd_id, af, bsl, encap_type), + )); + } } } } diff --git a/holo-routing/src/northbound/mod.rs b/holo-routing/src/northbound/mod.rs index b1086764..dee0f4bd 100644 --- a/holo-routing/src/northbound/mod.rs +++ b/holo-routing/src/northbound/mod.rs @@ -29,6 +29,7 @@ impl ProviderBase for Master { "ietf-segment-routing", "ietf-segment-routing-common", "ietf-segment-routing-mpls", + "ietf-bier", ] } diff --git a/holo-tools/yang-coverage.sh b/holo-tools/yang-coverage.sh index 3cf60949..e153aaff 100755 --- a/holo-tools/yang-coverage.sh +++ b/holo-tools/yang-coverage.sh @@ -12,6 +12,7 @@ cargo run --bin yang_coverage --\ -m ietf-routing-policy\ -m ietf-segment-routing\ -m ietf-segment-routing-mpls\ + -m ietf-bier\ -m ietf-key-chain\ -m ietf-bfd\ -m ietf-bfd-ip-mh\ diff --git a/holo-utils/src/bier.rs b/holo-utils/src/bier.rs new file mode 100644 index 00000000..3d6c2d4b --- /dev/null +++ b/holo-utils/src/bier.rs @@ -0,0 +1,153 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; +use std::collections::{BTreeMap, HashMap}; + +use derive_new::new; +use holo_yang::{ToYang, TryFromYang}; +use ipnetwork::IpNetwork; +use serde::{Deserialize, Serialize}; + +use crate::ip::AddressFamily; + +pub type SubDomainId = u8; +pub type BfrId = u16; + +#[derive(Clone, Debug, Default)] +#[derive(Deserialize, Serialize)] +pub struct BierCfg { + #[serde(with = "vectorize")] + pub sd_cfg: BTreeMap<(SubDomainId, AddressFamily), BierSubDomainCfg>, + #[serde(with = "vectorize")] + pub bift_cfg: HashMap, +} + +#[derive(Clone, Debug)] +#[derive(Deserialize, Serialize)] +pub struct BierSubDomainCfg { + pub sd_id: SubDomainId, + pub af: AddressFamily, + pub bfr_prefix: IpNetwork, + pub underlay_protocol: UnderlayProtocolType, + pub mt_id: u8, + pub bfr_id: BfrId, + pub bsl: Bsl, + pub ipa: u8, + pub bar: u8, + pub load_balance_num: u8, + #[serde(with = "vectorize")] + pub encap: BTreeMap<(Bsl, BierEncapsulationType), BierEncapsulation>, +} + +pub type BierInBiftIdBase = u32; +pub type BierInBiftIdEncoding = bool; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum BierInBiftId { + Base(BierInBiftIdBase), + Encoding(BierInBiftIdEncoding), +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(new)] +#[derive(Deserialize, Serialize)] +pub struct BierEncapsulation { + pub bsl: Bsl, + pub encap_type: BierEncapsulationType, + pub max_si: u8, + pub in_bift_id: BierInBiftId, +} + +#[derive(Clone, Debug)] +#[derive(Deserialize, Serialize)] +pub struct BierBiftCfg { + // TODO +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum BierEncapsulationType { + Mpls, + Ipv6, + Ethernet, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum UnderlayProtocolType { + IsIs, + Ospf, + Bgp, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Deserialize, Serialize)] +pub enum Bsl { + _64, + _128, + _256, + _512, + _1024, + _2048, + _4096, +} + +// ===== YANG impl ===== + +impl TryFromYang for UnderlayProtocolType { + fn try_from_yang(value: &str) -> Option { + match value { + "IS-IS" => Some(Self::IsIs), + "OSPF" => Some(Self::Ospf), + "BGP" => Some(Self::Bgp), + _ => None, + } + } +} + +impl TryFromYang for Bsl { + fn try_from_yang(value: &str) -> Option { + match value { + "64-bit" => Some(Bsl::_64), + "128-bit" => Some(Bsl::_128), + "256-bit" => Some(Bsl::_256), + "512-bit" => Some(Bsl::_512), + "1024-bit" => Some(Bsl::_1024), + "2048-bit" => Some(Bsl::_2048), + "4096-bit" => Some(Bsl::_4096), + _ => None, + } + } +} + +impl TryFromYang for BierEncapsulationType { + fn try_from_yang(value: &str) -> Option { + match value { + "ietf-bier:bier-encapsulation-mpls" => { + Some(BierEncapsulationType::Mpls) + } + "ietf-bier:bier-encapsulation-ipv6" => { + Some(BierEncapsulationType::Ipv6) + } + "ietf-bier:bier-encapsulation-ethernet" => { + Some(BierEncapsulationType::Ethernet) + } + _ => None, + } + } +} + +impl ToYang for BierEncapsulationType { + fn to_yang(&self) -> Cow<'static, str> { + match self { + Self::Mpls => "ietf-bier:bier-encapsulation-mpls".into(), + Self::Ipv6 => "ietf-bier:bier-encapsulation-ipv6".into(), + Self::Ethernet => "ietf-bier:bier-encapsulation-ethernet".into(), + } + } +} diff --git a/holo-utils/src/ibus.rs b/holo-utils/src/ibus.rs index 389b06bd..986723c7 100644 --- a/holo-utils/src/ibus.rs +++ b/holo-utils/src/ibus.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; use tokio::sync::broadcast::{Receiver, Sender}; use crate::bfd; +use crate::bier::{BierCfg, BierEncapsulationType, Bsl, SubDomainId}; use crate::ip::AddressFamily; use crate::keychain::Keychain; use crate::policy::{MatchSets, Policy}; @@ -107,6 +108,10 @@ pub enum IbusMsg { SrCfgUpd(Arc), // Segment Routing configuration event. SrCfgEvent(SrCfgEvent), + // BIER configuration update. + BierCfgUpd(Arc), + // BIER configuration event. + BierCfgEvent(BierCfgEvent), } // Type of Segment Routing configuration change. @@ -115,3 +120,9 @@ pub enum SrCfgEvent { LabelRangeUpdate, PrefixSidUpdate(AddressFamily), } + +// Type of BIER configuration events. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum BierCfgEvent { + EncapUpdate(SubDomainId, AddressFamily, Bsl, BierEncapsulationType), +} diff --git a/holo-utils/src/lib.rs b/holo-utils/src/lib.rs index 1ca2ef2a..1bfc088f 100644 --- a/holo-utils/src/lib.rs +++ b/holo-utils/src/lib.rs @@ -16,6 +16,7 @@ use pickledb::PickleDb; pub mod bfd; pub mod bgp; +pub mod bier; pub mod bytes; pub mod capabilities; pub mod crypto; diff --git a/holo-yang/Cargo.toml b/holo-yang/Cargo.toml index 143f803b..289fe0b0 100644 --- a/holo-yang/Cargo.toml +++ b/holo-yang/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "holo-yang" -version = "0.5.4" +version = "0.5.5" authors.workspace = true license.workspace = true edition.workspace = true diff --git a/holo-yang/modules/deviations/holo-ietf-bier-deviations.yang b/holo-yang/modules/deviations/holo-ietf-bier-deviations.yang new file mode 100644 index 00000000..49ac4e9e --- /dev/null +++ b/holo-yang/modules/deviations/holo-ietf-bier-deviations.yang @@ -0,0 +1,291 @@ +module holo-ietf-bier-deviations { + yang-version 1.1; + namespace "http://holo-routing.org/yang/holo-ietf-bier-deviations"; + prefix holo-ietf-bier-deviations; + + import ietf-routing { + prefix rt; + } + + import ietf-bier { + prefix bier; + } + + import iana-routing-types { + prefix iana-rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + + organization + "Holo Routing Stack"; + + description + "This module defines deviation statements for the ietf-bier + module."; + + typedef bsl { + type enumeration { + enum "64-bit"{ + description + "bitstringlength is 64"; + } + enum "128-bit"{ + description + "bitstringlength is 128"; + } + enum "256-bit"{ + description + "bitstringlength is 256"; + } + enum "512-bit"{ + description + "bitstringlength is 512"; + } + enum "1024-bit"{ + description + "bitstringlength is 1024"; + } + enum "2048-bit"{ + description + "bitstringlength is 2048"; + } + enum "4096-bit"{ + description + "bitstringlength is 4096"; + } + } + default + "256-bit"; + description + "list of the bitstringlength type to be supported."; + } + + deviation "/bier:bfr-id-collision" { + deviate not-supported; + } + + deviation "/bier:bfr-id-collision/bier:bfr-id-collision" { + deviate not-supported; + } + + deviation "/bier:bfr-id-collision/bier:bfr-id-collision/bier:received-bfr-id" { + deviate not-supported; + } + + deviation "/bier:bfr-id-out-of-range" { + deviate not-supported; + } + + deviation "/bier:bfr-id-out-of-range/bier:received-bfr-id" { + deviate not-supported; + } + + deviation "/bier:bfr-zero" { + deviate not-supported; + } + + deviation "/bier:bfr-zero/bier:ipv4-bfr-prefix" { + deviate not-supported; + } + + deviation "/bier:bfr-zero/bier:ipv6-bfr-prefix" { + deviate not-supported; + } + + deviation "/bier:sub-domain-id-collision" { + deviate not-supported; + } + + deviation "/bier:sub-domain-id-collision/bier:received-sub-domain-id" { + deviate not-supported; + } + + deviation "/bier:sub-domain-id-collision/bier:received-mt-id" { + deviate not-supported; + } + + /* + deviation "/rt:routing/bier:bier" { + deviate not-supported; + } + */ + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:sub-domain-id" { + deviate replace { + type uint8; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:address-family" { + deviate replace { + type iana-rt-types:address-family; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bfr-prefix" { + deviate add { + mandatory "true"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:underlay-protocol-type" { + deviate add { + mandatory "true"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:mt-id" { + deviate replace { + type uint8; + } + deviate add { + default "0"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bfr-id" { + deviate add { + mandatory "true"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bsl" { + deviate replace { + type bsl; + } + deviate add { + mandatory "true"; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:igp-algorithm" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bier-algorithm" { + deviate add { + default "0"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:load-balance-num" { + deviate add { + default "16"; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:bsl" { + deviate replace { + type bsl; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:encapsulation-type" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:max-si" { + deviate replace { + type uint8; + } + deviate add { + mandatory "true"; + } + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id" { + deviate delete { + default "in-bift-id-base"; + } + deviate add { + mandatory "true"; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id/bier:in-bift-id-base" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id/bier:in-bift-id-base/bier:in-bift-id-base" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id/bier:in-bift-id-encoding" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id/bier:in-bift-id-encoding/bier:in-bift-id-encoding" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:bift" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:bfr-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bsl" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:bfr-nbr" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:encapsulation-type" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id/bier:out-bift-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id/bier:out-bift-id/bier:out-bift-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id/bier:out-bift-id/bier:out-bift-id/bier:out-bift-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id/bier:out-bift-id/bier:out-bift-id-encoding" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:bift/bier:birt-bitstringlength/bier:bfr-nbr/bier:out-bift-id/bier:out-bift-id/bier:out-bift-id-encoding/bier:out-bift-id-encoding" { + deviate not-supported; + } +} diff --git a/holo-yang/modules/ietf/ietf-bier@2023-09-16.yang b/holo-yang/modules/ietf/ietf-bier@2023-09-16.yang new file mode 100644 index 00000000..2c7a895e --- /dev/null +++ b/holo-yang/modules/ietf/ietf-bier@2023-09-16.yang @@ -0,0 +1,451 @@ +module ietf-bier { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-bier"; + prefix "bier"; + + import ietf-routing { + prefix "rt"; + reference + "RFC 8349: A YANG Data Model for Routing Management (NMDA Version)"; + } + import ietf-interfaces { + prefix "if"; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + import ietf-inet-types { + prefix "inet"; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-isis { + prefix "isis"; + reference "RFC 9130: YANG Data Model for the IS-IS Protocol"; + } + import ietf-ospf { + prefix "ospf"; + reference "RFC 9129: YANG Data Model for the OSPF Protocol"; + } + import iana-routing-types { + prefix iana-rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + + organization + "IETF BIER(Bit Indexed Explicit Replication) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/bier/> + WG List: <mailto:bier@ietf.org> + WG Chair: Tony Przygienda + <mailto:tonysietf@gmail.com> + + WG Chair: Greg Shepherd + <mailto:gjshep@gmail.com> + + + Editor: Ran Chen + <mailto:chen.ran@zte.com.cn> + Editor: Fangwei Hu + <mailto:hu.fangwei@zte.com.cn> + Editor: Zheng Zhang + <mailto:zhang.zheng@zte.com.cn> + Editor: Xianxian Dai + <mailto:dai.xianxian@zte.com.cn> + Editor: Mahesh Sivakumar + <mailto:masivaku@cisco.com> + "; + description + "The YANG module defines a generic configuration model + for BIER.; + + This YANG module conforms to the Network Management + Datastore Architecture (NMDA), as described in RFC 8242. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX + (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself + for full legal notices."; + + reference + "RFC XXXX: YANG Data Model for BIER"; + revision 2023-09-12 { + description + "initial version."; + reference + "RFC XXXX: YANG Data Model for BIER "; + } + + /* Identities */ + identity bier-encapsulation { + description + "Base identity for BIER encapsulation."; + } + identity bier-encapsulation-mpls { + base bier-encapsulation; + description + "This identity represents MPLS encapsulation for bier."; + } + identity bier-encapsulation-ipv6 { + base bier-encapsulation; + description + "This identity represents ipv6 encapsulation for bier."; + } + identity bier-encapsulation-ethernet { + base bier-encapsulation; + description + "This identity represents ethernet encapsulation for bier."; + } + + identity address-family { + description + "Base identity from which identities describing address + families are derived."; + } + identity ipv4 { + base address-family; + description + "This identity represents an IPv4 address family."; + } + identity ipv6 { + base address-family; + description + "This identity represents an IPv6 address family."; + } + + /* typedef */ + typedef underlay-protocol-type { + type enumeration { + enum IS-IS { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled IS-IS."; + } + enum OSPF { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled OSPF."; + } + enum BGP { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled BGP."; + } + } + description + "List of the underlay protocol to be supported."; + } + + typedef bsl { + type enumeration { + enum IS-IS { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled IS-IS."; + } + enum OSPF { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled OSPF."; + } + enum BGP { + description + "This BIER subdomains configuration can be read and + advertise by BIER enabled BGP."; + } + } + description + "list of the underlay protocol to be supported."; + } + + augment "/rt:routing" { + description + "This augments routing-instance configuration with bier."; + container bier { + description + "BIER subdomain configuration."; + list sub-domain { + key "sub-domain-id address-family"; + description + "The parameters of the BIER subdomain. "; + + leaf sub-domain-id { + type uint16; + description + "The bier sub-domain-id"; + } + + leaf address-family { + type identityref { + base address-family; + } + mandatory true; + description + "Address family."; + } + + leaf bfr-prefix { + type inet:ip-prefix; + description + "the bfr prefix."; + } + + leaf underlay-protocol-type { + type underlay-protocol-type; + description + "List of the underlay protocol to be supported.."; + } + + leaf mt-id { + type uint16; + description + "The multi-topology identifier"; + } + + leaf bfr-id { + type uint16; + description + "Configure the unique BFR-id value within the BIER + subdomain for the BFIR/BFER device, and BFR doesnot + need a BFR-id, but for diagnostics purposes of the IGP, + highly recommended to assign one - but beyond max-si*bls."; + } + + leaf bsl { + type bsl; + description + "The length of the bitstring in the BIER encapsulation + within the BIER subdomain."; + } + + leaf igp-algorithm { + type uint8; + default "0"; + description + "Calculation type value ranges from 0 to 255 both + inclusive from the IGP Algorithm Types registry + defined under Interior Gateway Protocol (IGP) + Parameters IANA registries.If the required calculation + type is Shortest Path First, the value 0 SHOULD appear + in this field."; + } + + leaf bier-algorithm { + type uint8; + description + "Calculation type value ranges from 0 to 255 both inclusive + from the BIER Algorithm registry.Specifies a BIER-specific + Algorithm and BIER-specific Constraints used to either modify, + enhance, or replace the calculation of underlay paths to reach + other BFRs as defined by the IPA value as defined in RFC9272."; + } + + leaf load-balance-num { + type uint8; + description + "The multicast load balance num."; + } + + list encapsulation { + key "bsl encapsulation-type"; + description + "The BIER encapsulation type.When MPLS is used as the + transport, the Bit Indexed Forwarding Table (BIFT) is + identified by a MPLS Label. When non-MPLS transport is + used, the BIFT is identified by a 20bit value."; + leaf bsl { + type bsl; + description + "The length of the bitstring in the BIER encapsulation + within the BIER subdomain."; + } + + leaf encapsulation-type { + type identityref { + base bier-encapsulation; + } + description + "The BIER encapsulation that can be used in either + MPLS networks or non-MPLS networks."; + } + + leaf max-si { + type uint16; + description + "Maximum Set Identifier.The SI value in the subdomain + is an integer from 0 to max-si."; + } + + container in-bift-id { + description + "In BIFT-ID specification."; + choice in-bift-id { + default "in-bift-id-base"; + description + "Options for specifying in-bift-id"; + case in-bift-id-base { + leaf in-bift-id-base { + type uint32; + description + "The first BIFT ID value, there are maximum SI+1 BIFT + IDs in total as define in RFC8401."; + } + } + case in-bift-id-encoding { + leaf in-bift-id-encoding { + type boolean; + default "false"; + description + "setting this attribute to 'true' will enable + calculation of in-bift-id based on ."; + } + } + } + } + } + } + list bift { + key "bfr-id"; + description + "BIER forwarding tabel."; + + leaf bfr-id { + type uint16; + description + "The unique BFR-id value within the BIER + subdomain for the BFIR/BFER device."; + } + list birt-bitstringlength { + key "bsl"; + description + "specify BSL's bfr-nbr, encapsulation-type and + out-bift-id in the BIER forwarding tabel."; + leaf bsl { + type bsl; + description + "Configure the bitstring length in BIFT in the + BIER subdomain"; + } + list bfr-nbr { + key bfr-nbr; + description + "bfr-nbr."; + leaf bfr-nbr { + type inet:ip-prefix; + description + "bfr-nbr."; + } + leaf encapsulation-type { + type identityref { + base bier-encapsulation; + } + description + "The BIER encapsulation that can be used in either + MPLS networks or non-MPLS networks."; + } + container out-bift-id { + description + "Out BIFT-ID specification."; + choice out-bift-id { + default "out-bift-id"; + description + "Options for specifying out-bift-id"; + case out-bift-id { + leaf out-bift-id { + type uint32; + description + "Configure the out-bift-id"; + } + } + case out-bift-id-encoding { + leaf out-bift-id-encoding { + type boolean; + default "false"; + description + "setting this attribute to 'true' will enable + calculation of out-bift-id based on ."; + } + } + } + } + } + } + } + } + } + + notification bfr-id-collision { + description + "This notification is sent when BFR-id received from + different routers collide."; + list bfr-id-collision { + description + "List of BFR-id that collide."; + leaf received-bfr-id { + type uint16; + description + "Value of the BFR-id received."; + } + } + } + + notification bfr-id-out-of-range { + description + "This notification is sent when a BFR-id is received + that is is larger than locally configured (bsl * max-si). + The notification generation must be throttled with at + least a 5-second gap between notifications."; + leaf received-bfr-id { + type uint16; + description + "Value of the BFR-id received."; + } + } + + notification bfr-zero { + description + "This notification is sent when an invalid value + associated with prefix."; + leaf ipv4-bfr-prefix { + type inet:ipv4-prefix; + description + "BIER ipv4 bfr prefix"; + } + leaf ipv6-bfr-prefix{ + type inet:ipv6-prefix; + description + "BIER ipv6 bfr prefix"; + } + } + + notification sub-domain-id-collision { + description + "This notification is sent when sub-domain-id received from + different routers collide."; + leaf received-sub-domain-id { + type uint16; + description + "Value of the sub-domain-id received."; + } + leaf received-mt-id{ + type uint16; + description + "Value of the multi-topology ID received."; + } + } + } diff --git a/holo-yang/src/lib.rs b/holo-yang/src/lib.rs index 55c599bc..49f837ef 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -53,6 +53,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { include_str!("../modules/ietf/ietf-bfd-types@2022-09-22.yang"), EmbeddedModuleKey::new("ietf-bfd", Some("2022-09-22"), None, None) => include_str!("../modules/ietf/ietf-bfd@2022-09-22.yang"), + EmbeddedModuleKey::new("ietf-bier", Some("2023-09-16"), None, None) => + include_str!("../modules/ietf/ietf-bier@2023-09-16.yang"), EmbeddedModuleKey::new("ietf-bgp", Some("2023-07-05"), None, None) => include_str!("../modules/ietf/ietf-bgp@2023-07-05.yang"), EmbeddedModuleKey::new("ietf-bgp", Some("2023-07-05"), Some("ietf-bgp-capabilities"), Some("2023-07-05")) => @@ -133,6 +135,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { // IETF Holo deviations EmbeddedModuleKey::new("holo-ietf-bgp-deviations", None, None, None) => include_str!("../modules/deviations/holo-ietf-bgp-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-bier-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-bier-deviations.yang"), EmbeddedModuleKey::new("holo-ietf-mpls-ldp-deviations", None, None, None) => include_str!("../modules/deviations/holo-ietf-mpls-ldp-deviations.yang"), EmbeddedModuleKey::new("holo-ietf-if-extensions-deviations", None, None, None) => @@ -188,6 +192,7 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = "ietf-bfd", "ietf-bgp", "ietf-bgp-policy", + "ietf-bier", "ietf-routing-types", "ietf-if-extensions", "ietf-if-vlan-encapsulation",