Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: BIER extension for OSPFv3 #16

Merged
merged 13 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions holo-ospf/src/bier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Copyright (c) The Holo Core Contributors
//
// SPDX-License-Identifier: MIT
//

use holo_utils::bier::{BierInfo, Bsl, UnderlayProtocolType};
use holo_utils::ip::IpNetworkKind;

use crate::instance::InstanceUpView;
use crate::packet::tlv::BierSubSubTlv;
use crate::route::RouteNet;
use crate::spf::SpfIntraAreaNetwork;
use crate::version::Version;

pub(crate) fn bier_route_add<V>(
instance: &InstanceUpView<'_, V>,
new_route: &mut RouteNet<V>,
stub: &SpfIntraAreaNetwork<'_, V>,
) where
V: Version,
{
let bier_cfg = &instance.shared.bier_config;

// 1. Does the BFR match a locally configured BIER sub-domain?
stub.bier.iter().for_each(|tlv| {
if instance.config.bier.mt_id == tlv.mt_id
&& let Some(sd_cfg) = bier_cfg
.sd_cfg
.get(&(tlv.sub_domain_id, stub.prefix.address_family()))
&& sd_cfg.underlay_protocol == UnderlayProtocolType::Ospf
{
// 2. Register entry in BIRT for each supported bitstring length by the BFR prefix
// TODO: Use BAR and IPA

// TODO: Sanity check on bitstring lengths upon LSA reception

let bfr_bss: Vec<Bsl> = tlv
.subtlvs
.iter()
.filter_map(|stlv| match stlv {
BierSubSubTlv::BierEncapSubSubTlv(encap) => {
Bsl::try_from(encap.bs_len).ok()
}
})
.collect();

if !bfr_bss.is_empty() {
new_route.bier_info = Some(BierInfo {
bfr_bss,
sd_id: tlv.sub_domain_id,
bfr_id: tlv.bfr_id,
})
}
}
});
}
23 changes: 22 additions & 1 deletion holo-ospf/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::sync::Arc;

use chrono::Utc;
use holo_utils::bfd;
use holo_utils::ibus::SrCfgEvent;
use holo_utils::ibus::{BierCfgEvent, SrCfgEvent};

use crate::area::{Area, AreaType};
use crate::collections::{
Expand Down Expand Up @@ -1429,6 +1429,27 @@ where
Ok(())
}

// ===== BIER configuration change event =====

pub(crate) fn process_bier_cfg_change<V>(
instance: &mut Instance<V>,
change: BierCfgEvent,
) -> Result<(), Error<V>>
where
V: Version,
{
if let Some((instance, arenas)) = instance.as_up()
&& instance.config.bier.enabled
{
V::lsa_orig_event(
&instance,
arenas,
LsaOriginateEvent::BierCfgChange { change },
)?;
}
Ok(())
}

// ===== BFD state update event =====

pub(crate) fn process_bfd_state_update<V>(
Expand Down
7 changes: 7 additions & 0 deletions holo-ospf/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,10 +780,17 @@ where
IbusMsg::SrCfgUpd(sr_config) => {
instance.shared.sr_config = sr_config;
}
// BIER configuration update.
IbusMsg::BierCfgUpd(bier_config) => {
instance.shared.bier_config = bier_config.clone();
}
// SR configuration event.
IbusMsg::SrCfgEvent(event) => {
events::process_sr_cfg_change(instance, event)?
}
IbusMsg::BierCfgEvent(event) => {
events::process_bier_cfg_change(instance, event)?
}
// Ignore other events.
_ => {}
}
Expand Down
1 change: 1 addition & 0 deletions holo-ospf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#![feature(btree_extract_if, hash_extract_if, ip, let_chains)]

pub mod area;
pub mod bier;
pub mod collections;
pub mod debug;
pub mod error;
Expand Down
6 changes: 5 additions & 1 deletion holo-ospf/src/lsdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::time::Instant;
use bitflags::bitflags;
use chrono::Utc;
use derive_new::new;
use holo_utils::ibus::SrCfgEvent;
use holo_utils::ibus::{BierCfgEvent, SrCfgEvent};
use holo_utils::task::TimeoutTask;
use holo_utils::UnboundedSender;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -124,6 +124,10 @@ pub enum LsaOriginateEvent {
area_id: AreaId,
iface_id: InterfaceId,
},
BierEnableChange,
BierCfgChange {
change: BierCfgEvent,
},
}

#[derive(Debug)]
Expand Down
70 changes: 70 additions & 0 deletions holo-ospf/src/northbound/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub enum Event {
RerunSpf,
UpdateSummaries,
ReinstallRoutes,
BierEnableChange(bool),
}

pub static VALIDATION_CALLBACKS_OSPFV2: Lazy<ValidationCallbacks> =
Expand Down Expand Up @@ -102,6 +103,15 @@ pub struct InstanceCfg {
pub extended_lsa: bool,
pub sr_enabled: bool,
pub instance_id: u8,
pub bier: BierOspfCfg,
}

#[derive(Debug)]
pub struct BierOspfCfg {
pub mt_id: u8,
pub enabled: bool,
pub advertise: bool,
pub receive: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -778,6 +788,35 @@ where
let mtu_ignore = args.dnode.get_bool();
iface.config.mtu_ignore = mtu_ignore;
})
.path(ospf::bier::mt_id::PATH)
.modify_apply(|instance, args| {
let mt_id = args.dnode.get_u8();
instance.config.bier.mt_id = mt_id;

// TODO: should reoriginate LSA
})
.delete_apply(|instance, _args| {
let mt_id = 0;
instance.config.bier.mt_id = mt_id;
})
.path(ospf::bier::bier::enable::PATH)
.modify_apply(|instance, args| {
let enable = args.dnode.get_bool();
instance.config.bier.enabled = enable;

let event_queue = args.event_queue;
event_queue.insert(Event::BierEnableChange(enable));
})
.path(ospf::bier::bier::advertise::PATH)
.modify_apply(|instance, args| {
let advertise = args.dnode.get_bool();
instance.config.bier.advertise = advertise;
})
.path(ospf::bier::bier::receive::PATH)
.modify_apply(|instance, args| {
let receive = args.dnode.get_bool();
instance.config.bier.receive = receive;
})
.build()
}

Expand Down Expand Up @@ -1471,6 +1510,22 @@ where
}
}
}
Event::BierEnableChange(bier_enabled) => {
if let Some((instance, _arenas)) = self.as_up() {
// (Re)originate LSAs that might have been affected.
instance
.tx
.protocol_input
.lsa_orig_event(LsaOriginateEvent::BierEnableChange);

// Purge BIRT if bier disabled or re-install routes if enabled
if bier_enabled {
self.process_event(Event::ReinstallRoutes).await;
} else {
let _ = instance.tx.ibus.send(IbusMsg::BierPurge);
nrybowski marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Event::RerunSpf => {
if let Some((instance, _)) = self.as_up() {
instance
Expand Down Expand Up @@ -1559,6 +1614,21 @@ impl Default for InstanceCfg {
extended_lsa,
sr_enabled,
instance_id,
bier: Default::default(),
}
}
}

impl Default for BierOspfCfg {
fn default() -> Self {
let enabled = ospf::bier::bier::enable::DFLT;
let advertise = ospf::bier::bier::advertise::DFLT;
let receive = ospf::bier::bier::receive::DFLT;
Self {
mt_id: 0,
enabled,
advertise,
receive,
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions holo-ospf/src/ospfv2/spf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ impl SpfVersion<Self> for Ospfv2 {
prefix_options: Default::default(),
metric: 0,
prefix_sids,
// FIXME: BIER not supported yet for OSPFv2
bier: Default::default(),
});
}
VertexLsa::Router(lsa) => {
Expand Down Expand Up @@ -523,6 +525,8 @@ impl SpfVersion<Self> for Ospfv2 {
prefix_options: Default::default(),
metric,
prefix_sids,
// FIXME: BIER not supported yet for OSPFv2
bier: Default::default(),
}
}),
)
Expand Down
80 changes: 77 additions & 3 deletions holo-ospf/src/ospfv3/lsdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use std::collections::{hash_map, BTreeMap, HashMap};
use std::net::{IpAddr, Ipv4Addr};

use holo_utils::ibus::SrCfgEvent;
use holo_utils::bier::{BierEncapsulationType, BierInBiftId, BiftId};
use holo_utils::ibus::{BierCfgEvent, SrCfgEvent};
use holo_utils::ip::{AddressFamily, IpNetworkKind};
use holo_utils::mpls::Label;
use holo_utils::sr::{IgpAlgoType, Sid, SidLastHopBehavior};
Expand Down Expand Up @@ -35,8 +36,9 @@ use crate::packet::lsa::{
Lsa, LsaHdrVersion, LsaKey, LsaScope, LsaTypeVersion, PrefixSidVersion,
};
use crate::packet::tlv::{
PrefixSidFlags, RouterInfoCaps, RouterInfoCapsTlv, SidLabelRangeTlv,
SrAlgoTlv, SrLocalBlockTlv,
BierEncapId, BierEncapSubSubTlv, BierSubSubTlv, BierSubTlv, PrefixSidFlags,
RouterInfoCaps, RouterInfoCapsTlv, SidLabelRangeTlv, SrAlgoTlv,
SrLocalBlockTlv,
};
use crate::route::{SummaryNet, SummaryNetFlags, SummaryRtr};
use crate::version::Ospfv3;
Expand Down Expand Up @@ -279,6 +281,23 @@ impl LsdbVersion<Self> for Ospfv3 {
}
}
}
LsaOriginateEvent::BierEnableChange => {
// Reoriginate Intra-area-prefix-LSA(s) in all areas.
for area in arenas.areas.iter() {
lsa_orig_intra_area_prefix(area, instance, arenas);
}
}
LsaOriginateEvent::BierCfgChange { change } => match change {
BierCfgEvent::EncapUpdate(af)
| BierCfgEvent::SubDomainUpdate(af) => {
if af == instance.state.af {
for area in arenas.areas.iter() {
// Reoriginate Intra-area-prefix-LSA(s) in all areas.
lsa_orig_intra_area_prefix(area, instance, arenas);
}
}
}
},
};

Ok(())
Expand Down Expand Up @@ -717,6 +736,7 @@ fn lsa_orig_intra_area_prefix(
arenas: &InstanceArenas<Ospfv3>,
) {
let sr_config = &instance.shared.sr_config;
let bier_config = &instance.shared.bier_config;
let lsdb_id = LsdbId::Area(area.id);
let extended_lsa = instance.config.extended_lsa;
let adv_rtr = instance.state.router_id;
Expand Down Expand Up @@ -817,6 +837,60 @@ fn lsa_orig_intra_area_prefix(
}
}

// Add BIER Sub-TLV(s) if BIER is enabled and allowed to advertise
if instance.config.bier.enabled && instance.config.bier.advertise {
bier_config
.sd_cfg
.iter()
// Search for subdomain configuration(s) for current prefix
.filter(|((_, af), sd_cfg)| {
af == &AddressFamily::Ipv6 && sd_cfg.bfr_prefix == prefix
})
.for_each(|((sd_id, _), sd_cfg)| {
// BIER prefix has configured encap ?
let bier_encaps = sd_cfg
.encap
.iter()
.filter_map(|((bsl, encap_type), encap)| {
match encap_type {
BierEncapsulationType::Mpls => {
// TODO: where is the label defined?
Some(BierEncapId::Mpls(Label::new(0)))
}
_ => match encap.in_bift_id {
BierInBiftId::Base(id) => Some(id),
BierInBiftId::Encoding(true) => Some(0),
_ => None,
}
.map(|id| {
BierEncapId::NonMpls(BiftId::new(id))
}),
}
.map(|id| {
BierSubSubTlv::BierEncapSubSubTlv(
BierEncapSubSubTlv::new(
encap.max_si,
id,
(*bsl).into(),
),
)
})
})
.collect::<Vec<BierSubSubTlv>>();

let bier = BierSubTlv::new(
*sd_id,
sd_cfg.mt_id,
sd_cfg.bfr_id,
sd_cfg.bar,
sd_cfg.ipa,
bier_encaps,
);

entry.bier.push(bier);
});
}

prefixes.push(entry);
}
let ref_lsa = LsaKey::new(
Expand Down
Loading
Loading