From 1ccc8fae36d69575780377a5f57f32ee6a10dc97 Mon Sep 17 00:00:00 2001 From: nrybowski Date: Fri, 5 Apr 2024 05:48:51 +0200 Subject: [PATCH] Add basic BIER support Partial implementation of updated draft-ietf-bier-bier-yang-08 - sub-domain-id updated from u16 to u8 per RFC8279 - mt_id updated from u16 to u8 per draft-ietf-ospf-mt-ospfv3-03 - address_family updated from custom format to iana-routing-types:address_family - fixed some typos in the YANG model Signed-off-by: Nicolas Rybowski --- holo-protocol/src/lib.rs | 4 + holo-routing/src/lib.rs | 4 + holo-routing/src/northbound/configuration.rs | 298 +++++++++++- holo-routing/src/northbound/mod.rs | 1 + holo-routing/src/northbound/state.rs | 12 +- holo-utils/src/bier.rs | 154 ++++++ holo-utils/src/ibus.rs | 11 + holo-utils/src/lib.rs | 1 + .../deviations/ietf-bier-holo-deviations.yang | 279 +++++++++++ .../modules/ietf/ietf-bier@2023-09-16.yang | 451 ++++++++++++++++++ holo-yang/src/lib.rs | 5 + 11 files changed, 1217 insertions(+), 3 deletions(-) create mode 100644 holo-utils/src/bier.rs create mode 100644 holo-yang/modules/deviations/ietf-bier-holo-deviations.yang create mode 100644 holo-yang/modules/ietf/ietf-bier@2023-09-16.yang diff --git a/holo-protocol/src/lib.rs b/holo-protocol/src/lib.rs index 7e41c9c6..60db5df5 100644 --- a/holo-protocol/src/lib.rs +++ b/holo-protocol/src/lib.rs @@ -18,6 +18,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; @@ -98,6 +99,8 @@ pub struct InstanceShared { pub policies: Policies, // Global Segment Routing configuration. pub sr_config: Arc, + // Global BIER configuration. + pub bier_config: Arc, } /// Instance input message. @@ -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 7526b1b8..3e19a9d6 100644 --- a/holo-routing/src/lib.rs +++ b/holo-routing/src/lib.rs @@ -20,6 +20,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; @@ -51,6 +52,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, } @@ -132,6 +135,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 6d6250d1..e0751aa4 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,8 @@ pub enum ListEntry { StaticRoute(IpNetwork), StaticRouteNexthop(IpNetwork, String), SrCfgPrefixSid(IpNetwork, IgpAlgoType), + BierCfgSubDomain(SubDomainId, AddressFamily), + BierCfgEncapsulation(Bsl, BierEncapsulationType), } #[derive(Debug, EnumAsInner)] @@ -61,6 +67,8 @@ pub enum Event { SrCfgUpdate, SrCfgLabelRangeUpdate, SrCfgPrefixSidUpdate(AddressFamily), + BierCfgUpdate, + BierCfgEncapUpdate(SubDomainId, AddressFamily, Bsl, BierEncapsulationType), } // ===== configuration structs ===== @@ -625,6 +633,254 @@ 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 addr_family = 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 mt_id = args.dnode.get_u8_relative("./mt-id").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 ipa = args.dnode.get_u8_relative("./igp-algorithm").unwrap(); + let bar = args.dnode.get_u8_relative("./bier-algorithm").unwrap(); + let load_balance_num = args.dnode.get_u8_relative("./load-balance-num").unwrap(); + let sd_cfg = BierSubDomainCfg::new(sd_id, addr_family, bfr_prefix, underlay_protocol, + mt_id, bfr_id, bsl, ipa, bar, load_balance_num, BTreeMap::new()); + master.bier_config.sd_cfg.insert((sd_id, addr_family), 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 addr_family = args.dnode.get_af_relative("./address-family").unwrap(); + master.bier_config.sd_cfg.remove(&(sd_id, addr_family)); + + 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 addr_family = dnode.get_af_relative("./address-family").unwrap(); + ListEntry::BierCfgSubDomain(sd_id, addr_family) + }) + .path(bier::sub_domain::bfr_prefix::PATH) + .modify_apply(|context, args| { + let bfr_prefix = args.dnode.get_prefix(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.bfr_prefix = bfr_prefix; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::underlay_protocol_type::PATH) + .modify_apply(|context, args| { + let underlay_protocol = args.dnode.get_string(); + let underlay_protocol = UnderlayProtocolType::try_from_yang(&underlay_protocol).unwrap(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.underlay_protocol = underlay_protocol; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::mt_id::PATH) + .modify_apply(|context, args| { + let mt_id = args.dnode.get_u8(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.mt_id = mt_id; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::bfr_id::PATH) + .modify_apply(|context, args| { + let bfr_id = args.dnode.get_u16(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.bfr_id = bfr_id; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::bsl::PATH) + .modify_apply(|context, args| { + let bsl = args.dnode.get_string(); + let bsl = Bsl::try_from_yang(&bsl).unwrap(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.bsl = bsl; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::igp_algorithm::PATH) + .modify_apply(|context, args| { + let ipa = args.dnode.get_u8(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + 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 bar = args.dnode.get_u8(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.bar = bar; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::load_balance_num::PATH) + .modify_apply(|context, args| { + let load_balance_num = args.dnode.get_u8(); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + sd_cfg.load_balance_num = load_balance_num; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::encapsulation::PATH) + .create_apply(|context, args| { + 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); + + let (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + 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, addr_family, bsl, encap_type)); + }) + .delete_apply(|context, args| { + 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 (sd_id, addr_family) = args.list_entry.into_bier_cfg_sub_domain().unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).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 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(bsl, encap_type) + }) + .path(bier::sub_domain::encapsulation::max_si::PATH) + .modify_apply(|context, args| { + let max_si = args.dnode.get_u8(); + + let sd_id = args.dnode.get_u8_relative("../../sub-domain-id").unwrap(); + let addr_family = args.dnode.get_af_relative("../../address-family").unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + + let (bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + encap.max_si = max_si; + + let event_queue = args.event_queue; + event_queue.insert(Event::BierCfgUpdate); + event_queue.insert(Event::BierCfgEncapUpdate(sd_id, addr_family, bsl, encap_type)); + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) + .path(bier::sub_domain::encapsulation::in_bift_id::in_bift_id_base::PATH) + .modify_apply(|context, args| { + let in_bift_id_base = args.dnode.get_u32(); + + if let Some(sd_id) = args.dnode.get_u8_relative("../../sub-domain-id") { + let addr_family = args.dnode.get_af_relative("../../address-family").unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + + let (bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + 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, addr_family, 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 in_bift_id_encoding = args.dnode.get_bool(); + + if let Some(sd_id) = args.dnode.get_u8_relative("../../sub-domain-id") { + let addr_family = args.dnode.get_af_relative("../../address-family").unwrap(); + let sd_cfg = context.bier_config.sd_cfg.get_mut(&(sd_id, addr_family)).unwrap(); + + let (bsl, encap_type) = args.list_entry.into_bier_cfg_encapsulation().unwrap(); + let encap = sd_cfg.encap.get_mut(&(bsl, encap_type)).unwrap(); + 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, addr_family, bsl, encap_type)); + } + }) + .delete_apply(|_context, _args| { + // Nothing to do. + }) .build() } @@ -652,6 +908,25 @@ fn load_validation_callbacks() -> ValidationCallbacks { Ok(()) }) + .path(bier::sub_domain::PATH) + .validate(|args| { + let addr_family = args.dnode.get_af_relative("./address-family").unwrap(); + let mt_id = args.dnode.get_u8_relative("./mt-id").unwrap(); + + // Enforce configured address family. + if let Some(bfr_prefix) = args.dnode.get_prefix_relative("./bfr-prefix") && + bfr_prefix.address_family() != addr_family + { + return Err("Configured address family differs from BFR prefix address family.".to_owned()); + } + + // Enforce MT-ID value per RFC4915. + if mt_id > 128 { + return Err("Invalid MT-ID per RFC4915".to_owned()); + } + + Ok(()) + }) .build() } @@ -799,6 +1074,25 @@ 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, addr_family, bsl, encap_type) => { + let _ = self.ibus_tx.send(IbusMsg::BierCfgEvent( + BierCfgEvent::EncapUpdate( + sd_id, + addr_family, + 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-routing/src/northbound/state.rs b/holo-routing/src/northbound/state.rs index 883236b7..c575a42d 100644 --- a/holo-routing/src/northbound/state.rs +++ b/holo-routing/src/northbound/state.rs @@ -13,8 +13,8 @@ use holo_northbound::state::{ Callbacks, CallbacksBuilder, ListEntryKind, Provider, }; 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::mpls::Label; use holo_utils::protocol::Protocol; @@ -408,6 +408,16 @@ fn load_callbacks() -> Callbacks { None } }) + .path(bier::sub_domain::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) + .path(bier::sub_domain::encapsulation::PATH) + .get_iterate(|_master, _args| { + // No operational data under this list. + None + }) .build() } diff --git a/holo-utils/src/bier.rs b/holo-utils/src/bier.rs new file mode 100644 index 00000000..72ffc9a3 --- /dev/null +++ b/holo-utils/src/bier.rs @@ -0,0 +1,154 @@ +// +// 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(new)] +#[derive(Deserialize, Serialize)] +pub struct BierSubDomainCfg { + pub sd_id: SubDomainId, + pub addr_family: 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 49b7e013..0faabfa8 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}; @@ -103,6 +104,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. @@ -111,3 +116,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 f6f32101..2883011e 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/modules/deviations/ietf-bier-holo-deviations.yang b/holo-yang/modules/deviations/ietf-bier-holo-deviations.yang new file mode 100644 index 00000000..86d2641e --- /dev/null +++ b/holo-yang/modules/deviations/ietf-bier-holo-deviations.yang @@ -0,0 +1,279 @@ +module ietf-bier-holo-deviations { + yang-version 1.1; + namespace "http://holo-routing.org/yang/ietf-bier-holo-deviations"; + prefix ietf-bier-holo-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 not-supported; + } + */ + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:underlay-protocol-type" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:mt-id" { + deviate replace { + type uint8; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bfr-id" { + deviate not-supported; + } + */ + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:bsl" { + deviate replace { + type bsl; + } + } + + /* + 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 not-supported; + } + */ + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:load-balance-num" { + deviate not-supported; + } + */ + + /* + 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; + } + } + + /* + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id" { + deviate not-supported; + } + + deviation "/rt:routing/bier:bier/bier:sub-domain/bier:encapsulation/bier:in-bift-id/bier:in-bift-id" { + 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" { + 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 1446c3f0..8d3b5c5b 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -54,6 +54,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")) => @@ -130,6 +132,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { EmbeddedModuleKey::new("holo-ospf-dev", None, None, None) => include_str!("../modules/augmentations/holo-ospf-dev.yang"), // IETF Holo deviations + EmbeddedModuleKey::new("ietf-bier-holo-deviations", None, None, None) => + include_str!("../modules/deviations/ietf-bier-holo-deviations.yang"), EmbeddedModuleKey::new("ietf-bgp-holo-deviations", None, None, None) => include_str!("../modules/deviations/ietf-bgp-holo-deviations.yang"), EmbeddedModuleKey::new("ietf-mpls-ldp-holo-deviations", None, None, None) => @@ -183,6 +187,7 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = "ietf-bfd-ip-sh", "ietf-bfd-types", "ietf-bfd", + "ietf-bier", "ietf-bgp", "ietf-bgp-policy", "ietf-routing-types",