Skip to content

Commit

Permalink
VRRP WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul-weqe authored and rwestphal committed Jun 26, 2024
1 parent 5ca5f1d commit 3cff08d
Show file tree
Hide file tree
Showing 40 changed files with 3,978 additions and 26 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"holo-routing",
"holo-tools",
"holo-utils",
"holo-vrrp",
"holo-yang",
]
default-members = ["holo-daemon"]
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ Holo supports the following IETF RFCs and Internet drafts:
* RFC 2453 - RIP Version 2
* RFC 4822 - RIPv2 Cryptographic Authentication

##### VRRP

* RFC 3768 - Virtual Router Redundancy Protocol (VRRP)

##### IETF YANG implementation coverage

| Module | Configuration | State | RPCs | Notifications | Total |
Expand All @@ -234,6 +238,7 @@ Holo supports the following IETF RFCs and Internet drafts:
| ietf-routing@2018-03-13 | 100.00% | 85.71% | - | - | [92.31%](http://westphal.com.br/holo/ietf-routing.html) |
| ietf-segment-routing-mpls@2021-05-26 | 62.50% | 0.00% | - | 23.53% | [32.76%](http://westphal.com.br/holo/ietf-segment-routing-mpls.html) |
| ietf-segment-routing@2021-05-26 | 100.00% | - | - | - | [100.00%](http://westphal.com.br/holo/ietf-segment-routing.html) |
| ietf-vrrp@2018-03-13 | 25.53% | 40.00% | - | 25.00% | [31.73%](http://westphal.com.br/holo/[email protected]) |

## Funding

Expand Down
3 changes: 3 additions & 0 deletions holo-daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ holo-protocol = { path = "../holo-protocol" }
holo-rip = { path = "../holo-rip", optional = true }
holo-routing = { path = "../holo-routing", optional = true }
holo-utils = { path = "../holo-utils" }
holo-vrrp = { path = "../holo-vrrp", optional = true }
holo-yang = { path = "../holo-yang" }

[build-dependencies]
Expand All @@ -67,6 +68,7 @@ default = [
"ldp",
"ospf",
"rip",
"vrrp",
]

# Base components
Expand All @@ -81,6 +83,7 @@ bgp = ["holo-bgp", "holo-routing/bgp"]
ldp = ["holo-ldp", "holo-routing/ldp"]
ospf = ["holo-ospf", "holo-routing/ospf"]
rip = ["holo-rip", "holo-routing/rip"]
vrrp = ["holo-vrrp", "holo-interface/vrrp"]

# Other features
io_uring = ["tokio-uring"]
17 changes: 10 additions & 7 deletions holo-daemon/src/northbound/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use holo_northbound::configuration::{CommitPhase, ConfigChange};
use holo_northbound::{
api as papi, CallbackKey, CallbackOp, NbDaemonSender, NbProviderReceiver,
};
use holo_protocol::InstanceShared;
use holo_utils::ibus::{IbusReceiver, IbusSender};
use holo_utils::task::TimeoutTask;
use holo_utils::yang::SchemaNodeExt;
Expand Down Expand Up @@ -632,13 +633,20 @@ fn start_providers(
let ibus_rx_keychain = ibus_tx.subscribe();
let ibus_rx_policy = ibus_rx;

let shared = InstanceShared {
db: Some(db),
event_recorder_config: Some(config.event_recorder.clone()),
..Default::default()
};

// Start holo-interface.
#[cfg(feature = "interface")]
{
let daemon_tx = holo_interface::start(
provider_tx.clone(),
ibus_tx.clone(),
ibus_rx_interface,
shared.clone(),
);
providers.push(daemon_tx);
}
Expand Down Expand Up @@ -668,13 +676,8 @@ fn start_providers(
// Start holo-routing.
#[cfg(feature = "routing")]
{
let daemon_tx = holo_routing::start(
provider_tx,
ibus_tx,
ibus_rx_routing,
db,
config.event_recorder.clone(),
);
let daemon_tx =
holo_routing::start(provider_tx, ibus_tx, ibus_rx_routing, shared);
providers.push(daemon_tx);
}

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 @@ -68,6 +68,11 @@ pub(crate) fn create_context() {
modules_add::<Instance<Ripv2>>(&mut modules);
modules_add::<Instance<Ripng>>(&mut modules);
}
#[cfg(feature = "vrrp")]
{
use holo_vrrp::interface::Interface;
modules_add::<Interface>(&mut modules);
}

// Create YANG context and load all required modules and their deviations.
let mut yang_ctx = yang::new_context();
Expand Down
7 changes: 7 additions & 0 deletions holo-interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ ipnetwork.workspace = true
netlink-packet-route.workspace = true
netlink-packet-core.workspace = true
netlink-sys.workspace = true
regex.workspace = true
rtnetlink.workspace = true
tokio.workspace = true
tracing.workspace = true
yang2.workspace = true

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

holo-vrrp = { path = "../holo-vrrp", optional = true }

[lints]
workspace = true

[features]
vrrp = ["holo-vrrp"]
4 changes: 4 additions & 0 deletions holo-interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::net::{IpAddr, Ipv4Addr};

use bitflags::bitflags;
use generational_arena::{Arena, Index};
use holo_northbound::NbDaemonSender;
use holo_utils::ibus::IbusSender;
use holo_utils::ip::Ipv4NetworkExt;
use holo_utils::southbound::{AddressFlags, InterfaceFlags};
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct Interface {
pub flags: InterfaceFlags,
pub addresses: BTreeMap<IpNetwork, InterfaceAddress>,
pub owner: Owner,
pub vrrp: Option<NbDaemonSender>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -123,6 +125,7 @@ impl Interfaces {
flags: InterfaceFlags::default(),
addresses: Default::default(),
owner: Owner::CONFIG,
vrrp: None,
};

let iface_idx = self.arena.insert(iface);
Expand Down Expand Up @@ -214,6 +217,7 @@ impl Interfaces {
flags,
addresses: Default::default(),
owner: Owner::SYSTEM,
vrrp: None,
};

// Notify protocol instances about the interface update.
Expand Down
5 changes: 5 additions & 0 deletions holo-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use holo_northbound::{
process_northbound_msg, NbDaemonReceiver, NbDaemonSender, NbProviderSender,
ProviderBase,
};
use holo_protocol::InstanceShared;
use holo_utils::ibus::{IbusReceiver, IbusSender};
use tokio::sync::mpsc;
use tracing::Instrument;
Expand All @@ -29,6 +30,8 @@ pub struct Master {
pub nb_tx: NbProviderSender,
// Internal bus Tx channel.
pub ibus_tx: IbusSender,
// Shared data among all protocol instances.
pub shared: InstanceShared,
// Netlink socket.
pub netlink_handle: rtnetlink::Handle,
// List of interfaces.
Expand Down Expand Up @@ -73,6 +76,7 @@ pub fn start(
nb_tx: NbProviderSender,
ibus_tx: IbusSender,
ibus_rx: IbusReceiver,
shared: InstanceShared,
) -> NbDaemonSender {
let (nb_daemon_tx, nb_daemon_rx) = mpsc::channel(4);

Expand All @@ -83,6 +87,7 @@ pub fn start(
let mut master = Master {
nb_tx,
ibus_tx,
shared,
netlink_handle,
interfaces: Default::default(),
};
Expand Down
76 changes: 72 additions & 4 deletions holo-interface/src/northbound/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@
// SPDX-License-Identifier: MIT
//

use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};
use std::net::IpAddr;
use std::sync::LazyLock as Lazy;

use async_trait::async_trait;
use enum_as_inner::EnumAsInner;
use holo_northbound::configuration::{
self, Callbacks, CallbacksBuilder, Provider, ValidationCallbacks,
ValidationCallbacksBuilder,
self, Callbacks, CallbacksBuilder, ConfigChanges, Provider,
ValidationCallbacks, ValidationCallbacksBuilder,
};
use holo_northbound::yang::interfaces;
use holo_northbound::{CallbackKey, NbDaemonSender};
use holo_protocol::spawn_protocol_task;
use holo_utils::yang::DataNodeRefExt;
use ipnetwork::IpNetwork;

use crate::interface::Owner;
use crate::northbound::REGEX_VRRP;
use crate::{netlink, Master};

static VALIDATION_CALLBACKS: Lazy<ValidationCallbacks> =
Expand All @@ -45,6 +48,7 @@ pub enum Event {
VlanCreate(String, u16),
AddressInstall(String, IpAddr, u8),
AddressUninstall(String, IpAddr, u8),
VrrpStart(String),
}

// ===== configuration structs =====
Expand All @@ -66,7 +70,10 @@ fn load_callbacks() -> Callbacks<Master> {
.create_apply(|master, args| {
let ifname = args.dnode.get_string_relative("./name").unwrap();

master.interfaces.add(ifname);
master.interfaces.add(ifname.clone());

let event_queue = args.event_queue;
event_queue.insert(Event::VrrpStart(ifname));
})
.delete_apply(|_master, args| {
let ifname = args.list_entry.into_interface().unwrap();
Expand Down Expand Up @@ -302,6 +309,48 @@ impl Provider for Master {
Some(&CALLBACKS)
}

fn nested_callbacks() -> Option<Vec<CallbackKey>> {
let keys: Vec<Vec<CallbackKey>> = vec![
#[cfg(feature = "vrrp")]
holo_vrrp::northbound::configuration::CALLBACKS.keys(),
];

Some(keys.concat())
}

fn relay_changes(
&self,
changes: ConfigChanges,
) -> Vec<(ConfigChanges, NbDaemonSender)> {
// Create hash table that maps changes to the appropriate child
// instances.
let mut changes_map: HashMap<String, ConfigChanges> = HashMap::new();
for change in changes {
// HACK: parse interface name from VRRP configuration changes.
let caps = REGEX_VRRP.captures(&change.1).unwrap();
let ifname = caps.get(1).unwrap().as_str().to_owned();

// Move configuration change to the appropriate interface bucket.
changes_map.entry(ifname).or_default().push(change);
}
changes_map
.into_iter()
.filter_map(|(ifname, changes)| {
self.interfaces
.get_by_name(&ifname)
.and_then(|iface| iface.vrrp.clone())
.map(|nb_tx| (changes, nb_tx))
})
.collect::<Vec<_>>()
}

fn relay_validation(&self) -> Vec<NbDaemonSender> {
self.interfaces
.iter()
.filter_map(|iface| iface.vrrp.clone())
.collect()
}

async fn process_event(&mut self, event: Event) {
match event {
Event::InterfaceDelete(ifname) => {
Expand Down Expand Up @@ -377,6 +426,25 @@ impl Provider for Master {
.await;
}
}
Event::VrrpStart(ifname) => {
#[cfg(feature = "vrrp")]
{
if let Some(iface) =
self.interfaces.get_mut_by_name(&ifname)
{
let vrrp = spawn_protocol_task::<
holo_vrrp::interface::Interface,
>(
ifname,
&self.nb_tx,
&self.ibus_tx,
Default::default(),
self.shared.clone(),
);
iface.vrrp = Some(vrrp);
}
}
}
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions holo-interface/src/northbound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
pub mod configuration;
pub mod state;

use std::sync::LazyLock as Lazy;

use holo_northbound::rpc::Provider;
use holo_northbound::yang::interfaces;
use holo_northbound::ProviderBase;
use regex::Regex;
use tracing::{debug_span, Span};

use crate::Master;
Expand Down Expand Up @@ -36,3 +40,15 @@ impl ProviderBase for Master {

// No RPC/Actions to implement.
impl Provider for Master {}

// ===== regular expressions =====

// Matches on the protocol type and instance name of a YANG path.
static REGEX_VRRP_STR: Lazy<String> = Lazy::new(|| {
format!(
r"{}\[name='(.+?)'\]/ietf-ip:ipv4/ietf-vrrp:vrrp/*",
interfaces::interface::PATH
)
});
pub static REGEX_VRRP: Lazy<Regex> =
Lazy::new(|| Regex::new(&REGEX_VRRP_STR).unwrap());
Loading

0 comments on commit 3cff08d

Please sign in to comment.