diff --git a/Cargo.toml b/Cargo.toml index e500401f..35ebdb4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "holo-routing", "holo-tools", "holo-utils", + "holo-vrrp", "holo-yang", ] default-members = ["holo-daemon"] diff --git a/README.md b/README.md index bbc14874..822ac8df 100644 --- a/README.md +++ b/README.md @@ -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 | @@ -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 | 51.06% | 60.47% | - | 41.67% | [53.92%](http://westphal.com.br/holo/ietf-vrrp@2018-03-13.coverage.md) | ## Funding diff --git a/holo-daemon/Cargo.toml b/holo-daemon/Cargo.toml index 3990ec98..2cd2896f 100644 --- a/holo-daemon/Cargo.toml +++ b/holo-daemon/Cargo.toml @@ -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] @@ -67,6 +68,7 @@ default = [ "ldp", "ospf", "rip", + "vrrp", ] # Base components @@ -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-routing/vrrp"] # Other features io_uring = ["tokio-uring"] diff --git a/holo-daemon/src/northbound/yang.rs b/holo-daemon/src/northbound/yang.rs index d16a691f..cbb8b235 100644 --- a/holo-daemon/src/northbound/yang.rs +++ b/holo-daemon/src/northbound/yang.rs @@ -68,6 +68,11 @@ pub(crate) fn create_context() { modules_add::>(&mut modules); modules_add::>(&mut modules); } + #[cfg(feature = "vrrp")] + { + use holo_vrrp::instance::Instance; + modules_add::(&mut modules); + } // Create YANG context and load all required modules and their deviations. let mut yang_ctx = yang::new_context(); diff --git a/holo-routing/Cargo.toml b/holo-routing/Cargo.toml index 39d1ae5b..02f4f7da 100644 --- a/holo-routing/Cargo.toml +++ b/holo-routing/Cargo.toml @@ -31,6 +31,7 @@ holo-bgp = { path = "../holo-bgp", optional = true } holo-ldp = { path = "../holo-ldp", optional = true } holo-ospf = { path = "../holo-ospf", optional = true } holo-rip = { path = "../holo-rip", optional = true } +holo-vrrp = { path = "../holo-vrrp", optional = true } [lints] workspace = true @@ -41,3 +42,4 @@ bgp = ["holo-bgp"] ldp = ["holo-ldp"] ospf = ["holo-ospf"] rip = ["holo-rip"] +vrrp = ["holo-vrrp"] diff --git a/holo-tools/yang-coverage.sh b/holo-tools/yang-coverage.sh index 446bd24b..8cd987f2 100755 --- a/holo-tools/yang-coverage.sh +++ b/holo-tools/yang-coverage.sh @@ -22,4 +22,5 @@ cargo run --bin yang_coverage --\ -m ietf-ospf\ -m ietf-ospf-sr-mpls\ -m ietf-ospfv3-extended-lsa\ - -m ietf-rip + -m ietf-rip\ + -m ietf-vrrp diff --git a/holo-utils/src/protocol.rs b/holo-utils/src/protocol.rs index 15d48073..62b53679 100644 --- a/holo-utils/src/protocol.rs +++ b/holo-utils/src/protocol.rs @@ -25,6 +25,7 @@ pub enum Protocol { RIPV2, RIPNG, STATIC, + VRRP, } // ===== impl Protocol ===== @@ -41,6 +42,7 @@ impl std::fmt::Display for Protocol { Protocol::RIPV2 => write!(f, "ripv2"), Protocol::RIPNG => write!(f, "ripng"), Protocol::STATIC => write!(f, "static"), + Protocol::VRRP => write!(f, "vrrp"), } } } @@ -59,6 +61,7 @@ impl FromStr for Protocol { "ripv2" => Ok(Protocol::RIPV2), "ripng" => Ok(Protocol::RIPNG), "static" => Ok(Protocol::STATIC), + "vrrp" => Ok(Protocol::VRRP), _ => Err(()), } } @@ -76,6 +79,7 @@ impl ToYang for Protocol { Protocol::RIPV2 => "ietf-rip:ripv2".into(), Protocol::RIPNG => "ietf-rip:ripng".into(), Protocol::STATIC => "ietf-routing:static".into(), + Protocol::VRRP => "holo-vrrp:vrrp".into(), } } } @@ -92,6 +96,7 @@ impl TryFromYang for Protocol { "ietf-rip:ripv2" => Some(Protocol::RIPV2), "ietf-rip:ripng" => Some(Protocol::RIPNG), "ietf-routing:static" => Some(Protocol::STATIC), + "holo-vrrp:vrrp" => Some(Protocol::VRRP), _ => None, } } diff --git a/holo-vrrp/Cargo.toml b/holo-vrrp/Cargo.toml new file mode 100644 index 00000000..cd9cacfc --- /dev/null +++ b/holo-vrrp/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "holo-vrrp" +version.workspace = true +authors.workspace = true # TODO +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 +ipnetwork.workspace = true +itertools.workspace = true +libc.workspace = true +num-derive.workspace = true +num-traits.workspace = true +rand.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_with.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" } + +[dev-dependencies] +holo-vrrp = { path = ".", features = ["testing"] } +holo-protocol = { path = "../holo-protocol", features = ["testing"] } +holo-utils = { path = "../holo-utils", features = ["testing"] } + +[lints] +workspace = true + +[features] +default = [] +testing = [] diff --git a/holo-vrrp/LICENSE b/holo-vrrp/LICENSE new file mode 100644 index 00000000..4481fc10 --- /dev/null +++ b/holo-vrrp/LICENSE @@ -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. diff --git a/holo-vrrp/src/debug.rs b/holo-vrrp/src/debug.rs new file mode 100644 index 00000000..95312777 --- /dev/null +++ b/holo-vrrp/src/debug.rs @@ -0,0 +1,101 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use tracing::{debug, debug_span}; + +use crate::packet::Packet; + +// VRRP debug messages. +#[derive(Debug)] +pub enum Debug<'a> { + InstanceCreate, + InstanceDelete, + InstanceStart, + InstanceStop(InstanceInactiveReason), + // Network + PacketRx(&'a IpAddr, &'a Packet), + PacketTx(&'a IpAddr, &'a Packet), +} + +// Reason why an VRRP instance is inactive. +#[derive(Debug)] +pub enum InstanceInactiveReason { + AdminDown, + MissingRouterId, +} + +// ===== impl Debug ===== + +impl<'a> Debug<'a> { + // Log debug message using the tracing API. + pub(crate) fn log(&self) { + match self { + Debug::InstanceCreate + | Debug::InstanceDelete + | Debug::InstanceStart => { + // Parent span(s): vrrp-instance + debug!("{}", self); + } + Debug::InstanceStop(reason) => { + // Parent span(s): vrrp-instance + debug!(%reason, "{}", self); + } + Debug::PacketRx(src, packet) => { + // Parent span(s): vrrp-instance + debug_span!("network").in_scope(|| { + debug_span!("input").in_scope(|| { + let data = serde_json::to_string(&packet).unwrap(); + debug!(%src, %data, "{}", self); + }) + }) + } + Debug::PacketTx(addr, packet) => { + // Parent span(s): vrrp-instance:network:output + let data = serde_json::to_string(&packet).unwrap(); + debug!(%addr, %data, "{}", self); + } + } + } +} + +impl<'a> std::fmt::Display for Debug<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Debug::InstanceCreate => { + write!(f, "instance created") + } + Debug::InstanceDelete => { + write!(f, "instance deleted") + } + Debug::InstanceStart => { + write!(f, "starting instance") + } + Debug::InstanceStop(..) => { + write!(f, "stopping instance") + } + Debug::PacketRx(..) | Debug::PacketTx(..) => { + write!(f, "packet") + } + } + } +} + +// ===== impl InstanceInactiveReason ===== + +impl std::fmt::Display for InstanceInactiveReason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InstanceInactiveReason::AdminDown => { + write!(f, "administrative status down") + } + InstanceInactiveReason::MissingRouterId => { + write!(f, "missing router-id") + } + } + } +} diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs new file mode 100644 index 00000000..ed08b6b6 --- /dev/null +++ b/holo-vrrp/src/error.rs @@ -0,0 +1,136 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use tracing::warn; + +// BGP errors. +#[derive(Debug)] +pub enum Error { + // I/O errors + IoError(IoError), + // TODO other errors +} + +// BGP I/O errors. +#[derive(Debug)] +pub enum IoError { + SocketError(std::io::Error), + MulticastJoinError(IpAddr, std::io::Error), + MulticastLeaveError(IpAddr, std::io::Error), + RecvError(std::io::Error), + RecvMissingSourceAddr, + SendError(std::io::Error), +} + +// ===== impl Error ===== + +impl Error { + pub(crate) fn log(&self) { + match self { + Error::IoError(error) => { + error.log(); + } + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::IoError(error) => error.fmt(f), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::IoError(error) => Some(error), + _ => None, + } + } +} + +impl From for Error { + fn from(error: IoError) -> Error { + Error::IoError(error) + } +} + +// ===== impl IoError ===== + +impl IoError { + pub(crate) fn log(&self) { + match self { + IoError::SocketError(error) => { + warn!(error = %with_source(error), "{}", self); + } + IoError::MulticastJoinError(addr, error) + | IoError::MulticastLeaveError(addr, error) => { + warn!(?addr, error = %with_source(error), "{}", self); + } + IoError::RecvError(error) | IoError::SendError(error) => { + warn!(error = %with_source(error), "{}", self); + } + IoError::RecvMissingSourceAddr => { + warn!("{}", self); + } + } + } +} + +impl std::fmt::Display for IoError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IoError::SocketError(..) => { + write!(f, "failed to create raw IP socket") + } + IoError::MulticastJoinError(..) => { + write!(f, "failed to join multicast group") + } + IoError::MulticastLeaveError(..) => { + write!(f, "failed to leave multicast group") + } + IoError::RecvError(..) => { + write!(f, "failed to receive IP packet") + } + IoError::RecvMissingSourceAddr => { + write!( + f, + "failed to retrieve source address from received packet" + ) + } + IoError::SendError(..) => { + write!(f, "failed to send IP packet") + } + } + } +} + +impl std::error::Error for IoError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + IoError::SocketError(error) + | IoError::MulticastJoinError(_, error) + | IoError::MulticastLeaveError(_, error) + | IoError::RecvError(error) + | IoError::SendError(error) => Some(error), + _ => None, + } + } +} + +// ===== global functions ===== + +fn with_source(error: E) -> String { + if let Some(source) = error.source() { + format!("{} ({})", error, with_source(source)) + } else { + error.to_string() + } +} diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs new file mode 100644 index 00000000..3508c073 --- /dev/null +++ b/holo-vrrp/src/events.rs @@ -0,0 +1,23 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use crate::error::Error; +use crate::instance::Instance; +use crate::packet::{DecodeResult, Packet}; + +// ===== Network packet receipt ===== + +pub(crate) fn process_packet( + _instance: &mut Instance, + _src: IpAddr, + _packet: DecodeResult, +) -> Result<(), Error> { + // TODO + + Ok(()) +} diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs new file mode 100644 index 00000000..f9507e04 --- /dev/null +++ b/holo-vrrp/src/instance.rs @@ -0,0 +1,269 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; + +use async_trait::async_trait; +use holo_protocol::{ + InstanceChannelsTx, InstanceShared, MessageReceiver, ProtocolInstance, +}; +use holo_utils::ibus::IbusMsg; +use holo_utils::protocol::Protocol; +use holo_utils::{Receiver, Sender, UnboundedReceiver, UnboundedSender}; +use tokio::sync::mpsc; + +use crate::debug::{Debug, InstanceInactiveReason}; +use crate::error::{Error, IoError}; +use crate::northbound::configuration::InstanceCfg; +use crate::tasks::messages::input::NetRxPacketMsg; +use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; +use crate::{events, southbound}; + +#[derive(Debug)] +pub struct Instance { + // Instance name. + pub name: String, + // Instance system data. + pub system: InstanceSys, + // Instance configuration data. + pub config: InstanceCfg, + // Instance state data. + pub state: Option, + // Instance Tx channels. + pub tx: InstanceChannelsTx, + // Shared data. + pub shared: InstanceShared, +} + +#[derive(Debug, Default)] +pub struct InstanceSys { + // System Router ID. + pub router_id: Option, +} + +#[derive(Debug)] +pub struct InstanceState { + // Instance Router ID. + pub router_id: Ipv4Addr, +} + +#[derive(Clone, Debug)] +pub struct ProtocolInputChannelsTx { + // Packet Rx event. + pub net_packet_rx: Sender, +} + +#[derive(Debug)] +pub struct ProtocolInputChannelsRx { + // Packet Rx event. + pub net_packet_rx: Receiver, +} + +// ===== impl Instance ===== + +/* +impl Instance { + // Checks if the instance needs to be started or stopped in response to a + // northbound or southbound event. + // + // Note: Router ID updates are ignored if the instance is already active. + pub(crate) async fn update(&mut self) { + let router_id = self.get_router_id(); + + match self.is_ready(router_id) { + Ok(()) if !self.is_active() => { + self.start(router_id.unwrap()).await; + } + Err(reason) if self.is_active() => { + self.stop(reason); + } + _ => (), + } + } + + // Starts the BGP instance. + async fn start(&mut self, router_id: Ipv4Addr) { + Debug::InstanceStart.log(); + + match InstanceState::new(router_id, &self.tx).await { + Ok(state) => { + // Store instance initial state. + self.state = Some(state); + } + Err(error) => { + Error::InstanceStartError(Box::new(error)).log(); + } + } + } + + // Stops the BGP instance. + fn stop(&mut self, reason: InstanceInactiveReason) { + let Some((mut instance, neighbors)) = self.as_up() else { + return; + }; + + Debug::InstanceStop(reason).log(); + + // Stop neighbors. + let error_code = ErrorCode::Cease; + let error_subcode = CeaseSubcode::AdministrativeShutdown; + for nbr in neighbors.values_mut() { + let msg = NotificationMsg::new(error_code, error_subcode); + nbr.fsm_event(&mut instance, fsm::Event::Stop(Some(msg))); + } + + // Clear instance state. + self.state = None; + } + + // Returns whether the BGP instance is operational. + fn is_active(&self) -> bool { + self.state.is_some() + } + + // Returns whether the instance is ready for BGP operation. + fn is_ready( + &self, + router_id: Option, + ) -> Result<(), InstanceInactiveReason> { + if router_id.is_none() { + return Err(InstanceInactiveReason::MissingRouterId); + } + + Ok(()) + } + + // Retrieves the Router ID from configuration or system information. + // Prioritizes the configured Router ID, using the system's Router ID as a + // fallback. + fn get_router_id(&self) -> Option { + self.config.identifier.or(self.system.router_id) + } +} +*/ + +#[async_trait] +impl ProtocolInstance for Instance { + const PROTOCOL: Protocol = Protocol::VRRP; + + type ProtocolInputMsg = ProtocolInputMsg; + type ProtocolOutputMsg = ProtocolOutputMsg; + type ProtocolInputChannelsTx = ProtocolInputChannelsTx; + type ProtocolInputChannelsRx = ProtocolInputChannelsRx; + + async fn new( + name: String, + shared: InstanceShared, + tx: InstanceChannelsTx, + ) -> Instance { + Debug::InstanceCreate.log(); + + Instance { + name, + system: Default::default(), + config: Default::default(), + state: None, + tx, + shared, + } + } + + async fn init(&mut self) { + // Request information about the system Router ID. + southbound::router_id_query(&self.tx.ibus); + } + + async fn shutdown(mut self) { + // TODO + // Ensure instance is disabled before exiting. + //self.stop(InstanceInactiveReason::AdminDown); + Debug::InstanceDelete.log(); + } + + async fn process_ibus_msg(&mut self, msg: IbusMsg) { + if let Err(error) = process_ibus_msg(self, msg).await { + error.log(); + } + } + + fn process_protocol_msg(&mut self, msg: ProtocolInputMsg) { + if let Err(error) = match msg { + // Received network packet. + ProtocolInputMsg::NetRxPacket(msg) => { + events::process_packet(self, msg.src, msg.packet) + } + } { + error.log(); + } + } + + fn protocol_input_channels( + ) -> (ProtocolInputChannelsTx, ProtocolInputChannelsRx) { + let (net_packet_rxp, net_packet_rxc) = mpsc::channel(4); + + let tx = ProtocolInputChannelsTx { + net_packet_rx: net_packet_rxp, + }; + let rx = ProtocolInputChannelsRx { + net_packet_rx: net_packet_rxc, + }; + + (tx, rx) + } + + #[cfg(feature = "testing")] + fn test_dir() -> String { + format!("{}/tests/conformance", env!("CARGO_MANIFEST_DIR"),) + } +} + +// ===== impl InstanceState ===== + +impl InstanceState {} + +// ===== impl ProtocolInputChannelsRx ===== + +#[async_trait] +impl MessageReceiver for ProtocolInputChannelsRx { + async fn recv(&mut self) -> Option { + tokio::select! { + biased; + msg = self.net_packet_rx.recv() => { + msg.map(ProtocolInputMsg::NetRxPacket) + } + } + } +} + +// ===== helper functions ===== + +async fn process_ibus_msg( + instance: &mut Instance, + msg: IbusMsg, +) -> Result<(), Error> { + match msg { + // Router ID update notification. + IbusMsg::RouterIdUpdate(router_id) => { + southbound::process_router_id_update(instance, router_id).await; + } + // Interface update notification. + IbusMsg::InterfaceUpd(msg) => { + southbound::process_iface_update(instance, msg); + } + // Interface address addition notification. + IbusMsg::InterfaceAddressAdd(msg) => { + southbound::process_addr_add(instance, msg); + } + // Interface address delete notification. + IbusMsg::InterfaceAddressDel(msg) => { + southbound::process_addr_del(instance, msg); + } + // Ignore other events. + _ => {} + } + + Ok(()) +} diff --git a/holo-vrrp/src/lib.rs b/holo-vrrp/src/lib.rs new file mode 100644 index 00000000..c3cc40f0 --- /dev/null +++ b/holo-vrrp/src/lib.rs @@ -0,0 +1,21 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![cfg_attr( + feature = "testing", + allow(dead_code, unused_variables, unused_imports) +)] +#![feature(let_chains)] + +pub mod debug; +pub mod error; +pub mod events; +pub mod instance; +pub mod network; +pub mod northbound; +pub mod packet; +pub mod southbound; +pub mod tasks; diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs new file mode 100644 index 00000000..8151c7f1 --- /dev/null +++ b/holo-vrrp/src/network.rs @@ -0,0 +1,7 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +// TODO diff --git a/holo-vrrp/src/northbound/configuration.rs b/holo-vrrp/src/northbound/configuration.rs new file mode 100644 index 00000000..56b6b28b --- /dev/null +++ b/holo-vrrp/src/northbound/configuration.rs @@ -0,0 +1,177 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![allow(clippy::derivable_impls)] + +use std::collections::{BTreeMap, HashMap}; +use std::net::{IpAddr, Ipv4Addr}; +use std::sync::LazyLock as Lazy; + +use async_trait::async_trait; +use enum_as_inner::EnumAsInner; +use holo_northbound::configuration::{ + Callbacks, CallbacksBuilder, Provider, ValidationCallbacks, + ValidationCallbacksBuilder, +}; +use holo_northbound::yang::interfaces; +use holo_utils::yang::DataNodeRefExt; +use holo_yang::TryFromYang; + +use crate::instance::Instance; + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry { + #[default] + None, +} + +#[derive(Debug)] +pub enum Resource {} + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum Event {} + +pub static VALIDATION_CALLBACKS: Lazy = + Lazy::new(load_validation_callbacks); +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +// ===== configuration structs ===== + +#[derive(Debug)] +pub struct InstanceCfg {} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + CallbacksBuilder::::default() + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::PATH) + .create_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .lookup(|_instance, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::version::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::log_state_change::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::preempt::enabled::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::preempt::hold_time::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::priority::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::accept_mode::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::advertise_interval_sec::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::advertise_interval_centi_sec::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::track::interfaces::interface::PATH) + .create_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .lookup(|_instance, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::track::interfaces::interface::priority_decrement::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::track::networks::network::PATH) + .create_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .lookup(|_instance, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::track::networks::network::priority_decrement::PATH) + .modify_apply(|_instance, _args| { + // TODO: implement me! + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::virtual_ipv4_addresses::virtual_ipv4_address::PATH) + .create_apply(|_instance, _args| { + // TODO: implement me! + }) + .delete_apply(|_instance, _args| { + // TODO: implement me! + }) + .lookup(|_instance, _list_entry, _dnode| { + // TODO: implement me! + todo!(); + }) + .build() +} + +fn load_validation_callbacks() -> ValidationCallbacks { + ValidationCallbacksBuilder::default().build() +} + +// ===== impl Instance ===== + +#[async_trait] +impl Provider for Instance { + type ListEntry = ListEntry; + type Event = Event; + type Resource = Resource; + + fn validation_callbacks() -> Option<&'static ValidationCallbacks> { + Some(&VALIDATION_CALLBACKS) + } + + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } + + async fn process_event(&mut self, event: Event) { + // TODO + } +} + +// ===== configuration defaults ===== + +impl Default for InstanceCfg { + fn default() -> InstanceCfg { + InstanceCfg {} + } +} diff --git a/holo-vrrp/src/northbound/mod.rs b/holo-vrrp/src/northbound/mod.rs new file mode 100644 index 00000000..0110cf58 --- /dev/null +++ b/holo-vrrp/src/northbound/mod.rs @@ -0,0 +1,36 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +pub mod configuration; +pub mod notification; +pub mod state; +pub mod yang; + +use holo_northbound::ProviderBase; +use holo_yang::ToYang; +use tracing::{debug_span, Span}; + +use crate::instance::Instance; + +// ===== impl Instance ===== + +impl ProviderBase for Instance { + fn yang_modules() -> &'static [&'static str] { + &["ietf-vrrp", "holo-vrrp"] + } + + fn top_level_node(&self) -> String { + // TODO + String::new() + } + + fn debug_span(name: &str) -> Span { + debug_span!("vrrp-instance", %name) + } +} + +// No RPC/Actions to implement. +impl holo_northbound::rpc::Provider for Instance {} diff --git a/holo-vrrp/src/northbound/notification.rs b/holo-vrrp/src/northbound/notification.rs new file mode 100644 index 00000000..8151c7f1 --- /dev/null +++ b/holo-vrrp/src/northbound/notification.rs @@ -0,0 +1,7 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +// TODO diff --git a/holo-vrrp/src/northbound/state.rs b/holo-vrrp/src/northbound/state.rs new file mode 100644 index 00000000..f6df591b --- /dev/null +++ b/holo-vrrp/src/northbound/state.rs @@ -0,0 +1,102 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; +use std::sync::{atomic, Arc, LazyLock as Lazy}; + +use enum_as_inner::EnumAsInner; +use holo_northbound::state::{ + Callbacks, CallbacksBuilder, ListEntryKind, Provider, +}; +use holo_northbound::yang::{interfaces, vrrp}; +use holo_utils::option::OptionExt; +use holo_yang::ToYang; + +use crate::instance::Instance; + +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry { + #[default] + None, +} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + CallbacksBuilder::::default() + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::PATH) + .get_iterate(|_instance, _args| { + // TODO: implement me! + None + }) + .get_object(|_instance, _args| { + use interfaces::interface::ipv4::vrrp::vrrp_instance::VrrpInstance; + Box::new(VrrpInstance { + vrid: todo!(), + state: todo!(), + is_owner: todo!(), + last_adv_source: todo!(), + up_datetime: todo!(), + master_down_interval: todo!(), + skew_time: todo!(), + last_event: todo!(), + new_master_reason: todo!(), + }) + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::statistics::PATH) + .get_object(|_instance, _args| { + use interfaces::interface::ipv4::vrrp::vrrp_instance::statistics::Statistics; + Box::new(Statistics { + discontinuity_datetime: todo!(), + master_transitions: todo!(), + advertisement_rcvd: todo!(), + advertisement_sent: todo!(), + priority_zero_pkts_rcvd: todo!(), + priority_zero_pkts_sent: todo!(), + invalid_type_pkts_rcvd: todo!(), + packet_length_errors: todo!(), + }) + }) + .path(vrrp::PATH) + .get_object(|_instance, _args| { + use vrrp::Vrrp; + Box::new(Vrrp { + virtual_routers: todo!(), + interfaces: todo!(), + }) + }) + .path(vrrp::statistics::PATH) + .get_object(|_instance, _args| { + use vrrp::statistics::Statistics; + Box::new(Statistics { + discontinuity_datetime: todo!(), + checksum_errors: todo!(), + version_errors: todo!(), + vrid_errors: todo!(), + ip_ttl_errors: todo!(), + }) + }) + .build() +} + +// ===== impl Instance ===== + +impl Provider for Instance { + // TODO + const STATE_PATH: &'static str = ""; + + type ListEntry<'a> = ListEntry; + + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } +} + +// ===== impl ListEntry ===== + +impl ListEntryKind for ListEntry {} diff --git a/holo-vrrp/src/northbound/yang.rs b/holo-vrrp/src/northbound/yang.rs new file mode 100644 index 00000000..8151c7f1 --- /dev/null +++ b/holo-vrrp/src/northbound/yang.rs @@ -0,0 +1,7 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +// TODO diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs new file mode 100644 index 00000000..e3163ea3 --- /dev/null +++ b/holo-vrrp/src/packet.rs @@ -0,0 +1,75 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +//use bitflags::bitflags; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +//use holo_utils::bytes::TLS_BUF; +//use num_derive::FromPrimitive; +//use num_traits::FromPrimitive; +use serde::{Deserialize, Serialize}; + +// Type aliases. +pub type DecodeResult = Result; + +// +// VRRP Packet Format. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Type | Adver Int | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | IP Address (1) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | . | +// | . | +// | . | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | IP Address (n) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Authentication Data (1) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Authentication Data (2) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct Packet { + // TODO +} + +// VRRP decode errors. +#[derive(Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum DecodeError { + // TODO +} + +// ===== impl Packet ===== + +impl Packet { + // Encodes VRRP packet into a bytes buffer. + pub fn encode(&self) -> BytesMut { + todo!() + } + + // Decodes VRRP packet from a bytes buffer. + pub fn decode(_data: &[u8]) -> Result { + todo!() + } +} + +// ===== impl DecodeError ===== + +impl std::fmt::Display for DecodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl std::error::Error for DecodeError {} diff --git a/holo-vrrp/src/southbound.rs b/holo-vrrp/src/southbound.rs new file mode 100644 index 00000000..524bc84a --- /dev/null +++ b/holo-vrrp/src/southbound.rs @@ -0,0 +1,40 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; + +use holo_utils::ibus::{IbusMsg, IbusSender}; +use holo_utils::southbound::{AddressMsg, InterfaceUpdateMsg}; + +use crate::instance::Instance; + +// ===== global functions ===== + +pub(crate) fn router_id_query(ibus_tx: &IbusSender) { + let _ = ibus_tx.send(IbusMsg::RouterIdQuery); +} + +pub(crate) async fn process_router_id_update( + _instance: &mut Instance, + _router_id: Option, +) { + // TODO +} + +pub(crate) fn process_iface_update( + _instance: &mut Instance, + _msg: InterfaceUpdateMsg, +) { + // TODO +} + +pub(crate) fn process_addr_add(_instance: &mut Instance, _msg: AddressMsg) { + // TODO +} + +pub(crate) fn process_addr_del(_instance: &mut Instance, _msg: AddressMsg) { + // TODO +} diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs new file mode 100644 index 00000000..3fcd6a87 --- /dev/null +++ b/holo-vrrp/src/tasks.rs @@ -0,0 +1,154 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::sync::Arc; + +use holo_utils::socket::{AsyncFd, Socket}; +//use std::time::Duration; +use holo_utils::task::Task; +use holo_utils::{Sender, UnboundedReceiver, UnboundedSender}; +use tracing::{debug_span, Instrument}; + +use crate::debug::Debug; +//use crate::network; + +// +// VRRP tasks diagram: +// +--------------+ +// | northbound | +// +--------------+ +// | ^ +// | | +// northbound_rx (1x) V | (1x) northbound_tx +// +--------------+ +// | | +// net_rx (Nx) -> | instance | -> (Nx) net_tx +// | | +// +--------------+ +// ibus_tx (1x) | ^ (1x) ibus_rx +// | | +// V | +// +--------------+ +// | ibus | +// +--------------+ +// + +// BGP inter-task message types. +pub mod messages { + use std::net::IpAddr; + use std::sync::Arc; + + use serde::{Deserialize, Serialize}; + + use crate::packet::{DecodeError, Packet}; + + // Type aliases. + pub type ProtocolInputMsg = input::ProtocolMsg; + pub type ProtocolOutputMsg = output::ProtocolMsg; + + // Input messages (child task -> main task). + pub mod input { + use super::*; + + #[derive(Debug, Deserialize, Serialize)] + pub enum ProtocolMsg { + NetRxPacket(NetRxPacketMsg), + // TODO + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct NetRxPacketMsg { + pub src: IpAddr, + pub packet: Result, + } + } + + // Output messages (main task -> child task). + pub mod output { + use super::*; + + #[derive(Debug, Serialize)] + pub enum ProtocolMsg { + NetTxPacket(NetTxPacketMsg), + } + + #[derive(Clone, Debug, Serialize)] + pub struct NetTxPacketMsg { + pub packet: Packet, + pub src: IpAddr, + pub dst: IpAddr, + } + } +} + +// ===== VRRP tasks ===== + +// Network Rx task. +pub(crate) fn net_rx( + socket: Arc>, + net_packet_rxp: &Sender, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span1 = debug_span!("network"); + let _span1_guard = span1.enter(); + let span2 = debug_span!("input"); + let _span2_guard = span2.enter(); + + let net_packet_rxp = net_packet_rxp.clone(); + + let span = tracing::span::Span::current(); + Task::spawn( + async move { + let _span_enter = span.enter(); + //let _ = network::read_loop(socket, net_packet_rxp).await; + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + Task::spawn(async move { std::future::pending().await }) + } +} + +// Network Tx task. +#[allow(unused_mut)] +pub(crate) fn net_tx( + socket: Arc>, + mut net_packet_txc: UnboundedReceiver, + #[cfg(feature = "testing")] proto_output_tx: &Sender< + messages::ProtocolOutputMsg, + >, +) -> Task<()> { + #[cfg(not(feature = "testing"))] + { + let span1 = debug_span!("network"); + let _span1_guard = span1.enter(); + let span2 = debug_span!("output"); + let _span2_guard = span2.enter(); + + let span = tracing::span::Span::current(); + Task::spawn( + async move { + let _span_enter = span.enter(); + //network::write_loop(socket, net_packet_txc).await; + } + .in_current_span(), + ) + } + #[cfg(feature = "testing")] + { + let proto_output_tx = proto_output_tx.clone(); + Task::spawn(async move { + // Relay message to the test framework. + while let Some(msg) = net_packet_txc.recv().await { + let msg = messages::ProtocolOutputMsg::NetTxPacket(msg); + let _ = proto_output_tx.send(msg).await; + } + }) + } +} diff --git a/holo-vrrp/tests/conformance/mod.rs b/holo-vrrp/tests/conformance/mod.rs new file mode 100644 index 00000000..8151c7f1 --- /dev/null +++ b/holo-vrrp/tests/conformance/mod.rs @@ -0,0 +1,7 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +// TODO diff --git a/holo-vrrp/tests/mod.rs b/holo-vrrp/tests/mod.rs new file mode 100644 index 00000000..f505cbe8 --- /dev/null +++ b/holo-vrrp/tests/mod.rs @@ -0,0 +1,8 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +mod conformance; +mod packet; diff --git a/holo-vrrp/tests/packet/mod.rs b/holo-vrrp/tests/packet/mod.rs new file mode 100644 index 00000000..8151c7f1 --- /dev/null +++ b/holo-vrrp/tests/packet/mod.rs @@ -0,0 +1,7 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +// TODO diff --git a/holo-yang/modules/augmentations/holo-vrrp.yang b/holo-yang/modules/augmentations/holo-vrrp.yang new file mode 100644 index 00000000..cd269eb7 --- /dev/null +++ b/holo-yang/modules/augmentations/holo-vrrp.yang @@ -0,0 +1,12 @@ +module holo-vrrp { + yang-version 1.1; + namespace "http://holo-routing.org/yang/holo-vrrp"; + prefix holo-vrrp; + + organization + "Holo Routing Stack"; + + description + "This module defines augment statements for the ietf-vrrp + module."; +} diff --git a/holo-yang/modules/deviations/ietf-vrrp-holo-deviations.yang b/holo-yang/modules/deviations/ietf-vrrp-holo-deviations.yang new file mode 100644 index 00000000..51323f6d --- /dev/null +++ b/holo-yang/modules/deviations/ietf-vrrp-holo-deviations.yang @@ -0,0 +1,614 @@ +module ietf-vrrp-holo-deviations { + yang-version 1.1; + namespace "http://holo-routing.org/yang/ietf-vrrp-holo-deviations"; + prefix ietf-vrrp-holo-deviations; + + import ietf-interfaces { + prefix if; + } + + import ietf-ip { + prefix ip; + } + + import ietf-vrrp { + prefix vrrp; + } + + organization + "Holo Routing Stack"; + + description + "This module defines deviation statements for the ietf-vrrp + module."; + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:version" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:log-state-change" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt/vrrp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt/vrrp:hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:priority" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:accept-mode" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-choice" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-choice/vrrp:v2" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-choice/vrrp:v2/vrrp:advertise-interval-sec" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-choice/vrrp:v3" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-choice/vrrp:v3/vrrp:advertise-interval-centi-sec" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface/vrrp:interface" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface/vrrp:priority-decrement" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network/vrrp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network/vrrp:priority-decrement" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv4-addresses" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv4-addresses/vrrp:virtual-ipv4-address" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv4-addresses/vrrp:virtual-ipv4-address/vrrp:ipv4-address" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:state" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:is-owner" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:last-adv-source" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:up-datetime" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:master-down-interval" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:skew-time" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:last-event" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:new-master-reason" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:discontinuity-datetime" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:master-transitions" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:advertisement-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:advertisement-sent" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:priority-zero-pkts-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:priority-zero-pkts-sent" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:invalid-type-pkts-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:packet-length-errors" { + deviate not-supported; + } + */ + + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp" { + deviate not-supported; + } + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:version" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:log-state-change" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt/vrrp:enabled" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:preempt/vrrp:hold-time" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:priority" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:accept-mode" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:advertise-interval-centi-sec" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface/vrrp:interface" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:interfaces/vrrp:interface/vrrp:priority-decrement" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network/vrrp:prefix" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:track/vrrp:networks/vrrp:network/vrrp:priority-decrement" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv6-addresses" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv6-addresses/vrrp:virtual-ipv6-address" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:virtual-ipv6-addresses/vrrp:virtual-ipv6-address/vrrp:ipv6-address" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:state" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:is-owner" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:last-adv-source" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:up-datetime" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:master-down-interval" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:skew-time" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:last-event" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:new-master-reason" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:discontinuity-datetime" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:master-transitions" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:advertisement-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:advertisement-sent" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:priority-zero-pkts-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:priority-zero-pkts-sent" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:invalid-type-pkts-rcvd" { + deviate not-supported; + } + */ + + /* + deviation "/if:interfaces/if:interface/ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:statistics/vrrp:packet-length-errors" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:virtual-routers" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:interfaces" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics/vrrp:discontinuity-datetime" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics/vrrp:checksum-errors" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics/vrrp:version-errors" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics/vrrp:vrid-errors" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp/vrrp:statistics/vrrp:ip-ttl-errors" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp-new-master-event" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp-new-master-event/vrrp:master-ip-address" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp-new-master-event/vrrp:new-master-reason" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp-protocol-error-event" { + deviate not-supported; + } + */ + + /* + deviation "/vrrp:vrrp-protocol-error-event/vrrp:protocol-error-reason" { + deviate not-supported; + } + */ + + deviation "/vrrp:vrrp-virtual-router-error-event" { + deviate not-supported; + } +} diff --git a/holo-yang/modules/ietf/ietf-vrrp@2018-03-13.yang b/holo-yang/modules/ietf/ietf-vrrp@2018-03-13.yang new file mode 100644 index 00000000..462158c1 --- /dev/null +++ b/holo-yang/modules/ietf/ietf-vrrp@2018-03-13.yang @@ -0,0 +1,1064 @@ +module ietf-vrrp { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-vrrp"; + prefix "vrrp"; + + import ietf-inet-types { + prefix "inet"; + } + + import ietf-yang-types { + prefix "yang"; + } + + import ietf-interfaces { + prefix "if"; + } + + import ietf-ip { + prefix "ip"; + } + + organization + "IETF Routing Area Working Group (RTGWG)"; + contact + "WG Web: + WG List: + + Editor: Xufeng Liu + + + Editor: Athanasios Kyparlis + + Editor: Ravi Parikh + + + Editor: Acee Lindem + + + Editor: Mingui Zhang + "; + + description + "This YANG module defines a model for managing Virtual Router + Redundancy Protocol (VRRP) versions 2 and 3. + + Copyright (c) 2018 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Simplified BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8347; see the + RFC itself for full legal notices."; + + revision 2018-03-13 { + description + "Initial revision."; + reference + "RFC 8347: A YANG Data Model for the Virtual Router Redundancy + Protocol (VRRP) + RFC 2787: Definitions of Managed Objects for the Virtual + Router Redundancy Protocol + RFC 3768: Virtual Router Redundancy Protocol (VRRP) + RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6 + RFC 6527: Definitions of Managed Objects for the Virtual + Router Redundancy Protocol Version 3 (VRRPv3)"; + } + + /* + * Features + */ + + feature validate-interval-errors { + description + "This feature indicates that the system validates that the + advertisement interval from advertisement packets received + is the same as the interval configured for the local + VRRP router."; + } + + feature validate-address-list-errors { + description + "This feature indicates that the system validates that + the address list from received packets matches the + locally configured list for the VRRP router."; + } + + /* + * Typedefs + */ + + typedef new-master-reason-type { + type enumeration { + enum not-master { + description + "The virtual router has never transitioned to master + state."; + } + enum priority { + description + "Priority was higher."; + } + enum preempted { + description + "The master was preempted."; + } + enum no-response { + description + "Previous master did not respond."; + } + } + description + "Indicates why the virtual router has transitioned to + master state."; + } // new-master-reason-type + + /* + * Identities + */ + + /* vrrp-event-type identity and its derivatives. */ + identity vrrp-event-type { + description + "Indicates the type of a VRRP protocol event."; + } + identity vrrp-event-none { + base vrrp-event-type; + description + "Indicates a non-meaningful event."; + } + identity vrrp-event-startup { + base vrrp-event-type; + description + "Indicates that a VRRP router has initiated the protocol."; + } + identity vrrp-event-shutdown { + base vrrp-event-type; + description + "Indicates that a VRRP router has closed down the protocol."; + } + identity vrrp-event-higher-priority-backup { + base vrrp-event-type; + description + "Indicates that a backup router has a higher priority than + the current master."; + } + identity vrrp-event-master-timeout { + base vrrp-event-type; + description + "Indicates that the current master has not sent an + advertisement within the limit of master-down-interval."; + } + identity vrrp-event-interface-up { + base vrrp-event-type; + description + "Indicates that the VRRP-enabled interface has become + 'operational up'."; + } + identity vrrp-event-interface-down { + base vrrp-event-type; + description + "Indicates that the VRRP-enabled interface has become + 'operational down'."; + } + identity vrrp-event-no-primary-ip-address { + base vrrp-event-type; + description + "Indicates that the primary IP address on the VRRP-enabled + interface has become unavailable."; + } + identity vrrp-event-primary-ip-address { + base vrrp-event-type; + description + "Indicates that the primary IP address on the VRRP-enabled + interface has become available."; + } + identity vrrp-event-no-virtual-ip-addresses { + base vrrp-event-type; + description + "Indicates that there are no virtual IP addresses on the + virtual router."; + } + identity vrrp-event-virtual-ip-addresses { + base vrrp-event-type; + description + "Indicates that there are virtual IP addresses on the + virtual router."; + } + identity vrrp-event-preempt-hold-timeout { + base vrrp-event-type; + description + "Indicates that the configured preemption hold time has + passed."; + } + identity vrrp-event-lower-priority-master { + base vrrp-event-type; + description + "Indicates that there is a lower-priority VRRP master."; + } + identity vrrp-event-owner-preempt { + base vrrp-event-type; + description + "Indicates that the owner has preempted another router to + become the master."; + } + + /* vrrp-error-global identity and its derivatives. */ + identity vrrp-error-global { + description + "Indicates the type of a VRRP error that occurred + for a packet before it reaches a VRRP router."; + } + identity checksum-error { + base vrrp-error-global; + description + "A packet has been received with an invalid VRRP checksum + value."; + } + identity ip-ttl-error { + base vrrp-error-global; + description + "A packet has been received with IP TTL (Time-To-Live) + not equal to 255."; + } + identity version-error { + base vrrp-error-global; + description + "A packet has been received with an unknown or unsupported + version number."; + } + identity vrid-error { + base vrrp-error-global; + description + "A packet has been received with a Virtual Router Identifier + (VRID) that is not valid for any virtual router on this + router."; + } + + /* vrrp-error-virtual-router identity and its derivatives. */ + identity vrrp-error-virtual-router { + description + "Indicates the type of a VRRP error that occurred + after a packet reaches a VRRP router."; + } + identity address-list-error { + base vrrp-error-virtual-router; + description + "A packet has been received with an address list that + does not match the locally configured address list for + the virtual router."; + } + identity interval-error { + base vrrp-error-virtual-router; + description + "A packet has been received with an advertisement interval + different than the interval configured for the local + virtual router."; + } + identity packet-length-error { + base vrrp-error-virtual-router; + description + "A packet has been received with a packet length less + than the length of the VRRP header."; + } + + /* vrrp-state-type identity and its derivatives. */ + identity vrrp-state-type { + description + "Indicates the state of a virtual router."; + } + identity initialize { + base vrrp-state-type; + description + "Indicates that the virtual router is waiting + for a startup event."; + } + identity backup { + base vrrp-state-type; + description + "Indicates that the virtual router is monitoring the + availability of the master router."; + } + identity master { + base vrrp-state-type; + description + "Indicates that the virtual router is forwarding + packets for IP addresses that are associated with + this virtual router."; + } + + /* vrrp-version identity and its derivatives. */ + identity vrrp-version { + description + "The version of VRRP."; + } + identity vrrp-v2 { + base vrrp-version; + description + "Indicates version 2 of VRRP."; + } + identity vrrp-v3 { + base vrrp-version; + description + "Indicates version 3 of VRRP."; + } + + /* + * Groupings + */ + + grouping vrrp-common-attributes { + description + "Group of VRRP attributes common to versions 2 and 3."; + + leaf vrid { + type uint8 { + range "1..255"; + } + description + "Virtual Router ID (i.e., VRID)."; + } + + leaf version { + type identityref { + base vrrp:vrrp-version; + } + mandatory true; + description + "Version 2 or 3 of VRRP."; + } + + leaf log-state-change { + type boolean; + default "false"; + description + "Generates VRRP state change messages each time the + VRRP instance changes state (from 'up' to 'down' + or 'down' to 'up')."; + } + + container preempt { + description + "Enables a higher-priority VRRP backup router to preempt a + lower-priority VRRP master."; + leaf enabled { + type boolean; + default "true"; + description + "'true' if preemption is enabled."; + } + leaf hold-time { + type uint16; + units seconds; + default 0; + description + "Hold time, in seconds, for which a higher-priority VRRP + backup router must wait before preempting a lower-priority + VRRP master."; + } + } + + leaf priority { + type uint8 { + range "1..254"; + } + default 100; + description + "Configures the VRRP election priority for the backup + virtual router."; + } + + leaf accept-mode { + when "derived-from-or-self(current()/../version, 'vrrp-v3')" { + description + "Applicable only to version 3."; + } + type boolean; + default "false"; + description + "Controls whether a virtual router in master state will + accept packets addressed to the address owner's IPvX address + as its own if it is not the IPvX address owner. The default + is 'false'. Deployments that rely on, for example, pinging + the address owner's IPvX address may wish to configure + accept-mode to 'true'. + + Note: IPv6 Neighbor Solicitations and Neighbor + Advertisements MUST NOT be dropped when accept-mode + is 'false'."; + } + } // vrrp-common-attributes + + grouping vrrp-ipv4-attributes { + description + "Group of VRRP attributes for IPv4."; + + uses vrrp-common-attributes; + + choice advertise-interval-choice { + description + "The options for the advertisement interval at which VRRPv2 + or VRRPv3 advertisements are sent from the specified + interface."; + + case v2 { + when "derived-from-or-self(version, 'vrrp-v2')" { + description + "Applicable only to version 2."; + } + leaf advertise-interval-sec { + type uint8 { + range "1..254"; + } + units seconds; + default 1; + description + "Configures the interval that VRRPv2 advertisements + are sent from the specified interface."; + } + } + case v3 { + when "derived-from-or-self(version, 'vrrp-v3')" { + description + "Applicable only to version 3."; + } + leaf advertise-interval-centi-sec { + type uint16 { + range "1..4095"; + } + units centiseconds; + default 100; + description + "Configures the interval that VRRPv3 advertisements + are sent from the specified interface."; + } + } + } // advertise-interval-choice + + container track { + description + "Enables the specified VRRP instance to track interfaces + or networks."; + container interfaces { + description + "Enables the specified VRRPv2 or VRRPv3 instance to track + interfaces. Interface tracking prevents traffic loss by + detecting the availability of interfaces. The operational + states of other interfaces are associated with the + priority of a VRRP router. When a tracked interface + becomes unavailable (or 'operational down'), the priority + of the VRRP router decrements. When an unavailable + interface becomes available again, the priority of the + VRRP router is incremented by the same amount."; + + list interface { + key "interface"; + description + "Interface to track."; + leaf interface { + type if:interface-ref; + must "/if:interfaces/if:interface[if:name=current()]/" + + "ip:ipv4" { + description + "Interface is IPv4."; + } + description + "Interface to track."; + } + leaf priority-decrement { + type uint8 { + range "1..254"; + } + default 10; + description + "Specifies how much to decrement the priority of the + VRRP instance if the interface goes down."; + } + } // interface + } // interfaces + + container networks { + description + "Enables the VRRPv2 or VRRPv3 router instance to track the + specified networks through their IPv4 network prefixes. + Network tracking prevents traffic loss by detecting + network connectivity failure. The states of + connectivity to some networks are associated with the + priority of a VRRP router. When connectivity to a + tracked network represented by its prefix is lost, the + priority of the VRRP router decrements. When an + unavailable network is again reachable, the priority of + the VRRP router is incremented by the same amount."; + list network { + key "prefix"; + description + "Enables the specified VRRPv2 or VRRPv3 instance to + track an IPv4 network by specifying the prefix of the + IPv4 network."; + + leaf prefix { + type inet:ipv4-prefix; + description + "The IPv4 prefix of the network to track."; + } + + leaf priority-decrement { + type uint8 { + range "1..254"; + } + default 10; + description + "Specifies how much to decrement the priority of the + VRRP router if there is a failure in the IPv4 + network."; + } + } // network + } // networks + } // track + + container virtual-ipv4-addresses { + description + "Configures the virtual IPv4 address for the + VRRP interface."; + + list virtual-ipv4-address { + key "ipv4-address"; + max-elements 16; + description + "Virtual IPv4 addresses for a single VRRP instance. For a + VRRP owner router, the virtual address must match one + of the IPv4 addresses configured on the interface + corresponding to the virtual router."; + + leaf ipv4-address { + type inet:ipv4-address; + description + "An IPv4 address associated with a virtual router."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. Section 1.2"; + } + } // virtual-ipv4-address + } // virtual-ipv4-addresses + } // vrrp-ipv4-attributes + + grouping vrrp-ipv6-attributes { + description + "Group of VRRP attributes for IPv6."; + + uses vrrp-common-attributes; + + leaf advertise-interval-centi-sec { + type uint16 { + range "1..4095"; + } + units centiseconds; + default 100; + description + "Configures the interval that VRRPv3 advertisements + are sent from the specified interface."; + } + + container track { + description + "Enables the specified VRRP instance to track interfaces + or networks."; + container interfaces { + description + "Enables the specified VRRPv2 or VRRPv3 instance to track + interfaces. Interface tracking prevents traffic loss by + detecting the availability of interfaces. The operational + states of other interfaces are associated with the + priority of a VRRP router. When a tracked interface + becomes unavailable (or 'operational down'), the priority + of the VRRP router decrements. When an unavailable + interface becomes available again, the priority of the + VRRP router is incremented by the same amount."; + list interface { + key "interface"; + description + "Interface to track."; + + leaf interface { + type if:interface-ref; + must "/if:interfaces/if:interface[if:name=current()]/" + + "ip:ipv6" { + description + "Interface is IPv6."; + } + description + "Interface to track."; + } + + leaf priority-decrement { + type uint8 { + range "1..254"; + } + default 10; + description + "Specifies how much to decrement the priority of the + VRRP instance if the interface goes down."; + } + } // interface + } // interfaces + + container networks { + description + "Enables the VRRPv2 or VRRPv3 router instance to track the + specified networks through their IPv6 network prefixes. + Network tracking prevents traffic loss by detecting + network connectivity failure. The states of + connectivity to some networks are associated with the + priority of a VRRP router. When connectivity to a + tracked network represented by its prefix is lost, the + priority of the VRRP router decrements. When an + unavailable network is again reachable, the priority of + the VRRP router is incremented by the same amount."; + list network { + key "prefix"; + description + "Enables the specified VRRPv2 or VRRPv3 instance to + track an IPv6 network by specifying the prefix of the + IPv6 network."; + + leaf prefix { + type inet:ipv6-prefix; + description + "The IPv6 prefix of the network to track."; + } + + leaf priority-decrement { + type uint8 { + range "1..254"; + } + default 10; + description + "Specifies how much to decrement the priority of the + VRRP router if there is a failure in the IPv6 + network."; + } + } // network + } // networks + } // track + + container virtual-ipv6-addresses { + description + "Configures the virtual IPv6 address for the + VRRP interface."; + list virtual-ipv6-address { + key "ipv6-address"; + max-elements 2; + description + "Two IPv6 addresses are allowed. The first address must + be a link-local address. The second address can be a + link-local or global address."; + + leaf ipv6-address { + type inet:ipv6-address; + description + "An IPv6 address associated with a virtual router."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. Section 1.3"; + } + } // virtual-ipv6-address + } // virtual-ipv6-addresses + } // vrrp-ipv6-attributes + + grouping vrrp-state-attributes { + description + "Group of VRRP state attributes."; + + leaf state { + type identityref { + base vrrp:vrrp-state-type; + } + config false; + description + "Operational state."; + } + + leaf is-owner { + type boolean; + config false; + description + "Set to 'true' if this virtual router is the owner."; + } + + leaf last-adv-source { + type inet:ip-address; + config false; + description + "Last advertised IPv4/IPv6 source address."; + } + + leaf up-datetime { + type yang:date-and-time; + config false; + description + "The date and time when this virtual router + transitioned out of 'init' state."; + } + + leaf master-down-interval { + type uint32; + units centiseconds; + config false; + description + "Time interval for the backup virtual router to declare + 'master down'."; + } + + leaf skew-time { + type uint32; + units microseconds; + config false; + description + "Calculated based on the priority and advertisement + interval configuration command parameters. See RFC 3768."; + } + + leaf last-event { + type identityref { + base vrrp:vrrp-event-type; + } + config false; + description + "Last reported event."; + } + + leaf new-master-reason { + type new-master-reason-type; + config false; + description + "Indicates why the virtual router has transitioned to + master state."; + } + + container statistics { + config false; + description + "VRRP statistics."; + + leaf discontinuity-datetime { + type yang:date-and-time; + description + "The time on the most recent occasion at which any one or + more of the VRRP statistics counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time that the + local management subsystem re-initialized itself."; + } + + leaf master-transitions { + type yang:counter32; + description + "The total number of times that this virtual router's + state has transitioned to 'master'."; + } + + leaf advertisement-rcvd { + type yang:counter64; + description + "The total number of VRRP advertisements received by + this virtual router."; + } + + leaf advertisement-sent { + type yang:counter64; + description + "The total number of VRRP advertisements sent by + this virtual router."; + } + + leaf interval-errors { + if-feature validate-interval-errors; + type yang:counter64; + description + "The total number of VRRP advertisement packets received + with an advertisement interval different than the + interval configured for the local virtual router."; + } + + leaf priority-zero-pkts-rcvd { + type yang:counter64; + description + "The total number of VRRP packets received by the + virtual router with a priority of 0."; + } + + leaf priority-zero-pkts-sent { + type yang:counter64; + description + "The total number of VRRP packets sent by the + virtual router with a priority of 0."; + } + + leaf invalid-type-pkts-rcvd { + type yang:counter64; + description + "The number of VRRP packets received by the virtual + router with an invalid value in the 'type' field."; + } + leaf address-list-errors { + if-feature validate-address-list-errors; + type yang:counter64; + description + "The total number of packets received with an + address list that does not match the locally + configured address list for the virtual router."; + } + + leaf packet-length-errors { + type yang:counter64; + description + "The total number of packets received with a packet + length less than the length of the VRRP header."; + } + } // statistics + } // vrrp-state-attributes + + grouping vrrp-global-state-attributes { + description + "Group of VRRP global state attributes."; + + leaf virtual-routers { + type uint32; + description + "Number of configured virtual routers."; + } + + leaf interfaces { + type uint32; + description + "Number of interfaces with VRRP configured."; + } + + container statistics { + description + "VRRP global statistics."; + + leaf discontinuity-datetime { + type yang:date-and-time; + description + "The time on the most recent occasion at which any + one or more of checksum-errors, version-errors, + vrid-errors, or ip-ttl-errors suffered a + discontinuity. + + If no such discontinuities have occurred since the last + re-initialization of the local management subsystem, + then this node contains the time that the local management + subsystem re-initialized itself."; + } + + leaf checksum-errors { + type yang:counter64; + description + "The total number of VRRP packets received with an invalid + VRRP checksum value."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. Section 5.2.8"; + } + + leaf version-errors { + type yang:counter64; + description + "The total number of VRRP packets received with an unknown + or unsupported version number."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. Section 5.2.1"; + } + + leaf vrid-errors { + type yang:counter64; + description + "The total number of VRRP packets received with a VRID that + is not valid for any virtual router on this router."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. Section 5.2.3"; + } + + leaf ip-ttl-errors { + type yang:counter64; + description + "The total number of VRRP packets received by the + virtual router with IP TTL (IPv4) or Hop Limit (IPv6) + not equal to 255."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6. + Sections 5.1.1.3 and 5.1.2.3"; + } + } // statistics + } // vrrp-global-state-attributes + + /* + * Configuration data and operational state data nodes + */ + + augment "/if:interfaces/if:interface/ip:ipv4" { + description + "Augments IPv4 interface."; + + container vrrp { + description + "Configures VRRP version 2 or 3 for IPv4."; + + list vrrp-instance { + key "vrid"; + description + "Defines a virtual router, identified by a VRID, within the + IPv4 address space."; + + uses vrrp-ipv4-attributes; + uses vrrp-state-attributes; + } + } + } // augments ipv4 + + augment "/if:interfaces/if:interface/ip:ipv6" { + description + "Augments IPv6 interface."; + + container vrrp { + description + "Configures VRRP version 3 for IPv6."; + + list vrrp-instance { + must "derived-from-or-self(version, 'vrrp-v3')" { + description + "IPv6 is only supported by version 3."; + } + key "vrid"; + description + "Defines a virtual router, identified by a VRID, within the + IPv6 address space."; + + uses vrrp-ipv6-attributes; + uses vrrp-state-attributes; + } + } + } // augments ipv6 + + container vrrp { + config false; + description + "VRRP data at the global level."; + + uses vrrp-global-state-attributes; + } + + /* + * Notifications + */ + + notification vrrp-new-master-event { + description + "Notification event for the election of a new VRRP master."; + leaf master-ip-address { + type inet:ip-address; + mandatory true; + description + "IPv4 or IPv6 address of the new master."; + } + leaf new-master-reason { + type new-master-reason-type; + mandatory true; + description + "Indicates why the virtual router has transitioned to + master state."; + } + } + + notification vrrp-protocol-error-event { + description + "Notification event for a VRRP protocol error."; + leaf protocol-error-reason { + type identityref { + base vrrp:vrrp-error-global; + } + mandatory true; + description + "Indicates the reason for the protocol error."; + } + } + + notification vrrp-virtual-router-error-event { + description + "Notification event for an error that happened on a + virtual router."; + leaf interface { + type if:interface-ref; + mandatory true; + description + "Indicates the interface on which the event has occurred."; + } + + choice ip-version { + mandatory true; + description + "The error may have happened on either an IPv4 virtual + router or an IPv6 virtual router. The information + related to a specific IP version is provided by one of + the following cases."; + case ipv4 { + description + "IPv4."; + container ipv4 { + description + "Error information for IPv4."; + leaf vrid { + type leafref { + path "/if:interfaces/if:interface" + + "[if:name = current()/../../vrrp:interface]/" + + "ip:ipv4/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid"; + } + mandatory true; + description + "Indicates the virtual router on which the event has + occurred."; + } + } + } + case ipv6 { + description + "IPv6."; + container ipv6 { + description + "Error information for IPv6."; + leaf vrid { + type leafref { + path "/if:interfaces/if:interface" + + "[if:name = current()/../../vrrp:interface]/" + + "ip:ipv6/vrrp:vrrp/vrrp:vrrp-instance/vrrp:vrid"; + } + mandatory true; + description + "Indicates the virtual router on which the event has + occurred."; + } + } + } + } + + leaf virtual-router-error-reason { + type identityref { + base vrrp:vrrp-error-virtual-router; + } + mandatory true; + description + "Indicates the reason for the virtual router error."; + } + } +} diff --git a/holo-yang/src/lib.rs b/holo-yang/src/lib.rs index abcb3aaf..7a41be7e 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -119,6 +119,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { include_str!("../modules/ietf/ietf-tcp@2022-09-11.yang"), EmbeddedModuleKey::new("ietf-tcp-common", Some("2023-04-17"), None, None) => include_str!("../modules/ietf/ietf-tcp-common@2023-04-17.yang"), + EmbeddedModuleKey::new("ietf-vrrp", Some("2018-03-13"), None, None) => + include_str!("../modules/ietf/ietf-vrrp@2018-03-13.yang"), // IETF Holo augmentations EmbeddedModuleKey::new("holo-bgp", None, None, None) => include_str!("../modules/augmentations/holo-bgp.yang"), @@ -126,6 +128,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { include_str!("../modules/augmentations/holo-ospf.yang"), EmbeddedModuleKey::new("holo-ospf-dev", None, None, None) => include_str!("../modules/augmentations/holo-ospf-dev.yang"), + EmbeddedModuleKey::new("holo-vrrp", None, None, None) => + include_str!("../modules/augmentations/holo-vrrp.yang"), // IETF Holo deviations EmbeddedModuleKey::new("ietf-bgp-holo-deviations", None, None, None) => include_str!("../modules/deviations/ietf-bgp-holo-deviations.yang"), @@ -159,6 +163,8 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { include_str!("../modules/deviations/ietf-routing-policy-holo-deviations.yang"), EmbeddedModuleKey::new("ietf-segment-routing-mpls-holo-deviations", None, None, None) => include_str!("../modules/deviations/ietf-segment-routing-mpls-holo-deviations.yang"), + EmbeddedModuleKey::new("ietf-vrrp-holo-deviations", None, None, None) => + include_str!("../modules/deviations/ietf-vrrp-holo-deviations.yang"), } }); @@ -202,10 +208,12 @@ pub static YANG_IMPLEMENTED_MODULES: Lazy> = "ietf-ospfv3-extended-lsa", "ietf-rip", "ietf-tcp", + "ietf-vrrp", // IETF Holo augmentations "holo-bgp", "holo-ospf", "holo-ospf-dev", + "holo-vrrp", ] });