Skip to content

Commit

Permalink
isis: implement SPF back-off delay state machine
Browse files Browse the repository at this point in the history
This commit implements the SPF Delay State Machine specified by RFC
8405. It handles the back-off timing, but the actual SPF calculation
will come later :)

Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed Oct 18, 2024
1 parent f34740d commit 525a950
Show file tree
Hide file tree
Showing 9 changed files with 401 additions and 5 deletions.
18 changes: 18 additions & 0 deletions holo-isis/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::interface::{DisCandidate, Interface};
use crate::network::MulticastAddr;
use crate::packet::pdu::{Lsp, Pdu};
use crate::packet::LevelNumber;
use crate::spf;

// IS-IS debug messages.
#[derive(Debug)]
Expand Down Expand Up @@ -46,6 +47,9 @@ pub enum Debug<'a> {
LspPurge(LevelNumber, &'a Lsp, LspPurgeReason),
LspDelete(LevelNumber, &'a Lsp),
LspRefresh(LevelNumber, &'a Lsp),
// SPF
SpfDelayFsmEvent(LevelNumber, spf::fsm::State, spf::fsm::Event),
SpfDelayFsmTransition(LevelNumber, spf::fsm::State, spf::fsm::State),
}

// Reason why an IS-IS instance is inactive.
Expand Down Expand Up @@ -159,6 +163,14 @@ impl Debug<'_> {
// Parent span(s): isis-instance
debug!(?level, lsp_id = %lsp.lsp_id.to_yang(), seqno = %lsp.seqno, len = %lsp.raw.len(), ?reason, "{}", self);
}
Debug::SpfDelayFsmEvent(level, state, event) => {
// Parent span(s): isis-instance
debug!(?level, ?state, ?event, "{}", self);
}
Debug::SpfDelayFsmTransition(level, old_state, new_state) => {
// Parent span(s): isis-instance
debug!(?level, ?old_state, ?new_state, "{}", self);
}
}
}
}
Expand Down Expand Up @@ -223,6 +235,12 @@ impl std::fmt::Display for Debug<'_> {
Debug::LspRefresh(..) => {
write!(f, "refreshing LSP")
}
Debug::SpfDelayFsmEvent(..) => {
write!(f, "SPF Delay FSM event")
}
Debug::SpfDelayFsmTransition(..) => {
write!(f, "SPF Delay FSM state transition")
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions holo-isis/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::collections::{
use crate::instance::InstanceArenas;
use crate::network::MulticastAddr;
use crate::packet::error::DecodeError;
use crate::packet::LevelNumber;
use crate::spf;

// IS-IS errors.
#[derive(Debug)]
Expand All @@ -30,6 +32,7 @@ pub enum Error {
AdjacencyReject(InterfaceIndex, [u8; 6], AdjacencyRejectError),
// Other
CircuitIdAllocationFailed,
SpfDelayUnexpectedEvent(LevelNumber, spf::fsm::State, spf::fsm::Event),
InterfaceStartError(String, Box<Error>),
InstanceStartError(Box<Error>),
}
Expand Down Expand Up @@ -91,6 +94,9 @@ impl Error {
Error::CircuitIdAllocationFailed => {
warn!("{}", self);
}
Error::SpfDelayUnexpectedEvent(level, state, event) => {
warn!(?level, ?state, ?event, "{}", self);
}
Error::InterfaceStartError(name, error) => {
error!(%name, error = %with_source(error), "{}", self);
}
Expand Down Expand Up @@ -121,6 +127,9 @@ impl std::fmt::Display for Error {
Error::CircuitIdAllocationFailed => {
write!(f, "failed to allocate Circuit ID")
}
Error::SpfDelayUnexpectedEvent(..) => {
write!(f, "unexpected SPF Delay FSM event")
}
Error::InterfaceStartError(..) => {
write!(f, "failed to start interface")
}
Expand Down
14 changes: 13 additions & 1 deletion holo-isis/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use crate::packet::error::{DecodeError, DecodeResult};
use crate::packet::pdu::{Hello, HelloVariant, Lsp, Pdu, Snp, SnpTlvs};
use crate::packet::tlv::LspEntry;
use crate::packet::{LanId, LevelNumber, LevelType, LspId};
use crate::tasks;
use crate::tasks::messages::input::DisElectionMsg;
use crate::{spf, tasks};

// ===== Network PDU receipt =====

Expand Down Expand Up @@ -1168,3 +1168,15 @@ pub(crate) fn process_lsp_refresh(

Ok(())
}

// ===== SPF Delay FSM event =====

pub(crate) fn process_spf_delay_event(
instance: &mut InstanceUpView<'_>,
arenas: &mut InstanceArenas,
level: LevelNumber,
event: spf::fsm::Event,
) -> Result<(), Error> {
// Trigger SPF Delay FSM event.
spf::fsm(level, event, instance, arenas)
}
31 changes: 30 additions & 1 deletion holo-isis/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ use crate::interface::CircuitIdAllocator;
use crate::lsdb::{LspEntry, LspLogEntry};
use crate::northbound::configuration::InstanceCfg;
use crate::packet::{LevelNumber, LevelType, Levels};
use crate::spf::SpfScheduler;
use crate::tasks::messages::input::{
AdjHoldTimerMsg, DisElectionMsg, LspDeleteMsg, LspOriginateMsg,
LspPurgeMsg, LspRefreshMsg, NetRxPduMsg, SendCsnpMsg, SendPsnpMsg,
SpfDelayEventMsg,
};
use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg};
use crate::{events, lsdb, southbound, tasks};
use crate::{events, lsdb, southbound, spf, tasks};

#[derive(Debug)]
pub struct Instance {
Expand Down Expand Up @@ -73,6 +75,8 @@ pub struct InstanceState {
pub lsp_orig_last: Option<Instant>,
pub lsp_orig_backoff: Option<TimeoutTask>,
pub lsp_orig_pending: Option<LevelType>,
// SPF scheduler state.
pub spf_sched: Levels<SpfScheduler>,
// Event counters.
pub counters: Levels<InstanceCounters>,
pub discontinuity_time: DateTime<Utc>,
Expand Down Expand Up @@ -124,6 +128,8 @@ pub struct ProtocolInputChannelsTx {
pub lsp_delete: UnboundedSender<LspDeleteMsg>,
// LSP refresh event.
pub lsp_refresh: UnboundedSender<LspRefreshMsg>,
// SPF Delay FSM event.
pub spf_delay_event: UnboundedSender<SpfDelayEventMsg>,
}

#[derive(Debug)]
Expand All @@ -146,6 +152,8 @@ pub struct ProtocolInputChannelsRx {
pub lsp_delete: UnboundedReceiver<LspDeleteMsg>,
// LSP refresh event.
pub lsp_refresh: UnboundedReceiver<LspRefreshMsg>,
// SPF Delay FSM event.
pub spf_delay_event: UnboundedReceiver<SpfDelayEventMsg>,
}

pub struct InstanceUpView<'a> {
Expand Down Expand Up @@ -319,6 +327,7 @@ impl ProtocolInstance for Instance {
let (lsp_purgep, lsp_purgec) = mpsc::unbounded_channel();
let (lsp_deletep, lsp_deletec) = mpsc::unbounded_channel();
let (lsp_refreshp, lsp_refreshc) = mpsc::unbounded_channel();
let (spf_delay_eventp, spf_delay_eventc) = mpsc::unbounded_channel();

let tx = ProtocolInputChannelsTx {
net_pdu_rx: net_pdu_rxp,
Expand All @@ -330,6 +339,7 @@ impl ProtocolInstance for Instance {
lsp_purge: lsp_purgep,
lsp_delete: lsp_deletep,
lsp_refresh: lsp_refreshp,
spf_delay_event: spf_delay_eventp,
};
let rx = ProtocolInputChannelsRx {
net_pdu_rx: net_pdu_rxc,
Expand All @@ -341,6 +351,7 @@ impl ProtocolInstance for Instance {
lsp_purge: lsp_purgec,
lsp_delete: lsp_deletec,
lsp_refresh: lsp_refreshc,
spf_delay_event: spf_delay_eventc,
};

(tx, rx)
Expand All @@ -362,6 +373,7 @@ impl InstanceState {
lsp_orig_last: None,
lsp_orig_backoff: None,
lsp_orig_pending: None,
spf_sched: Default::default(),
counters: Default::default(),
discontinuity_time: Utc::now(),
lsp_log: Default::default(),
Expand Down Expand Up @@ -394,6 +406,14 @@ impl ProtocolInputChannelsTx {
};
let _ = self.lsp_refresh.send(msg);
}

pub(crate) fn spf_delay_event(
&self,
level: LevelNumber,
event: spf::fsm::Event,
) {
let _ = self.spf_delay_event.send(SpfDelayEventMsg { level, event });
}
}

// ===== impl ProtocolInputChannelsRx =====
Expand Down Expand Up @@ -430,6 +450,9 @@ impl MessageReceiver<ProtocolInputMsg> for ProtocolInputChannelsRx {
msg = self.lsp_refresh.recv() => {
msg.map(ProtocolInputMsg::LspRefresh)
}
msg = self.spf_delay_event.recv() => {
msg.map(ProtocolInputMsg::SpfDelayEvent)
}
}
}
}
Expand Down Expand Up @@ -602,6 +625,12 @@ fn process_protocol_msg(
msg.lse_key,
)?;
}
// SPF Delay FSM event.
ProtocolInputMsg::SpfDelayEvent(msg) => {
events::process_spf_delay_event(
instance, arenas, msg.level, msg.event,
)?
}
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions holo-isis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ pub mod network;
pub mod northbound;
pub mod packet;
pub mod southbound;
pub mod spf;
pub mod tasks;
10 changes: 9 additions & 1 deletion holo-isis/src/lsdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use crate::packet::consts::LspFlags;
use crate::packet::pdu::{Lsp, LspTlvs};
use crate::packet::tlv::{ExtIpv4Reach, ExtIsReach, Ipv4Reach, IsReach, Nlpid};
use crate::packet::{LanId, LevelNumber, LspId};
use crate::tasks;
use crate::tasks::messages::input::LspPurgeMsg;
use crate::{spf, tasks};

// LSP ZeroAge lifetime.
pub const LSP_ZERO_AGE_LIFETIME: u64 = 60;
Expand Down Expand Up @@ -437,6 +437,14 @@ pub(crate) fn install<'a>(
};
log_lsp(instance, level, lsp_log_id.clone(), None, reason);

// Schedule SPF run if necessary.
if content_change {
instance
.tx
.protocol_input
.spf_delay_event(level, spf::fsm::Event::Igp);
}

lse
}

Expand Down
20 changes: 19 additions & 1 deletion holo-isis/src/northbound/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::debug::InterfaceInactiveReason;
use crate::instance::Instance;
use crate::interface::InterfaceType;
use crate::packet::{AreaAddr, LevelNumber, LevelType, SystemId};
use crate::spf;
use crate::tasks::messages::input::DisElectionMsg;

#[derive(Debug, Default)]
Expand Down Expand Up @@ -1069,7 +1070,16 @@ impl Provider for Instance {
}
}
}
Event::RerunSpf => {}
Event::RerunSpf => {
if let Some((instance, _)) = self.as_up() {
for level in instance.config.levels() {
instance.tx.protocol_input.spf_delay_event(
level,
spf::fsm::Event::ConfigChange,
);
}
}
}
}
}
}
Expand All @@ -1085,6 +1095,14 @@ impl InstanceCfg {

true
}

// Returns the levels supported by the instance.
pub(crate) fn levels(&self) -> SmallVec<[LevelNumber; 2]> {
[LevelNumber::L1, LevelNumber::L2]
.into_iter()
.filter(|level| self.level_type.intersects(level))
.collect()
}
}

impl InterfaceCfg {
Expand Down
Loading

0 comments on commit 525a950

Please sign in to comment.