From 3cff08d4d6fc851a04329f56c82d046b500a6cf5 Mon Sep 17 00:00:00 2001 From: weqe Date: Tue, 25 Jun 2024 15:28:01 -0300 Subject: [PATCH 01/22] VRRP WIP --- Cargo.toml | 1 + README.md | 5 + holo-daemon/Cargo.toml | 3 + holo-daemon/src/northbound/core.rs | 17 +- holo-daemon/src/northbound/yang.rs | 5 + holo-interface/Cargo.toml | 7 + holo-interface/src/interface.rs | 4 + holo-interface/src/lib.rs | 5 + .../src/northbound/configuration.rs | 76 +- holo-interface/src/northbound/mod.rs | 16 + holo-interface/src/northbound/state.rs | 45 +- holo-routing/src/lib.rs | 11 +- holo-tools/yang-coverage.sh | 3 +- holo-utils/src/protocol.rs | 5 + holo-vrrp/Cargo.toml | 44 + holo-vrrp/LICENSE | 19 + holo-vrrp/src/debug.rs | 65 + holo-vrrp/src/error.rs | 164 +++ holo-vrrp/src/events.rs | 23 + holo-vrrp/src/instance.rs | 128 ++ holo-vrrp/src/interface.rs | 229 ++++ holo-vrrp/src/lib.rs | 22 + holo-vrrp/src/network.rs | 104 ++ holo-vrrp/src/northbound/configuration.rs | 200 ++++ holo-vrrp/src/northbound/mod.rs | 34 + holo-vrrp/src/northbound/notification.rs | 29 + holo-vrrp/src/northbound/state.rs | 92 ++ holo-vrrp/src/northbound/yang.rs | 73 ++ holo-vrrp/src/packet.rs | 417 +++++++ holo-vrrp/src/southbound.rs | 50 + holo-vrrp/src/tasks.rs | 159 +++ holo-vrrp/tests/conformance/mod.rs | 7 + holo-vrrp/tests/mod.rs | 8 + holo-vrrp/tests/packet/ipv4.rs | 111 ++ holo-vrrp/tests/packet/mod.rs | 8 + holo-vrrp/tests/packet/vrrp.rs | 103 ++ .../modules/augmentations/holo-vrrp.yang | 26 + .../deviations/ietf-vrrp-holo-deviations.yang | 611 ++++++++++ .../modules/ietf/ietf-vrrp@2018-03-13.yang | 1064 +++++++++++++++++ holo-yang/src/lib.rs | 11 + 40 files changed, 3978 insertions(+), 26 deletions(-) create mode 100644 holo-vrrp/Cargo.toml create mode 100644 holo-vrrp/LICENSE create mode 100644 holo-vrrp/src/debug.rs create mode 100644 holo-vrrp/src/error.rs create mode 100644 holo-vrrp/src/events.rs create mode 100644 holo-vrrp/src/instance.rs create mode 100644 holo-vrrp/src/interface.rs create mode 100644 holo-vrrp/src/lib.rs create mode 100644 holo-vrrp/src/network.rs create mode 100644 holo-vrrp/src/northbound/configuration.rs create mode 100644 holo-vrrp/src/northbound/mod.rs create mode 100644 holo-vrrp/src/northbound/notification.rs create mode 100644 holo-vrrp/src/northbound/state.rs create mode 100644 holo-vrrp/src/northbound/yang.rs create mode 100644 holo-vrrp/src/packet.rs create mode 100644 holo-vrrp/src/southbound.rs create mode 100644 holo-vrrp/src/tasks.rs create mode 100644 holo-vrrp/tests/conformance/mod.rs create mode 100644 holo-vrrp/tests/mod.rs create mode 100644 holo-vrrp/tests/packet/ipv4.rs create mode 100644 holo-vrrp/tests/packet/mod.rs create mode 100644 holo-vrrp/tests/packet/vrrp.rs create mode 100644 holo-yang/modules/augmentations/holo-vrrp.yang create mode 100644 holo-yang/modules/deviations/ietf-vrrp-holo-deviations.yang create mode 100644 holo-yang/modules/ietf/ietf-vrrp@2018-03-13.yang 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..3c54be23 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 | 25.53% | 40.00% | - | 25.00% | [31.73%](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..5e54c346 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-interface/vrrp"] # Other features io_uring = ["tokio-uring"] diff --git a/holo-daemon/src/northbound/core.rs b/holo-daemon/src/northbound/core.rs index bb0a7342..04c326ca 100644 --- a/holo-daemon/src/northbound/core.rs +++ b/holo-daemon/src/northbound/core.rs @@ -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; @@ -632,6 +633,12 @@ fn start_providers( let ibus_rx_keychain = ibus_tx.subscribe(); let ibus_rx_policy = ibus_rx; + let shared = InstanceShared { + db: Some(db), + event_recorder_config: Some(config.event_recorder.clone()), + ..Default::default() + }; + // Start holo-interface. #[cfg(feature = "interface")] { @@ -639,6 +646,7 @@ fn start_providers( provider_tx.clone(), ibus_tx.clone(), ibus_rx_interface, + shared.clone(), ); providers.push(daemon_tx); } @@ -668,13 +676,8 @@ fn start_providers( // Start holo-routing. #[cfg(feature = "routing")] { - let daemon_tx = holo_routing::start( - provider_tx, - ibus_tx, - ibus_rx_routing, - db, - config.event_recorder.clone(), - ); + let daemon_tx = + holo_routing::start(provider_tx, ibus_tx, ibus_rx_routing, shared); providers.push(daemon_tx); } diff --git a/holo-daemon/src/northbound/yang.rs b/holo-daemon/src/northbound/yang.rs index d16a691f..42428cba 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::interface::Interface; + 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-interface/Cargo.toml b/holo-interface/Cargo.toml index 46509c10..623974a2 100644 --- a/holo-interface/Cargo.toml +++ b/holo-interface/Cargo.toml @@ -17,14 +17,21 @@ ipnetwork.workspace = true netlink-packet-route.workspace = true netlink-packet-core.workspace = true netlink-sys.workspace = true +regex.workspace = true rtnetlink.workspace = true tokio.workspace = true tracing.workspace = true yang2.workspace = true holo-northbound = { path = "../holo-northbound" } +holo-protocol = { path = "../holo-protocol" } holo-utils = { path = "../holo-utils" } holo-yang = { path = "../holo-yang" } +holo-vrrp = { path = "../holo-vrrp", optional = true } + [lints] workspace = true + +[features] +vrrp = ["holo-vrrp"] diff --git a/holo-interface/src/interface.rs b/holo-interface/src/interface.rs index 386634c6..d03284ad 100644 --- a/holo-interface/src/interface.rs +++ b/holo-interface/src/interface.rs @@ -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}; @@ -38,6 +39,7 @@ pub struct Interface { pub flags: InterfaceFlags, pub addresses: BTreeMap, pub owner: Owner, + pub vrrp: Option, } #[derive(Debug)] @@ -123,6 +125,7 @@ impl Interfaces { flags: InterfaceFlags::default(), addresses: Default::default(), owner: Owner::CONFIG, + vrrp: None, }; let iface_idx = self.arena.insert(iface); @@ -214,6 +217,7 @@ impl Interfaces { flags, addresses: Default::default(), owner: Owner::SYSTEM, + vrrp: None, }; // Notify protocol instances about the interface update. diff --git a/holo-interface/src/lib.rs b/holo-interface/src/lib.rs index f431687e..1045811c 100644 --- a/holo-interface/src/lib.rs +++ b/holo-interface/src/lib.rs @@ -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; @@ -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. @@ -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); @@ -83,6 +87,7 @@ pub fn start( let mut master = Master { nb_tx, ibus_tx, + shared, netlink_handle, interfaces: Default::default(), }; diff --git a/holo-interface/src/northbound/configuration.rs b/holo-interface/src/northbound/configuration.rs index 8acdaec9..62825c75 100644 --- a/holo-interface/src/northbound/configuration.rs +++ b/holo-interface/src/northbound/configuration.rs @@ -4,21 +4,24 @@ // SPDX-License-Identifier: MIT // -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::net::IpAddr; use std::sync::LazyLock as Lazy; use async_trait::async_trait; use enum_as_inner::EnumAsInner; use holo_northbound::configuration::{ - self, Callbacks, CallbacksBuilder, Provider, ValidationCallbacks, - ValidationCallbacksBuilder, + self, Callbacks, CallbacksBuilder, ConfigChanges, Provider, + ValidationCallbacks, ValidationCallbacksBuilder, }; use holo_northbound::yang::interfaces; +use holo_northbound::{CallbackKey, NbDaemonSender}; +use holo_protocol::spawn_protocol_task; use holo_utils::yang::DataNodeRefExt; use ipnetwork::IpNetwork; use crate::interface::Owner; +use crate::northbound::REGEX_VRRP; use crate::{netlink, Master}; static VALIDATION_CALLBACKS: Lazy = @@ -45,6 +48,7 @@ pub enum Event { VlanCreate(String, u16), AddressInstall(String, IpAddr, u8), AddressUninstall(String, IpAddr, u8), + VrrpStart(String), } // ===== configuration structs ===== @@ -66,7 +70,10 @@ fn load_callbacks() -> Callbacks { .create_apply(|master, args| { let ifname = args.dnode.get_string_relative("./name").unwrap(); - master.interfaces.add(ifname); + master.interfaces.add(ifname.clone()); + + let event_queue = args.event_queue; + event_queue.insert(Event::VrrpStart(ifname)); }) .delete_apply(|_master, args| { let ifname = args.list_entry.into_interface().unwrap(); @@ -302,6 +309,48 @@ impl Provider for Master { Some(&CALLBACKS) } + fn nested_callbacks() -> Option> { + let keys: Vec> = vec![ + #[cfg(feature = "vrrp")] + holo_vrrp::northbound::configuration::CALLBACKS.keys(), + ]; + + Some(keys.concat()) + } + + fn relay_changes( + &self, + changes: ConfigChanges, + ) -> Vec<(ConfigChanges, NbDaemonSender)> { + // Create hash table that maps changes to the appropriate child + // instances. + let mut changes_map: HashMap = HashMap::new(); + for change in changes { + // HACK: parse interface name from VRRP configuration changes. + let caps = REGEX_VRRP.captures(&change.1).unwrap(); + let ifname = caps.get(1).unwrap().as_str().to_owned(); + + // Move configuration change to the appropriate interface bucket. + changes_map.entry(ifname).or_default().push(change); + } + changes_map + .into_iter() + .filter_map(|(ifname, changes)| { + self.interfaces + .get_by_name(&ifname) + .and_then(|iface| iface.vrrp.clone()) + .map(|nb_tx| (changes, nb_tx)) + }) + .collect::>() + } + + fn relay_validation(&self) -> Vec { + self.interfaces + .iter() + .filter_map(|iface| iface.vrrp.clone()) + .collect() + } + async fn process_event(&mut self, event: Event) { match event { Event::InterfaceDelete(ifname) => { @@ -377,6 +426,25 @@ impl Provider for Master { .await; } } + Event::VrrpStart(ifname) => { + #[cfg(feature = "vrrp")] + { + if let Some(iface) = + self.interfaces.get_mut_by_name(&ifname) + { + let vrrp = spawn_protocol_task::< + holo_vrrp::interface::Interface, + >( + ifname, + &self.nb_tx, + &self.ibus_tx, + Default::default(), + self.shared.clone(), + ); + iface.vrrp = Some(vrrp); + } + } + } } } } diff --git a/holo-interface/src/northbound/mod.rs b/holo-interface/src/northbound/mod.rs index dcb2034d..9d4572ff 100644 --- a/holo-interface/src/northbound/mod.rs +++ b/holo-interface/src/northbound/mod.rs @@ -7,8 +7,12 @@ pub mod configuration; pub mod state; +use std::sync::LazyLock as Lazy; + use holo_northbound::rpc::Provider; +use holo_northbound::yang::interfaces; use holo_northbound::ProviderBase; +use regex::Regex; use tracing::{debug_span, Span}; use crate::Master; @@ -36,3 +40,15 @@ impl ProviderBase for Master { // No RPC/Actions to implement. impl Provider for Master {} + +// ===== regular expressions ===== + +// Matches on the protocol type and instance name of a YANG path. +static REGEX_VRRP_STR: Lazy = Lazy::new(|| { + format!( + r"{}\[name='(.+?)'\]/ietf-ip:ipv4/ietf-vrrp:vrrp/*", + interfaces::interface::PATH + ) +}); +pub static REGEX_VRRP: Lazy = + Lazy::new(|| Regex::new(®EX_VRRP_STR).unwrap()); diff --git a/holo-interface/src/northbound/state.rs b/holo-interface/src/northbound/state.rs index 1b9086cd..91e594fb 100644 --- a/holo-interface/src/northbound/state.rs +++ b/holo-interface/src/northbound/state.rs @@ -4,26 +4,45 @@ // SPDX-License-Identifier: MIT // +use std::borrow::Cow; use std::sync::LazyLock as Lazy; +use enum_as_inner::EnumAsInner; use holo_northbound::state::{ Callbacks, CallbacksBuilder, ListEntryKind, Provider, }; +use holo_northbound::yang::interfaces; +use holo_northbound::{CallbackKey, NbDaemonSender}; +use crate::interface::Interface; use crate::Master; pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); -#[derive(Debug, Default)] -pub enum ListEntry { +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry<'a> { #[default] None, + Interface(&'a Interface), } // ===== callbacks ===== fn load_callbacks() -> Callbacks { - CallbacksBuilder::default().build() + CallbacksBuilder::::default() + .path(interfaces::interface::PATH) + .get_iterate(|master, _args| { + let iter = master.interfaces.iter().map(ListEntry::Interface); + Some(Box::new(iter)) + }) + .get_object(|_master, args| { + use interfaces::interface::Interface; + let iface = args.list_entry.as_interface().unwrap(); + Box::new(Interface { + name: Cow::Borrowed(&iface.name), + }) + }) + .build() } // ===== impl Master ===== @@ -31,13 +50,29 @@ fn load_callbacks() -> Callbacks { impl Provider for Master { const STATE_PATH: &'static str = "/ietf-interfaces:interfaces"; - type ListEntry<'a> = ListEntry; + type ListEntry<'a> = ListEntry<'a>; fn callbacks() -> Option<&'static Callbacks> { Some(&CALLBACKS) } + + fn nested_callbacks() -> Option> { + let keys: Vec> = vec![ + #[cfg(feature = "vrrp")] + holo_vrrp::northbound::state::CALLBACKS.keys(), + ]; + + Some(keys.concat()) + } } // ===== impl ListEntry ===== -impl ListEntryKind for ListEntry {} +impl<'a> ListEntryKind for ListEntry<'a> { + fn child_task(&self) -> Option { + match self { + ListEntry::Interface(iface) => iface.vrrp.clone(), + _ => None, + } + } +} diff --git a/holo-routing/src/lib.rs b/holo-routing/src/lib.rs index 6e4be6d0..ea4a8acb 100644 --- a/holo-routing/src/lib.rs +++ b/holo-routing/src/lib.rs @@ -18,12 +18,11 @@ use holo_northbound::{ process_northbound_msg, NbDaemonReceiver, NbDaemonSender, NbProviderSender, ProviderBase, }; -use holo_protocol::{event_recorder, InstanceShared}; +use holo_protocol::InstanceShared; use holo_utils::ibus::{IbusReceiver, IbusSender}; use holo_utils::protocol::Protocol; use holo_utils::southbound::InterfaceFlags; use holo_utils::sr::SrCfg; -use holo_utils::Database; use ipnetwork::IpNetwork; use tokio::sync::mpsc; use tracing::Instrument; @@ -109,17 +108,11 @@ pub fn start( nb_tx: NbProviderSender, ibus_tx: IbusSender, ibus_rx: IbusReceiver, - db: Database, - event_recorder_config: event_recorder::Config, + shared: InstanceShared, ) -> NbDaemonSender { let (nb_daemon_tx, nb_daemon_rx) = mpsc::channel(4); tokio::spawn(async move { - let shared = InstanceShared { - db: Some(db), - event_recorder_config: Some(event_recorder_config), - ..Default::default() - }; let mut master = Master { nb_tx, ibus_tx, 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..9bcb6572 --- /dev/null +++ b/holo-vrrp/Cargo.toml @@ -0,0 +1,44 @@ +[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 +socket2.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..6c48b3de --- /dev/null +++ b/holo-vrrp/src/debug.rs @@ -0,0 +1,65 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use tracing::{debug, debug_span}; + +use crate::packet::VrrpPacket; + +// VRRP debug messages. +#[derive(Debug)] +pub enum Debug<'a> { + InstanceCreate, + InstanceDelete, + // Network + PacketRx(&'a IpAddr, &'a VrrpPacket), + PacketTx(&'a IpAddr, &'a VrrpPacket), +} + +// ===== impl Debug ===== + +impl<'a> Debug<'a> { + // Log debug message using the tracing API. + pub(crate) fn log(&self) { + match self { + Debug::InstanceCreate | Debug::InstanceDelete => { + // Parent span(s): vrrp-instance + debug!("{}", 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::PacketRx(..) | Debug::PacketTx(..) => { + write!(f, "packet") + } + } + } +} diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs new file mode 100644 index 00000000..52b1d167 --- /dev/null +++ b/holo-vrrp/src/error.rs @@ -0,0 +1,164 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; + +use tracing::{warn, warn_span}; + +// VRRP errors. +#[derive(Debug)] +pub enum Error { + // I/O errors + IoError(IoError), + + // other errors + VridError, + AddressListError(Vec, Vec), + IntervalError, +} + +// VRRP 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(); + } + Error::VridError => { + warn_span!("virtual_router").in_scope(|| warn!("{}", self)); + } + Error::AddressListError(_, _) => { + warn_span!("virtual_router").in_scope(|| warn!("{}", self)); + } + Error::IntervalError => { + warn_span!("virtual_router").in_scope(|| warn!("{}", self)); + } + } + } +} + +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), + Error::VridError => { + write!( + f, + "virtual router id(VRID) not matching locally configured" + ) + } + Error::AddressListError(..) => { + write!( + f, + "received address list not matching local address list" + ) + } + Error::IntervalError => { + write!(f, "received advert interval not matching local configured advert interval") + } + } + } +} + +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..a8ddd125 --- /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::interface::Interface; +use crate::packet::{DecodeResult, VrrpPacket}; + +// ===== Network packet receipt ===== + +pub(crate) fn process_packet( + _interface: &mut Interface, + _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..24f6c52f --- /dev/null +++ b/holo-vrrp/src/instance.rs @@ -0,0 +1,128 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; + +use chrono::{DateTime, Utc}; + +use crate::northbound::configuration::InstanceCfg; + +#[derive(Debug)] +pub struct Instance { + // Instance configuration data. + pub config: InstanceCfg, + // Instance state data. + pub state: InstanceState, +} + +#[derive(Debug)] +pub struct InstanceState { + pub state: State, + pub last_adv_src: Option, + pub up_time: Option>, + pub last_event: Event, + pub new_master_reason: MasterReason, + // TODO: interval/timer tasks + pub statistics: Statistics, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum State { + Initialize, + Backup, + Master, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Event { + None, + Startup, + Shutdown, + HigherPriorityBackup, + MasterTimeout, + InterfaceUp, + InterfaceDown, + NoPrimaryIpAddress, + PrimaryIpAddress, + NoVirtualIpAddresses, + VirtualIpAddresses, + PreemptHoldTimeout, + LowerPriorityMaster, + OwnerPreempt, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum MasterReason { + NotMaster, + Priority, + Preempted, + NoResponse, +} + +#[derive(Debug)] +pub struct Statistics { + pub discontinuity_time: DateTime, + pub master_transitions: u32, + pub adv_rcvd: u64, + pub adv_sent: u64, + pub interval_errors: u64, + pub priority_zero_pkts_rcvd: u64, + pub priority_zero_pkts_sent: u64, + pub invalid_type_pkts_rcvd: u64, + pub pkt_length_errors: u64, + pub checksum_errors: u64, + pub version_errors: u64, + pub vrid_errors: u64, + pub ip_ttl_errors: u64, +} + +// ===== impl Instance ===== + +impl Instance { + pub(crate) fn new() -> Self { + Instance { + config: Default::default(), + state: InstanceState::new(), + } + } +} + +// ===== impl InstanceState ===== + +impl InstanceState { + pub(crate) fn new() -> Self { + InstanceState { + state: State::Initialize, + last_adv_src: None, + up_time: None, + last_event: Event::None, + new_master_reason: MasterReason::NotMaster, + statistics: Default::default(), + } + } +} + +// ===== impl Statistics ===== + +impl Default for Statistics { + fn default() -> Self { + Statistics { + discontinuity_time: Utc::now(), + master_transitions: 0, + adv_rcvd: 0, + adv_sent: 0, + interval_errors: 0, + priority_zero_pkts_rcvd: 0, + priority_zero_pkts_sent: 0, + invalid_type_pkts_rcvd: 0, + pkt_length_errors: 0, + checksum_errors: 0, + version_errors: 0, + vrid_errors: 0, + ip_ttl_errors: 0, + } + } +} diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs new file mode 100644 index 00000000..8abdca92 --- /dev/null +++ b/holo-vrrp/src/interface.rs @@ -0,0 +1,229 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::collections::{BTreeMap, BTreeSet}; +use std::sync::Arc; + +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::socket::{AsyncFd, Socket}; +use holo_utils::southbound::InterfaceFlags; +use holo_utils::task::Task; +use holo_utils::{Receiver, Sender, UnboundedSender}; +use ipnetwork::Ipv4Network; +use tokio::sync::mpsc; + +use crate::error::{Error, IoError}; +use crate::instance::Instance; +use crate::tasks::messages::input::NetRxPacketMsg; +use crate::tasks::messages::output::NetTxPacketMsg; +use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; +use crate::{events, network, southbound, tasks}; + +#[derive(Debug)] +pub struct Interface { + // Interface name. + pub name: String, + // Interface system data. + pub system: InterfaceSys, + // Interface raw sockets and Tx/Rx tasks. + pub net: InterfaceNet, + // Interface VRRP instances. + pub instances: BTreeMap, + // Tx channels. + pub tx: InstanceChannelsTx, + // Shared data. + pub shared: InstanceShared, +} + +#[derive(Debug, Default)] +pub struct InterfaceSys { + // Interface flags. + pub flags: InterfaceFlags, + // Interface index. + pub ifindex: Option, + // Interface IPv4 addresses. + pub addresses: BTreeSet, +} + +#[derive(Debug)] +pub struct InterfaceNet { + // Raw sockets. + pub socket_vrrp: Arc>, + pub socket_arp: Arc>, + // Network Tx/Rx tasks. + _net_tx_task: Task<()>, + _net_rx_task: Task<()>, + // Network Tx output channel. + pub net_tx_packetp: UnboundedSender, +} + +#[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 Interface ===== + +#[async_trait] +impl ProtocolInstance for Interface { + 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, + ) -> Interface { + // TODO: proper error handling + let net = InterfaceNet::new(&name, &tx) + .expect("Failed to initialize VRRP network tasks"); + Interface { + name, + system: Default::default(), + net, + instances: Default::default(), + tx, + shared, + } + } + + 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 InterfaceNet ===== + +impl InterfaceNet { + fn new( + ifname: &str, + instance_channels_tx: &InstanceChannelsTx, + ) -> Result { + // Create raw sockets. + let socket_vrrp = network::socket_vrrp(ifname) + .map_err(IoError::SocketError) + .and_then(|socket| { + AsyncFd::new(socket).map_err(IoError::SocketError) + }) + .map(Arc::new)?; + let socket_arp = network::socket_arp(ifname) + .map_err(IoError::SocketError) + .and_then(|socket| { + AsyncFd::new(socket).map_err(IoError::SocketError) + }) + .map(Arc::new)?; + + // Start network Tx/Rx tasks. + let (net_tx_packetp, net_tx_packetc) = mpsc::unbounded_channel(); + let net_tx_task = tasks::net_tx( + socket_vrrp.clone(), + socket_arp.clone(), + net_tx_packetc, + #[cfg(feature = "testing")] + &instance_channels_tx.protocol_output, + ); + let net_rx_task = tasks::net_rx( + socket_vrrp.clone(), + &instance_channels_tx.protocol_input.net_packet_rx, + ); + + Ok(InterfaceNet { + socket_vrrp, + socket_arp, + _net_tx_task: net_tx_task, + _net_rx_task: net_rx_task, + net_tx_packetp, + }) + } +} + +// ===== 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( + interface: &mut Interface, + msg: IbusMsg, +) -> Result<(), Error> { + match msg { + // Interface update notification. + IbusMsg::InterfaceUpd(msg) => { + southbound::process_iface_update(interface, msg); + } + // Interface address addition notification. + IbusMsg::InterfaceAddressAdd(msg) => { + southbound::process_addr_add(interface, msg); + } + // Interface address delete notification. + IbusMsg::InterfaceAddressDel(msg) => { + southbound::process_addr_del(interface, 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..d4acf624 --- /dev/null +++ b/holo-vrrp/src/lib.rs @@ -0,0 +1,22 @@ +// +// 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 interface; +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..5efc234f --- /dev/null +++ b/holo-vrrp/src/network.rs @@ -0,0 +1,104 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::IpAddr; +use std::sync::Arc; + +use holo_utils::socket::{AsyncFd, Socket}; +use holo_utils::{capabilities, Sender, UnboundedReceiver}; +use libc::ETH_P_ALL; +use socket2::{Domain, Protocol, Type}; +use tokio::sync::mpsc::error::SendError; + +use crate::error::IoError; +use crate::packet::VrrpPacket; +use crate::tasks::messages::input::NetRxPacketMsg; +use crate::tasks::messages::output::NetTxPacketMsg; + +pub(crate) fn socket_vrrp(_ifname: &str) -> Result { + #[cfg(not(feature = "testing"))] + { + // TODO + let socket = capabilities::raise(|| { + Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) + })?; + socket.set_broadcast(true)?; + Ok(socket) + } + #[cfg(feature = "testing")] + { + Ok(Socket {}) + } +} + +pub(crate) fn socket_arp(_ifname: &str) -> Result { + #[cfg(not(feature = "testing"))] + { + // TODO + let socket = capabilities::raise(|| { + Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) + })?; + socket.set_broadcast(true)?; + Ok(socket) + } + #[cfg(feature = "testing")] + { + Ok(Socket {}) + } +} + +#[cfg(not(feature = "testing"))] +async fn send_packet_vrrp( + _socket: &AsyncFd, + _src: IpAddr, + _dst: IpAddr, + _packet: VrrpPacket, +) -> Result<(), IoError> { + // TODO + Ok(()) +} + +#[cfg(not(feature = "testing"))] +async fn send_packet_arp( + _socket: &AsyncFd, + // TODO: add other params +) -> Result<(), IoError> { + // TODO + Ok(()) +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn write_loop( + socket_vrrp: Arc>, + socket_arp: Arc>, + mut net_tx_packetc: UnboundedReceiver, +) { + while let Some(msg) = net_tx_packetc.recv().await { + match msg { + NetTxPacketMsg::Vrrp { packet, src, dst } => { + if let Err(error) = + send_packet_vrrp(&socket_vrrp, src, dst, packet).await + { + error.log(); + } + } + NetTxPacketMsg::Arp {} => { + if let Err(error) = send_packet_arp(&socket_arp).await { + error.log(); + } + } + } + } +} + +#[cfg(not(feature = "testing"))] +pub(crate) async fn read_loop( + _socket_vrrp: Arc>, + _net_packet_rxp: Sender, +) -> Result<(), SendError> { + // TODO: receive VRRP packets + Ok(()) +} diff --git a/holo-vrrp/src/northbound/configuration.rs b/holo-vrrp/src/northbound/configuration.rs new file mode 100644 index 00000000..39fcf8f3 --- /dev/null +++ b/holo-vrrp/src/northbound/configuration.rs @@ -0,0 +1,200 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +#![allow(clippy::derivable_impls)] + +use std::collections::BTreeSet; +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 ipnetwork::Ipv4Network; + +use crate::instance::Instance; +use crate::interface::Interface; + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry { + #[default] + None, + Vrid(u8), +} + +#[derive(Debug)] +pub enum Resource {} + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum Event { + InstanceCreate { vrid: u8 }, + InstanceDelete { vrid: u8 }, +} + +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 { + pub log_state_change: bool, + pub preempt: bool, + pub priority: u8, + pub advertise_interval: u8, + pub virtual_addresses: BTreeSet, +} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + CallbacksBuilder::::default() + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::PATH) + .create_apply(|interface, args| { + let vrid = args.dnode.get_u8_relative("./vrid").unwrap(); + let instance = Instance::new(); + interface.instances.insert(vrid, instance); + + let event_queue = args.event_queue; + event_queue.insert(Event::InstanceCreate { vrid }); + }) + .delete_apply(|_interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + + let event_queue = args.event_queue; + event_queue.insert(Event::InstanceDelete { vrid }); + }) + .lookup(|_instance, _list_entry, dnode| { + let vrid = dnode.get_u8_relative("./vrid").unwrap(); + ListEntry::Vrid(vrid) + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::version::PATH) + .modify_apply(|_interface, _args| { + // Nothing to do. + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::log_state_change::PATH) + .modify_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let log_state_change = args.dnode.get_bool(); + instance.config.log_state_change = log_state_change; + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::preempt::enabled::PATH) + .modify_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let preempt = args.dnode.get_bool(); + instance.config.preempt = preempt; + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::priority::PATH) + .modify_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let priority = args.dnode.get_u8(); + instance.config.priority = priority; + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::advertise_interval_sec::PATH) + .modify_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let advertise_interval = args.dnode.get_u8(); + instance.config.advertise_interval = advertise_interval; + }) + .delete_apply(|_interface, _args| { + // Nothing to do. + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::virtual_ipv4_addresses::virtual_ipv4_address::PATH) + .create_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let addr = args.dnode.get_prefix4(); + instance.config.virtual_addresses.insert(addr); + }) + .delete_apply(|interface, args| { + let vrid = args.list_entry.into_vrid().unwrap(); + let instance = interface.instances.get_mut(&vrid).unwrap(); + + let addr = args.dnode.get_prefix4(); + instance.config.virtual_addresses.remove(&addr); + }) + .lookup(|_instance, _list_entry, _dnode| { + ListEntry::None + }) + .build() +} + +fn load_validation_callbacks() -> ValidationCallbacks { + ValidationCallbacksBuilder::default() + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::version::PATH) + .validate(|args| { + let version = args.dnode.get_string(); + if version != "ietf-vrrp:vrrp-v2" { + return Err(format!("unsupported VRRP version",)); + } + + Ok(()) + }) + .build() +} + +// ===== impl Interface ===== + +#[async_trait] +impl Provider for Interface { + 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) { + match event { + Event::InstanceCreate { vrid } => { + // TODO + } + Event::InstanceDelete { vrid } => { + // TODO + } + } + } +} + +// ===== configuration defaults ===== + +impl Default for InstanceCfg { + fn default() -> InstanceCfg { + use interfaces::interface::ipv4::vrrp; + + let log_state_change = vrrp::vrrp_instance::log_state_change::DFLT; + let preempt = vrrp::vrrp_instance::preempt::enabled::DFLT; + let priority = vrrp::vrrp_instance::priority::DFLT; + let advertise_interval = + vrrp::vrrp_instance::advertise_interval_sec::DFLT; + InstanceCfg { + log_state_change, + preempt, + priority, + advertise_interval, + virtual_addresses: Default::default(), + } + } +} diff --git a/holo-vrrp/src/northbound/mod.rs b/holo-vrrp/src/northbound/mod.rs new file mode 100644 index 00000000..66e33f8a --- /dev/null +++ b/holo-vrrp/src/northbound/mod.rs @@ -0,0 +1,34 @@ +// +// 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 tracing::{debug_span, Span}; + +use crate::interface::Interface; + +// ===== impl Interface ===== + +impl ProviderBase for Interface { + fn yang_modules() -> &'static [&'static str] { + &["ietf-vrrp", "holo-vrrp"] + } + + fn top_level_node(&self) -> String { + "/ietf-interfaces:interfaces".to_owned() + } + + fn debug_span(interface: &str) -> Span { + debug_span!("vrrp", %interface) + } +} + +// No RPC/Actions to implement. +impl holo_northbound::rpc::Provider for Interface {} diff --git a/holo-vrrp/src/northbound/notification.rs b/holo-vrrp/src/northbound/notification.rs new file mode 100644 index 00000000..e1245243 --- /dev/null +++ b/holo-vrrp/src/northbound/notification.rs @@ -0,0 +1,29 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; +use std::net::Ipv4Addr; + +use holo_northbound::{notification, yang, NbProviderSender}; +use holo_yang::ToYang; + +use crate::instance::MasterReason; + +// ===== global functions ===== + +pub(crate) fn new_master_event( + nb_tx: &NbProviderSender, + addr: Ipv4Addr, + reason: MasterReason, +) { + use yang::vrrp_new_master_event::{self, VrrpNewMasterEvent}; + + let data = VrrpNewMasterEvent { + master_ip_address: Some(Cow::Owned(addr.into())), + new_master_reason: Some(reason.to_yang()), + }; + notification::send(nb_tx, vrrp_new_master_event::PATH, data); +} diff --git a/holo-vrrp/src/northbound/state.rs b/holo-vrrp/src/northbound/state.rs new file mode 100644 index 00000000..eee30281 --- /dev/null +++ b/holo-vrrp/src/northbound/state.rs @@ -0,0 +1,92 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; +use std::sync::LazyLock as Lazy; + +use enum_as_inner::EnumAsInner; +use holo_northbound::state::{ + Callbacks, CallbacksBuilder, ListEntryKind, Provider, +}; +use holo_northbound::yang::interfaces; +use holo_utils::option::OptionExt; +use holo_yang::ToYang; + +use crate::instance::Instance; +use crate::interface::Interface; + +pub static CALLBACKS: Lazy> = Lazy::new(load_callbacks); + +#[derive(Debug, Default, EnumAsInner)] +pub enum ListEntry<'a> { + #[default] + None, + Instance(u8, &'a Instance), +} + +// ===== callbacks ===== + +fn load_callbacks() -> Callbacks { + CallbacksBuilder::::default() + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::PATH) + .get_iterate(|interface, _args| { + let iter = interface.instances.iter().map(|(vrid, instance)| ListEntry::Instance(*vrid, instance)); + Some(Box::new(iter)) + }) + .get_object(|_interface, args| { + use interfaces::interface::ipv4::vrrp::vrrp_instance::VrrpInstance; + let (vrid, instance) = args.list_entry.as_instance().unwrap(); + Box::new(VrrpInstance { + vrid: *vrid, + state: Some(instance.state.state.to_yang()), + // TODO + is_owner: None, + last_adv_source: instance.state.last_adv_src.map(std::convert::Into::into).map(Cow::Owned).ignore_in_testing(), + up_datetime: instance.state.up_time.as_ref().ignore_in_testing(), + // TODO + master_down_interval: None, + // TODO + skew_time: None, + last_event: Some(instance.state.last_event.to_yang()).ignore_in_testing(), + new_master_reason: Some(instance.state.new_master_reason.to_yang()), + }) + }) + .path(interfaces::interface::ipv4::vrrp::vrrp_instance::statistics::PATH) + .get_object(|_interface, args| { + use interfaces::interface::ipv4::vrrp::vrrp_instance::statistics::Statistics; + let (_, instance) = args.list_entry.as_instance().unwrap(); + let statistics = &instance.state.statistics; + Box::new(Statistics { + discontinuity_datetime: Some(&statistics.discontinuity_time).ignore_in_testing(), + master_transitions: Some(statistics.master_transitions).ignore_in_testing(), + advertisement_rcvd: Some(statistics.adv_rcvd).ignore_in_testing(), + advertisement_sent: Some(statistics.adv_sent).ignore_in_testing(), + interval_errors: Some(statistics.interval_errors).ignore_in_testing(), + priority_zero_pkts_rcvd: Some(statistics.priority_zero_pkts_rcvd).ignore_in_testing(), + priority_zero_pkts_sent: Some(statistics.priority_zero_pkts_sent).ignore_in_testing(), + invalid_type_pkts_rcvd: Some(statistics.invalid_type_pkts_rcvd).ignore_in_testing(), + packet_length_errors: Some(statistics.pkt_length_errors).ignore_in_testing(), + }) + }) + .build() +} + +// ===== impl Interface ===== + +impl Provider for Interface { + // TODO + const STATE_PATH: &'static str = ""; + + type ListEntry<'a> = ListEntry<'a>; + + fn callbacks() -> Option<&'static Callbacks> { + Some(&CALLBACKS) + } +} + +// ===== impl ListEntry ===== + +impl<'a> ListEntryKind for ListEntry<'a> {} diff --git a/holo-vrrp/src/northbound/yang.rs b/holo-vrrp/src/northbound/yang.rs new file mode 100644 index 00000000..5e38cd4d --- /dev/null +++ b/holo-vrrp/src/northbound/yang.rs @@ -0,0 +1,73 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::borrow::Cow; + +use holo_yang::ToYang; + +use crate::instance::{Event, MasterReason, State}; + +// ===== ToYang implementations ===== + +impl ToYang for State { + fn to_yang(&self) -> Cow<'static, str> { + match self { + State::Initialize => "ietf-vrrp:initialize".into(), + State::Backup => "ietf-vrrp::backup".into(), + State::Master => "ietf-vrrp::master".into(), + } + } +} + +impl ToYang for Event { + fn to_yang(&self) -> Cow<'static, str> { + match self { + Event::None => "ietf-vrrp:vrrp-event-none".into(), + Event::Startup => "ietf-vrrp:vrrp-event-startup".into(), + Event::Shutdown => "ietf-vrrp:vrrp-event-shutdown".into(), + Event::HigherPriorityBackup => { + "ietf-vrrp:vrrp-event-higher-priority-backup".into() + } + Event::MasterTimeout => { + "ietf-vrrp:vrrp-event-master-timeout".into() + } + Event::InterfaceUp => "ietf-vrrp:vrrp-event-interface-up".into(), + Event::InterfaceDown => { + "ietf-vrrp:vrrp-event-interface-down".into() + } + Event::NoPrimaryIpAddress => { + "ietf-vrrp:vrrp-event-no-primary-ip-address".into() + } + Event::PrimaryIpAddress => { + "ietf-vrrp:vrrp-event-primary-ip-address".into() + } + Event::NoVirtualIpAddresses => { + "ietf-vrrp:vrrp-event-no-virtual-ip-addresses".into() + } + Event::VirtualIpAddresses => { + "ietf-vrrp:vrrp-event-virtual-ip-addresses".into() + } + Event::PreemptHoldTimeout => { + "ietf-vrrp:vrrp-event-preempt-hold-timeout".into() + } + Event::LowerPriorityMaster => { + "ietf-vrrp:vrrp-event-lower-priority-master".into() + } + Event::OwnerPreempt => "ietf-vrrp:vrrp-event-owner-preempt".into(), + } + } +} + +impl ToYang for MasterReason { + fn to_yang(&self) -> Cow<'static, str> { + match self { + MasterReason::NotMaster => "not-master".into(), + MasterReason::Priority => "priority".into(), + MasterReason::Preempted => "preempted".into(), + MasterReason::NoResponse => "no-response".into(), + } + } +} diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs new file mode 100644 index 00000000..6eecc774 --- /dev/null +++ b/holo-vrrp/src/packet.rs @@ -0,0 +1,417 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// +use std::net::Ipv4Addr; + +//use bitflags::bitflags; +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use holo_utils::bytes::{BytesExt, BytesMutExt}; +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 VrrpPacket { + pub version: u8, + pub hdr_type: u8, + pub vrid: u8, + pub priority: u8, + pub count_ip: u8, + pub auth_type: u8, + pub adver_int: u8, + pub checksum: u16, + pub ip_addresses: Vec, + + // the following two are only used for backward compatibility. + pub auth_data: u32, + pub auth_data2: u32, +} + +// IP packet header +// +// 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| IHL |Type of Service| Total Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Identification |Flags| Fragment Offset | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Time to Live | Protocol | Header Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Destination Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Options | Padding | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct Ipv4Packet { + pub version: u8, + pub ihl: u8, + pub tos: u8, + pub total_length: u16, + pub identification: u16, + pub flags: u8, + pub offset: u16, + pub ttl: u8, + pub protocol: u8, + pub checksum: u16, + pub src_address: Ipv4Addr, + pub dst_address: Ipv4Addr, + pub options: Option, + pub padding: Option, +} + +#[derive(Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum DecodeError { + ChecksumError, + PacketLengthError(PacketLengthError), + IpTtlError(u8), + VersionError(u8), +} + +#[derive(Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub enum PacketLengthError { + // A maximum number of 16 IP addresses are allowed for + // VRRP. Referenced from count_ip field + AddressCount(usize), + + // specified on the vrrp-ietf. when length of the + // vrrp packet is less than 16 bytes. + TooShort(usize), + + // total + TooLong(usize), + + // when the number of ips specified under count_ip + // does not correspond to the actual length of the packet + // (total_length = 16 + (4 * no of ips)) + CorruptedLength, +} + +// ===== impl Packet ===== + +impl VrrpPacket { + const MIN_PKT_LENGTH: usize = 16; + const MAX_PKT_LENGTH: usize = 80; + const MAX_IP_COUNT: usize = 16; + + // Encodes VRRP packet into a bytes buffer. + pub fn encode(&self) -> BytesMut { + let mut buf = BytesMut::with_capacity(114); + let ver_type = (self.version << 4) | self.hdr_type; + buf.put_u8(self.vrid); + buf.put_u8(self.priority); + buf.put_u8(self.count_ip); + buf.put_u8(self.auth_type); + buf.put_u8(self.adver_int); + buf.put_u16(self.checksum); + for addr in &self.ip_addresses { + buf.put_ipv4(addr); + } + buf.put_u32(self.auth_data); + buf.put_u32(self.auth_data2); + buf + } + + // Decodes VRRP packet from a bytes buffer. + pub fn decode(data: &[u8]) -> DecodeResult { + // 1. pkt length verification + let pkt_size = data.len(); + let count_ip = data[3]; + + // with the minimum number of valid IP addresses being 0, + // The minimum number of bytes for the VRRP packet is 16 + if pkt_size < Self::MIN_PKT_LENGTH { + return Err(DecodeError::PacketLengthError( + PacketLengthError::TooShort(pkt_size), + )); + } + + // with the max number of valid IP addresses being 16, + // The maximum number of bytes the VRRP packet can be is 80 + if pkt_size > Self::MAX_PKT_LENGTH { + return Err(DecodeError::PacketLengthError( + PacketLengthError::TooLong(pkt_size), + )); + } + + // max number of IP addresses allowed. + // This will be based on the count_ip field + if count_ip as usize > Self::MAX_IP_COUNT { + return Err(DecodeError::PacketLengthError( + PacketLengthError::AddressCount(count_ip as usize), + )); + } + + // A Malory may have declared a wrong number of ips + // in count_ip than they actually have in the body. This may + // lead to trying to read data that is either out of bounds or + // fully not reading data sent. + if (count_ip * 4) + 16 != pkt_size as u8 { + return Err(DecodeError::PacketLengthError( + PacketLengthError::CorruptedLength, + )); + } + + let mut buf: Bytes = Bytes::copy_from_slice(data); + let ver_type = buf.get_u8(); + let version = ver_type >> 4; + let hdr_type = ver_type & 0x0F; + let vrid = buf.get_u8(); + let priority = buf.get_u8(); + let count_ip = buf.get_u8(); + let auth_type = buf.get_u8(); + let adver_int = buf.get_u8(); + let checksum = buf.get_u16(); + + // confirm checksum. checksum position is the third item in 16 bit words + let calculated_checksum = checksum::calculate(data, 3); + if calculated_checksum != checksum { + return Err(DecodeError::ChecksumError); + } + + let mut ip_addresses: Vec = vec![]; + for addr in 0..count_ip { + ip_addresses.push(buf.get_ipv4()); + } + let auth_data = buf.get_u32(); + let auth_data2 = buf.get_u32(); + + Ok(Self { + version, + hdr_type, + vrid, + priority, + count_ip, + auth_type, + adver_int, + checksum, + ip_addresses, + auth_data, + auth_data2, + }) + } +} + +impl Ipv4Packet { + const MIN_HDR_LENGTH: usize = 20; + const MAX_HDR_LENGTH: usize = 24; + + fn encode(&self) -> BytesMut { + let mut buf = BytesMut::new(); + + // ver_ihl -> version[4 bits] + ihl[4 bits] + let ver_ihl: u8 = (self.version << 4) | self.ihl; + buf.put_u8(ver_ihl); + buf.put_u8(self.tos); + buf.put_u16(self.total_length); + buf.put_u16(self.identification); + + // flag_off -> flags[4 bits] + offset[12 bits] + let flag_off: u16 = ((self.flags as u16) << 12) | self.offset; + buf.put_u16(flag_off); + buf.put_u8(self.ttl); + buf.put_u8(self.protocol); + buf.put_u16(self.checksum); + buf.put_ipv4(&self.src_address); + buf.put_ipv4(&self.dst_address); + + // the header length for IP is between 20 and 24 + // when 24, the options and padding fields are present. + if let (Some(options), Some(padding)) = (self.options, self.padding) { + let opt_pad: u32 = (options << 8) | (padding as u32); + buf.put_u32(opt_pad); + } + buf + } + + pub fn decode(data: &[u8]) -> DecodeResult { + let mut buf = Bytes::copy_from_slice(data); + + // ver_ihl -> version[4 bits] + ihl[4 bits] + let ver_ihl = buf.get_u8(); + let version = ver_ihl >> 4; + let ihl = ver_ihl & 0x0F; + + // verify if header length matches packet information + // A Malory may have declared a wrong number of ips + // in count_ip than they actually have in the body. This may + // lead to trying to read data that is either out of bounds or + // fully not reading data sent. + if ihl as usize != data.len() / 4 { + return Err(DecodeError::PacketLengthError( + PacketLengthError::CorruptedLength, + )); + } + + if ihl < (Self::MIN_HDR_LENGTH as u8 / 4) { + return Err(DecodeError::PacketLengthError( + PacketLengthError::TooShort(ihl as usize * 4), + )); + } + + if ihl > (Self::MAX_HDR_LENGTH as u8 / 4) { + return Err(DecodeError::PacketLengthError( + PacketLengthError::TooLong(ihl as usize * 4), + )); + } + + let tos = buf.get_u8(); + let total_length = buf.get_u16(); + let identification = buf.get_u16(); + + // flag_off -> flags[4 bits] + offset[12 bits] + let flag_off = buf.get_u16(); + let flags: u8 = (flag_off >> 12) as u8; + let offset: u16 = flag_off & 0xFFF; + + let ttl = buf.get_u8(); + let protocol = buf.get_u8(); + let checksum = buf.get_u16(); + // confirm checksum. checksum position is the 5th 16 bit word + let calculated_checksum = checksum::calculate(data, 5); + if calculated_checksum != checksum { + return Err(DecodeError::ChecksumError); + } + + let src_address = buf.get_ipv4(); + let dst_address = buf.get_ipv4(); + + let mut options: Option = None; + let mut padding: Option = None; + + if ihl > Self::MIN_HDR_LENGTH as u8 { + let opt_pad = buf.get_u32(); + options = Some(opt_pad >> 8); + padding = Some((opt_pad & 0xFF) as u8); + } + Ok(Self { + version, + ihl, + tos, + total_length, + identification, + flags, + offset, + ttl, + protocol, + checksum, + src_address, + dst_address, + options, + padding, + }) + } +} + +mod checksum { + pub(super) fn calculate(data: &[u8], checksum_position: usize) -> u16 { + let mut result: u16 = 0; + + // since data is in u8's, we need pairs of the data to get u16 + for (i, pair) in data.chunks(2).enumerate() { + // the fifth pair is the checksum field, which is ignored + if i == checksum_position { + continue; + } + + result = + add_values(result, ((pair[0] as u16) << 8) | pair[1] as u16); + } + + // do a one's complement to get the sum + !result + } + + fn add_values(mut first: u16, mut second: u16) -> u16 { + let mut carry: u32 = 10; + let mut result: u16 = 0; + + while carry != 0 { + let tmp_res = (first as u32 + second as u32) as u32; + result = (tmp_res & 0xFFFF) as u16; + carry = tmp_res >> 16; + first = result as u16; + second = carry as u16; + } + result + } +} + +// ===== impl DecodeError ===== + +impl std::fmt::Display for DecodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DecodeError::ChecksumError => { + write!(f, "Checksum is not valid") + } + + DecodeError::IpTtlError(rx_ttl) => { + write!(f, "TTL less than 255: {rx_ttl}") + } + DecodeError::VersionError(rx_version) => { + write!(f, "Invalid version: {rx_version}") + } + DecodeError::PacketLengthError(err) => { + std::fmt::Display::fmt(err, f) + } + } + } +} + +impl std::fmt::Display for PacketLengthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PacketLengthError::TooLong(rx_len) => { + write!(f, "Too many bytes for VRRP packet: {rx_len}") + } + PacketLengthError::TooShort(rx_len) => { + write!(f, "Not enough bytes for VRRP packets: {rx_len}") + } + PacketLengthError::AddressCount(rx_count) => { + write!(f, "Too many IP addresses {rx_count}") + } + PacketLengthError::CorruptedLength => { + write!( + f, + "Count_ip not corresponding with no of bytes in packet" + ) + } + } + } +} + +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..5735afad --- /dev/null +++ b/holo-vrrp/src/southbound.rs @@ -0,0 +1,50 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use holo_utils::southbound::{AddressMsg, InterfaceUpdateMsg}; +use ipnetwork::IpNetwork; + +use crate::interface::Interface; + +// ===== global functions ===== + +pub(crate) fn process_iface_update( + iface: &mut Interface, + msg: InterfaceUpdateMsg, +) { + if msg.ifname != iface.name { + return; + } + + iface.system.flags = msg.flags; + iface.system.ifindex = Some(msg.ifindex); + + // TODO: trigger protocol event? +} + +pub(crate) fn process_addr_add(iface: &mut Interface, msg: AddressMsg) { + if msg.ifname != iface.name { + return; + } + + if let IpNetwork::V4(addr) = msg.addr { + iface.system.addresses.insert(addr); + + // TODO: trigger protocol event? + } +} + +pub(crate) fn process_addr_del(iface: &mut Interface, msg: AddressMsg) { + if msg.ifname != iface.name { + return; + } + + if let IpNetwork::V4(addr) = msg.addr { + iface.system.addresses.remove(&addr); + + // TODO: trigger protocol event? + } +} diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs new file mode 100644 index 00000000..3cbe5b3b --- /dev/null +++ b/holo-vrrp/src/tasks.rs @@ -0,0 +1,159 @@ +// +// 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}; +use tracing::{debug_span, Instrument}; + +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 serde::{Deserialize, Serialize}; + + use crate::packet::{DecodeError, VrrpPacket}; + + // 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 enum NetTxPacketMsg { + Vrrp { + packet: VrrpPacket, + src: IpAddr, + dst: IpAddr, + }, + Arp { + // TODO + }, + } + } +} + +// ===== VRRP tasks ===== + +// Network Rx task. +pub(crate) fn net_rx( + socket_vrrp: 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_vrrp, 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_vrrp: Arc>, + socket_arp: 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_vrrp, socket_arp, 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/ipv4.rs b/holo-vrrp/tests/packet/ipv4.rs new file mode 100644 index 00000000..3f4ffc9d --- /dev/null +++ b/holo-vrrp/tests/packet/ipv4.rs @@ -0,0 +1,111 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use std::net::Ipv4Addr; + +use holo_vrrp::packet::{DecodeError, Ipv4Packet, PacketLengthError}; + +// the ipv4 Packet header details that will be used in IPV4 tests. +// It may have slight modifications based on the specific test +// but that will be specified beforehand. +// +// - version: 4 +// - header length: 5 +// - tos: 0 +// - total length: 52 +// - identification: 0x6cb8 +// - flags: 0b0100000000000000 +// - ttl: 51 +// - protocol: 6 [TCP] +// - checksum: 0xfe74 +// - source_address: 208.115.231.106 +// - destination_address: 192.168.100.16 +fn valid_pkt_data() -> [u8; 20] { + [ + 0x45, 0x00, 0x00, 0x34, 0x6c, 0xb8, 0x40, 0x00, 0x33, 0x06, 0xfe, 0x74, + 0xd0, 0x73, 0xe7, 0x6a, 0xc0, 0xa8, 0x64, 0x10, + ] +} + +#[test] +fn encode_valid_pkt() { + let pkt_wrapped = Ipv4Packet::decode(&valid_pkt_data()); + assert!(pkt_wrapped.is_ok()); + + let pkt = pkt_wrapped.unwrap(); + let expected = Ipv4Packet { + version: 4, + ihl: 5, + tos: 0, + total_length: 52, + identification: 0x6cb8, + flags: 0b0100, + offset: 0b0000000000000000, + ttl: 51, + protocol: 6, + checksum: 0xfe74, + src_address: Ipv4Addr::new(208, 115, 231, 106), + dst_address: Ipv4Addr::new(192, 168, 100, 16), + options: None, + padding: None, + }; + assert_eq!(expected, pkt); +} + +#[test] +fn test_hdr_length_corruption() { + let data = &mut valid_pkt_data(); + + // change length from 4 to 5 + data[0] = 0x44; + + let pkt = Ipv4Packet::decode(data); + assert_eq!( + pkt, + Err(DecodeError::PacketLengthError( + PacketLengthError::CorruptedLength + )) + ); +} + +#[test] +fn test_header_too_short() { + let data = [ + 0x43, 0x00, 0x00, 0x34, 0x6c, 0xb8, 0x40, 0x00, 0x33, 0x06, 0xfe, 0x74, + ]; + let pkt = Ipv4Packet::decode(&data); + assert_eq!( + pkt, + Err(DecodeError::PacketLengthError(PacketLengthError::TooShort( + 12 + ))) + ); +} + +#[test] +fn test_header_too_long() { + let data = &mut [0x00; 28]; + data[0] = 0x47; + let pkt = Ipv4Packet::decode(data); + assert_eq!( + pkt, + Err(DecodeError::PacketLengthError(PacketLengthError::TooLong( + 28 + ))) + ); +} + +#[test] +fn test_invalid_checksum() { + let mut data = &mut valid_pkt_data(); + + // change the checksum fields to be sth invalid + data[10] = 0x10; + data[11] = 0x01; + + let pkt = Ipv4Packet::decode(data); + assert_eq!(pkt, Err(DecodeError::ChecksumError)); +} diff --git a/holo-vrrp/tests/packet/mod.rs b/holo-vrrp/tests/packet/mod.rs new file mode 100644 index 00000000..9fef7bed --- /dev/null +++ b/holo-vrrp/tests/packet/mod.rs @@ -0,0 +1,8 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +mod ipv4; +mod vrrp; diff --git a/holo-vrrp/tests/packet/vrrp.rs b/holo-vrrp/tests/packet/vrrp.rs new file mode 100644 index 00000000..84a27520 --- /dev/null +++ b/holo-vrrp/tests/packet/vrrp.rs @@ -0,0 +1,103 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use bytes::Buf; +use holo_vrrp::packet::{DecodeError, PacketLengthError, VrrpPacket}; + +/* +generally in the packet tests we will use the following packet structure +with slight modifications to be done on a per test basis(the changes will +be specified) + +Valid VRRP packet with the following params: + - ver_type: 21 [version: 2, header_type: 1] + - vrid: 51 + - priority: 101 + - count_ip: 1 + - auth_type: 0 + - adver_int: 1 + - checksum: 0x54db + - ip_addresses: [192.168.100.100] + - auth_data: 0 + - auth_data2: 0 + */ +fn valid_pkt_data() -> [u8; 20] { + [ + 0x21, 0x33, 0x65, 0x01, 0x00, 0x01, 0x54, 0xbd, 0xc0, 0xa8, 0x64, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] +} + +// test the valid packet being decoded +#[test] +fn test_valid_decoding() { + let vrrp_pkt = VrrpPacket::decode(&valid_pkt_data()); + assert!(vrrp_pkt.is_ok()); +} + +// make the VRRP packet too short. We will use 10 bytes. +#[test] +fn test_pkt_too_short() { + let vrrp_pkt = VrrpPacket::decode(&[0x00; 10]); + assert_eq!( + vrrp_pkt, + Err(DecodeError::PacketLengthError(PacketLengthError::TooShort( + 10 + ))) + ); +} + +// the length of the entire packet is too long +#[test] +fn test_pkt_too_long() { + let vrrp_pkt = VrrpPacket::decode(&[0x00; 100]); + assert_eq!( + vrrp_pkt, + Err(DecodeError::PacketLengthError(PacketLengthError::TooLong( + 100 + ))) + ); +} + +// test when the packet is too long in length +// we set count_ip as 17 +#[test] +fn test_count_ip_too_high() { + let data: &mut [u8] = &mut valid_pkt_data(); + data[3] = 17; + let vrrp_pkt = VrrpPacket::decode(data); + assert_eq!( + vrrp_pkt, + Err(DecodeError::PacketLengthError( + PacketLengthError::AddressCount(17) + )) + ); +} + +// let us claim we have 3 ip addresses yet we have only one +// we set count_ip as 17 +#[test] +fn test_count_ip_corrupted() { + let data: &mut [u8] = &mut valid_pkt_data(); + data[3] = 3; + let vrrp_pkt = VrrpPacket::decode(data); + assert_eq!( + vrrp_pkt, + Err(DecodeError::PacketLengthError( + PacketLengthError::CorruptedLength + )) + ); +} + +#[test] +fn test_invalid_checksum() { + let data = &mut valid_pkt_data(); + data[6] = 0x01; + data[7] = 0xde; + + let pkt = VrrpPacket::decode(data); + assert_eq!(pkt, Err(DecodeError::ChecksumError)); +} diff --git a/holo-yang/modules/augmentations/holo-vrrp.yang b/holo-yang/modules/augmentations/holo-vrrp.yang new file mode 100644 index 00000000..7eedcb71 --- /dev/null +++ b/holo-yang/modules/augmentations/holo-vrrp.yang @@ -0,0 +1,26 @@ +module holo-vrrp { + yang-version 1.1; + namespace "http://holo-routing.org/yang/holo-vrrp"; + prefix holo-vrrp; + + import ietf-routing { + prefix rt; + } + + organization + "Holo Routing Stack"; + + description + "This module defines augment statements for the ietf-vrrp + module."; + + /* + * Identities. + */ + + identity vrrp { + base rt:routing-protocol; + description + "VRRP protocol."; + } +} 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..92253c52 --- /dev/null +++ b/holo-yang/modules/deviations/ietf-vrrp-holo-deviations.yang @@ -0,0 +1,611 @@ +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 replace { + mandatory false; + } + deviate add { + default "vrrp:vrrp-v2"; + } + } + + /* + 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 693ba4ef..5167e0f2 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", ] }); @@ -248,6 +256,9 @@ pub static YANG_FEATURES: Lazy>> = "ietf-segment-routing-common" => vec![ "sid-last-hop-behavior", ], + "ietf-vrrp" => vec![ + "validate-interval-errors", + ], } }); From 788714a7b5a08f44dc08ecdcbeaea07667aa085a Mon Sep 17 00:00:00 2001 From: weqe Date: Fri, 28 Jun 2024 12:30:43 +0300 Subject: [PATCH 02/22] Initialize and test transmission of basic VRRP packet --- holo-vrrp/src/error.rs | 114 ++++++++++++++++++------- holo-vrrp/src/network.rs | 29 +++++-- holo-vrrp/src/packet.rs | 146 +++++++++------------------------ holo-vrrp/tests/packet/ipv4.rs | 16 ++-- holo-vrrp/tests/packet/vrrp.rs | 19 ++--- 5 files changed, 157 insertions(+), 167 deletions(-) diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index 52b1d167..2fc04bb5 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT // -use std::net::IpAddr; +use std::{fmt::{Debug, Display}, net::IpAddr}; use tracing::{warn, warn_span}; @@ -14,10 +14,24 @@ pub enum Error { // I/O errors IoError(IoError), - // other errors - VridError, - AddressListError(Vec, Vec), + // ietf yang specific errors + GlobalError(GlobalError), + VirtualRouterError(VirtualRouterError) +} + +#[derive(Debug)] +pub enum GlobalError { + ChecksumError, + IpTtlError, + VersionError, + VridError +} + +#[derive(Debug)] +pub enum VirtualRouterError { + AddressListError, IntervalError, + PacketLengthError } // VRRP I/O errors. @@ -39,15 +53,35 @@ impl Error { Error::IoError(error) => { error.log(); } - Error::VridError => { - warn_span!("virtual_router").in_scope(|| warn!("{}", self)); - } - Error::AddressListError(_, _) => { - warn_span!("virtual_router").in_scope(|| warn!("{}", self)); - } - Error::IntervalError => { - warn_span!("virtual_router").in_scope(|| warn!("{}", self)); - } + Error::GlobalError(error) => { + match error { + GlobalError::ChecksumError => { + warn_span!("global_error").in_scope(|| { warn!("invalid checksum received") }) + }, + GlobalError::IpTtlError => { + warn_span!("global_error").in_scope(|| { warn!("TTL for IP packet is not 255.") }) + }, + GlobalError::VersionError => { + warn_span!("global_error").in_scope(|| { warn!("invalid version received. only version 2 accepted.") }) + }, + GlobalError::VridError => { + warn_span!("global_error").in_scope(|| { warn!("vrid is not locally configured. ") }) + }, + } + }, + Error::VirtualRouterError(error) => { + match error { + VirtualRouterError::AddressListError => { + warn_span!("vr_error").in_scope(|| { warn!("addresses received not locally configured") }) + }, + VirtualRouterError::IntervalError => { + warn_span!("vr_error").in_scope(|| { warn!("interval does not match locally configured interval") }) + }, + VirtualRouterError::PacketLengthError => { + warn_span!("vr_error").in_scope(|| { warn!("packet length error") }); + }, + } + }, } } } @@ -55,22 +89,44 @@ impl Error { 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), - Error::VridError => { - write!( - f, - "virtual router id(VRID) not matching locally configured" - ) - } - Error::AddressListError(..) => { - write!( - f, - "received address list not matching local address list" - ) - } - Error::IntervalError => { - write!(f, "received advert interval not matching local configured advert interval") - } + Error::IoError(error) => std::fmt::Display::fmt(error, f), + Error::GlobalError(error) => std::fmt::Display::fmt(error, f), + Error::VirtualRouterError(error) => std::fmt::Display::fmt(error, f) + } + } +} + +impl std::fmt::Display for GlobalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GlobalError::ChecksumError => { + write!(f, "incorrect checksum received") + }, + GlobalError::IpTtlError => { + write!(f, "invalid ttl received. IP ttl for vrrp should always be 255") + }, + GlobalError::VersionError => { + write!(f, "invalid VRRP version received. only version 2 accepted") + }, + GlobalError::VridError => { + write!(f, "vrid received is not in the configured VRIDs") + }, + } + } +} + +impl std::fmt::Display for VirtualRouterError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VirtualRouterError::AddressListError => { + write!(f, "VRRP address received not in configured addresses") + }, + VirtualRouterError::IntervalError => { + write!(f, "VRRP interval received not match locally configured interval") + }, + VirtualRouterError::PacketLengthError => { + write!(f, "the VRRP packet should be between 16 bytes and 80 bytes. received packet not in range.") + }, } } } diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index 5efc234f..e7519e5d 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -18,10 +18,9 @@ use crate::packet::VrrpPacket; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; -pub(crate) fn socket_vrrp(_ifname: &str) -> Result { +pub fn socket_vrrp(_ifname: &str) -> Result { #[cfg(not(feature = "testing"))] { - // TODO let socket = capabilities::raise(|| { Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) })?; @@ -51,14 +50,28 @@ pub(crate) fn socket_arp(_ifname: &str) -> Result { } #[cfg(not(feature = "testing"))] -async fn send_packet_vrrp( - _socket: &AsyncFd, +pub async fn send_packet_vrrp( + socket: &AsyncFd, _src: IpAddr, _dst: IpAddr, - _packet: VrrpPacket, -) -> Result<(), IoError> { - // TODO - Ok(()) + packet: VrrpPacket, +) -> Result { + use std::net::{Ipv4Addr, SocketAddrV4}; + + let buf: &[u8] = &packet.encode(); + let saddr = SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 8), 0); + + socket + .async_io(tokio::io::Interest::WRITABLE, |sock| { + sock.send_to( + &buf, + &saddr.into() + ) + .map_err(|errno| errno.into()) + }) + .await + .map_err(IoError::SendError) + } #[cfg(not(feature = "testing"))] diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 6eecc774..bcc4f5e9 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -3,9 +3,9 @@ // // SPDX-License-Identifier: MIT // -use std::net::Ipv4Addr; -//use bitflags::bitflags; +use crate::error::{self, Error, GlobalError, VirtualRouterError}; +use std::net::Ipv4Addr; use bytes::{Buf, BufMut, Bytes, BytesMut}; use holo_utils::bytes::{BytesExt, BytesMutExt}; use serde::{Deserialize, Serialize}; @@ -95,29 +95,28 @@ pub struct Ipv4Packet { #[derive(Deserialize, Serialize)] pub enum DecodeError { ChecksumError, - PacketLengthError(PacketLengthError), - IpTtlError(u8), - VersionError(u8), + PacketLengthError, + IpTtlError, + VersionError, } -#[derive(Debug, Eq, PartialEq)] -#[derive(Deserialize, Serialize)] -pub enum PacketLengthError { - // A maximum number of 16 IP addresses are allowed for - // VRRP. Referenced from count_ip field - AddressCount(usize), - - // specified on the vrrp-ietf. when length of the - // vrrp packet is less than 16 bytes. - TooShort(usize), - - // total - TooLong(usize), - - // when the number of ips specified under count_ip - // does not correspond to the actual length of the packet - // (total_length = 16 + (4 * no of ips)) - CorruptedLength, +impl DecodeError { + pub fn err(&self) -> error::Error { + match self { + DecodeError::ChecksumError => { + Error::GlobalError(GlobalError::ChecksumError) + }, + DecodeError::PacketLengthError => { + Error::VirtualRouterError(VirtualRouterError::PacketLengthError) + }, + DecodeError::IpTtlError => { + Error::GlobalError(GlobalError::IpTtlError) + }, + DecodeError::VersionError => { + Error::GlobalError(GlobalError::VersionError) + }, + } + } } // ===== impl Packet ===== @@ -131,6 +130,7 @@ impl VrrpPacket { pub fn encode(&self) -> BytesMut { let mut buf = BytesMut::with_capacity(114); let ver_type = (self.version << 4) | self.hdr_type; + buf.put_u8(ver_type); buf.put_u8(self.vrid); buf.put_u8(self.priority); buf.put_u8(self.count_ip); @@ -140,6 +140,7 @@ impl VrrpPacket { for addr in &self.ip_addresses { buf.put_ipv4(addr); } + buf.put_u32(self.auth_data); buf.put_u32(self.auth_data2); buf @@ -151,40 +152,15 @@ impl VrrpPacket { let pkt_size = data.len(); let count_ip = data[3]; - // with the minimum number of valid IP addresses being 0, - // The minimum number of bytes for the VRRP packet is 16 - if pkt_size < Self::MIN_PKT_LENGTH { - return Err(DecodeError::PacketLengthError( - PacketLengthError::TooShort(pkt_size), - )); - } - - // with the max number of valid IP addresses being 16, - // The maximum number of bytes the VRRP packet can be is 80 - if pkt_size > Self::MAX_PKT_LENGTH { - return Err(DecodeError::PacketLengthError( - PacketLengthError::TooLong(pkt_size), - )); - } - - // max number of IP addresses allowed. - // This will be based on the count_ip field - if count_ip as usize > Self::MAX_IP_COUNT { - return Err(DecodeError::PacketLengthError( - PacketLengthError::AddressCount(count_ip as usize), - )); - } - - // A Malory may have declared a wrong number of ips - // in count_ip than they actually have in the body. This may - // lead to trying to read data that is either out of bounds or - // fully not reading data sent. - if (count_ip * 4) + 16 != pkt_size as u8 { - return Err(DecodeError::PacketLengthError( - PacketLengthError::CorruptedLength, - )); - } + if pkt_size < Self::MIN_PKT_LENGTH + || pkt_size >Self::MAX_PKT_LENGTH + || count_ip as usize > Self::MAX_IP_COUNT // too many Virtual IPs being described + || (count_ip * 4) + 16 != pkt_size as u8 // length of packet is not same as length expected (as calculated from count_ip) + { + return Err(DecodeError::PacketLengthError) + } + let mut buf: Bytes = Bytes::copy_from_slice(data); let ver_type = buf.get_u8(); let version = ver_type >> 4; @@ -206,6 +182,7 @@ impl VrrpPacket { for addr in 0..count_ip { ip_addresses.push(buf.get_ipv4()); } + let auth_data = buf.get_u32(); let auth_data2 = buf.get_u32(); @@ -219,8 +196,8 @@ impl VrrpPacket { adver_int, checksum, ip_addresses, - auth_data, - auth_data2, + auth_data: auth_data, + auth_data2: auth_data2, }) } } @@ -271,21 +248,15 @@ impl Ipv4Packet { // lead to trying to read data that is either out of bounds or // fully not reading data sent. if ihl as usize != data.len() / 4 { - return Err(DecodeError::PacketLengthError( - PacketLengthError::CorruptedLength, - )); + return Err(DecodeError::PacketLengthError); } if ihl < (Self::MIN_HDR_LENGTH as u8 / 4) { - return Err(DecodeError::PacketLengthError( - PacketLengthError::TooShort(ihl as usize * 4), - )); + return Err(DecodeError::PacketLengthError); } if ihl > (Self::MAX_HDR_LENGTH as u8 / 4) { - return Err(DecodeError::PacketLengthError( - PacketLengthError::TooLong(ihl as usize * 4), - )); + return Err(DecodeError::PacketLengthError); } let tos = buf.get_u8(); @@ -336,8 +307,8 @@ impl Ipv4Packet { } } -mod checksum { - pub(super) fn calculate(data: &[u8], checksum_position: usize) -> u16 { +pub mod checksum { + pub fn calculate(data: &[u8], checksum_position: usize) -> u16 { let mut result: u16 = 0; // since data is in u8's, we need pairs of the data to get u16 @@ -374,44 +345,9 @@ mod checksum { impl std::fmt::Display for DecodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DecodeError::ChecksumError => { - write!(f, "Checksum is not valid") - } - - DecodeError::IpTtlError(rx_ttl) => { - write!(f, "TTL less than 255: {rx_ttl}") - } - DecodeError::VersionError(rx_version) => { - write!(f, "Invalid version: {rx_version}") - } - DecodeError::PacketLengthError(err) => { - std::fmt::Display::fmt(err, f) - } - } + self.err().fmt(f) } } -impl std::fmt::Display for PacketLengthError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PacketLengthError::TooLong(rx_len) => { - write!(f, "Too many bytes for VRRP packet: {rx_len}") - } - PacketLengthError::TooShort(rx_len) => { - write!(f, "Not enough bytes for VRRP packets: {rx_len}") - } - PacketLengthError::AddressCount(rx_count) => { - write!(f, "Too many IP addresses {rx_count}") - } - PacketLengthError::CorruptedLength => { - write!( - f, - "Count_ip not corresponding with no of bytes in packet" - ) - } - } - } -} impl std::error::Error for DecodeError {} diff --git a/holo-vrrp/tests/packet/ipv4.rs b/holo-vrrp/tests/packet/ipv4.rs index 3f4ffc9d..9a42e7f7 100644 --- a/holo-vrrp/tests/packet/ipv4.rs +++ b/holo-vrrp/tests/packet/ipv4.rs @@ -6,7 +6,7 @@ use std::net::Ipv4Addr; -use holo_vrrp::packet::{DecodeError, Ipv4Packet, PacketLengthError}; +use holo_vrrp::packet::{DecodeError, Ipv4Packet}; // the ipv4 Packet header details that will be used in IPV4 tests. // It may have slight modifications based on the specific test @@ -65,9 +65,7 @@ fn test_hdr_length_corruption() { let pkt = Ipv4Packet::decode(data); assert_eq!( pkt, - Err(DecodeError::PacketLengthError( - PacketLengthError::CorruptedLength - )) + Err(DecodeError::PacketLengthError) ); } @@ -79,9 +77,7 @@ fn test_header_too_short() { let pkt = Ipv4Packet::decode(&data); assert_eq!( pkt, - Err(DecodeError::PacketLengthError(PacketLengthError::TooShort( - 12 - ))) + Err(DecodeError::PacketLengthError) ); } @@ -92,15 +88,13 @@ fn test_header_too_long() { let pkt = Ipv4Packet::decode(data); assert_eq!( pkt, - Err(DecodeError::PacketLengthError(PacketLengthError::TooLong( - 28 - ))) + Err(DecodeError::PacketLengthError) ); } #[test] fn test_invalid_checksum() { - let mut data = &mut valid_pkt_data(); + let data = &mut valid_pkt_data(); // change the checksum fields to be sth invalid data[10] = 0x10; diff --git a/holo-vrrp/tests/packet/vrrp.rs b/holo-vrrp/tests/packet/vrrp.rs index 84a27520..132733de 100644 --- a/holo-vrrp/tests/packet/vrrp.rs +++ b/holo-vrrp/tests/packet/vrrp.rs @@ -4,8 +4,7 @@ // SPDX-License-Identifier: MIT // -use bytes::Buf; -use holo_vrrp::packet::{DecodeError, PacketLengthError, VrrpPacket}; +use holo_vrrp::packet::{DecodeError, VrrpPacket}; /* generally in the packet tests we will use the following packet structure @@ -44,9 +43,7 @@ fn test_pkt_too_short() { let vrrp_pkt = VrrpPacket::decode(&[0x00; 10]); assert_eq!( vrrp_pkt, - Err(DecodeError::PacketLengthError(PacketLengthError::TooShort( - 10 - ))) + Err(DecodeError::PacketLengthError) ); } @@ -56,9 +53,7 @@ fn test_pkt_too_long() { let vrrp_pkt = VrrpPacket::decode(&[0x00; 100]); assert_eq!( vrrp_pkt, - Err(DecodeError::PacketLengthError(PacketLengthError::TooLong( - 100 - ))) + Err(DecodeError::PacketLengthError) ); } @@ -71,9 +66,7 @@ fn test_count_ip_too_high() { let vrrp_pkt = VrrpPacket::decode(data); assert_eq!( vrrp_pkt, - Err(DecodeError::PacketLengthError( - PacketLengthError::AddressCount(17) - )) + Err(DecodeError::PacketLengthError) ); } @@ -86,9 +79,7 @@ fn test_count_ip_corrupted() { let vrrp_pkt = VrrpPacket::decode(data); assert_eq!( vrrp_pkt, - Err(DecodeError::PacketLengthError( - PacketLengthError::CorruptedLength - )) + Err(DecodeError::PacketLengthError) ); } From 76f62eada925437b5ab2342385155f311cb3d744 Mon Sep 17 00:00:00 2001 From: weqe Date: Fri, 28 Jun 2024 12:42:35 +0300 Subject: [PATCH 03/22] cargo fmt --- holo-vrrp/src/error.rs | 36 +++++++++++++++++++++------------- holo-vrrp/src/network.rs | 8 ++------ holo-vrrp/src/packet.rs | 26 ++++++++++++------------ holo-vrrp/tests/packet/ipv4.rs | 15 +++----------- holo-vrrp/tests/packet/vrrp.rs | 20 ++++--------------- 5 files changed, 43 insertions(+), 62 deletions(-) diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index 2fc04bb5..9bcb1fb0 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -4,7 +4,10 @@ // SPDX-License-Identifier: MIT // -use std::{fmt::{Debug, Display}, net::IpAddr}; +use std::{ + fmt::{Debug, Display}, + net::IpAddr, +}; use tracing::{warn, warn_span}; @@ -16,7 +19,7 @@ pub enum Error { // ietf yang specific errors GlobalError(GlobalError), - VirtualRouterError(VirtualRouterError) + VirtualRouterError(VirtualRouterError), } #[derive(Debug)] @@ -24,14 +27,14 @@ pub enum GlobalError { ChecksumError, IpTtlError, VersionError, - VridError + VridError, } #[derive(Debug)] pub enum VirtualRouterError { AddressListError, IntervalError, - PacketLengthError + PacketLengthError, } // VRRP I/O errors. @@ -90,8 +93,10 @@ impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::IoError(error) => std::fmt::Display::fmt(error, f), - Error::GlobalError(error) => std::fmt::Display::fmt(error, f), - Error::VirtualRouterError(error) => std::fmt::Display::fmt(error, f) + Error::GlobalError(error) => std::fmt::Display::fmt(error, f), + Error::VirtualRouterError(error) => { + std::fmt::Display::fmt(error, f) + } } } } @@ -101,16 +106,19 @@ impl std::fmt::Display for GlobalError { match self { GlobalError::ChecksumError => { write!(f, "incorrect checksum received") - }, + } GlobalError::IpTtlError => { write!(f, "invalid ttl received. IP ttl for vrrp should always be 255") - }, + } GlobalError::VersionError => { - write!(f, "invalid VRRP version received. only version 2 accepted") - }, + write!( + f, + "invalid VRRP version received. only version 2 accepted" + ) + } GlobalError::VridError => { write!(f, "vrid received is not in the configured VRIDs") - }, + } } } } @@ -120,13 +128,13 @@ impl std::fmt::Display for VirtualRouterError { match self { VirtualRouterError::AddressListError => { write!(f, "VRRP address received not in configured addresses") - }, + } VirtualRouterError::IntervalError => { write!(f, "VRRP interval received not match locally configured interval") - }, + } VirtualRouterError::PacketLengthError => { write!(f, "the VRRP packet should be between 16 bytes and 80 bytes. received packet not in range.") - }, + } } } } diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index e7519e5d..6b55ba38 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -63,15 +63,11 @@ pub async fn send_packet_vrrp( socket .async_io(tokio::io::Interest::WRITABLE, |sock| { - sock.send_to( - &buf, - &saddr.into() - ) - .map_err(|errno| errno.into()) + sock.send_to(&buf, &saddr.into()) + .map_err(|errno| errno.into()) }) .await .map_err(IoError::SendError) - } #[cfg(not(feature = "testing"))] diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index bcc4f5e9..94bbdf1c 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -5,10 +5,10 @@ // use crate::error::{self, Error, GlobalError, VirtualRouterError}; -use std::net::Ipv4Addr; use bytes::{Buf, BufMut, Bytes, BytesMut}; use holo_utils::bytes::{BytesExt, BytesMutExt}; use serde::{Deserialize, Serialize}; +use std::net::Ipv4Addr; // Type aliases. pub type DecodeResult = Result; @@ -105,16 +105,16 @@ impl DecodeError { match self { DecodeError::ChecksumError => { Error::GlobalError(GlobalError::ChecksumError) - }, + } DecodeError::PacketLengthError => { Error::VirtualRouterError(VirtualRouterError::PacketLengthError) - }, + } DecodeError::IpTtlError => { Error::GlobalError(GlobalError::IpTtlError) - }, + } DecodeError::VersionError => { Error::GlobalError(GlobalError::VersionError) - }, + } } } } @@ -152,15 +152,14 @@ impl VrrpPacket { let pkt_size = data.len(); let count_ip = data[3]; - if pkt_size < Self::MIN_PKT_LENGTH - || pkt_size >Self::MAX_PKT_LENGTH - || count_ip as usize > Self::MAX_IP_COUNT // too many Virtual IPs being described - || (count_ip * 4) + 16 != pkt_size as u8 // length of packet is not same as length expected (as calculated from count_ip) - { - return Err(DecodeError::PacketLengthError) - } + if pkt_size < Self::MIN_PKT_LENGTH + || pkt_size > Self::MAX_PKT_LENGTH + || count_ip as usize > Self::MAX_IP_COUNT + || (count_ip * 4) + 16 != pkt_size as u8 + { + return Err(DecodeError::PacketLengthError); + } - let mut buf: Bytes = Bytes::copy_from_slice(data); let ver_type = buf.get_u8(); let version = ver_type >> 4; @@ -349,5 +348,4 @@ impl std::fmt::Display for DecodeError { } } - impl std::error::Error for DecodeError {} diff --git a/holo-vrrp/tests/packet/ipv4.rs b/holo-vrrp/tests/packet/ipv4.rs index 9a42e7f7..978abc8a 100644 --- a/holo-vrrp/tests/packet/ipv4.rs +++ b/holo-vrrp/tests/packet/ipv4.rs @@ -63,10 +63,7 @@ fn test_hdr_length_corruption() { data[0] = 0x44; let pkt = Ipv4Packet::decode(data); - assert_eq!( - pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(pkt, Err(DecodeError::PacketLengthError)); } #[test] @@ -75,10 +72,7 @@ fn test_header_too_short() { 0x43, 0x00, 0x00, 0x34, 0x6c, 0xb8, 0x40, 0x00, 0x33, 0x06, 0xfe, 0x74, ]; let pkt = Ipv4Packet::decode(&data); - assert_eq!( - pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(pkt, Err(DecodeError::PacketLengthError)); } #[test] @@ -86,10 +80,7 @@ fn test_header_too_long() { let data = &mut [0x00; 28]; data[0] = 0x47; let pkt = Ipv4Packet::decode(data); - assert_eq!( - pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(pkt, Err(DecodeError::PacketLengthError)); } #[test] diff --git a/holo-vrrp/tests/packet/vrrp.rs b/holo-vrrp/tests/packet/vrrp.rs index 132733de..ea884cff 100644 --- a/holo-vrrp/tests/packet/vrrp.rs +++ b/holo-vrrp/tests/packet/vrrp.rs @@ -41,20 +41,14 @@ fn test_valid_decoding() { #[test] fn test_pkt_too_short() { let vrrp_pkt = VrrpPacket::decode(&[0x00; 10]); - assert_eq!( - vrrp_pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(vrrp_pkt, Err(DecodeError::PacketLengthError)); } // the length of the entire packet is too long #[test] fn test_pkt_too_long() { let vrrp_pkt = VrrpPacket::decode(&[0x00; 100]); - assert_eq!( - vrrp_pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(vrrp_pkt, Err(DecodeError::PacketLengthError)); } // test when the packet is too long in length @@ -64,10 +58,7 @@ fn test_count_ip_too_high() { let data: &mut [u8] = &mut valid_pkt_data(); data[3] = 17; let vrrp_pkt = VrrpPacket::decode(data); - assert_eq!( - vrrp_pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(vrrp_pkt, Err(DecodeError::PacketLengthError)); } // let us claim we have 3 ip addresses yet we have only one @@ -77,10 +68,7 @@ fn test_count_ip_corrupted() { let data: &mut [u8] = &mut valid_pkt_data(); data[3] = 3; let vrrp_pkt = VrrpPacket::decode(data); - assert_eq!( - vrrp_pkt, - Err(DecodeError::PacketLengthError) - ); + assert_eq!(vrrp_pkt, Err(DecodeError::PacketLengthError)); } #[test] From 808255b8e4b41ae7ef793872080047526e2323b7 Mon Sep 17 00:00:00 2001 From: weqe Date: Sat, 29 Jun 2024 13:37:10 +0300 Subject: [PATCH 04/22] Introduction of sending ARP pakcets. --- holo-vrrp/src/network.rs | 55 ++++++++++++++++++--------- holo-vrrp/src/packet.rs | 70 +++++++++++++++++++++++++++++++++++ holo-vrrp/tests/packet/arp.rs | 44 ++++++++++++++++++++++ holo-vrrp/tests/packet/mod.rs | 1 + 4 files changed, 152 insertions(+), 18 deletions(-) create mode 100644 holo-vrrp/tests/packet/arp.rs diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index 6b55ba38..d8d7a337 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT // -use std::net::IpAddr; +use std::net::{IpAddr, SocketAddrV4, Ipv4Addr}; use std::sync::Arc; use holo_utils::socket::{AsyncFd, Socket}; @@ -14,16 +14,20 @@ use socket2::{Domain, Protocol, Type}; use tokio::sync::mpsc::error::SendError; use crate::error::IoError; -use crate::packet::VrrpPacket; +use crate::packet::{VrrpPacket, ArpPacket}; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; -pub fn socket_vrrp(_ifname: &str) -> Result { +pub(crate) fn socket_vrrp(ifname: &str) -> Result { #[cfg(not(feature = "testing"))] { let socket = capabilities::raise(|| { Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) })?; + + capabilities::raise(|| { + socket.bind_device(Some(ifname.as_bytes())) + })?; socket.set_broadcast(true)?; Ok(socket) } @@ -33,14 +37,15 @@ pub fn socket_vrrp(_ifname: &str) -> Result { } } -pub(crate) fn socket_arp(_ifname: &str) -> Result { +pub(crate) fn socket_arp(ifname: &str) -> Result { #[cfg(not(feature = "testing"))] { - // TODO + let eth_p_all = 0x0003; let socket = capabilities::raise(|| { - Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) + Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(eth_p_all))) })?; socket.set_broadcast(true)?; + socket.bind_device(Some(ifname.as_bytes())); Ok(socket) } #[cfg(feature = "testing")] @@ -50,20 +55,19 @@ pub(crate) fn socket_arp(_ifname: &str) -> Result { } #[cfg(not(feature = "testing"))] -pub async fn send_packet_vrrp( +pub(crate) async fn send_packet_vrrp( socket: &AsyncFd, _src: IpAddr, _dst: IpAddr, packet: VrrpPacket, ) -> Result { - use std::net::{Ipv4Addr, SocketAddrV4}; let buf: &[u8] = &packet.encode(); let saddr = SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 8), 0); socket .async_io(tokio::io::Interest::WRITABLE, |sock| { - sock.send_to(&buf, &saddr.into()) + sock.send_to(buf, &saddr.into()) .map_err(|errno| errno.into()) }) .await @@ -71,12 +75,25 @@ pub async fn send_packet_vrrp( } #[cfg(not(feature = "testing"))] -async fn send_packet_arp( - _socket: &AsyncFd, - // TODO: add other params -) -> Result<(), IoError> { - // TODO - Ok(()) +pub(crate) async fn send_packet_arp( + socket: &AsyncFd, + packet: ArpPacket +) -> Result { + + let buf: &[u8]= &packet.encode(); + let target = Ipv4Addr::from(packet.clone().target_proto_address); + let saddr = SocketAddrV4::new(target, 0); + + socket + .async_io(tokio::io::Interest::WRITABLE, |sock| { + sock.send_to(&buf, &saddr.into()) + .map_err(|errno| { + println!("error...{:#?}", errno); + errno.into() + }) + }) + .await + .map_err(IoError::SendError) } #[cfg(not(feature = "testing"))] @@ -95,9 +112,11 @@ pub(crate) async fn write_loop( } } NetTxPacketMsg::Arp {} => { - if let Err(error) = send_packet_arp(&socket_arp).await { - error.log(); - } + + // if let Err(error) = send_packet_arp(&socket_arp).await { + // error.log(); + // } + } } } diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 94bbdf1c..f0f83f9e 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -91,6 +91,21 @@ pub struct Ipv4Packet { pub padding: Option, } + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct ArpPacket { + pub hw_type: u16, + pub proto_type: u16, + pub hw_length: u8, + pub proto_length: u8, + pub operation: u16, + pub sender_hw_address: [u8; 6], // src mac + pub sender_proto_address: [u8; 4], // src ip + pub target_hw_address: [u8; 6], // src mac + pub target_proto_address: [u8; 4] // src ip +} + #[derive(Debug, Eq, PartialEq)] #[derive(Deserialize, Serialize)] pub enum DecodeError { @@ -306,6 +321,61 @@ impl Ipv4Packet { } } + +impl ArpPacket { + pub fn encode(&self) -> BytesMut { + let mut buf = BytesMut::with_capacity(28); + buf.put_u16(self.hw_type); + buf.put_u16(self.proto_type); + buf.put_u8(self.hw_length); + buf.put_u8(self.proto_length); + buf.put_u16(self.operation); + + for x in self.sender_hw_address { buf.put_u8(x); } + for x in self.sender_proto_address { buf.put_u8(x); } + for x in self.target_hw_address { buf.put_u8(x) } + for x in self.target_proto_address { buf.put_u8(x) } + buf + } + + pub fn decode(data: &[u8]) ->DecodeResult { + if data.len() != 28 { + return Err(DecodeError::PacketLengthError) + } + let mut buf = Bytes::copy_from_slice(data); + + let hw_type = buf.get_u16(); + let proto_type = buf.get_u16(); + let hw_length = buf.get_u8(); + let proto_length = buf.get_u8(); + let operation = buf.get_u16(); + let mut sender_hw_address: [u8; 6] = [0_u8; 6]; + for x in 0..6 { sender_hw_address[x] = buf.get_u8(); } + + let mut sender_proto_address: [u8; 4] = [0_u8; 4]; + for x in 0..4 { sender_proto_address[x] = buf.get_u8(); } + + let mut target_hw_address: [u8; 6] = [0_u8; 6]; + for x in 0..6 { target_hw_address[x] = buf.get_u8(); } + + let mut target_proto_address: [u8; 4] = [0_u8; 4]; + for x in 0..4 { target_hw_address[x] = buf.get_u8(); } + + Ok(Self { + hw_type, + proto_type, + hw_length, + proto_length, + operation, + sender_hw_address, + sender_proto_address, + target_hw_address, + target_proto_address, + }) + + } +} + pub mod checksum { pub fn calculate(data: &[u8], checksum_position: usize) -> u16 { let mut result: u16 = 0; diff --git a/holo-vrrp/tests/packet/arp.rs b/holo-vrrp/tests/packet/arp.rs new file mode 100644 index 00000000..bfee3c9b --- /dev/null +++ b/holo-vrrp/tests/packet/arp.rs @@ -0,0 +1,44 @@ +// +// Copyright (c) The Holo Core Contributors +// +// SPDX-License-Identifier: MIT +// + +use holo_vrrp::packet::{ArpPacket, DecodeError}; + + +/* +ARP packet => + + +hw_type: 1, +proto_type: 0x0800, +hw_length: 6, +proto_length: 4, +operation: 1, +sender_hw_address: [0xd4, 0xb1, 0x08, 0x4c, 0xbb, 0xf9], // src mac +sender_proto_address: [192, 168, 100, 1], // src ip +pub target_hw_address: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // src mac +pub target_proto_address: [192, 168, 100, 16] // src ip + +*/ +fn valid_pkt_data() -> [u8; 28] { + [ + + 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xd4, 0xb1, 0x08, 0x4c, 0xbb, 0xf9, + 0xc0, 0xa8, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x64, 0x10 + ] +} + + +#[test] +fn encode_valid_pkt(){ + let pkt_wrapped = ArpPacket::decode(&valid_pkt_data()); + assert!(pkt_wrapped.is_ok()); +} + +#[test] +fn test_pkt_invalid_length() { + let pkt = ArpPacket::decode(&[0x01]); + assert_eq!(pkt, Err(DecodeError::PacketLengthError)); +} diff --git a/holo-vrrp/tests/packet/mod.rs b/holo-vrrp/tests/packet/mod.rs index 9fef7bed..a21841f3 100644 --- a/holo-vrrp/tests/packet/mod.rs +++ b/holo-vrrp/tests/packet/mod.rs @@ -6,3 +6,4 @@ mod ipv4; mod vrrp; +mod arp; \ No newline at end of file From 2ddf8ef35293e4b4af04a14ccc95c166728775a0 Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 4 Jul 2024 14:32:09 +0300 Subject: [PATCH 05/22] ARP packet sending...still to work on malformed packet errors --- holo-vrrp/src/network.rs | 92 ++++++++++++++++++++++++++++------------ holo-vrrp/src/packet.rs | 74 +++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 29 deletions(-) diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index d8d7a337..ab46040b 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -4,17 +4,19 @@ // SPDX-License-Identifier: MIT // +use std::io; use std::net::{IpAddr, SocketAddrV4, Ipv4Addr}; +use std::os::fd::FromRawFd; use std::sync::Arc; -use holo_utils::socket::{AsyncFd, Socket}; +use holo_utils::socket::AsyncFd; use holo_utils::{capabilities, Sender, UnboundedReceiver}; -use libc::ETH_P_ALL; -use socket2::{Domain, Protocol, Type}; +use libc::{socket, AF_PACKET, ETH_P_ALL, ETH_P_ARP, SOCK_RAW}; +use socket2::{Domain, Protocol, Type, Socket}; use tokio::sync::mpsc::error::SendError; use crate::error::IoError; -use crate::packet::{VrrpPacket, ArpPacket}; +use crate::packet::{VrrpPacket, ArpPacket, EthernetFrame}; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; @@ -37,20 +39,22 @@ pub(crate) fn socket_vrrp(ifname: &str) -> Result { } } -pub(crate) fn socket_arp(ifname: &str) -> Result { +pub fn socket_arp(ifname: &str) -> Result { #[cfg(not(feature = "testing"))] { - let eth_p_all = 0x0003; - let socket = capabilities::raise(|| { - Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(eth_p_all))) + let sock = capabilities::raise(|| { + Socket::new(Domain::PACKET, Type::RAW, Some(Protocol::from(ETH_P_ARP))) })?; - socket.set_broadcast(true)?; - socket.bind_device(Some(ifname.as_bytes())); - Ok(socket) + capabilities::raise(|| { + sock.bind_device(Some(ifname.as_bytes())); + sock.set_broadcast(true); + }); + Ok(sock) + } #[cfg(feature = "testing")] { - Ok(Socket {}) + Ok(Socket { }) } } @@ -75,25 +79,57 @@ pub(crate) async fn send_packet_vrrp( } #[cfg(not(feature = "testing"))] -pub(crate) async fn send_packet_arp( - socket: &AsyncFd, - packet: ArpPacket +pub fn send_packet_arp( + sock: Socket, + ifname: &str, + eth_frame: EthernetFrame, + arp_packet: ArpPacket, ) -> Result { + use std::{ffi::{CString, NulError}, os::{self, fd::AsRawFd}}; + + use bytes::Buf; + use libc::{c_void, if_indextoname, if_nametoindex, sendto, sockaddr, sockaddr_ll}; + + use crate::packet::ARPframe; + let mut arpframe = ARPframe::new(eth_frame, arp_packet); + + let c_ifname = match CString::new(ifname.clone()){ + Ok(c_ifname) => c_ifname, + Err(err) => return Err(IoError::SocketError(std::io::Error::new(io::ErrorKind::NotFound, err))), + }; + let ifindex = unsafe { libc::if_nametoindex(c_ifname.as_ptr()) }; - let buf: &[u8]= &packet.encode(); - let target = Ipv4Addr::from(packet.clone().target_proto_address); - let saddr = SocketAddrV4::new(target, 0); + let mut sa = sockaddr_ll { + sll_family: AF_PACKET as u16, + sll_protocol: 0x806_u16.to_be(), + sll_ifindex: ifindex as i32, + sll_hatype: 0, + sll_pkttype: 0, + sll_halen: 0, + sll_addr: [0; 8], + }; + + unsafe { + let ptr_sockaddr = std::mem::transmute::<*mut sockaddr_ll, *mut sockaddr>(&mut sa); + + match sendto( + sock.as_raw_fd(), + &mut arpframe as *mut _ as *const c_void, + std::mem::size_of_val(&arpframe), + 0, + ptr_sockaddr, + std::mem::size_of_val(&sa) as u32 + ) { + -1 => { + Err(IoError::SendError(io::Error::last_os_error())) + }, + fd => { + Ok(fd as usize) + } + } + + } - socket - .async_io(tokio::io::Interest::WRITABLE, |sock| { - sock.send_to(&buf, &saddr.into()) - .map_err(|errno| { - println!("error...{:#?}", errno); - errno.into() - }) - }) - .await - .map_err(IoError::SendError) } #[cfg(not(feature = "testing"))] diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index f0f83f9e..b9cfb0d0 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -91,6 +91,54 @@ pub struct Ipv4Packet { pub padding: Option, } +#[repr(C)] +pub struct ARPframe { + // Ethernet Header + pub dst_mac: [u8; 6], // destination MAC address + pub src_mac: [u8; 6], // source MAC address + pub ethertype: u16, // ether type + + // ARP + pub hardware_type: u16, // network link type (0x1=ethernet) + pub protocol_type: u16, // upper-layer protocol for resolution + pub hw_addr_len: u8, // length of hardware address (bytes) + pub proto_addr_len: u8, // upper-layer protocol address length + pub opcode: u16, // operation (0x1=request, 0x2=reply) + pub sender_hw_addr: [u8; 6], // sender hardware address + pub sender_proto_addr: [u8; 4], // internetwork address of sender + pub target_hw_addr: [u8; 6], // hardware address of target + pub target_proto_addr: [u8; 4], // internetwork address of target +} + +impl ARPframe { + pub fn new(eth_pkt: EthernetFrame, arp_pkt: ArpPacket) -> Self { + Self { + dst_mac: eth_pkt.dst_mac, + src_mac: eth_pkt.src_mac, + ethertype: eth_pkt.ethertype.to_be(), + + + hardware_type: arp_pkt.hw_type.to_be(), + protocol_type: arp_pkt.proto_type.to_be(), + hw_addr_len: arp_pkt.hw_length, + proto_addr_len: arp_pkt.hw_length, + opcode: arp_pkt.operation.to_be(), + + sender_hw_addr: arp_pkt.sender_hw_address, + sender_proto_addr: arp_pkt.sender_proto_address, + target_hw_addr: arp_pkt.target_hw_address, + target_proto_addr: arp_pkt.target_proto_address, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Serialize)] +pub struct EthernetFrame { + pub dst_mac: [u8; 6], + pub src_mac: [u8; 6], + pub ethertype: u16 +} #[derive(Clone, Debug, Eq, PartialEq)] #[derive(Deserialize, Serialize)] @@ -321,6 +369,30 @@ impl Ipv4Packet { } } +impl EthernetFrame { + pub fn encode(&self) -> BytesMut { + let mut buf = BytesMut::with_capacity(14); + self.dst_mac.iter().for_each(|i| buf.put_u8(*i)); + self.src_mac.iter().for_each(|i| buf.put_u8(*i)); + buf.put_u16(self.ethertype); + buf + } + + pub fn decode(data: &[u8]) -> DecodeResult { + let mut buf = Bytes::copy_from_slice(data); + let mut dst_mac: [u8; 6] = [0u8; 6]; + let mut src_mac: [u8; 6] = [0u8; 6]; + + for x in 0..6 { dst_mac[x] = buf.get_u8(); } + for x in 0..6 { src_mac[x] = buf.get_u8(); } + + Ok(Self { + dst_mac, + src_mac, + ethertype: buf.get_u16(), + }) + } +} impl ArpPacket { pub fn encode(&self) -> BytesMut { @@ -372,8 +444,8 @@ impl ArpPacket { target_hw_address, target_proto_address, }) - } + } pub mod checksum { From 94737871b33593f776069be69b5bfb918321ff13 Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 4 Jul 2024 17:40:58 +0300 Subject: [PATCH 06/22] solve malformed packet error. --- holo-vrrp/Cargo.toml | 3 --- holo-vrrp/src/packet.rs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/holo-vrrp/Cargo.toml b/holo-vrrp/Cargo.toml index 9bcb6572..f0d429c3 100644 --- a/holo-vrrp/Cargo.toml +++ b/holo-vrrp/Cargo.toml @@ -36,9 +36,6 @@ 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/src/packet.rs b/holo-vrrp/src/packet.rs index b9cfb0d0..5d53dac4 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -121,7 +121,7 @@ impl ARPframe { hardware_type: arp_pkt.hw_type.to_be(), protocol_type: arp_pkt.proto_type.to_be(), hw_addr_len: arp_pkt.hw_length, - proto_addr_len: arp_pkt.hw_length, + proto_addr_len: arp_pkt.proto_length, opcode: arp_pkt.operation.to_be(), sender_hw_addr: arp_pkt.sender_hw_address, From b826aec2ac5369b30bbc928bf703f9e1eef13727 Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 4 Jul 2024 17:54:10 +0300 Subject: [PATCH 07/22] remove wrong use of socket. --- holo-vrrp/src/network.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index ab46040b..e603278a 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -9,10 +9,10 @@ use std::net::{IpAddr, SocketAddrV4, Ipv4Addr}; use std::os::fd::FromRawFd; use std::sync::Arc; -use holo_utils::socket::AsyncFd; +use holo_utils::socket::{AsyncFd, Socket}; use holo_utils::{capabilities, Sender, UnboundedReceiver}; use libc::{socket, AF_PACKET, ETH_P_ALL, ETH_P_ARP, SOCK_RAW}; -use socket2::{Domain, Protocol, Type, Socket}; +use socket2::{Domain, Protocol, Type}; use tokio::sync::mpsc::error::SendError; use crate::error::IoError; From 6f7eaab287cc62d5d5b4af849e5aa507c0701a58 Mon Sep 17 00:00:00 2001 From: weqe Date: Mon, 8 Jul 2024 23:15:54 +0300 Subject: [PATCH 08/22] Initialize timers. --- holo-vrrp/src/instance.rs | 23 ++++++++++++++++++++++- holo-vrrp/src/packet.rs | 7 +++++++ holo-vrrp/src/tasks.rs | 30 +++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 24f6c52f..319a674c 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -7,15 +7,30 @@ use std::net::Ipv4Addr; use chrono::{DateTime, Utc}; +use holo_utils::task::TimeoutTask; -use crate::northbound::configuration::InstanceCfg; +use crate::{northbound::configuration::InstanceCfg, packet::VrrpPacket}; #[derive(Debug)] pub struct Instance { + // Instance configuration data. pub config: InstanceCfg, + // Instance state data. pub state: InstanceState, + + // timers + pub timer: VrrpTimer, + +} + + +#[derive(Debug)] +pub enum VrrpTimer { + Null, + AdverTimer(TimeoutTask), + MasterDownTimer(TimeoutTask) } #[derive(Debug)] @@ -25,6 +40,9 @@ pub struct InstanceState { pub up_time: Option>, pub last_event: Event, pub new_master_reason: MasterReason, + pub skew_time: u32, + pub master_down_interval: u32, + // TODO: interval/timer tasks pub statistics: Statistics, } @@ -86,6 +104,7 @@ impl Instance { Instance { config: Default::default(), state: InstanceState::new(), + timer: VrrpTimer::Null } } } @@ -101,6 +120,8 @@ impl InstanceState { last_event: Event::None, new_master_reason: MasterReason::NotMaster, statistics: Default::default(), + skew_time: 0, + master_down_interval: 0, } } } diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 5d53dac4..8411a789 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -262,6 +262,13 @@ impl VrrpPacket { auth_data2: auth_data2, }) } + + pub(crate) fn generate_checksum(&mut self) { + self.checksum = checksum::calculate( + self.encode().chunk(), + 3 + ); + } } impl Ipv4Packet { diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 3cbe5b3b..31abd032 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -5,13 +5,15 @@ // use std::sync::Arc; +use std::time::Duration; use holo_utils::socket::{AsyncFd, Socket}; //use std::time::Duration; -use holo_utils::task::Task; +use holo_utils::task::{Task, TimeoutTask}; use holo_utils::{Sender, UnboundedReceiver}; use tracing::{debug_span, Instrument}; +use crate::instance::{Instance, VrrpTimer}; use crate::network; // @@ -157,3 +159,29 @@ pub(crate) fn net_tx( }) } } + +fn set_timer( + instance: &mut Instance +) { + + match instance.state.state { + + crate::instance::State::Initialize => { + instance.timer = VrrpTimer::Null; + }, + crate::instance::State::Backup => { + let timer = TimeoutTask::new( + Duration::from_secs(instance.state.master_down_interval as u64), + move || async move { } + ); + instance.timer = VrrpTimer::MasterDownTimer(timer); + }, + crate::instance::State::Master => { + let timer = TimeoutTask::new( + Duration::from_secs(instance.config.advertise_interval as u64), + move || async move { } + ); + instance.timer = VrrpTimer::AdverTimer(timer); + }, + } +} \ No newline at end of file From 0f2fe2e535186cb17bc034aeb644eec83a3bc63c Mon Sep 17 00:00:00 2001 From: weqe Date: Mon, 8 Jul 2024 23:18:12 +0300 Subject: [PATCH 09/22] cargo fmt changes --- holo-vrrp/src/error.rs | 6 +- holo-vrrp/src/instance.rs | 14 ++--- holo-vrrp/src/network.rs | 67 +++++++++++----------- holo-vrrp/src/packet.rs | 105 ++++++++++++++++++++-------------- holo-vrrp/src/tasks.rs | 18 +++--- holo-vrrp/tests/packet/arp.rs | 16 +++--- holo-vrrp/tests/packet/mod.rs | 2 +- 7 files changed, 119 insertions(+), 109 deletions(-) diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index 9bcb1fb0..e9c5d2ad 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -4,10 +4,8 @@ // SPDX-License-Identifier: MIT // -use std::{ - fmt::{Debug, Display}, - net::IpAddr, -}; +use std::fmt::{Debug, Display}; +use std::net::IpAddr; use tracing::{warn, warn_span}; diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 319a674c..d62fb452 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -9,28 +9,26 @@ use std::net::Ipv4Addr; use chrono::{DateTime, Utc}; use holo_utils::task::TimeoutTask; -use crate::{northbound::configuration::InstanceCfg, packet::VrrpPacket}; +use crate::northbound::configuration::InstanceCfg; +use crate::packet::VrrpPacket; #[derive(Debug)] pub struct Instance { - // Instance configuration data. pub config: InstanceCfg, - + // Instance state data. pub state: InstanceState, - // timers + // timers pub timer: VrrpTimer, - } - #[derive(Debug)] pub enum VrrpTimer { Null, AdverTimer(TimeoutTask), - MasterDownTimer(TimeoutTask) + MasterDownTimer(TimeoutTask), } #[derive(Debug)] @@ -104,7 +102,7 @@ impl Instance { Instance { config: Default::default(), state: InstanceState::new(), - timer: VrrpTimer::Null + timer: VrrpTimer::Null, } } } diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index e603278a..dd348e6c 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -5,7 +5,7 @@ // use std::io; -use std::net::{IpAddr, SocketAddrV4, Ipv4Addr}; +use std::net::{IpAddr, Ipv4Addr, SocketAddrV4}; use std::os::fd::FromRawFd; use std::sync::Arc; @@ -16,7 +16,7 @@ use socket2::{Domain, Protocol, Type}; use tokio::sync::mpsc::error::SendError; use crate::error::IoError; -use crate::packet::{VrrpPacket, ArpPacket, EthernetFrame}; +use crate::packet::{ArpPacket, EthernetFrame, VrrpPacket}; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; @@ -26,10 +26,8 @@ pub(crate) fn socket_vrrp(ifname: &str) -> Result { let socket = capabilities::raise(|| { Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) })?; - - capabilities::raise(|| { - socket.bind_device(Some(ifname.as_bytes())) - })?; + + capabilities::raise(|| socket.bind_device(Some(ifname.as_bytes())))?; socket.set_broadcast(true)?; Ok(socket) } @@ -43,18 +41,21 @@ pub fn socket_arp(ifname: &str) -> Result { #[cfg(not(feature = "testing"))] { let sock = capabilities::raise(|| { - Socket::new(Domain::PACKET, Type::RAW, Some(Protocol::from(ETH_P_ARP))) + Socket::new( + Domain::PACKET, + Type::RAW, + Some(Protocol::from(ETH_P_ARP)), + ) })?; capabilities::raise(|| { sock.bind_device(Some(ifname.as_bytes())); sock.set_broadcast(true); }); Ok(sock) - } #[cfg(feature = "testing")] { - Ok(Socket { }) + Ok(Socket {}) } } @@ -65,7 +66,6 @@ pub(crate) async fn send_packet_vrrp( _dst: IpAddr, packet: VrrpPacket, ) -> Result { - let buf: &[u8] = &packet.encode(); let saddr = SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 8), 0); @@ -85,20 +85,29 @@ pub fn send_packet_arp( eth_frame: EthernetFrame, arp_packet: ArpPacket, ) -> Result { - use std::{ffi::{CString, NulError}, os::{self, fd::AsRawFd}}; + use std::ffi::{CString, NulError}; + use std::os::fd::AsRawFd; + use std::os::{self}; use bytes::Buf; - use libc::{c_void, if_indextoname, if_nametoindex, sendto, sockaddr, sockaddr_ll}; + use libc::{ + c_void, if_indextoname, if_nametoindex, sendto, sockaddr, sockaddr_ll, + }; use crate::packet::ARPframe; let mut arpframe = ARPframe::new(eth_frame, arp_packet); - let c_ifname = match CString::new(ifname.clone()){ + let c_ifname = match CString::new(ifname.clone()) { Ok(c_ifname) => c_ifname, - Err(err) => return Err(IoError::SocketError(std::io::Error::new(io::ErrorKind::NotFound, err))), + Err(err) => { + return Err(IoError::SocketError(std::io::Error::new( + io::ErrorKind::NotFound, + err, + ))) + } }; let ifindex = unsafe { libc::if_nametoindex(c_ifname.as_ptr()) }; - + let mut sa = sockaddr_ll { sll_family: AF_PACKET as u16, sll_protocol: 0x806_u16.to_be(), @@ -110,26 +119,21 @@ pub fn send_packet_arp( }; unsafe { - let ptr_sockaddr = std::mem::transmute::<*mut sockaddr_ll, *mut sockaddr>(&mut sa); - + let ptr_sockaddr = + std::mem::transmute::<*mut sockaddr_ll, *mut sockaddr>(&mut sa); + match sendto( - sock.as_raw_fd(), - &mut arpframe as *mut _ as *const c_void, - std::mem::size_of_val(&arpframe), - 0, - ptr_sockaddr, - std::mem::size_of_val(&sa) as u32 + sock.as_raw_fd(), + &mut arpframe as *mut _ as *const c_void, + std::mem::size_of_val(&arpframe), + 0, + ptr_sockaddr, + std::mem::size_of_val(&sa) as u32, ) { - -1 => { - Err(IoError::SendError(io::Error::last_os_error())) - }, - fd => { - Ok(fd as usize) - } + -1 => Err(IoError::SendError(io::Error::last_os_error())), + fd => Ok(fd as usize), } - } - } #[cfg(not(feature = "testing"))] @@ -152,7 +156,6 @@ pub(crate) async fn write_loop( // if let Err(error) = send_packet_arp(&socket_arp).await { // error.log(); // } - } } } diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 8411a789..1c27491d 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -4,11 +4,13 @@ // SPDX-License-Identifier: MIT // -use crate::error::{self, Error, GlobalError, VirtualRouterError}; +use std::net::Ipv4Addr; + use bytes::{Buf, BufMut, Bytes, BytesMut}; use holo_utils::bytes::{BytesExt, BytesMutExt}; use serde::{Deserialize, Serialize}; -use std::net::Ipv4Addr; + +use crate::error::{self, Error, GlobalError, VirtualRouterError}; // Type aliases. pub type DecodeResult = Result; @@ -99,35 +101,34 @@ pub struct ARPframe { pub ethertype: u16, // ether type // ARP - pub hardware_type: u16, // network link type (0x1=ethernet) - pub protocol_type: u16, // upper-layer protocol for resolution - pub hw_addr_len: u8, // length of hardware address (bytes) - pub proto_addr_len: u8, // upper-layer protocol address length - pub opcode: u16, // operation (0x1=request, 0x2=reply) - pub sender_hw_addr: [u8; 6], // sender hardware address + pub hardware_type: u16, // network link type (0x1=ethernet) + pub protocol_type: u16, // upper-layer protocol for resolution + pub hw_addr_len: u8, // length of hardware address (bytes) + pub proto_addr_len: u8, // upper-layer protocol address length + pub opcode: u16, // operation (0x1=request, 0x2=reply) + pub sender_hw_addr: [u8; 6], // sender hardware address pub sender_proto_addr: [u8; 4], // internetwork address of sender - pub target_hw_addr: [u8; 6], // hardware address of target + pub target_hw_addr: [u8; 6], // hardware address of target pub target_proto_addr: [u8; 4], // internetwork address of target } impl ARPframe { pub fn new(eth_pkt: EthernetFrame, arp_pkt: ArpPacket) -> Self { Self { - dst_mac: eth_pkt.dst_mac, - src_mac: eth_pkt.src_mac, - ethertype: eth_pkt.ethertype.to_be(), + dst_mac: eth_pkt.dst_mac, + src_mac: eth_pkt.src_mac, + ethertype: eth_pkt.ethertype.to_be(), - - hardware_type: arp_pkt.hw_type.to_be(), - protocol_type: arp_pkt.proto_type.to_be(), - hw_addr_len: arp_pkt.hw_length, + hardware_type: arp_pkt.hw_type.to_be(), + protocol_type: arp_pkt.proto_type.to_be(), + hw_addr_len: arp_pkt.hw_length, proto_addr_len: arp_pkt.proto_length, - opcode: arp_pkt.operation.to_be(), - - sender_hw_addr: arp_pkt.sender_hw_address, - sender_proto_addr: arp_pkt.sender_proto_address, - target_hw_addr: arp_pkt.target_hw_address, - target_proto_addr: arp_pkt.target_proto_address, + opcode: arp_pkt.operation.to_be(), + + sender_hw_addr: arp_pkt.sender_hw_address, + sender_proto_addr: arp_pkt.sender_proto_address, + target_hw_addr: arp_pkt.target_hw_address, + target_proto_addr: arp_pkt.target_proto_address, } } } @@ -137,7 +138,7 @@ impl ARPframe { pub struct EthernetFrame { pub dst_mac: [u8; 6], pub src_mac: [u8; 6], - pub ethertype: u16 + pub ethertype: u16, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -148,10 +149,10 @@ pub struct ArpPacket { pub hw_length: u8, pub proto_length: u8, pub operation: u16, - pub sender_hw_address: [u8; 6], // src mac + pub sender_hw_address: [u8; 6], // src mac pub sender_proto_address: [u8; 4], // src ip - pub target_hw_address: [u8; 6], // src mac - pub target_proto_address: [u8; 4] // src ip + pub target_hw_address: [u8; 6], // src mac + pub target_proto_address: [u8; 4], // src ip } #[derive(Debug, Eq, PartialEq)] @@ -264,10 +265,7 @@ impl VrrpPacket { } pub(crate) fn generate_checksum(&mut self) { - self.checksum = checksum::calculate( - self.encode().chunk(), - 3 - ); + self.checksum = checksum::calculate(self.encode().chunk(), 3); } } @@ -389,9 +387,13 @@ impl EthernetFrame { let mut buf = Bytes::copy_from_slice(data); let mut dst_mac: [u8; 6] = [0u8; 6]; let mut src_mac: [u8; 6] = [0u8; 6]; - - for x in 0..6 { dst_mac[x] = buf.get_u8(); } - for x in 0..6 { src_mac[x] = buf.get_u8(); } + + for x in 0..6 { + dst_mac[x] = buf.get_u8(); + } + for x in 0..6 { + src_mac[x] = buf.get_u8(); + } Ok(Self { dst_mac, @@ -410,16 +412,24 @@ impl ArpPacket { buf.put_u8(self.proto_length); buf.put_u16(self.operation); - for x in self.sender_hw_address { buf.put_u8(x); } - for x in self.sender_proto_address { buf.put_u8(x); } - for x in self.target_hw_address { buf.put_u8(x) } - for x in self.target_proto_address { buf.put_u8(x) } + for x in self.sender_hw_address { + buf.put_u8(x); + } + for x in self.sender_proto_address { + buf.put_u8(x); + } + for x in self.target_hw_address { + buf.put_u8(x) + } + for x in self.target_proto_address { + buf.put_u8(x) + } buf } - pub fn decode(data: &[u8]) ->DecodeResult { + pub fn decode(data: &[u8]) -> DecodeResult { if data.len() != 28 { - return Err(DecodeError::PacketLengthError) + return Err(DecodeError::PacketLengthError); } let mut buf = Bytes::copy_from_slice(data); @@ -429,16 +439,24 @@ impl ArpPacket { let proto_length = buf.get_u8(); let operation = buf.get_u16(); let mut sender_hw_address: [u8; 6] = [0_u8; 6]; - for x in 0..6 { sender_hw_address[x] = buf.get_u8(); } + for x in 0..6 { + sender_hw_address[x] = buf.get_u8(); + } let mut sender_proto_address: [u8; 4] = [0_u8; 4]; - for x in 0..4 { sender_proto_address[x] = buf.get_u8(); } + for x in 0..4 { + sender_proto_address[x] = buf.get_u8(); + } let mut target_hw_address: [u8; 6] = [0_u8; 6]; - for x in 0..6 { target_hw_address[x] = buf.get_u8(); } + for x in 0..6 { + target_hw_address[x] = buf.get_u8(); + } let mut target_proto_address: [u8; 4] = [0_u8; 4]; - for x in 0..4 { target_hw_address[x] = buf.get_u8(); } + for x in 0..4 { + target_hw_address[x] = buf.get_u8(); + } Ok(Self { hw_type, @@ -452,7 +470,6 @@ impl ArpPacket { target_proto_address, }) } - } pub mod checksum { diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 31abd032..4d33a231 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -160,28 +160,24 @@ pub(crate) fn net_tx( } } -fn set_timer( - instance: &mut Instance -) { - +fn set_timer(instance: &mut Instance) { match instance.state.state { - crate::instance::State::Initialize => { instance.timer = VrrpTimer::Null; - }, + } crate::instance::State::Backup => { let timer = TimeoutTask::new( Duration::from_secs(instance.state.master_down_interval as u64), - move || async move { } + move || async move {}, ); instance.timer = VrrpTimer::MasterDownTimer(timer); - }, + } crate::instance::State::Master => { let timer = TimeoutTask::new( Duration::from_secs(instance.config.advertise_interval as u64), - move || async move { } + move || async move {}, ); instance.timer = VrrpTimer::AdverTimer(timer); - }, + } } -} \ No newline at end of file +} diff --git a/holo-vrrp/tests/packet/arp.rs b/holo-vrrp/tests/packet/arp.rs index bfee3c9b..d3ee624e 100644 --- a/holo-vrrp/tests/packet/arp.rs +++ b/holo-vrrp/tests/packet/arp.rs @@ -6,9 +6,8 @@ use holo_vrrp::packet::{ArpPacket, DecodeError}; - /* -ARP packet => +ARP packet => hw_type: 1, @@ -16,23 +15,22 @@ proto_type: 0x0800, hw_length: 6, proto_length: 4, operation: 1, -sender_hw_address: [0xd4, 0xb1, 0x08, 0x4c, 0xbb, 0xf9], // src mac +sender_hw_address: [0xd4, 0xb1, 0x08, 0x4c, 0xbb, 0xf9], // src mac sender_proto_address: [192, 168, 100, 1], // src ip -pub target_hw_address: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // src mac +pub target_hw_address: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], // src mac pub target_proto_address: [192, 168, 100, 16] // src ip */ fn valid_pkt_data() -> [u8; 28] { [ - - 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xd4, 0xb1, 0x08, 0x4c, 0xbb, 0xf9, - 0xc0, 0xa8, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x64, 0x10 + 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0xd4, 0xb1, 0x08, 0x4c, + 0xbb, 0xf9, 0xc0, 0xa8, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xa8, 0x64, 0x10, ] } - #[test] -fn encode_valid_pkt(){ +fn encode_valid_pkt() { let pkt_wrapped = ArpPacket::decode(&valid_pkt_data()); assert!(pkt_wrapped.is_ok()); } diff --git a/holo-vrrp/tests/packet/mod.rs b/holo-vrrp/tests/packet/mod.rs index a21841f3..6a4cd235 100644 --- a/holo-vrrp/tests/packet/mod.rs +++ b/holo-vrrp/tests/packet/mod.rs @@ -4,6 +4,6 @@ // SPDX-License-Identifier: MIT // +mod arp; mod ipv4; mod vrrp; -mod arp; \ No newline at end of file From 76ae178ffd5df70f0078fb7550a7597a81b2694b Mon Sep 17 00:00:00 2001 From: weqe Date: Tue, 9 Jul 2024 00:12:56 +0300 Subject: [PATCH 10/22] Change listener for advert to interval task --- holo-vrrp/src/instance.rs | 4 ++-- holo-vrrp/src/network.rs | 2 +- holo-vrrp/src/tasks.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index d62fb452..36557164 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -7,7 +7,7 @@ use std::net::Ipv4Addr; use chrono::{DateTime, Utc}; -use holo_utils::task::TimeoutTask; +use holo_utils::task::{IntervalTask, TimeoutTask}; use crate::northbound::configuration::InstanceCfg; use crate::packet::VrrpPacket; @@ -27,7 +27,7 @@ pub struct Instance { #[derive(Debug)] pub enum VrrpTimer { Null, - AdverTimer(TimeoutTask), + AdverTimer(IntervalTask), MasterDownTimer(TimeoutTask), } diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index dd348e6c..3316eeac 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -97,7 +97,7 @@ pub fn send_packet_arp( use crate::packet::ARPframe; let mut arpframe = ARPframe::new(eth_frame, arp_packet); - let c_ifname = match CString::new(ifname.clone()) { + let c_ifname = match CString::new(ifname) { Ok(c_ifname) => c_ifname, Err(err) => { return Err(IoError::SocketError(std::io::Error::new( diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 4d33a231..f7773ad7 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -8,8 +8,7 @@ use std::sync::Arc; use std::time::Duration; use holo_utils::socket::{AsyncFd, Socket}; -//use std::time::Duration; -use holo_utils::task::{Task, TimeoutTask}; +use holo_utils::task::{IntervalTask, Task, TimeoutTask}; use holo_utils::{Sender, UnboundedReceiver}; use tracing::{debug_span, Instrument}; @@ -173,8 +172,9 @@ fn set_timer(instance: &mut Instance) { instance.timer = VrrpTimer::MasterDownTimer(timer); } crate::instance::State::Master => { - let timer = TimeoutTask::new( + let timer = IntervalTask::new( Duration::from_secs(instance.config.advertise_interval as u64), + true, move || async move {}, ); instance.timer = VrrpTimer::AdverTimer(timer); From fa620192d6e5fc86f14937cc68e5855c27c81b5a Mon Sep 17 00:00:00 2001 From: weqe Date: Wed, 17 Jul 2024 18:44:47 +0300 Subject: [PATCH 11/22] Initial event handling --- holo-vrrp/src/events.rs | 46 ++++++++++++++++++++ holo-vrrp/src/instance.rs | 14 ++++++ holo-vrrp/src/interface.rs | 34 ++++++++++++++- holo-vrrp/src/northbound/configuration.rs | 5 ++- holo-vrrp/src/packet.rs | 18 ++++---- holo-vrrp/src/tasks.rs | 53 ++++++++++++++++++----- 6 files changed, 147 insertions(+), 23 deletions(-) diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index a8ddd125..6a8d14be 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -7,8 +7,11 @@ use std::net::IpAddr; use crate::error::Error; +use crate::instance::Instance; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpPacket}; +use crate::instance::State; +use crate::tasks; // ===== Network packet receipt ===== @@ -21,3 +24,46 @@ pub(crate) fn process_packet( Ok(()) } + + +pub(crate) fn process_vrrp_packet( + interface: &mut Interface, + packet: DecodeResult +) -> Result<(), Error>{ + let pkt = packet.unwrap(); + let mut instance = interface.instances.get_mut(&pkt.vrid).unwrap(); + + // errors will be modified to occupy the other statistics + instance.state.statistics.adv_rcvd += 1; + + match instance.state.state { + State::Initialize => { + + } + State::Backup => { + if pkt.priority == 0 { + tasks::set_master_down_timer(instance, instance.state.skew_time as u64); + } + + else if !instance.config.preempt || pkt.priority >= instance.config.priority { + instance.reset_timer(); + } + + } + State::Master => { + if pkt.priority == 0 { + instance.reset_timer(); + } + + + else if (pkt.priority > instance.config.priority) || ( + pkt.priority == instance.config.priority + // && check if primary IP of sender is greater than the local primary IP + ){ + instance.transition_state(State::Backup); + } + } + } + + Ok(()) +} diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 36557164..87c26086 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -4,6 +4,8 @@ // SPDX-License-Identifier: MIT // +use core::task; +use std::borrow::BorrowMut; use std::net::Ipv4Addr; use chrono::{DateTime, Utc}; @@ -11,6 +13,7 @@ use holo_utils::task::{IntervalTask, TimeoutTask}; use crate::northbound::configuration::InstanceCfg; use crate::packet::VrrpPacket; +use crate::{network, tasks}; #[derive(Debug)] pub struct Instance { @@ -105,6 +108,16 @@ impl Instance { timer: VrrpTimer::Null, } } + + pub(crate) fn transition_state(&mut self, state: State) { + self.state.state = state; + tasks::set_timer(self); + } + + pub(crate) fn reset_timer(&mut self) { + tasks::reset_timer(self); + } + } // ===== impl InstanceState ===== @@ -145,3 +158,4 @@ impl Default for Statistics { } } } + diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 8abdca92..b55932e5 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -5,6 +5,7 @@ // use std::collections::{BTreeMap, BTreeSet}; +use std::net::Ipv4Addr; use std::sync::Arc; use async_trait::async_trait; @@ -22,6 +23,7 @@ use tokio::sync::mpsc; use crate::error::{Error, IoError}; use crate::instance::Instance; +use crate::packet::VrrpPacket; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; @@ -79,6 +81,36 @@ pub struct ProtocolInputChannelsRx { // ===== impl Interface ===== +impl Interface { + fn send_advert(&self, vrid: u8) { + if let Some(instance) = self.instances.get(&vrid) { + + // send advertisement then reset the timer. + let mut ip_addresses: Vec = vec![]; + for addr in &instance.config.virtual_addresses { + ip_addresses.push(addr.ip()); + } + + let mut packet = VrrpPacket { + version: 2, + hdr_type: 1, + vrid: u8::default(), + priority: instance.config.priority, + count_ip: instance.config.virtual_addresses.len() as u8, + auth_type: 0, + adver_int: instance.config.advertise_interval, + checksum: 0, + ip_addresses, + auth_data: 0, + auth_data2: 0 + }; + packet.generate_checksum(); + //network::send_packet_vrrp(socket, src, dst, packet); + + } + } +} + #[async_trait] impl ProtocolInstance for Interface { const PROTOCOL: Protocol = Protocol::VRRP; @@ -116,7 +148,7 @@ impl ProtocolInstance for Interface { if let Err(error) = match msg { // Received network packet. ProtocolInputMsg::NetRxPacket(msg) => { - events::process_packet(self, msg.src, msg.packet) + events::process_vrrp_packet(self, msg.packet) } } { error.log(); diff --git a/holo-vrrp/src/northbound/configuration.rs b/holo-vrrp/src/northbound/configuration.rs index 39fcf8f3..4eabd119 100644 --- a/holo-vrrp/src/northbound/configuration.rs +++ b/holo-vrrp/src/northbound/configuration.rs @@ -169,10 +169,11 @@ impl Provider for Interface { async fn process_event(&mut self, event: Event) { match event { Event::InstanceCreate { vrid } => { - // TODO + let instance = Instance::new(); + self.instances.insert(vrid, instance); } Event::InstanceDelete { vrid } => { - // TODO + self.instances.remove(&vrid); } } } diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 1c27491d..790a6ed6 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -216,14 +216,6 @@ impl VrrpPacket { let pkt_size = data.len(); let count_ip = data[3]; - if pkt_size < Self::MIN_PKT_LENGTH - || pkt_size > Self::MAX_PKT_LENGTH - || count_ip as usize > Self::MAX_IP_COUNT - || (count_ip * 4) + 16 != pkt_size as u8 - { - return Err(DecodeError::PacketLengthError); - } - let mut buf: Bytes = Bytes::copy_from_slice(data); let ver_type = buf.get_u8(); let version = ver_type >> 4; @@ -233,6 +225,16 @@ impl VrrpPacket { let count_ip = buf.get_u8(); let auth_type = buf.get_u8(); let adver_int = buf.get_u8(); + + if pkt_size < Self::MIN_PKT_LENGTH + || pkt_size > Self::MAX_PKT_LENGTH + || count_ip as usize > Self::MAX_IP_COUNT + || (count_ip * 4) + 16 != pkt_size as u8 + { + return Err(DecodeError::PacketLengthError); + } + + let checksum = buf.get_u16(); // confirm checksum. checksum position is the third item in 16 bit words diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index f7773ad7..6208fe88 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -4,6 +4,7 @@ // SPDX-License-Identifier: MIT // +use std::borrow::BorrowMut; use std::sync::Arc; use std::time::Duration; @@ -159,25 +160,53 @@ pub(crate) fn net_tx( } } -fn set_timer(instance: &mut Instance) { +// handling the timers... +pub(crate) fn set_timer(instance: &mut Instance) { match instance.state.state { crate::instance::State::Initialize => { instance.timer = VrrpTimer::Null; } crate::instance::State::Backup => { - let timer = TimeoutTask::new( - Duration::from_secs(instance.state.master_down_interval as u64), - move || async move {}, - ); - instance.timer = VrrpTimer::MasterDownTimer(timer); + set_master_down_timer(instance, instance.state.master_down_interval as u64); } crate::instance::State::Master => { - let timer = IntervalTask::new( - Duration::from_secs(instance.config.advertise_interval as u64), - true, - move || async move {}, - ); - instance.timer = VrrpTimer::AdverTimer(timer); + set_adver_timer(instance, instance.config.advertise_interval as u64) } } } + +pub(crate) fn set_master_down_timer(instance: &mut Instance, period: u64) { + let timer = TimeoutTask::new( + Duration::from_secs(period), + move || async move {} + ); + instance.timer = VrrpTimer::MasterDownTimer(timer); +} + +fn set_adver_timer(instance: &mut Instance, period: u64){ + let timer = IntervalTask::new( + Duration::from_secs(period), + true, + move || async move {} + ); + instance.timer = VrrpTimer::AdverTimer(timer); +} + + +pub(crate) fn reset_timer(instance: &mut Instance) { + match instance.timer { + VrrpTimer::AdverTimer(ref mut t) => { + t.reset( + Some(Duration::from_secs(instance.config.advertise_interval as u64)), + ); + + }, + VrrpTimer::MasterDownTimer(ref mut t) => { + t.reset( + Some(Duration::from_secs(instance.state.master_down_interval as u64)) + ); + }, + _ => {} + } +} + From a76a0b3e30f417ffaf86356db95e55565e877dc5 Mon Sep 17 00:00:00 2001 From: weqe Date: Wed, 17 Jul 2024 18:50:50 +0300 Subject: [PATCH 12/22] cargo fmt --- holo-vrrp/src/events.rs | 39 ++++++++++++++++++-------------------- holo-vrrp/src/instance.rs | 4 +--- holo-vrrp/src/interface.rs | 8 +++----- holo-vrrp/src/packet.rs | 1 - holo-vrrp/src/tasks.rs | 36 +++++++++++++++++------------------ 5 files changed, 39 insertions(+), 49 deletions(-) diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index 6a8d14be..7df665e4 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -7,10 +7,9 @@ use std::net::IpAddr; use crate::error::Error; -use crate::instance::Instance; +use crate::instance::{Instance, State}; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpPacket}; -use crate::instance::State; use crate::tasks; // ===== Network packet receipt ===== @@ -25,41 +24,39 @@ pub(crate) fn process_packet( Ok(()) } - pub(crate) fn process_vrrp_packet( interface: &mut Interface, - packet: DecodeResult -) -> Result<(), Error>{ + packet: DecodeResult, +) -> Result<(), Error> { let pkt = packet.unwrap(); - let mut instance = interface.instances.get_mut(&pkt.vrid).unwrap(); + let mut instance = interface.instances.get_mut(&pkt.vrid).unwrap(); // errors will be modified to occupy the other statistics instance.state.statistics.adv_rcvd += 1; match instance.state.state { - State::Initialize => { - - } + State::Initialize => {} State::Backup => { if pkt.priority == 0 { - tasks::set_master_down_timer(instance, instance.state.skew_time as u64); - } - - else if !instance.config.preempt || pkt.priority >= instance.config.priority { + tasks::set_master_down_timer( + instance, + instance.state.skew_time as u64, + ); + } else if !instance.config.preempt + || pkt.priority >= instance.config.priority + { instance.reset_timer(); } - } State::Master => { if pkt.priority == 0 { instance.reset_timer(); - } - - - else if (pkt.priority > instance.config.priority) || ( - pkt.priority == instance.config.priority - // && check if primary IP of sender is greater than the local primary IP - ){ + } else if (pkt.priority > instance.config.priority) + || ( + pkt.priority == instance.config.priority + // && check if primary IP of sender is greater than the local primary IP + ) + { instance.transition_state(State::Backup); } } diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 87c26086..390a399b 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -115,9 +115,8 @@ impl Instance { } pub(crate) fn reset_timer(&mut self) { - tasks::reset_timer(self); + tasks::reset_timer(self); } - } // ===== impl InstanceState ===== @@ -158,4 +157,3 @@ impl Default for Statistics { } } } - diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index b55932e5..55977644 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -84,7 +84,6 @@ pub struct ProtocolInputChannelsRx { impl Interface { fn send_advert(&self, vrid: u8) { if let Some(instance) = self.instances.get(&vrid) { - // send advertisement then reset the timer. let mut ip_addresses: Vec = vec![]; for addr in &instance.config.virtual_addresses { @@ -102,13 +101,12 @@ impl Interface { checksum: 0, ip_addresses, auth_data: 0, - auth_data2: 0 + auth_data2: 0, }; packet.generate_checksum(); //network::send_packet_vrrp(socket, src, dst, packet); - } - } + } } #[async_trait] @@ -148,7 +146,7 @@ impl ProtocolInstance for Interface { if let Err(error) = match msg { // Received network packet. ProtocolInputMsg::NetRxPacket(msg) => { - events::process_vrrp_packet(self, msg.packet) + events::process_vrrp_packet(self, msg.packet) } } { error.log(); diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 790a6ed6..ff26b0af 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -234,7 +234,6 @@ impl VrrpPacket { return Err(DecodeError::PacketLengthError); } - let checksum = buf.get_u16(); // confirm checksum. checksum position is the third item in 16 bit words diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 6208fe88..d134bced 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -167,7 +167,10 @@ pub(crate) fn set_timer(instance: &mut Instance) { instance.timer = VrrpTimer::Null; } crate::instance::State::Backup => { - set_master_down_timer(instance, instance.state.master_down_interval as u64); + set_master_down_timer( + instance, + instance.state.master_down_interval as u64, + ); } crate::instance::State::Master => { set_adver_timer(instance, instance.config.advertise_interval as u64) @@ -176,37 +179,32 @@ pub(crate) fn set_timer(instance: &mut Instance) { } pub(crate) fn set_master_down_timer(instance: &mut Instance, period: u64) { - let timer = TimeoutTask::new( - Duration::from_secs(period), - move || async move {} - ); + let timer = + TimeoutTask::new(Duration::from_secs(period), move || async move {}); instance.timer = VrrpTimer::MasterDownTimer(timer); } -fn set_adver_timer(instance: &mut Instance, period: u64){ +fn set_adver_timer(instance: &mut Instance, period: u64) { let timer = IntervalTask::new( Duration::from_secs(period), true, - move || async move {} + move || async move {}, ); instance.timer = VrrpTimer::AdverTimer(timer); } - pub(crate) fn reset_timer(instance: &mut Instance) { match instance.timer { VrrpTimer::AdverTimer(ref mut t) => { - t.reset( - Some(Duration::from_secs(instance.config.advertise_interval as u64)), - ); - - }, - VrrpTimer::MasterDownTimer(ref mut t) => { - t.reset( - Some(Duration::from_secs(instance.state.master_down_interval as u64)) - ); - }, + t.reset(Some(Duration::from_secs( + instance.config.advertise_interval as u64, + ))); + } + VrrpTimer::MasterDownTimer(ref mut t) => { + t.reset(Some(Duration::from_secs( + instance.state.master_down_interval as u64, + ))); + } _ => {} } } - From 8afa889d49f7272901c59297b5fc66ae2a66b20c Mon Sep 17 00:00:00 2001 From: weqe Date: Sun, 28 Jul 2024 19:20:08 +0300 Subject: [PATCH 13/22] - Initiate gratutious ARP - Add fetching of MAC from interface.rs --- holo-vrrp/Cargo.toml | 3 ++ holo-vrrp/src/events.rs | 20 +++++----- holo-vrrp/src/instance.rs | 16 +++++++- holo-vrrp/src/interface.rs | 78 ++++++++++++++++++++++++++++++++++++-- holo-vrrp/src/network.rs | 8 ++-- holo-vrrp/src/tasks.rs | 34 +++++------------ 6 files changed, 113 insertions(+), 46 deletions(-) diff --git a/holo-vrrp/Cargo.toml b/holo-vrrp/Cargo.toml index f0d429c3..14fe9c68 100644 --- a/holo-vrrp/Cargo.toml +++ b/holo-vrrp/Cargo.toml @@ -25,6 +25,9 @@ tokio.workspace = true tracing.workspace = true yang2.workspace = true socket2.workspace = true +rtnetlink.workspace = true +futures.workspace = true +netlink-packet-route.workspace = true holo-northbound = { path = "../holo-northbound" } holo-protocol = { path = "../holo-protocol" } diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index 7df665e4..3b1b3dac 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -14,17 +14,8 @@ use crate::tasks; // ===== Network packet receipt ===== -pub(crate) fn process_packet( - _interface: &mut Interface, - _src: IpAddr, - _packet: DecodeResult, -) -> Result<(), Error> { - // TODO - - Ok(()) -} -pub(crate) fn process_vrrp_packet( +pub(crate) fn process_packet( interface: &mut Interface, packet: DecodeResult, ) -> Result<(), Error> { @@ -35,7 +26,14 @@ pub(crate) fn process_vrrp_packet( instance.state.statistics.adv_rcvd += 1; match instance.state.state { - State::Initialize => {} + + // RFC 3768 6.4.1 + State::Initialize => { + + if instance.config.priority == 255 { + interface.send_vrrp_advert(pkt.vrid); + } + } State::Backup => { if pkt.priority == 0 { tasks::set_master_down_timer( diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 390a399b..59a9ce62 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -7,6 +7,7 @@ use core::task; use std::borrow::BorrowMut; use std::net::Ipv4Addr; +use std::time::Duration; use chrono::{DateTime, Utc}; use holo_utils::task::{IntervalTask, TimeoutTask}; @@ -115,8 +116,21 @@ impl Instance { } pub(crate) fn reset_timer(&mut self) { - tasks::reset_timer(self); + match self.timer { + VrrpTimer::AdverTimer(ref mut t) => { + t.reset(Some(Duration::from_secs( + self.config.advertise_interval as u64, + ))); + } + VrrpTimer::MasterDownTimer(ref mut t) => { + t.reset(Some(Duration::from_secs( + self.state.master_down_interval as u64, + ))); + } + _ => {} + } } + } // ===== impl InstanceState ===== diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 55977644..3d61d6da 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -9,6 +9,7 @@ use std::net::Ipv4Addr; use std::sync::Arc; use async_trait::async_trait; +use futures::TryStreamExt; use holo_protocol::{ InstanceChannelsTx, InstanceShared, MessageReceiver, ProtocolInstance, }; @@ -19,11 +20,13 @@ use holo_utils::southbound::InterfaceFlags; use holo_utils::task::Task; use holo_utils::{Receiver, Sender, UnboundedSender}; use ipnetwork::Ipv4Network; +use netlink_packet_route::nlas::link::Nla; +use rtnetlink::new_connection; use tokio::sync::mpsc; use crate::error::{Error, IoError}; use crate::instance::Instance; -use crate::packet::VrrpPacket; +use crate::packet::{ArpPacket, EthernetFrame, VrrpPacket}; use crate::tasks::messages::input::NetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; @@ -82,7 +85,7 @@ pub struct ProtocolInputChannelsRx { // ===== impl Interface ===== impl Interface { - fn send_advert(&self, vrid: u8) { + pub(crate) fn send_vrrp_advert(&self, vrid: u8) { if let Some(instance) = self.instances.get(&vrid) { // send advertisement then reset the timer. let mut ip_addresses: Vec = vec![]; @@ -104,9 +107,76 @@ impl Interface { auth_data2: 0, }; packet.generate_checksum(); - //network::send_packet_vrrp(socket, src, dst, packet); + network::send_packet_vrrp(&self.net.socket_vrrp, packet); } } + + pub(crate) async fn send_gratuitous_arp(&self, vrid: u8) { + let ifname = &self.name; + + if let Some(instance) = self.instances.get(&vrid) { + + // send a gratuitous for each of the + // virutal IP addresses + for addr in instance.config.virtual_addresses.clone() { + + let arp_packet = ArpPacket { + hw_type: 1, + proto_type: 0x0800, + hw_length: 6, + proto_length: 4, + operation: 1, + sender_hw_address: [0x00, 0x00, 0x5e, 0x00, 0x01, vrid], + sender_proto_address: addr.ip().octets(), + target_hw_address: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // broadcast + target_proto_address: addr.ip().octets() + }; + + let eth_mac = self.get_mac(); + let mut mac_arr: &mut [u8; 6] = &mut [0u8; 6]; + if let Some(m_addr) = self.get_mac().await { + for (idx, value) in m_addr.iter().enumerate() { + mac_arr[idx] = *value; + } + } + let eth_frame = EthernetFrame { + ethertype: 0x806, + dst_mac: [0xff; 6], + src_mac: *mac_arr + }; + let _ = network::send_packet_arp( + &self.net.socket_arp, + &self.name, + eth_frame, + arp_packet + ); + + } + + } + } + + // fetches the interfaces mac address. + // This will be required during the sending of the ARP packet. + pub(crate) async fn get_mac(&self) -> Option> { + let ifname = self.name.clone(); + let (connection, handle, _) = new_connection().unwrap(); + tokio::spawn(connection); + + // get the necessary links then we filter. + let mut links = handle.link().get().match_name(ifname).execute(); + if let Some(msg) = links.try_next().await.unwrap() { + // breaking change if you are to upgrade netlink-packet-route + // package + for mac in msg.nlas.clone().into_iter() { + if let Nla::Address(mac) = mac { + return Some(mac) + } + } + } + return None + } + } #[async_trait] @@ -146,7 +216,7 @@ impl ProtocolInstance for Interface { if let Err(error) = match msg { // Received network packet. ProtocolInputMsg::NetRxPacket(msg) => { - events::process_vrrp_packet(self, msg.packet) + events::process_packet(self, msg.packet) } } { error.log(); diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index 3316eeac..a5cd3409 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -62,8 +62,6 @@ pub fn socket_arp(ifname: &str) -> Result { #[cfg(not(feature = "testing"))] pub(crate) async fn send_packet_vrrp( socket: &AsyncFd, - _src: IpAddr, - _dst: IpAddr, packet: VrrpPacket, ) -> Result { let buf: &[u8] = &packet.encode(); @@ -78,9 +76,9 @@ pub(crate) async fn send_packet_vrrp( .map_err(IoError::SendError) } -#[cfg(not(feature = "testing"))] +//#[cfg(not(feature = "testing"))] pub fn send_packet_arp( - sock: Socket, + sock: &AsyncFd, ifname: &str, eth_frame: EthernetFrame, arp_packet: ArpPacket, @@ -146,7 +144,7 @@ pub(crate) async fn write_loop( match msg { NetTxPacketMsg::Vrrp { packet, src, dst } => { if let Err(error) = - send_packet_vrrp(&socket_vrrp, src, dst, packet).await + send_packet_vrrp(&socket_vrrp, packet).await { error.log(); } diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index d134bced..4b83cbeb 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -37,7 +37,7 @@ use crate::network; // +--------------+ // -// BGP inter-task message types. +// VRRP inter-task message types. pub mod messages { use std::net::IpAddr; @@ -173,7 +173,14 @@ pub(crate) fn set_timer(instance: &mut Instance) { ); } crate::instance::State::Master => { - set_adver_timer(instance, instance.config.advertise_interval as u64) + let timer = IntervalTask::new( + Duration::from_secs(instance.config.advertise_interval as u64), + true, + move || async move { + todo!("send VRRP advertisement"); + }, + ); + instance.timer = VrrpTimer::AdverTimer(timer); } } } @@ -184,27 +191,4 @@ pub(crate) fn set_master_down_timer(instance: &mut Instance, period: u64) { instance.timer = VrrpTimer::MasterDownTimer(timer); } -fn set_adver_timer(instance: &mut Instance, period: u64) { - let timer = IntervalTask::new( - Duration::from_secs(period), - true, - move || async move {}, - ); - instance.timer = VrrpTimer::AdverTimer(timer); -} -pub(crate) fn reset_timer(instance: &mut Instance) { - match instance.timer { - VrrpTimer::AdverTimer(ref mut t) => { - t.reset(Some(Duration::from_secs( - instance.config.advertise_interval as u64, - ))); - } - VrrpTimer::MasterDownTimer(ref mut t) => { - t.reset(Some(Duration::from_secs( - instance.state.master_down_interval as u64, - ))); - } - _ => {} - } -} From 1e401fc379f045b22c8483f45df896299d560dfb Mon Sep 17 00:00:00 2001 From: weqe Date: Mon, 29 Jul 2024 23:27:04 +0300 Subject: [PATCH 14/22] Add mac address in notifications. --- holo-interface/src/ibus.rs | 1 + holo-interface/src/interface.rs | 3 +++ holo-interface/src/netlink.rs | 5 ++++- holo-interface/src/northbound/configuration.rs | 2 ++ holo-utils/src/southbound.rs | 1 + 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/holo-interface/src/ibus.rs b/holo-interface/src/ibus.rs index d6585254..aad6a85c 100644 --- a/holo-interface/src/ibus.rs +++ b/holo-interface/src/ibus.rs @@ -76,6 +76,7 @@ pub(crate) fn notify_interface_update(ibus_tx: &IbusSender, iface: &Interface) { ifindex: iface.ifindex.unwrap_or(0), mtu: iface.mtu.unwrap_or(0), flags: iface.flags, + mac_address: iface.config.mac_address.clone(), }); notify(ibus_tx, msg); } diff --git a/holo-interface/src/interface.rs b/holo-interface/src/interface.rs index d03284ad..dcc3887e 100644 --- a/holo-interface/src/interface.rs +++ b/holo-interface/src/interface.rs @@ -139,6 +139,7 @@ impl Interfaces { ifindex: u32, mtu: u32, flags: InterfaceFlags, + mac_address: Vec, netlink_handle: &rtnetlink::Handle, ibus_tx: Option<&IbusSender>, ) { @@ -173,6 +174,7 @@ impl Interfaces { iface.owner.insert(Owner::SYSTEM); iface.mtu = Some(mtu); iface.flags = flags; + iface.config.mac_address = mac_address; // Notify protocol instances about the interface update. // @@ -206,6 +208,7 @@ impl Interfaces { let iface = &self.arena[iface_idx]; iface.apply_config(ifindex, netlink_handle, self).await; } + } None => { // If the interface does not exist, create a new entry. diff --git a/holo-interface/src/netlink.rs b/holo-interface/src/netlink.rs index d32952e5..a353ffe4 100644 --- a/holo-interface/src/netlink.rs +++ b/holo-interface/src/netlink.rs @@ -45,6 +45,8 @@ async fn process_newlink_msg( let ifindex = msg.header.index; let mut ifname = None; let mut mtu = None; + let mut mac_address: Vec = Vec::default(); + let mut flags = InterfaceFlags::empty(); if msg.header.link_layer_type == ARPHRD_LOOPBACK { flags.insert(InterfaceFlags::LOOPBACK); @@ -56,6 +58,7 @@ async fn process_newlink_msg( match nla { Nla::IfName(nla_ifname) => ifname = Some(nla_ifname), Nla::Mtu(nla_mtu) => mtu = Some(nla_mtu), + Nla::Address(addr) => mac_address = addr, _ => (), } } @@ -67,7 +70,7 @@ async fn process_newlink_msg( let ibus_tx = notify.then_some(&master.ibus_tx); master .interfaces - .update(ifname, ifindex, mtu, flags, &master.netlink_handle, ibus_tx) + .update(ifname, ifindex, mtu, flags, mac_address, &master.netlink_handle, ibus_tx) .await; } diff --git a/holo-interface/src/northbound/configuration.rs b/holo-interface/src/northbound/configuration.rs index 62825c75..fa9092a2 100644 --- a/holo-interface/src/northbound/configuration.rs +++ b/holo-interface/src/northbound/configuration.rs @@ -60,6 +60,7 @@ pub struct InterfaceCfg { pub parent: Option, pub vlan_id: Option, pub addr_list: BTreeMap, + pub mac_address: Vec, } // ===== callbacks ===== @@ -461,6 +462,7 @@ impl Default for InterfaceCfg { parent: None, vlan_id: None, addr_list: Default::default(), + mac_address: Default::default(), } } } diff --git a/holo-utils/src/southbound.rs b/holo-utils/src/southbound.rs index 509a0b2c..4d579e69 100644 --- a/holo-utils/src/southbound.rs +++ b/holo-utils/src/southbound.rs @@ -71,6 +71,7 @@ pub struct InterfaceUpdateMsg { pub ifindex: u32, pub mtu: u32, pub flags: InterfaceFlags, + pub mac_address: Vec, } #[derive(Clone, Debug)] From 06106f430ceac6f834b706cea522cfb2d077fc2b Mon Sep 17 00:00:00 2001 From: weqe Date: Mon, 29 Jul 2024 23:34:43 +0300 Subject: [PATCH 15/22] Remove rnetlink and interface calls from the interface.rs file in `holo-vrrp` --- holo-vrrp/Cargo.toml | 3 --- holo-vrrp/src/interface.rs | 29 ----------------------------- 2 files changed, 32 deletions(-) diff --git a/holo-vrrp/Cargo.toml b/holo-vrrp/Cargo.toml index 14fe9c68..f0d429c3 100644 --- a/holo-vrrp/Cargo.toml +++ b/holo-vrrp/Cargo.toml @@ -25,9 +25,6 @@ tokio.workspace = true tracing.workspace = true yang2.workspace = true socket2.workspace = true -rtnetlink.workspace = true -futures.workspace = true -netlink-packet-route.workspace = true holo-northbound = { path = "../holo-northbound" } holo-protocol = { path = "../holo-protocol" } diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 3d61d6da..06f15581 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -9,7 +9,6 @@ use std::net::Ipv4Addr; use std::sync::Arc; use async_trait::async_trait; -use futures::TryStreamExt; use holo_protocol::{ InstanceChannelsTx, InstanceShared, MessageReceiver, ProtocolInstance, }; @@ -20,8 +19,6 @@ use holo_utils::southbound::InterfaceFlags; use holo_utils::task::Task; use holo_utils::{Receiver, Sender, UnboundedSender}; use ipnetwork::Ipv4Network; -use netlink_packet_route::nlas::link::Nla; -use rtnetlink::new_connection; use tokio::sync::mpsc; use crate::error::{Error, IoError}; @@ -132,13 +129,7 @@ impl Interface { target_proto_address: addr.ip().octets() }; - let eth_mac = self.get_mac(); let mut mac_arr: &mut [u8; 6] = &mut [0u8; 6]; - if let Some(m_addr) = self.get_mac().await { - for (idx, value) in m_addr.iter().enumerate() { - mac_arr[idx] = *value; - } - } let eth_frame = EthernetFrame { ethertype: 0x806, dst_mac: [0xff; 6], @@ -156,26 +147,6 @@ impl Interface { } } - // fetches the interfaces mac address. - // This will be required during the sending of the ARP packet. - pub(crate) async fn get_mac(&self) -> Option> { - let ifname = self.name.clone(); - let (connection, handle, _) = new_connection().unwrap(); - tokio::spawn(connection); - - // get the necessary links then we filter. - let mut links = handle.link().get().match_name(ifname).execute(); - if let Some(msg) = links.try_next().await.unwrap() { - // breaking change if you are to upgrade netlink-packet-route - // package - for mac in msg.nlas.clone().into_iter() { - if let Nla::Address(mac) = mac { - return Some(mac) - } - } - } - return None - } } From 8ddc171788820d6db04da21e87cabfdbef05f458 Mon Sep 17 00:00:00 2001 From: weqe Date: Mon, 29 Jul 2024 23:48:27 +0300 Subject: [PATCH 16/22] Complete gARP --- holo-vrrp/src/interface.rs | 9 +++++++-- holo-vrrp/src/southbound.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 06f15581..01526bba 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -53,6 +53,8 @@ pub struct InterfaceSys { pub ifindex: Option, // Interface IPv4 addresses. pub addresses: BTreeSet, + // interface Mac Address + pub mac_address: Vec, } #[derive(Debug)] @@ -129,11 +131,14 @@ impl Interface { target_proto_address: addr.ip().octets() }; - let mut mac_arr: &mut [u8; 6] = &mut [0u8; 6]; + let mut mac_addr: &mut [u8; 6] = &mut [0u8; 6]; + for (idx, item) in self.system.mac_address.iter().enumerate() { + mac_addr[idx] = *item; + } let eth_frame = EthernetFrame { ethertype: 0x806, dst_mac: [0xff; 6], - src_mac: *mac_arr + src_mac: *mac_addr }; let _ = network::send_packet_arp( &self.net.socket_arp, diff --git a/holo-vrrp/src/southbound.rs b/holo-vrrp/src/southbound.rs index 5735afad..b7ba2c49 100644 --- a/holo-vrrp/src/southbound.rs +++ b/holo-vrrp/src/southbound.rs @@ -21,7 +21,7 @@ pub(crate) fn process_iface_update( iface.system.flags = msg.flags; iface.system.ifindex = Some(msg.ifindex); - + iface.system.mac_address = msg.mac_address; // TODO: trigger protocol event? } From beff51095c6732cb9602b95f94a235106668b0bb Mon Sep 17 00:00:00 2001 From: weqe Date: Tue, 30 Jul 2024 14:20:29 +0300 Subject: [PATCH 17/22] Move macaddress from InterfaceCfg to Interface Change event code for borrow checker purposes Minor changes in `holo-vrrp` --- holo-interface/src/ibus.rs | 2 +- holo-interface/src/interface.rs | 6 +- .../src/northbound/configuration.rs | 2 - holo-vrrp/src/error.rs | 2 +- holo-vrrp/src/events.rs | 118 ++++++++++++------ holo-vrrp/src/instance.rs | 2 +- holo-vrrp/src/interface.rs | 6 +- 7 files changed, 94 insertions(+), 44 deletions(-) diff --git a/holo-interface/src/ibus.rs b/holo-interface/src/ibus.rs index aad6a85c..f63edeb8 100644 --- a/holo-interface/src/ibus.rs +++ b/holo-interface/src/ibus.rs @@ -76,7 +76,7 @@ pub(crate) fn notify_interface_update(ibus_tx: &IbusSender, iface: &Interface) { ifindex: iface.ifindex.unwrap_or(0), mtu: iface.mtu.unwrap_or(0), flags: iface.flags, - mac_address: iface.config.mac_address.clone(), + mac_address: iface.mac_address.clone(), }); notify(ibus_tx, msg); } diff --git a/holo-interface/src/interface.rs b/holo-interface/src/interface.rs index dcc3887e..f28329bd 100644 --- a/holo-interface/src/interface.rs +++ b/holo-interface/src/interface.rs @@ -38,6 +38,7 @@ pub struct Interface { pub mtu: Option, pub flags: InterfaceFlags, pub addresses: BTreeMap, + pub mac_address: Vec, pub owner: Owner, pub vrrp: Option, } @@ -126,6 +127,7 @@ impl Interfaces { addresses: Default::default(), owner: Owner::CONFIG, vrrp: None, + mac_address: Default::default(), }; let iface_idx = self.arena.insert(iface); @@ -156,6 +158,7 @@ impl Interfaces { if iface.name == ifname && iface.mtu == Some(mtu) && iface.flags == flags + && iface.mac_address == mac_address { return; } @@ -174,7 +177,7 @@ impl Interfaces { iface.owner.insert(Owner::SYSTEM); iface.mtu = Some(mtu); iface.flags = flags; - iface.config.mac_address = mac_address; + iface.mac_address = mac_address; // Notify protocol instances about the interface update. // @@ -221,6 +224,7 @@ impl Interfaces { addresses: Default::default(), owner: Owner::SYSTEM, vrrp: None, + mac_address: Default::default(), }; // Notify protocol instances about the interface update. diff --git a/holo-interface/src/northbound/configuration.rs b/holo-interface/src/northbound/configuration.rs index fa9092a2..62825c75 100644 --- a/holo-interface/src/northbound/configuration.rs +++ b/holo-interface/src/northbound/configuration.rs @@ -60,7 +60,6 @@ pub struct InterfaceCfg { pub parent: Option, pub vlan_id: Option, pub addr_list: BTreeMap, - pub mac_address: Vec, } // ===== callbacks ===== @@ -462,7 +461,6 @@ impl Default for InterfaceCfg { parent: None, vlan_id: None, addr_list: Default::default(), - mac_address: Default::default(), } } } diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index e9c5d2ad..6d7e2588 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -15,7 +15,7 @@ pub enum Error { // I/O errors IoError(IoError), - // ietf yang specific errors + // vrrp-ietf-yang-2018-03-13 specific errors GlobalError(GlobalError), VirtualRouterError(VirtualRouterError), } diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index 3b1b3dac..5cdb6bdb 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -1,61 +1,105 @@ // -// Copyright (c) The Holo Core Contributors -// // SPDX-License-Identifier: MIT // +use std::borrow::{Borrow, BorrowMut}; use std::net::IpAddr; use crate::error::Error; -use crate::instance::{Instance, State}; +use crate::instance::{Instance, InstanceState, State}; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpPacket}; use crate::tasks; // ===== Network packet receipt ===== - - +// pub(crate) fn process_packet( interface: &mut Interface, packet: DecodeResult, ) -> Result<(), Error> { - let pkt = packet.unwrap(); - let mut instance = interface.instances.get_mut(&pkt.vrid).unwrap(); + // Handle packet decoding errors + let pkt = match packet { + Ok(p) => p, + Err(e) => return Ok(()), // or handle the error appropriately + }; + + // To collect actions to be executed later + enum Action { + SendVrrpAdvert(u8), + SendGratuitousArp(u8), + ChangeState(u8, State), + SetMasterDownTimer(u8, u64), + ResetTimer(u8), + } + + let mut actions = Vec::new(); - // errors will be modified to occupy the other statistics - instance.state.statistics.adv_rcvd += 1; + { + // Handle missing instance + let instance = match interface.instances.get_mut(&pkt.vrid) { + Some(inst) => inst, + None => return Ok(()), // or handle the error appropriately + }; - match instance.state.state { - - // RFC 3768 6.4.1 - State::Initialize => { - - if instance.config.priority == 255 { - interface.send_vrrp_advert(pkt.vrid); + // Update statistics + instance.state.statistics.adv_rcvd += 1; + + // Handle the current state + match instance.state.state { + State::Initialize => { + if instance.config.priority == 255 { + actions.push(Action::SendVrrpAdvert(pkt.vrid)); + actions.push(Action::SendGratuitousArp(pkt.vrid)); + actions.push(Action::ChangeState(pkt.vrid, State::Master)); + } } - } - State::Backup => { - if pkt.priority == 0 { - tasks::set_master_down_timer( - instance, - instance.state.skew_time as u64, - ); - } else if !instance.config.preempt - || pkt.priority >= instance.config.priority - { - instance.reset_timer(); + State::Backup => { + if pkt.priority == 0 { + actions.push(Action::SetMasterDownTimer(pkt.vrid, instance.state.skew_time as u64)); + } else if !instance.config.preempt + || pkt.priority >= instance.config.priority + { + actions.push(Action::ResetTimer(pkt.vrid)); + } + } + State::Master => { + if pkt.priority == 0 { + actions.push(Action::ResetTimer(pkt.vrid)); + } else if pkt.priority > instance.config.priority + || (pkt.priority == instance.config.priority + // && check if primary IP of sender is greater than the local primary IP + ) { + actions.push(Action::ChangeState(pkt.vrid, State::Backup)); + } } } - State::Master => { - if pkt.priority == 0 { - instance.reset_timer(); - } else if (pkt.priority > instance.config.priority) - || ( - pkt.priority == instance.config.priority - // && check if primary IP of sender is greater than the local primary IP - ) - { - instance.transition_state(State::Backup); + } + + // Execute collected actions + for action in actions { + match action { + Action::SendVrrpAdvert(vrid) => interface.send_vrrp_advert(vrid), + Action::SendGratuitousArp(vrid) => { + tokio::runtime::Builder::new_current_thread() + .build() + .unwrap() + .block_on(interface.send_gratuitous_arp(vrid)); + //interface.send_gratuitous_arp(vrid).await + }, + Action::ChangeState(vrid, state) => { + if let Some(instance) = interface.instances.get_mut(&vrid) { + instance.change_state(state); + } + } + Action::SetMasterDownTimer(vrid, time) => { + if let Some(instance) = interface.instances.get_mut(&vrid) { + tasks::set_master_down_timer(instance, time); + } + } + Action::ResetTimer(vrid) => { + if let Some(instance) = interface.instances.get_mut(&vrid) { + instance.reset_timer(); + } } } } diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 59a9ce62..e795ac49 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -110,7 +110,7 @@ impl Instance { } } - pub(crate) fn transition_state(&mut self, state: State) { + pub(crate) fn change_state(&mut self, state: State) { self.state.state = state; tasks::set_timer(self); } diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 01526bba..f245afdb 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -121,11 +121,15 @@ impl Interface { let arp_packet = ArpPacket { hw_type: 1, + // for Ipv4 proto_type: 0x0800, + // mac address length hw_length: 6, proto_length: 4, operation: 1, - sender_hw_address: [0x00, 0x00, 0x5e, 0x00, 0x01, vrid], + // sender hw address is virtual mac. + // https://datatracker.ietf.org/doc/html/rfc3768#section-7.3 + sender_hw_address: [0x00, 0x00, 0x5e, 0x00, 0x01, vrid], sender_proto_address: addr.ip().octets(), target_hw_address: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // broadcast target_proto_address: addr.ip().octets() From 50ccef2d2f40ba928b513ab3fbeb81059984ab1f Mon Sep 17 00:00:00 2001 From: weqe Date: Tue, 30 Jul 2024 15:07:41 +0300 Subject: [PATCH 18/22] Change mac address datatype to [u8; 6] enhance events.rs with functions. --- holo-interface/src/interface.rs | 4 +- holo-interface/src/netlink.rs | 9 ++- holo-utils/src/southbound.rs | 2 +- holo-vrrp/src/error.rs | 5 ++ holo-vrrp/src/events.rs | 111 ++++++++++++++++++-------------- holo-vrrp/src/interface.rs | 9 +-- 6 files changed, 80 insertions(+), 60 deletions(-) diff --git a/holo-interface/src/interface.rs b/holo-interface/src/interface.rs index f28329bd..6f4adbbf 100644 --- a/holo-interface/src/interface.rs +++ b/holo-interface/src/interface.rs @@ -38,7 +38,7 @@ pub struct Interface { pub mtu: Option, pub flags: InterfaceFlags, pub addresses: BTreeMap, - pub mac_address: Vec, + pub mac_address: [u8; 6], pub owner: Owner, pub vrrp: Option, } @@ -141,7 +141,7 @@ impl Interfaces { ifindex: u32, mtu: u32, flags: InterfaceFlags, - mac_address: Vec, + mac_address: [u8; 6], netlink_handle: &rtnetlink::Handle, ibus_tx: Option<&IbusSender>, ) { diff --git a/holo-interface/src/netlink.rs b/holo-interface/src/netlink.rs index a353ffe4..64e72e5b 100644 --- a/holo-interface/src/netlink.rs +++ b/holo-interface/src/netlink.rs @@ -45,7 +45,7 @@ async fn process_newlink_msg( let ifindex = msg.header.index; let mut ifname = None; let mut mtu = None; - let mut mac_address: Vec = Vec::default(); + let mut mac_address: [u8; 6] = [0u8; 6]; let mut flags = InterfaceFlags::empty(); if msg.header.link_layer_type == ARPHRD_LOOPBACK { @@ -58,7 +58,12 @@ async fn process_newlink_msg( match nla { Nla::IfName(nla_ifname) => ifname = Some(nla_ifname), Nla::Mtu(nla_mtu) => mtu = Some(nla_mtu), - Nla::Address(addr) => mac_address = addr, + Nla::Address(addr) => { + mac_address = match addr.try_into() { + Ok(a) => a, + Err(e) => [0u8; 6] + }; + }, _ => (), } } diff --git a/holo-utils/src/southbound.rs b/holo-utils/src/southbound.rs index 4d579e69..3b22880a 100644 --- a/holo-utils/src/southbound.rs +++ b/holo-utils/src/southbound.rs @@ -71,7 +71,7 @@ pub struct InterfaceUpdateMsg { pub ifindex: u32, pub mtu: u32, pub flags: InterfaceFlags, - pub mac_address: Vec, + pub mac_address: [u8; 6], } #[derive(Clone, Debug)] diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index 6d7e2588..54cae245 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -14,6 +14,7 @@ use tracing::{warn, warn_span}; pub enum Error { // I/O errors IoError(IoError), + InterfaceError(String), // vrrp-ietf-yang-2018-03-13 specific errors GlobalError(GlobalError), @@ -54,6 +55,9 @@ impl Error { Error::IoError(error) => { error.log(); } + Error::InterfaceError(error) => { + warn_span!("vrrp_interface_error").in_scope(|| warn!(error)); + } Error::GlobalError(error) => { match error { GlobalError::ChecksumError => { @@ -91,6 +95,7 @@ impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::IoError(error) => std::fmt::Display::fmt(error, f), + Error::InterfaceError(error) => write!(f, "{}", error), Error::GlobalError(error) => std::fmt::Display::fmt(error, f), Error::VirtualRouterError(error) => { std::fmt::Display::fmt(error, f) diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index 5cdb6bdb..8c04724e 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -5,77 +5,93 @@ use std::borrow::{Borrow, BorrowMut}; use std::net::IpAddr; +use libc::wait; + use crate::error::Error; use crate::instance::{Instance, InstanceState, State}; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpPacket}; use crate::tasks; +// To collect actions to be executed later +enum Action { + SendVrrpAdvert(u8), + SendGratuitousArp(u8), + ChangeState(u8, State), + SetMasterDownTimer(u8, u64), + ResetTimer(u8), +} + // ===== Network packet receipt ===== -// pub(crate) fn process_packet( interface: &mut Interface, packet: DecodeResult, ) -> Result<(), Error> { // Handle packet decoding errors - let pkt = match packet { - Ok(p) => p, - Err(e) => return Ok(()), // or handle the error appropriately + let pkt = packet.unwrap(); + + // collect the actions that are required + let mut actions = match get_actions(interface, pkt) { + Ok(a) => a, + Err(e) => return Err(e) }; + + // execute all collected actions + handle_actions(interface, actions); + Ok(()) +} - // To collect actions to be executed later - enum Action { - SendVrrpAdvert(u8), - SendGratuitousArp(u8), - ChangeState(u8, State), - SetMasterDownTimer(u8, u64), - ResetTimer(u8), - } +// gets all the actions that are required to be done bacsed on the interface +// configs and incoming packet +fn get_actions(interface: &mut Interface, packet: VrrpPacket) -> Result, Error> { let mut actions = Vec::new(); - { - // Handle missing instance - let instance = match interface.instances.get_mut(&pkt.vrid) { - Some(inst) => inst, - None => return Ok(()), // or handle the error appropriately - }; + // Handle missing instance + let instance = match interface.instances.get_mut(&packet.vrid) { + Some(inst) => inst, + None => return Err( + Error::InterfaceError(String::from("unable to fetch VRRP instance from interface")) + ), + }; - // Update statistics - instance.state.statistics.adv_rcvd += 1; + // Update statistics + instance.state.statistics.adv_rcvd += 1; - // Handle the current state - match instance.state.state { - State::Initialize => { - if instance.config.priority == 255 { - actions.push(Action::SendVrrpAdvert(pkt.vrid)); - actions.push(Action::SendGratuitousArp(pkt.vrid)); - actions.push(Action::ChangeState(pkt.vrid, State::Master)); - } + // Handle the current state + match instance.state.state { + State::Initialize => { + if instance.config.priority == 255 { + actions.push(Action::SendVrrpAdvert(packet.vrid)); + actions.push(Action::SendGratuitousArp(packet.vrid)); + actions.push(Action::ChangeState(packet.vrid, State::Master)); } - State::Backup => { - if pkt.priority == 0 { - actions.push(Action::SetMasterDownTimer(pkt.vrid, instance.state.skew_time as u64)); - } else if !instance.config.preempt - || pkt.priority >= instance.config.priority - { - actions.push(Action::ResetTimer(pkt.vrid)); - } + } + State::Backup => { + if packet.priority == 0 { + actions.push(Action::SetMasterDownTimer(packet.vrid, instance.state.skew_time as u64)); + } else if !instance.config.preempt + || packet.priority >= instance.config.priority + { + actions.push(Action::ResetTimer(packet.vrid)); } - State::Master => { - if pkt.priority == 0 { - actions.push(Action::ResetTimer(pkt.vrid)); - } else if pkt.priority > instance.config.priority - || (pkt.priority == instance.config.priority - // && check if primary IP of sender is greater than the local primary IP - ) { - actions.push(Action::ChangeState(pkt.vrid, State::Backup)); - } + } + State::Master => { + if packet.priority == 0 { + actions.push(Action::ResetTimer(packet.vrid)); + } else if packet.priority > instance.config.priority + || (packet.priority == instance.config.priority + // && check if primary IP of sender is greater than the local primary IP + ) { + actions.push(Action::ChangeState(packet.vrid, State::Backup)); } } } + return Ok(actions) +} + - // Execute collected actions +fn handle_actions(interface: &mut Interface, actions: Vec) { for action in actions { match action { Action::SendVrrpAdvert(vrid) => interface.send_vrrp_advert(vrid), @@ -84,7 +100,6 @@ pub(crate) fn process_packet( .build() .unwrap() .block_on(interface.send_gratuitous_arp(vrid)); - //interface.send_gratuitous_arp(vrid).await }, Action::ChangeState(vrid, state) => { if let Some(instance) = interface.instances.get_mut(&vrid) { @@ -103,6 +118,4 @@ pub(crate) fn process_packet( } } } - - Ok(()) } diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index f245afdb..84e9aeb2 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -54,7 +54,7 @@ pub struct InterfaceSys { // Interface IPv4 addresses. pub addresses: BTreeSet, // interface Mac Address - pub mac_address: Vec, + pub mac_address: [u8; 6], } #[derive(Debug)] @@ -135,14 +135,11 @@ impl Interface { target_proto_address: addr.ip().octets() }; - let mut mac_addr: &mut [u8; 6] = &mut [0u8; 6]; - for (idx, item) in self.system.mac_address.iter().enumerate() { - mac_addr[idx] = *item; - } + let mut mac_addr = self.system.mac_address.clone(); let eth_frame = EthernetFrame { ethertype: 0x806, dst_mac: [0xff; 6], - src_mac: *mac_addr + src_mac: mac_addr }; let _ = network::send_packet_arp( &self.net.socket_arp, From c17e8c2bd067a83471b2468535c807519b4cdb45 Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 1 Aug 2024 01:21:54 +0300 Subject: [PATCH 19/22] Complete the vrrp advertisement handling. --- holo-vrrp/src/events.rs | 170 ++++++++++++++++++++++++------------- holo-vrrp/src/instance.rs | 9 +- holo-vrrp/src/interface.rs | 28 +++++- holo-vrrp/src/tasks.rs | 72 ++++++++++------ 4 files changed, 181 insertions(+), 98 deletions(-) diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index 8c04724e..c1b5d796 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -2,28 +2,28 @@ // SPDX-License-Identifier: MIT // +use core::task; use std::borrow::{Borrow, BorrowMut}; use std::net::IpAddr; - -use libc::wait; +use std::time::Duration; use crate::error::Error; -use crate::instance::{Instance, InstanceState, State}; +use crate::instance::{self, Instance, InstanceState, State}; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpPacket}; use crate::tasks; // To collect actions to be executed later enum Action { - SendVrrpAdvert(u8), - SendGratuitousArp(u8), - ChangeState(u8, State), - SetMasterDownTimer(u8, u64), - ResetTimer(u8), + // described in 6.4.1 part 1. Is when the instance owns the + // IP addresses associated with the virtual router + Initialize(VrrpPacket), + Backup(VrrpPacket), + Master(VrrpPacket), } // ===== Network packet receipt ===== -pub(crate) fn process_packet( +pub(crate) fn process_vrrp_packet( interface: &mut Interface, packet: DecodeResult, ) -> Result<(), Error> { @@ -31,22 +31,20 @@ pub(crate) fn process_packet( let pkt = packet.unwrap(); // collect the actions that are required - let mut actions = match get_actions(interface, pkt) { + let mut action = match get_action(interface, pkt) { Ok(a) => a, Err(e) => return Err(e) }; // execute all collected actions - handle_actions(interface, actions); + handle_actions(interface, action); Ok(()) } // gets all the actions that are required to be done bacsed on the interface // configs and incoming packet -fn get_actions(interface: &mut Interface, packet: VrrpPacket) -> Result, Error> { - let mut actions = Vec::new(); - +fn get_action(interface: &mut Interface, packet: VrrpPacket) -> Result { // Handle missing instance let instance = match interface.instances.get_mut(&packet.vrid) { Some(inst) => inst, @@ -60,62 +58,114 @@ fn get_actions(interface: &mut Interface, packet: VrrpPacket) -> Result { - if instance.config.priority == 255 { - actions.push(Action::SendVrrpAdvert(packet.vrid)); - actions.push(Action::SendGratuitousArp(packet.vrid)); - actions.push(Action::ChangeState(packet.vrid, State::Master)); - } - } - State::Backup => { - if packet.priority == 0 { - actions.push(Action::SetMasterDownTimer(packet.vrid, instance.state.skew_time as u64)); - } else if !instance.config.preempt - || packet.priority >= instance.config.priority - { - actions.push(Action::ResetTimer(packet.vrid)); - } - } - State::Master => { - if packet.priority == 0 { - actions.push(Action::ResetTimer(packet.vrid)); - } else if packet.priority > instance.config.priority - || (packet.priority == instance.config.priority - // && check if primary IP of sender is greater than the local primary IP - ) { - actions.push(Action::ChangeState(packet.vrid, State::Backup)); - } - } + State::Initialize => return Ok(Action::Initialize(packet)), + State::Backup => return Ok(Action::Backup(packet)), + State::Master => return Ok(Action::Master(packet)), } - return Ok(actions) } -fn handle_actions(interface: &mut Interface, actions: Vec) { - for action in actions { - match action { - Action::SendVrrpAdvert(vrid) => interface.send_vrrp_advert(vrid), - Action::SendGratuitousArp(vrid) => { - tokio::runtime::Builder::new_current_thread() - .build() - .unwrap() - .block_on(interface.send_gratuitous_arp(vrid)); - }, - Action::ChangeState(vrid, state) => { - if let Some(instance) = interface.instances.get_mut(&vrid) { - instance.change_state(state); - } +fn handle_actions(interface: &mut Interface, action: Action) { + match action { + Action::Initialize(pkt) => { + let vrid = pkt.vrid; + if vrid == 255 { + interface.send_vrrp_advert(vrid); + interface.send_gratuitous_arp(vrid); + interface.change_state(vrid, State::Master); + } else { + interface.change_state(vrid, State::Backup); } - Action::SetMasterDownTimer(vrid, time) => { - if let Some(instance) = interface.instances.get_mut(&vrid) { - tasks::set_master_down_timer(instance, time); + } + Action::Backup(pkt) => { + let vrid = pkt.vrid; + + if let Some(instance) = interface.instances.get_mut(&vrid) { + if pkt.priority == 0 { + let duration = Duration::from_secs_f32(instance.state.skew_time); + tasks::set_master_down_timer(interface, vrid, duration); + } + else { + // RFC 3768 Section 6.4.2 + // If Preempt Mode if False, or if the priority in the ADVERTISEMENT is + // greater than or equal to local priority then: + if (instance.config.preempt == false) + || (pkt.priority > instance.config.priority){ + instance.reset_timer(); + } + + // drop the packet + else { + return + } } } - Action::ResetTimer(vrid) => { - if let Some(instance) = interface.instances.get_mut(&vrid) { + + } + + Action::Master(pkt) => { + let vrid = pkt.vrid; + let mut send_ad = false; + if let Some(instance) = interface.instances.get_mut(&vrid) { + + if pkt.priority == 0 { + send_ad = true; + + instance.reset_timer(); } + + else if (pkt.priority > instance.config.priority) + // TODO: in RFC 3768 page 18, we have a requirement, where If the priority + // in the ADVERTISEMENT is equal to the local Priority and the primary IP + // Address of the sender is greater than the local primary IP Address, then we + // proceed. + // + // We can get our primary IP address, but purely from the VRRP packet we cannot + // get our senders primary. + // + { + interface.change_state(vrid, State::Backup); + } + + else { + return + } } + + if send_ad { + interface.send_vrrp_advert(vrid); + } + } } } + +// ====== Handle Master Down Timer ===== +// This is called when the master down timer fires. +// Basically When the Instance master down timer +// ticks down. +// +// RFC 3768 : Section 6.4.2 +// 'If the Master_Down_timer fires' +pub(crate) fn handle_master_down_timer( + interface: &mut Interface, + vrid: u8 +) -> Result<(), Error>{ + interface.send_vrrp_advert(vrid); + interface.send_gratuitous_arp(vrid); + + let instance: &mut Instance = match interface.instances.get_mut(&vrid) { + Some(i) => i, + None => { + return Err(Error::InterfaceError(String::from( + "unable to get VRRP instance from interface" + ))); + } + }; + interface.change_state(vrid, State::Master); + + Ok(()) +} + + diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index e795ac49..6094e486 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -42,7 +42,7 @@ pub struct InstanceState { pub up_time: Option>, pub last_event: Event, pub new_master_reason: MasterReason, - pub skew_time: u32, + pub skew_time: f32, pub master_down_interval: u32, // TODO: interval/timer tasks @@ -110,11 +110,6 @@ impl Instance { } } - pub(crate) fn change_state(&mut self, state: State) { - self.state.state = state; - tasks::set_timer(self); - } - pub(crate) fn reset_timer(&mut self) { match self.timer { VrrpTimer::AdverTimer(ref mut t) => { @@ -144,7 +139,7 @@ impl InstanceState { last_event: Event::None, new_master_reason: MasterReason::NotMaster, statistics: Default::default(), - skew_time: 0, + skew_time: 0.0, master_down_interval: 0, } } diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 84e9aeb2..0107db25 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -22,9 +22,9 @@ use ipnetwork::Ipv4Network; use tokio::sync::mpsc; use crate::error::{Error, IoError}; -use crate::instance::Instance; +use crate::instance::{Instance, State}; use crate::packet::{ArpPacket, EthernetFrame, VrrpPacket}; -use crate::tasks::messages::input::NetRxPacketMsg; +use crate::tasks::messages::input::{MasterDownTimerMsg, NetRxPacketMsg}; use crate::tasks::messages::output::NetTxPacketMsg; use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; use crate::{events, network, southbound, tasks}; @@ -73,17 +73,28 @@ pub struct InterfaceNet { pub struct ProtocolInputChannelsTx { // Packet Rx event. pub net_packet_rx: Sender, + // Master Down event + pub master_down_timer: Sender, } #[derive(Debug)] pub struct ProtocolInputChannelsRx { // Packet Rx event. pub net_packet_rx: Receiver, + // Master Down event + pub master_down_timer: Receiver, } // ===== impl Interface ===== impl Interface { + pub(crate) fn change_state(&mut self, vrid: u8, state: State) { + if let Some(instance) = self.instances.get_mut(&vrid) { + instance.state.state = state; + tasks::set_timer(self, vrid); + } + } + pub(crate) fn send_vrrp_advert(&self, vrid: u8) { if let Some(instance) = self.instances.get(&vrid) { // send advertisement then reset the timer. @@ -110,7 +121,7 @@ impl Interface { } } - pub(crate) async fn send_gratuitous_arp(&self, vrid: u8) { + pub(crate) fn send_gratuitous_arp(&self, vrid: u8) { let ifname = &self.name; if let Some(instance) = self.instances.get(&vrid) { @@ -193,7 +204,10 @@ impl ProtocolInstance for Interface { if let Err(error) = match msg { // Received network packet. ProtocolInputMsg::NetRxPacket(msg) => { - events::process_packet(self, msg.packet) + events::process_vrrp_packet(self, msg.packet) + } + ProtocolInputMsg::MasterDownTimer(msg) => { + events::handle_master_down_timer(self, msg.vrid) } } { error.log(); @@ -203,12 +217,15 @@ impl ProtocolInstance for Interface { fn protocol_input_channels( ) -> (ProtocolInputChannelsTx, ProtocolInputChannelsRx) { let (net_packet_rxp, net_packet_rxc) = mpsc::channel(4); + let (master_down_timerp, master_down_timerc) = mpsc::channel(4); let tx = ProtocolInputChannelsTx { net_packet_rx: net_packet_rxp, + master_down_timer: master_down_timerp, }; let rx = ProtocolInputChannelsRx { net_packet_rx: net_packet_rxc, + master_down_timer: master_down_timerc, }; (tx, rx) @@ -275,6 +292,9 @@ impl MessageReceiver for ProtocolInputChannelsRx { msg = self.net_packet_rx.recv() => { msg.map(ProtocolInputMsg::NetRxPacket) } + msg = self.master_down_timer.recv() => { + msg.map(ProtocolInputMsg::MasterDownTimer) + } } } } diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 4b83cbeb..b97440f2 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -11,9 +11,11 @@ use std::time::Duration; use holo_utils::socket::{AsyncFd, Socket}; use holo_utils::task::{IntervalTask, Task, TimeoutTask}; use holo_utils::{Sender, UnboundedReceiver}; +use messages::input::MasterDownTimerMsg; use tracing::{debug_span, Instrument}; use crate::instance::{Instance, VrrpTimer}; +use crate::interface::Interface; use crate::network; // @@ -25,7 +27,7 @@ use crate::network; // | | // northbound_rx (1x) V | (1x) northbound_tx // +--------------+ -// | | +// master_down_timer (Nx) -> | | -> (Nx) master_down_timer // net_rx (Nx) -> | instance | -> (Nx) net_tx // | | // +--------------+ @@ -56,7 +58,7 @@ pub mod messages { #[derive(Debug, Deserialize, Serialize)] pub enum ProtocolMsg { NetRxPacket(NetRxPacketMsg), - // TODO + MasterDownTimer(MasterDownTimerMsg), } #[derive(Debug, Deserialize, Serialize)] @@ -64,6 +66,11 @@ pub mod messages { pub src: IpAddr, pub packet: Result, } + + #[derive(Debug, Deserialize, Serialize)] + pub struct MasterDownTimerMsg { + pub vrid: u8, + } } // Output messages (main task -> child task). @@ -161,34 +168,45 @@ pub(crate) fn net_tx( } // handling the timers... -pub(crate) fn set_timer(instance: &mut Instance) { - match instance.state.state { - crate::instance::State::Initialize => { - instance.timer = VrrpTimer::Null; - } - crate::instance::State::Backup => { - set_master_down_timer( - instance, - instance.state.master_down_interval as u64, - ); - } - crate::instance::State::Master => { - let timer = IntervalTask::new( - Duration::from_secs(instance.config.advertise_interval as u64), - true, - move || async move { - todo!("send VRRP advertisement"); - }, - ); - instance.timer = VrrpTimer::AdverTimer(timer); +pub(crate) fn set_timer( interface: &mut Interface, vrid: u8 +) { + if let Some(instance) = interface.instances.get_mut(&vrid){ + match instance.state.state { + crate::instance::State::Initialize => { + instance.timer = VrrpTimer::Null; + } + crate::instance::State::Backup => { + let duration = Duration::from_secs(instance.state.master_down_interval as u64); + set_master_down_timer( + interface, + vrid, + duration + ); + } + crate::instance::State::Master => { + let timer = IntervalTask::new( + Duration::from_secs(instance.config.advertise_interval as u64), + true, + move || async move { + todo!("send VRRP advertisement"); + }, + ); + instance.timer = VrrpTimer::AdverTimer(timer); + } } } -} -pub(crate) fn set_master_down_timer(instance: &mut Instance, period: u64) { - let timer = - TimeoutTask::new(Duration::from_secs(period), move || async move {}); - instance.timer = VrrpTimer::MasterDownTimer(timer); } +// ==== Set Master Down Timer ==== +pub(crate) fn set_master_down_timer( + interface: &mut Interface, vrid: u8, duration: Duration // period: u64 +) { + let instance = interface.instances.get_mut(&vrid).unwrap(); + let tx = interface.tx.protocol_input.master_down_timer.clone(); + let timer = TimeoutTask::new(duration, move || async move { + tx.send(messages::input::MasterDownTimerMsg{ vrid }); + }); + instance.timer = VrrpTimer::MasterDownTimer(timer); +} From ad00284bbf05e20675dbcc94a5fcdb0de937a12e Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 1 Aug 2024 02:55:31 +0300 Subject: [PATCH 20/22] Cargo fmt --- holo-interface/src/interface.rs | 1 - holo-interface/src/netlink.rs | 16 +++++-- holo-vrrp/src/error.rs | 2 +- holo-vrrp/src/events.rs | 85 +++++++++++++++------------------ holo-vrrp/src/instance.rs | 1 - holo-vrrp/src/interface.rs | 36 ++++++-------- holo-vrrp/src/network.rs | 3 +- holo-vrrp/src/tasks.rs | 26 +++++----- 8 files changed, 81 insertions(+), 89 deletions(-) diff --git a/holo-interface/src/interface.rs b/holo-interface/src/interface.rs index 6f4adbbf..b11835cf 100644 --- a/holo-interface/src/interface.rs +++ b/holo-interface/src/interface.rs @@ -211,7 +211,6 @@ impl Interfaces { let iface = &self.arena[iface_idx]; iface.apply_config(ifindex, netlink_handle, self).await; } - } None => { // If the interface does not exist, create a new entry. diff --git a/holo-interface/src/netlink.rs b/holo-interface/src/netlink.rs index 64e72e5b..5548b71e 100644 --- a/holo-interface/src/netlink.rs +++ b/holo-interface/src/netlink.rs @@ -45,7 +45,7 @@ async fn process_newlink_msg( let ifindex = msg.header.index; let mut ifname = None; let mut mtu = None; - let mut mac_address: [u8; 6] = [0u8; 6]; + let mut mac_address: [u8; 6] = [0u8; 6]; let mut flags = InterfaceFlags::empty(); if msg.header.link_layer_type == ARPHRD_LOOPBACK { @@ -61,9 +61,9 @@ async fn process_newlink_msg( Nla::Address(addr) => { mac_address = match addr.try_into() { Ok(a) => a, - Err(e) => [0u8; 6] + Err(e) => [0u8; 6], }; - }, + } _ => (), } } @@ -75,7 +75,15 @@ async fn process_newlink_msg( let ibus_tx = notify.then_some(&master.ibus_tx); master .interfaces - .update(ifname, ifindex, mtu, flags, mac_address, &master.netlink_handle, ibus_tx) + .update( + ifname, + ifindex, + mtu, + flags, + mac_address, + &master.netlink_handle, + ibus_tx, + ) .await; } diff --git a/holo-vrrp/src/error.rs b/holo-vrrp/src/error.rs index 54cae245..5e75341d 100644 --- a/holo-vrrp/src/error.rs +++ b/holo-vrrp/src/error.rs @@ -14,7 +14,7 @@ use tracing::{warn, warn_span}; pub enum Error { // I/O errors IoError(IoError), - InterfaceError(String), + InterfaceError(String), // vrrp-ietf-yang-2018-03-13 specific errors GlobalError(GlobalError), diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index c1b5d796..ac2a517d 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -15,8 +15,8 @@ use crate::tasks; // To collect actions to be executed later enum Action { - // described in 6.4.1 part 1. Is when the instance owns the - // IP addresses associated with the virtual router + // described in 6.4.1 part 1. Is when the instance owns the + // IP addresses associated with the virtual router Initialize(VrrpPacket), Backup(VrrpPacket), Master(VrrpPacket), @@ -28,29 +28,33 @@ pub(crate) fn process_vrrp_packet( packet: DecodeResult, ) -> Result<(), Error> { // Handle packet decoding errors - let pkt = packet.unwrap(); + let pkt = packet.unwrap(); // collect the actions that are required let mut action = match get_action(interface, pkt) { Ok(a) => a, - Err(e) => return Err(e) + Err(e) => return Err(e), }; - + // execute all collected actions handle_actions(interface, action); Ok(()) } - // gets all the actions that are required to be done bacsed on the interface // configs and incoming packet -fn get_action(interface: &mut Interface, packet: VrrpPacket) -> Result { +fn get_action( + interface: &mut Interface, + packet: VrrpPacket, +) -> Result { // Handle missing instance let instance = match interface.instances.get_mut(&packet.vrid) { Some(inst) => inst, - None => return Err( - Error::InterfaceError(String::from("unable to fetch VRRP instance from interface")) - ), + None => { + return Err(Error::InterfaceError(String::from( + "unable to fetch VRRP instance from interface", + ))) + } }; // Update statistics @@ -62,8 +66,7 @@ fn get_action(interface: &mut Interface, packet: VrrpPacket) -> Result return Ok(Action::Backup(packet)), State::Master => return Ok(Action::Master(packet)), } -} - +} fn handle_actions(interface: &mut Interface, action: Action) { match action { @@ -82,84 +85,76 @@ fn handle_actions(interface: &mut Interface, action: Action) { if let Some(instance) = interface.instances.get_mut(&vrid) { if pkt.priority == 0 { - let duration = Duration::from_secs_f32(instance.state.skew_time); + let duration = + Duration::from_secs_f32(instance.state.skew_time); tasks::set_master_down_timer(interface, vrid, duration); - } - else { + } else { // RFC 3768 Section 6.4.2 // If Preempt Mode if False, or if the priority in the ADVERTISEMENT is // greater than or equal to local priority then: - if (instance.config.preempt == false) - || (pkt.priority > instance.config.priority){ - instance.reset_timer(); + if (instance.config.preempt == false) + || (pkt.priority > instance.config.priority) + { + instance.reset_timer(); } - // drop the packet else { - return + return; } } } - } Action::Master(pkt) => { let vrid = pkt.vrid; let mut send_ad = false; if let Some(instance) = interface.instances.get_mut(&vrid) { - if pkt.priority == 0 { - send_ad = true; + send_ad = true; - instance.reset_timer(); - } - - else if (pkt.priority > instance.config.priority) + } else if (pkt.priority > instance.config.priority) // TODO: in RFC 3768 page 18, we have a requirement, where If the priority // in the ADVERTISEMENT is equal to the local Priority and the primary IP // Address of the sender is greater than the local primary IP Address, then we - // proceed. + // proceed. // // We can get our primary IP address, but purely from the VRRP packet we cannot // get our senders primary. // { interface.change_state(vrid, State::Backup); - } - - else { - return + } else { + return; } } - + if send_ad { interface.send_vrrp_advert(vrid); } - } } } -// ====== Handle Master Down Timer ===== -// This is called when the master down timer fires. -// Basically When the Instance master down timer -// ticks down. +// ====== Handle Master Down Timer ===== +// This is called when the master down timer fires. +// Basically When the Instance master down timer +// ticks down. // -// RFC 3768 : Section 6.4.2 +// RFC 3768 : Section 6.4.2 // 'If the Master_Down_timer fires' pub(crate) fn handle_master_down_timer( - interface: &mut Interface, - vrid: u8 -) -> Result<(), Error>{ + interface: &mut Interface, + vrid: u8, +) -> Result<(), Error> { interface.send_vrrp_advert(vrid); interface.send_gratuitous_arp(vrid); - + let instance: &mut Instance = match interface.instances.get_mut(&vrid) { Some(i) => i, None => { return Err(Error::InterfaceError(String::from( - "unable to get VRRP instance from interface" + "unable to get VRRP instance from interface", ))); } }; @@ -167,5 +162,3 @@ pub(crate) fn handle_master_down_timer( Ok(()) } - - diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 6094e486..85837a4a 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -125,7 +125,6 @@ impl Instance { _ => {} } } - } // ===== impl InstanceState ===== diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 0107db25..a48645ef 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -73,7 +73,7 @@ pub struct InterfaceNet { pub struct ProtocolInputChannelsTx { // Packet Rx event. pub net_packet_rx: Sender, - // Master Down event + // Master Down event pub master_down_timer: Sender, } @@ -121,50 +121,44 @@ impl Interface { } } - pub(crate) fn send_gratuitous_arp(&self, vrid: u8) { - let ifname = &self.name; + pub(crate) fn send_gratuitous_arp(&self, vrid: u8) { + let ifname = &self.name; if let Some(instance) = self.instances.get(&vrid) { - - // send a gratuitous for each of the + // send a gratuitous for each of the // virutal IP addresses for addr in instance.config.virtual_addresses.clone() { - let arp_packet = ArpPacket { - hw_type: 1, - // for Ipv4 + hw_type: 1, + // for Ipv4 proto_type: 0x0800, // mac address length - hw_length: 6, + hw_length: 6, proto_length: 4, operation: 1, // sender hw address is virtual mac. // https://datatracker.ietf.org/doc/html/rfc3768#section-7.3 - sender_hw_address: [0x00, 0x00, 0x5e, 0x00, 0x01, vrid], - sender_proto_address: addr.ip().octets(), + sender_hw_address: [0x00, 0x00, 0x5e, 0x00, 0x01, vrid], + sender_proto_address: addr.ip().octets(), target_hw_address: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // broadcast - target_proto_address: addr.ip().octets() + target_proto_address: addr.ip().octets(), }; let mut mac_addr = self.system.mac_address.clone(); let eth_frame = EthernetFrame { ethertype: 0x806, dst_mac: [0xff; 6], - src_mac: mac_addr + src_mac: mac_addr, }; let _ = network::send_packet_arp( - &self.net.socket_arp, - &self.name, - eth_frame, - arp_packet + &self.net.socket_arp, + &self.name, + eth_frame, + arp_packet, ); - } - } } - - } #[async_trait] diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index a5cd3409..396129db 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -143,8 +143,7 @@ pub(crate) async fn write_loop( while let Some(msg) = net_tx_packetc.recv().await { match msg { NetTxPacketMsg::Vrrp { packet, src, dst } => { - if let Err(error) = - send_packet_vrrp(&socket_vrrp, packet).await + if let Err(error) = send_packet_vrrp(&socket_vrrp, packet).await { error.log(); } diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index b97440f2..2da62aa5 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -69,7 +69,7 @@ pub mod messages { #[derive(Debug, Deserialize, Serialize)] pub struct MasterDownTimerMsg { - pub vrid: u8, + pub vrid: u8, } } @@ -168,24 +168,23 @@ pub(crate) fn net_tx( } // handling the timers... -pub(crate) fn set_timer( interface: &mut Interface, vrid: u8 -) { - if let Some(instance) = interface.instances.get_mut(&vrid){ +pub(crate) fn set_timer(interface: &mut Interface, vrid: u8) { + if let Some(instance) = interface.instances.get_mut(&vrid) { match instance.state.state { crate::instance::State::Initialize => { instance.timer = VrrpTimer::Null; } crate::instance::State::Backup => { - let duration = Duration::from_secs(instance.state.master_down_interval as u64); - set_master_down_timer( - interface, - vrid, - duration + let duration = Duration::from_secs( + instance.state.master_down_interval as u64, ); + set_master_down_timer(interface, vrid, duration); } crate::instance::State::Master => { let timer = IntervalTask::new( - Duration::from_secs(instance.config.advertise_interval as u64), + Duration::from_secs( + instance.config.advertise_interval as u64, + ), true, move || async move { todo!("send VRRP advertisement"); @@ -195,18 +194,19 @@ pub(crate) fn set_timer( interface: &mut Interface, vrid: u8 } } } - } // ==== Set Master Down Timer ==== pub(crate) fn set_master_down_timer( - interface: &mut Interface, vrid: u8, duration: Duration // period: u64 + interface: &mut Interface, + vrid: u8, + duration: Duration, // period: u64 ) { let instance = interface.instances.get_mut(&vrid).unwrap(); let tx = interface.tx.protocol_input.master_down_timer.clone(); let timer = TimeoutTask::new(duration, move || async move { - tx.send(messages::input::MasterDownTimerMsg{ vrid }); + tx.send(messages::input::MasterDownTimerMsg { vrid }); }); instance.timer = VrrpTimer::MasterDownTimer(timer); } From 768e233f936aa9985aee37c86af1770feec1720c Mon Sep 17 00:00:00 2001 From: weqe Date: Thu, 1 Aug 2024 16:56:06 +0300 Subject: [PATCH 21/22] Deal with responses to ARP requests --- holo-vrrp/src/events.rs | 139 ++++++++++++++++++++++++++++++------- holo-vrrp/src/interface.rs | 11 +-- holo-vrrp/src/network.rs | 4 +- 3 files changed, 123 insertions(+), 31 deletions(-) diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index ac2a517d..1158d52f 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -7,16 +7,14 @@ use std::borrow::{Borrow, BorrowMut}; use std::net::IpAddr; use std::time::Duration; -use crate::error::Error; +use crate::error::{Error, IoError}; use crate::instance::{self, Instance, InstanceState, State}; use crate::interface::Interface; -use crate::packet::{DecodeResult, VrrpPacket}; -use crate::tasks; +use crate::packet::{ArpPacket, DecodeResult, EthernetFrame, VrrpPacket}; +use crate::{network, tasks}; // To collect actions to be executed later -enum Action { - // described in 6.4.1 part 1. Is when the instance owns the - // IP addresses associated with the virtual router +enum VrrpAction { Initialize(VrrpPacket), Backup(VrrpPacket), Master(VrrpPacket), @@ -28,28 +26,123 @@ pub(crate) fn process_vrrp_packet( packet: DecodeResult, ) -> Result<(), Error> { // Handle packet decoding errors - let pkt = packet.unwrap(); + let pkt = match packet { + Ok(pkt) => pkt, + Err(e) => return Err(Error::IoError( + IoError::RecvError(std::io::Error::new( + std::io::ErrorKind::Other, "problem receiving VRRP packet") + ) + )) + }; // collect the actions that are required - let mut action = match get_action(interface, pkt) { + let mut action = match get_vrrp_action(interface, pkt) { Ok(a) => a, Err(e) => return Err(e), }; // execute all collected actions - handle_actions(interface, action); + handle_vrrp_actions(interface, action); + Ok(()) +} + +pub(crate) async fn process_arp_packet( + interface: &mut Interface, + packet: DecodeResult +) -> Result<(), Error> { + // Handle packet decoding errors + let pkt = match packet { + Ok(pkt) => pkt, + Err(e) => return Err(Error::IoError( + IoError::RecvError(std::io::Error::new( + std::io::ErrorKind::Other, "problem receiving ARP packet") + ) + )) + + }; + + let mut vrid: Option = None; + let mut instance: Option<&mut Instance> = None; + + 'outer: for (vr, inst) in interface.instances.iter_mut() { + 'inner: for addr in inst.config.virtual_addresses.clone() { + let addr_arr = addr.ip().octets(); + if addr_arr == pkt.target_proto_address { + instance = Some(inst); + vrid = Some(*vr); + break 'inner + } + } + } + + let mut instance = match instance { + Some(i) => i, + // the target ip address in the ARP request is for none of the instances + None => return Ok(()) + }; + + match instance.state.state { + State::Initialize => {} + State::Backup => { + // ======================================================== + // RFC 3768 Section 6.4.2. Backup + // While in this state, a VRRP router MUST do the following + // ======================================================== + + // MUST NOT respond to ARP requests for the IP address(es) associated with the virutal + // router + } + State::Master => { + + // ======================================================== + // RFC 3768 Section 6.4.3. Master + // While in the {Maste} state the router functions as the forwarding router for the IP + // address(es) associated with the virtual router. + // While in this state, a VRRP router MUST do the following: + // ======================================================== + + + // MUST respond to ARP requests for the IP address(es) associated with the virtual + // router + + if pkt.operation == 1 { // if is ARP request + // build ARP response packet. + let mut arp_response_pkt = pkt.clone(); + arp_response_pkt.operation = 2; // reply operation + arp_response_pkt.sender_hw_address = pkt.target_hw_address; + arp_response_pkt.target_hw_address = pkt.sender_hw_address; + arp_response_pkt.sender_proto_address = pkt.target_proto_address; + arp_response_pkt.target_proto_address = pkt.sender_proto_address; + + // build ethernet packet + let eth_frame = EthernetFrame { + ethertype: 0x806, + src_mac: interface.system.mac_address, + dst_mac: pkt.sender_hw_address, + }; + + network::send_packet_arp( + &interface.net.socket_vrrp, + &interface.name, + eth_frame, + arp_response_pkt + ).await; + } + } + } + Ok(()) } // gets all the actions that are required to be done bacsed on the interface // configs and incoming packet -fn get_action( +fn get_vrrp_action( interface: &mut Interface, packet: VrrpPacket, -) -> Result { +) -> Result { // Handle missing instance let instance = match interface.instances.get_mut(&packet.vrid) { - Some(inst) => inst, + Some(instance) => instance, None => { return Err(Error::InterfaceError(String::from( "unable to fetch VRRP instance from interface", @@ -62,25 +155,25 @@ fn get_action( // Handle the current state match instance.state.state { - State::Initialize => return Ok(Action::Initialize(packet)), - State::Backup => return Ok(Action::Backup(packet)), - State::Master => return Ok(Action::Master(packet)), + State::Initialize => return Ok(VrrpAction::Initialize(packet)), + State::Backup => return Ok(VrrpAction::Backup(packet)), + State::Master => return Ok(VrrpAction::Master(packet)), } } -fn handle_actions(interface: &mut Interface, action: Action) { +async fn handle_vrrp_actions(interface: &mut Interface, action: VrrpAction) { match action { - Action::Initialize(pkt) => { + VrrpAction::Initialize(pkt) => { let vrid = pkt.vrid; if vrid == 255 { interface.send_vrrp_advert(vrid); - interface.send_gratuitous_arp(vrid); + interface.send_gratuitous_arp(vrid).await; interface.change_state(vrid, State::Master); } else { interface.change_state(vrid, State::Backup); } } - Action::Backup(pkt) => { + VrrpAction::Backup(pkt) => { let vrid = pkt.vrid; if let Some(instance) = interface.instances.get_mut(&vrid) { @@ -105,7 +198,7 @@ fn handle_actions(interface: &mut Interface, action: Action) { } } - Action::Master(pkt) => { + VrrpAction::Master(pkt) => { let vrid = pkt.vrid; let mut send_ad = false; if let Some(instance) = interface.instances.get_mut(&vrid) { @@ -136,11 +229,9 @@ fn handle_actions(interface: &mut Interface, action: Action) { } } + + // ====== Handle Master Down Timer ===== -// This is called when the master down timer fires. -// Basically When the Instance master down timer -// ticks down. -// // RFC 3768 : Section 6.4.2 // 'If the Master_Down_timer fires' pub(crate) fn handle_master_down_timer( diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index a48645ef..035c4eb9 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -95,9 +95,9 @@ impl Interface { } } - pub(crate) fn send_vrrp_advert(&self, vrid: u8) { + pub(crate) async fn send_vrrp_advert(&self, vrid: u8) { if let Some(instance) = self.instances.get(&vrid) { - // send advertisement then reset the timer. + let mut ip_addresses: Vec = vec![]; for addr in &instance.config.virtual_addresses { ip_addresses.push(addr.ip()); @@ -117,11 +117,11 @@ impl Interface { auth_data2: 0, }; packet.generate_checksum(); - network::send_packet_vrrp(&self.net.socket_vrrp, packet); + network::send_packet_vrrp(&self.net.socket_vrrp, packet).await; } } - pub(crate) fn send_gratuitous_arp(&self, vrid: u8) { + pub(crate) async fn send_gratuitous_arp(&self, vrid: u8) { let ifname = &self.name; if let Some(instance) = self.instances.get(&vrid) { @@ -155,10 +155,11 @@ impl Interface { &self.name, eth_frame, arp_packet, - ); + ).await; } } } + } #[async_trait] diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index 396129db..1e2a3dbf 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -76,8 +76,8 @@ pub(crate) async fn send_packet_vrrp( .map_err(IoError::SendError) } -//#[cfg(not(feature = "testing"))] -pub fn send_packet_arp( +#[cfg(not(feature = "testing"))] +pub async fn send_packet_arp( sock: &AsyncFd, ifname: &str, eth_frame: EthernetFrame, From c769958701787b9890265249109b1927df1e3160 Mon Sep 17 00:00:00 2001 From: weqe Date: Fri, 2 Aug 2024 00:26:01 +0300 Subject: [PATCH 22/22] fix yang modules in lib.rs --- holo-yang/src/lib.rs | 69 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/holo-yang/src/lib.rs b/holo-yang/src/lib.rs index 1c7953a1..dede9bd9 100644 --- a/holo-yang/src/lib.rs +++ b/holo-yang/src/lib.rs @@ -134,42 +134,43 @@ pub static YANG_EMBEDDED_MODULES: Lazy = Lazy::new(|| { 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"), - EmbeddedModuleKey::new("ietf-mpls-ldp-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-mpls-ldp-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-if-extensions-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-if-extensions-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-if-vlan-encapsulation-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-if-vlan-encapsulation-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-interfaces-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-interfaces-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-ip-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-ip-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-mpls-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-mpls-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-key-chain-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-key-chain-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-ospf-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-ospf-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-ospf-sr-mpls-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-ospf-sr-mpls-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-ospfv3-extended-lsa-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-ospfv3-extended-lsa-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-rip-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-rip-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-routing-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-routing-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-ipv6-unicast-routing-holo-deviations", None, None, None) => - include_str!("../modules/deviations/ietf-ipv6-unicast-routing-holo-deviations.yang"), - EmbeddedModuleKey::new("ietf-routing-policy-holo-deviations", None, None, None) => - 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"), + // IETF Holo load_deviations + EmbeddedModuleKey::new("holo-ietf-bgp-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-bgp-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-mpls-ldp-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-mpls-ldp-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-if-extensions-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-if-extensions-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-if-vlan-encapsulation-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-if-vlan-encapsulation-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-interfaces-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-interfaces-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-ip-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-ip-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-mpls-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-mpls-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-key-chain-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-key-chain-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-ospf-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-ospf-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-ospf-sr-mpls-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-ospf-sr-mpls-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-ospfv3-extended-lsa-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-ospfv3-extended-lsa-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-rip-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-rip-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-system-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-system-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-routing-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-routing-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-ipv6-unicast-routing-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-ipv6-unicast-routing-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-routing-policy-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-routing-policy-deviations.yang"), + EmbeddedModuleKey::new("holo-ietf-segment-routing-mpls-deviations", None, None, None) => + include_str!("../modules/deviations/holo-ietf-segment-routing-mpls-deviations.yang"), EmbeddedModuleKey::new("ietf-vrrp-holo-deviations", None, None, None) => include_str!("../modules/deviations/ietf-vrrp-holo-deviations.yang"), - } });