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); +}