Skip to content

Commit

Permalink
isis: import initial IS-IS implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Renato Westphal <[email protected]>
  • Loading branch information
rwestphal committed Oct 10, 2024
1 parent 57dd289 commit cc89009
Show file tree
Hide file tree
Showing 48 changed files with 16,081 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- name: Generate test result and coverage report
run: |
cargo install cargo2junit grcov;
cargo test $CARGO_OPTIONS -p holo-bfd -p holo-bgp -p holo-ldp -p holo-ospf -p holo-rip -- -Z unstable-options --format json | cargo2junit > results.xml;
cargo test $CARGO_OPTIONS -p holo-bfd -p holo-bgp -p holo-isis -p holo-ldp -p holo-ospf -p holo-rip -- -Z unstable-options --format json | cargo2junit > results.xml;
grcov . -s . -t lcov --llvm --ignore-not-existing --ignore "/*" --ignore "holo-*/tests/*" -o lcov.info;
- name: Upload test results
uses: EnricoMi/publish-unit-test-result-action@v1
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"holo-bgp",
"holo-daemon",
"holo-interface",
"holo-isis",
"holo-keychain",
"holo-ldp",
"holo-northbound",
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ specific needs.

## Compliance

Holo supports the following IETF RFCs and Internet drafts:
Holo supports the following Internet Standards:

##### BFD

Expand Down Expand Up @@ -174,6 +174,19 @@ Holo supports the following IETF RFCs and Internet drafts:
* RFC 8212 - Default External BGP (EBGP) Route Propagation Behavior without Policies
* RFC 8642 - Policy Behavior for Well-Known BGP Communities

##### IS-IS

* ISO/IEC 10589 - Information technology — Telecommunications and information exchange between systems — Intermediate System to Intermediate System intra-domain routeing information exchange protocol for use in conjunction with the protocol for providing the connectionless-mode network service (ISO 8473)
* RFC 1195 - Use of OSI IS-IS for Routing in TCP/IP and Dual Environments
* RFC 3719 - Recommendations for Interoperable Networks using Intermediate System to Intermediate System (IS-IS)
* RFC 3787 - Recommendations for Interoperable IP Networks using Intermediate System to Intermediate System (IS-IS)
* RFC 5301 - Dynamic Hostname Exchange Mechanism for IS-IS
* RFC 5304 - IS-IS Cryptographic Authentication
* RFC 5305 - IS-IS Extensions for Traffic Engineering
* RFC 5308 - Routing IPv6 with IS-IS
* RFC 5310 - IS-IS Generic Cryptographic Authentication
* RFC 8405 - Shortest Path First (SPF) Back-Off Delay Algorithm for Link-State IGPs

##### MPLS LDP

* RFC 5036 - LDP Specification
Expand Down Expand Up @@ -224,6 +237,7 @@ Holo supports the following IETF RFCs and Internet drafts:
| ietf-ip@2018-01-09 | 52.17% | 0.00% | - | - | [40.00%](http://westphal.com.br/holo/ietf-ip.html) |
| ietf-ipv4-unicast-routing@2018-03-13 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-ipv4-unicast-routing.html) |
| ietf-ipv6-unicast-routing@2018-03-13 | 40.62% | 100.00% | - | - | [45.71%](http://westphal.com.br/holo/ietf-ipv6-unicast-routing.html) |
| ietf-isis@2022-10-19 | 92.66% | 52.21% | 100.00% | 100.00% | [68.81%](http://westphal.com.br/holo/[email protected]) |
| ietf-key-chain@2017-04-18 | 100.00% | 100.00% | - | - | [100.00%](http://westphal.com.br/holo/ietf-key-chain.html) |
| ietf-mpls-ldp@2022-03-14 | 86.96% | 92.31% | 100.00% | 100.00% | [92.38%](http://westphal.com.br/holo/ietf-mpls-ldp.html) |
| ietf-mpls@2020-12-18 | 0.00% | 57.14% | - | - | [35.29%](http://westphal.com.br/holo/ietf-mpls.html) |
Expand Down
3 changes: 3 additions & 0 deletions holo-daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ yang3.workspace = true
holo-interface = { path = "../holo-interface", optional = true }
holo-bfd = { path = "../holo-bfd", optional = true }
holo-bgp = { path = "../holo-bgp", optional = true }
holo-isis = { path = "../holo-isis", optional = true }
holo-keychain = { path = "../holo-keychain", optional = true }
holo-ldp = { path = "../holo-ldp", optional = true }
holo-northbound = { path = "../holo-northbound" }
Expand Down Expand Up @@ -66,6 +67,7 @@ default = [
# Protocols
"bfd",
"bgp",
"isis",
"ldp",
"ospf",
"rip",
Expand All @@ -81,6 +83,7 @@ system = ["dep:holo-system"]
# Protocols
bfd = ["dep:holo-bfd", "holo-routing/bfd"]
bgp = ["dep:holo-bgp", "holo-routing/bgp"]
isis = ["dep:holo-isis", "holo-routing/isis"]
ldp = ["dep:holo-ldp", "holo-routing/ldp"]
ospf = ["dep:holo-ospf", "holo-routing/ospf"]
rip = ["dep:holo-rip", "holo-routing/rip"]
Expand Down
5 changes: 5 additions & 0 deletions holo-daemon/src/northbound/yang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pub(crate) fn create_context() {
use holo_bgp::instance::Instance;
modules_add::<Instance>(&mut modules);
}
#[cfg(feature = "isis")]
{
use holo_isis::instance::Instance;
modules_add::<Instance>(&mut modules);
}
#[cfg(feature = "ldp")]
{
use holo_ldp::instance::Instance;
Expand Down
57 changes: 57 additions & 0 deletions holo-isis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[package]
name = "holo-isis"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true

[dependencies]
async-trait.workspace = true
bitflags.workspace = true
bytes.workspace = true
chrono.workspace = true
derive-new.workspace = true
enum-as-inner.workspace = true
fletcher.workspace = true
generational-arena.workspace = true
ipnetwork.workspace = true
itertools.workspace = true
libc.workspace = true
nix.workspace = true
num-derive.workspace = true
num-traits.workspace = true
prefix-trie.workspace = true
rand.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_with.workspace = true
smallvec.workspace = true
socket2.workspace = true
tokio.workspace = true
tracing.workspace = true
yang3.workspace = true

holo-northbound = { path = "../holo-northbound" }
holo-protocol = { path = "../holo-protocol" }
holo-utils = { path = "../holo-utils" }
holo-yang = { path = "../holo-yang" }

[dev-dependencies]
criterion.workspace = true

holo-isis = { path = ".", features = ["testing", "deterministic"] }
holo-protocol = { path = "../holo-protocol", features = ["testing"] }
holo-utils = { path = "../holo-utils", features = ["testing"] }

[lints]
#workspace = true

[lints.clippy]
len_without_is_empty = "allow"
too_many_arguments = "allow"

[features]
default = []
testing = []
deterministic = []
19 changes: 19 additions & 0 deletions holo-isis/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2023 The Holo Core Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
164 changes: 164 additions & 0 deletions holo-isis/src/adjacency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//
// Copyright (c) The Holo Core Contributors
//
// SPDX-License-Identifier: MIT
//
// Sponsored by NLnet as part of the Next Generation Internet initiative.
// See: https://nlnet.nl/NGI0
//

use std::collections::BTreeSet;
use std::time::Instant;

use chrono::Utc;
use holo_utils::task::TimeoutTask;

use crate::collections::AdjacencyId;
use crate::debug::Debug;
use crate::instance::InstanceUpView;
use crate::interface::{Interface, InterfaceType};
use crate::northbound::notification;
use crate::packet::{AreaAddr, LanId, LevelType, SystemId};
use crate::tasks;

#[derive(Debug)]
pub struct Adjacency {
pub id: AdjacencyId,
pub snpa: [u8; 6],
pub system_id: SystemId,
pub level_capability: LevelType,
pub level_usage: LevelType,
pub state: AdjacencyState,
pub priority: Option<u8>,
pub lan_id: Option<LanId>,
pub area_addrs: BTreeSet<AreaAddr>,
pub neighbors: BTreeSet<[u8; 6]>,
pub last_uptime: Option<Instant>,
pub holdtimer: Option<TimeoutTask>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AdjacencyState {
Down,
Initializing,
Up,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AdjacencyEvent {
HelloOneWayRcvd,
HelloTwoWayRcvd,
HoldtimeExpired,
LinkDown,
Kill,
}

// ===== impl Adjacency =====

impl Adjacency {
// Creates new adjacency.
pub(crate) fn new(
id: AdjacencyId,
snpa: [u8; 6],
system_id: SystemId,
level_capability: LevelType,
level_usage: LevelType,
) -> Adjacency {
let adj = Adjacency {
id,
snpa,
system_id,
level_capability,
level_usage,
state: AdjacencyState::Down,
priority: None,
lan_id: None,
area_addrs: Default::default(),
neighbors: Default::default(),
last_uptime: None,
holdtimer: None,
};
Debug::AdjacencyCreate(&adj).log();
adj
}

// Transitions the adjacency state if different from the current one.
pub(crate) fn state_change(
&mut self,
iface: &mut Interface,
instance: &mut InstanceUpView<'_>,
event: AdjacencyEvent,
new_state: AdjacencyState,
) {
if self.state == new_state {
return;
}

// Log the state transition.
Debug::AdjacencyStateChange(self, new_state, event).log();

// Send YANG notification.
notification::adjacency_state_change(
instance, iface, self, new_state, event,
);

// Update counters.
if new_state == AdjacencyState::Up {
iface.state.event_counters.adjacency_number += 1;
self.last_uptime = Some(Instant::now());
} else if self.state == AdjacencyState::Up {
iface.state.event_counters.adjacency_number -= 1;
}
iface.state.event_counters.adjacency_changes += 1;
iface.state.discontinuity_time = Utc::now();

// ISO 10589 does not require periodic CSNP transmission on
// point-to-point interfaces. However, sending them helps prevent
// synchronization issues, especially in mesh-group setups.
if iface.config.interface_type == InterfaceType::PointToPoint {
if new_state == AdjacencyState::Up {
// Start CSNP interval task(s).
iface.csnp_interval_start(instance);
} else if self.state == AdjacencyState::Up {
// Stop CSNP interval task(s).
iface.csnp_interval_stop();
}
}

// If no adjacencies remain in the Up state, clear SRM and SSN lists.
if iface.state.event_counters.adjacency_number == 0 {
for level in iface.config.levels() {
iface.state.srm_list.get_mut(level).clear();
iface.state.ssn_list.get_mut(level).clear();
}
}

// Effectively transition to the new state.
self.state = new_state;

// Schedule LSP reorigination.
instance.schedule_lsp_origination(self.level_usage);
}

// Starts or resets the holdtime timer.
pub(crate) fn holdtimer_reset(
&mut self,
iface: &Interface,
instance: &InstanceUpView<'_>,
holdtime: u16,
) {
if let Some(holdtimer) = self.holdtimer.as_mut() {
holdtimer.reset(None);
} else {
let task =
tasks::adjacency_holdtimer(self, iface, instance, holdtime);
self.holdtimer = Some(task);
}
}
}

impl Drop for Adjacency {
fn drop(&mut self) {
Debug::AdjacencyDelete(self).log();
}
}
Loading

0 comments on commit cc89009

Please sign in to comment.