Skip to content

Commit

Permalink
vrrp: import initial VRRP implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Wekesa <[email protected]>
  • Loading branch information
Paul-weqe authored and rwestphal committed Dec 5, 2024
1 parent 4ba065b commit e3cb147
Show file tree
Hide file tree
Showing 48 changed files with 5,147 additions and 65 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"holo-system",
"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 @@ -211,6 +211,10 @@ Holo supports the following Internet Standards:
* 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 Down Expand Up @@ -240,6 +244,7 @@ Holo supports the following Internet Standards:
| 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-system@2014-08-06 | 26.67% | 60.00% | 0.00% | - | [38.24%](http://westphal.com.br/holo/[email protected]) |
| 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 @@ -44,6 +44,7 @@ holo-rip = { path = "../holo-rip", optional = true }
holo-routing = { path = "../holo-routing", optional = true }
holo-system = { path = "../holo-system", optional = true }
holo-utils = { path = "../holo-utils" }
holo-vrrp = { path = "../holo-vrrp", optional = true }
holo-yang = { path = "../holo-yang" }

[build-dependencies]
Expand Down Expand Up @@ -71,6 +72,7 @@ default = [
"ldp",
"ospf",
"rip",
"vrrp",
]

# Base components
Expand All @@ -87,6 +89,7 @@ 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"]
vrrp = ["holo-vrrp", "holo-interface/vrrp"]

# Other features
tokio_console = ["dep:console-subscriber"]
Expand Down
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 @@ -646,13 +647,20 @@ fn start_providers(
let ibus_rx_policy = ibus_tx.subscribe();
let ibus_rx_system = 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 @@ -693,13 +701,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 @@ -75,6 +75,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 @@ -18,15 +18,22 @@ netlink-packet-route.workspace = true
netlink-packet-core.workspace = true
netlink-packet-utils.workspace = true
netlink-sys.workspace = true
regex.workspace = true
rtnetlink.workspace = true
tokio.workspace = true
tracing.workspace = true
yang3.workspace = true
libc.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"]
35 changes: 34 additions & 1 deletion holo-interface/src/ibus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::Master;

// ===== global functions =====

pub(crate) fn process_msg(master: &mut Master, msg: IbusMsg) {
pub(crate) async fn process_msg(master: &mut Master, msg: IbusMsg) {
match msg {
IbusMsg::InterfaceDump => {
for iface in master.interfaces.iter() {
Expand Down Expand Up @@ -57,6 +57,39 @@ pub(crate) fn process_msg(master: &mut Master, msg: IbusMsg) {
master.interfaces.router_id(),
);
}
IbusMsg::MacvlanAdd(msg) => {
master
.interfaces
.create_macvlan_interface(
&master.netlink_handle,
&msg.parent_name,
msg.mac_address,
msg.name,
)
.await;
}
IbusMsg::MacvlanDel(ifname) => {
let _ = master
.interfaces
.delete_iface(&master.netlink_handle, ifname)
.await;
}
IbusMsg::InterfaceIpAddRequest(msg) => {
let _ = master
.interfaces
.add_iface_address(&master.netlink_handle, msg.ifname, msg.addr)
.await;
}
IbusMsg::InterfaceIpDelRequest(msg) => {
let _ = master
.interfaces
.delete_iface_address(
&master.netlink_handle,
msg.ifname,
msg.addr,
)
.await;
}
// Ignore other events.
_ => {}
}
Expand Down
71 changes: 69 additions & 2 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 @@ -39,6 +40,7 @@ pub struct Interface {
pub addresses: BTreeMap<IpNetwork, InterfaceAddress>,
pub mac_address: [u8; 6],
pub owner: Owner,
pub vrrp: Option<NbDaemonSender>,
}

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

let iface_idx = self.arena.insert(iface);
Expand Down Expand Up @@ -218,8 +221,9 @@ impl Interfaces {
mtu: Some(mtu),
flags,
addresses: Default::default(),
owner: Owner::SYSTEM,
mac_address,
owner: Owner::SYSTEM,
vrrp: None,
};

// Notify protocol instances about the interface update.
Expand Down Expand Up @@ -400,6 +404,69 @@ impl Interfaces {
}
}

// create macvlan interface
//
// the parent_name is the name of the interface that will be the parent of
// the macvlan interface being create. mvlan name is the macvlan name of the interface being created.
pub(crate) async fn create_macvlan_interface(
&self,
netlink_handle: &rtnetlink::Handle,
parent_name: &str,
mac_address: Option<[u8; 6]>,
mvlan_name: String,
) {
if let Some(interface) = self.get_by_name(parent_name) {
if let Some(ifindex) = interface.ifindex {
let _ = netlink::macvlan_create(
netlink_handle,
mvlan_name,
mac_address,
ifindex,
)
.await;
}
}
}

pub(crate) async fn delete_iface(
&self,
netlink_handle: &rtnetlink::Handle,
ifname: String,
) {
if let Some(iface) = self.get_by_name(&ifname)
&& let Some(ifindex) = iface.ifindex
{
let _ = netlink::iface_delete(netlink_handle, ifindex).await;
}
}

// adds an IP address to an interface
pub(crate) async fn add_iface_address(
&self,
netlink_handle: &rtnetlink::Handle,
ifname: String,
addr: IpNetwork,
) {
if let Some(iface) = self.get_by_name(&ifname)
&& let Some(ifindex) = iface.ifindex
{
netlink::addr_install(netlink_handle, ifindex, &addr).await;
}
}

pub(crate) async fn delete_iface_address(
&self,
netlink_handle: &rtnetlink::Handle,
ifname: String,
addr: IpNetwork,
) {
if let Some(iface) = self.get_by_name(&ifname)
&& let Some(ifindex) = iface.ifindex
{
netlink::addr_uninstall(netlink_handle, ifindex, &addr).await;
}
}

// Returns a reference to the interface corresponding to the given name.
pub(crate) fn get_by_name(&self, ifname: &str) -> Option<&Interface> {
self.name_tree
Expand Down
7 changes: 6 additions & 1 deletion 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 @@ -57,7 +60,7 @@ impl Master {
.await;
}
Ok(msg) = ibus_rx.recv() => {
ibus::process_msg(self, msg);
ibus::process_msg(self, msg).await;
}
Some((msg, _)) = netlink_rx.next() => {
netlink::process_msg(self, msg).await;
Expand All @@ -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
43 changes: 43 additions & 0 deletions holo-interface/src/netlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use tracing::{error, trace};
use crate::interface::Owner;
use crate::Master;

pub const MACVLAN_MODE_BRIDGE: u32 = 4;

pub type NetlinkMonitor =
UnboundedReceiver<(NetlinkMessage<RouteNetlinkMessage>, SocketAddr)>;

Expand Down Expand Up @@ -234,6 +236,47 @@ pub(crate) async fn vlan_create(
}
}

/// Creates MacVlan interface
/// uses RTM_NEWLINK.
///
/// # Arguments
///
/// * `parent_ifindex` - index of the primary interface this macvlan will be bridging from
/// * `name` - name of the macvlan link that we will be creating
pub(crate) async fn macvlan_create(
handle: &Handle,
name: String,
mac_address: Option<[u8; 6]>,
parent_ifindex: u32,
) {
// Create netlink request
let mut request = handle.link().add().macvlan(
name.clone(),
parent_ifindex,
MACVLAN_MODE_BRIDGE,
);

if let Some(address) = mac_address {
request = request.address(address.to_vec());
}

// Execute request.
if let Err(error) = request.execute().await {
error!(%parent_ifindex, %name, %error, "Failed to create MacVlan interface");
}
}

// Removes an interface completely from the system.
//
// At the moment, will mostly be used for uninstalling
// Macvlan interfaces when we are removing a VRRP instance.
pub(crate) async fn iface_delete(handle: &Handle, ifindex: u32) {
let request = handle.link().del(ifindex);
if let Err(err) = request.execute().await {
error!(%ifindex, %err, "failed to delete interface.");
}
}

pub(crate) async fn addr_install(
handle: &Handle,
ifindex: u32,
Expand Down
Loading

0 comments on commit e3cb147

Please sign in to comment.