diff --git a/holo-vrrp/src/events.rs b/holo-vrrp/src/events.rs index caee5f12..9a50763b 100644 --- a/holo-vrrp/src/events.rs +++ b/holo-vrrp/src/events.rs @@ -13,7 +13,7 @@ use std::time::Duration; use tracing::{debug, debug_span}; use crate::error::{Error, IoError}; -use crate::instance::State; +use crate::instance::{Event, MasterReason, State}; use crate::interface::Interface; use crate::packet::{DecodeResult, VrrpHdr}; use crate::tasks; @@ -130,14 +130,22 @@ fn handle_vrrp_actions(interface: &mut Interface, action: VrrpAction) { if vrid == 255 { interface.send_vrrp_advert(vrid); - interface.change_state(vrid, State::Master); + interface.change_state( + vrid, + State::Master, + MasterReason::Priority, + ); if let Some(instance) = interface.instances.get_mut(&vrid).take() { instance.send_gratuitous_arp(); } } else { - interface.change_state(vrid, State::Backup); + interface.change_state( + vrid, + State::Backup, + MasterReason::NotMaster, + ); } } VrrpAction::Backup(_src, pkt) => { @@ -194,7 +202,11 @@ fn handle_vrrp_actions(interface: &mut Interface, action: VrrpAction) { .unwrap() .network()) { - interface.change_state(vrid, State::Backup); + interface.change_state( + vrid, + State::Backup, + MasterReason::NotMaster, + ); } } @@ -214,10 +226,11 @@ pub(crate) fn handle_master_down_timer( ) -> Result<(), Error> { interface.send_vrrp_advert(vrid); if let Some(instance) = interface.instances.get_mut(&vrid) { + instance.state.last_event = Event::MasterTimeout; instance.send_gratuitous_arp(); } - interface.change_state(vrid, State::Master); + interface.change_state(vrid, State::Master, MasterReason::NoResponse); Ok(()) } diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index fc9b55c7..310ea6a5 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -8,6 +8,8 @@ // use std::net::Ipv4Addr; +use std::sync::atomic::AtomicU64; +use std::sync::Arc; use std::time::Duration; use chrono::{DateTime, Utc}; @@ -52,6 +54,7 @@ pub struct InstanceState { pub new_master_reason: MasterReason, pub skew_time: f32, pub master_down_interval: u32, + pub is_owner: bool, // TODO: interval/timer tasks pub statistics: Statistics, @@ -95,7 +98,7 @@ pub struct Statistics { pub discontinuity_time: DateTime, pub master_transitions: u32, pub adv_rcvd: u64, - pub adv_sent: u64, + pub adv_sent: Arc, pub interval_errors: u64, pub priority_zero_pkts_rcvd: u64, pub priority_zero_pkts_sent: u64, @@ -250,6 +253,7 @@ impl InstanceState { statistics: Default::default(), skew_time: 0.0, master_down_interval: 0, + is_owner: false, } } } @@ -262,7 +266,7 @@ impl Default for Statistics { discontinuity_time: Utc::now(), master_transitions: 0, adv_rcvd: 0, - adv_sent: 0, + adv_sent: Arc::new(AtomicU64::new(0)), interval_errors: 0, priority_zero_pkts_rcvd: 0, priority_zero_pkts_sent: 0, diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index 551475f4..f3f5c085 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -27,7 +27,7 @@ use tokio::sync::mpsc; use tracing::{debug, debug_span, error, error_span}; use crate::error::{Error, IoError}; -use crate::instance::{Instance, State}; +use crate::instance::{Instance, MasterReason, State}; use crate::packet::{DecodeError, VrrpPacket}; use crate::tasks::messages::input::{MasterDownTimerMsg, VrrpNetRxPacketMsg}; use crate::tasks::messages::output::NetTxPacketMsg; @@ -155,7 +155,12 @@ impl Interface { } } - pub(crate) fn change_state(&mut self, vrid: u8, state: State) { + pub(crate) fn change_state( + &mut self, + vrid: u8, + state: State, + new_master_reason: MasterReason, + ) { if let Some(instance) = self.instances.get_mut(&vrid) { debug_span!("change-state").in_scope(|| { if state == State::Backup { @@ -169,6 +174,7 @@ impl Interface { ); } } + instance.state.new_master_reason = new_master_reason; } else if state == State::Master { debug!(%vrid, "state to MASTER."); if let Some(ifindex) = instance.mac_vlan.system.ifindex { @@ -180,6 +186,7 @@ impl Interface { ); } } + instance.state.new_master_reason = new_master_reason; } }); @@ -239,7 +246,6 @@ impl Interface { // check for the exists instance... if let Some(instance) = self.instances.get_mut(&vrid) - // ...and confirm if the instance's parent Interface has an IP address && let Some(addr) = self.system.addresses.first() { diff --git a/holo-vrrp/src/northbound/configuration.rs b/holo-vrrp/src/northbound/configuration.rs index a9980ddd..85dd5d53 100644 --- a/holo-vrrp/src/northbound/configuration.rs +++ b/holo-vrrp/src/northbound/configuration.rs @@ -20,6 +20,7 @@ use holo_northbound::yang::interfaces; use holo_utils::yang::DataNodeRefExt; use ipnetwork::Ipv4Network; +use crate::instance::Event as LastEvent; use crate::instance::Instance; use crate::interface::Interface; @@ -70,7 +71,8 @@ fn load_callbacks() -> Callbacks { .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(vrid); + let mut instance = Instance::new(vrid); + instance.state.last_event = LastEvent::Startup; interface.instances.insert(vrid, instance); let event_queue = args.event_queue; @@ -184,7 +186,11 @@ impl Provider for Interface { // reminder to remove the following line. // currently up due to state not being properly maintained on startup. - self.change_state(vrid, crate::instance::State::Backup); + self.change_state( + vrid, + crate::instance::State::Backup, + crate::instance::MasterReason::NotMaster, + ); } Event::InstanceDelete { vrid } => { self.delete_instance(vrid); diff --git a/holo-vrrp/src/northbound/state.rs b/holo-vrrp/src/northbound/state.rs index 04b5c183..265f0d11 100644 --- a/holo-vrrp/src/northbound/state.rs +++ b/holo-vrrp/src/northbound/state.rs @@ -8,6 +8,7 @@ // use std::borrow::Cow; +use std::sync::atomic::Ordering; use std::sync::LazyLock as Lazy; use enum_as_inner::EnumAsInner; @@ -66,7 +67,7 @@ fn load_callbacks() -> Callbacks { discontinuity_datetime: Some(Cow::Borrowed(&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(), + advertisement_sent: Some(statistics.adv_sent.load(Ordering::Relaxed)).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(), diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index 08c0263d..38661adb 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -7,6 +7,7 @@ // See: https://nlnet.nl/NGI0 // +use std::sync::atomic::Ordering; use std::sync::Arc; use std::time::Duration; @@ -200,7 +201,6 @@ pub(crate) fn set_timer( // in case we are Master, we will be sending VRRP advertisements // every ADVERT_INTERVAL seconds until otherwise. crate::instance::State::Master => { - // ----------------- let src_ip = interface.system.addresses.first().unwrap().ip(); let ip_hdr = instance.adver_ipv4_pkt(src_ip); @@ -211,6 +211,7 @@ pub(crate) fn set_timer( vrrp: vrrp_hdr, }; let ifname = instance.mac_vlan.name.clone(); + let adv_sent = instance.state.statistics.adv_sent.clone(); // ----------------- if let Some(net) = &instance.mac_vlan.net { let net_tx = net.net_tx_packetp.clone(); @@ -220,10 +221,12 @@ pub(crate) fn set_timer( ), true, move || { + let adv_sent = adv_sent.clone(); let ifname = ifname.clone(); let net_tx = net_tx.clone(); let pkt = pkt.clone(); async move { + adv_sent.fetch_add(1, Ordering::Relaxed); let msg = NetTxPacketMsg::Vrrp { ifname, pkt }; let _ = net_tx.send(msg); }