Skip to content

Commit

Permalink
Add basic BIER support
Browse files Browse the repository at this point in the history
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 <nicolas.rybowski@uclouvain.be>
  • Loading branch information
nrybowski authored and rwestphal committed May 6, 2024
1 parent d095a53 commit d2f4c7f
Show file tree
Hide file tree
Showing 12 changed files with 1,218 additions and 4 deletions.
4 changes: 4 additions & 0 deletions holo-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -98,6 +99,8 @@ pub struct InstanceShared {
pub policies: Policies,
// Global Segment Routing configuration.
pub sr_config: Arc<SrCfg>,
// Global BIER configuration.
pub bier_config: Arc<BierCfg>,
}

/// Instance input message.
Expand Down Expand Up @@ -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()
}
}
Expand Down
4 changes: 4 additions & 0 deletions holo-routing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -51,6 +52,8 @@ pub struct Master {
pub static_routes: BTreeMap<IpNetwork, StaticRoute>,
// SR configuration data.
pub sr_config: SrCfg,
// BIER configuration data.
pub bier_config: BierCfg,
// Protocol instances.
pub instances: BTreeMap<InstanceId, NbDaemonSender>,
}
Expand Down Expand Up @@ -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(),
};

Expand Down
298 changes: 296 additions & 2 deletions holo-routing/src/northbound/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,6 +50,8 @@ pub enum ListEntry {
StaticRoute(IpNetwork),
StaticRouteNexthop(IpNetwork, String),
SrCfgPrefixSid(IpNetwork, IgpAlgoType),
BierCfgSubDomain(SubDomainId, AddressFamily),
BierCfgEncapsulation(Bsl, BierEncapsulationType),
}

#[derive(Debug, EnumAsInner)]
Expand All @@ -61,6 +67,8 @@ pub enum Event {
SrCfgUpdate,
SrCfgLabelRangeUpdate,
SrCfgPrefixSidUpdate(AddressFamily),
BierCfgUpdate,
BierCfgEncapUpdate(SubDomainId, AddressFamily, Bsl, BierEncapsulationType),
}

// ===== configuration structs =====
Expand Down Expand Up @@ -625,6 +633,254 @@ fn load_callbacks() -> Callbacks<Master> {
.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()
}

Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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,
),
));
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions holo-routing/src/northbound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ impl ProviderBase for Master {
"ietf-segment-routing",
"ietf-segment-routing-common",
"ietf-segment-routing-mpls",
"ietf-bier",
]
}

Expand Down

0 comments on commit d2f4c7f

Please sign in to comment.