Compare commits

..

3 Commits

Author SHA1 Message Date
Michal Humpula
c913fc0fb1 add constants to routes 2026-03-01 08:22:18 +01:00
Michal Humpula
e2e68c8e81 simplify env 2026-03-01 08:20:24 +01:00
Michal Humpula
3a289ecff2 simplify ping handling 2026-03-01 08:17:26 +01:00
2 changed files with 81 additions and 47 deletions

View File

@@ -54,6 +54,18 @@ struct Config {
failback_delay: u64, failback_delay: u64,
} }
fn apply_env_overrides(mut config: Config) -> Config {
config.primary_interface =
std::env::var("PRIMARY_INTERFACE").unwrap_or(config.primary_interface);
config.secondary_interface =
std::env::var("SECONDARY_INTERFACE").unwrap_or(config.secondary_interface);
config.primary_gateway = std::env::var("PRIMARY_GATEWAY").unwrap_or(config.primary_gateway);
config.secondary_gateway =
std::env::var("SECONDARY_GATEWAY").unwrap_or(config.secondary_gateway);
config.ping_target = std::env::var("PING_TARGET").unwrap_or(config.ping_target);
config
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let env = Env::default().filter_or("RUST_LOG", "info"); let env = Env::default().filter_or("RUST_LOG", "info");
@@ -64,22 +76,7 @@ async fn main() -> Result<()> {
let config = Config::parse(); let config = Config::parse();
// Override with environment variables if present // Override with environment variables if present
let primary_interface = let config_with_env = apply_env_overrides(config);
std::env::var("PRIMARY_INTERFACE").unwrap_or(config.primary_interface.clone());
let secondary_interface =
std::env::var("SECONDARY_INTERFACE").unwrap_or(config.secondary_interface.clone());
let primary_gateway =
std::env::var("PRIMARY_GATEWAY").unwrap_or(config.primary_gateway.clone());
let secondary_gateway =
std::env::var("SECONDARY_GATEWAY").unwrap_or(config.secondary_gateway.clone());
let ping_target = std::env::var("PING_TARGET").unwrap_or(config.ping_target.clone());
let mut config_with_env = config;
config_with_env.primary_interface = primary_interface;
config_with_env.secondary_interface = secondary_interface;
config_with_env.primary_gateway = primary_gateway;
config_with_env.secondary_gateway = secondary_gateway;
config_with_env.ping_target = ping_target;
debug!("Configuration: {:?}", config_with_env); debug!("Configuration: {:?}", config_with_env);
@@ -127,6 +124,45 @@ async fn main() -> Result<()> {
use state_machine::StateMachine; use state_machine::StateMachine;
async fn handle_ping_result(
result: pinger::PingResult,
interface_name: &str,
state_machine: &Arc<tokio::sync::Mutex<StateMachine>>,
last_failover: &Arc<tokio::sync::Mutex<Option<chrono::DateTime<Utc>>>>,
route_manager: &mut routing::RouteManager,
primary_gateway: &Ipv4Addr,
secondary_gateway: &Ipv4Addr,
config: &Config,
) -> Result<()> {
debug!("{} ping result: {}", interface_name, result);
let mut sm = state_machine.lock().await;
// Add result to appropriate history based on interface
if interface_name == "primary" {
sm.add_primary_result(result);
} else {
sm.add_secondary_result(result);
}
if let Some((old_state, new_state)) = sm.update_state() {
let mut last_failover_lock = last_failover.lock().await;
if new_state == state_machine::State::Fallback
&& old_state != state_machine::State::Fallback
{
*last_failover_lock = Some(Utc::now());
}
state_machine::handle_state_change(
new_state,
old_state,
route_manager,
primary_gateway,
secondary_gateway,
config,
)?;
}
Ok(())
}
async fn main_service( async fn main_service(
config: Config, config: Config,
primary_gateway: Ipv4Addr, primary_gateway: Ipv4Addr,
@@ -204,32 +240,30 @@ async fn main_service(
tokio::select! { tokio::select! {
// Handle primary ping results // Handle primary ping results
Some(result) = primary_rx.recv() => { Some(result) = primary_rx.recv() => {
debug!("Primary ping result: {}", result); handle_ping_result(
let mut sm = state_machine.lock().await; result,
sm.add_primary_result(result); "primary",
&state_machine,
if let Some((old_state, new_state)) = sm.update_state() { &last_failover,
let mut last_failover_lock = last_failover.lock().await; &mut route_manager,
if new_state == state_machine::State::Fallback && old_state != state_machine::State::Fallback { &primary_gateway,
*last_failover_lock = Some(Utc::now()); &secondary_gateway,
} &config,
state_machine::handle_state_change(new_state, old_state, &mut route_manager, &primary_gateway, &secondary_gateway, &config)?; ).await?;
}
} }
// Handle secondary ping results // Handle secondary ping results
Some(result) = secondary_rx.recv() => { Some(result) = secondary_rx.recv() => {
debug!("Secondary ping result: {}", result); handle_ping_result(
let mut sm = state_machine.lock().await; result,
sm.add_secondary_result(result); "secondary",
&state_machine,
if let Some((old_state, new_state)) = sm.update_state() { &last_failover,
let mut last_failover_lock = last_failover.lock().await; &mut route_manager,
if new_state == state_machine::State::Fallback && old_state != state_machine::State::Fallback { &primary_gateway,
*last_failover_lock = Some(Utc::now()); &secondary_gateway,
} &config,
state_machine::handle_state_change(new_state, old_state, &mut route_manager, &primary_gateway, &secondary_gateway, &config)?; ).await?;
}
} }
// Handle shutdown signal // Handle shutdown signal

View File

@@ -7,6 +7,11 @@ use std::net::Ipv4Addr;
const MAIN_TABLE_ID: u8 = 254; const MAIN_TABLE_ID: u8 = 254;
// Route metrics - higher priority = lower number
const FAILOVER_METRIC: u32 = 5;
const PRIMARY_METRIC: u32 = 10;
const SECONDARY_METRIC: u32 = 20;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RouteInfo { pub struct RouteInfo {
pub gateway: Ipv4Addr, pub gateway: Ipv4Addr,
@@ -60,8 +65,6 @@ impl RouteManager {
} }
pub fn set_primary_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> { pub fn set_primary_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> {
let primary_metric = 10;
// Remove existing routes for this interface if any // Remove existing routes for this interface if any
if let Some(pos) = self.routes.iter().position(|r| r.interface == interface) { if let Some(pos) = self.routes.iter().position(|r| r.interface == interface) {
let existing_route = self.routes[pos].clone(); let existing_route = self.routes[pos].clone();
@@ -73,7 +76,7 @@ impl RouteManager {
} }
// Add as primary route // Add as primary route
self.add_route(gateway, interface, primary_metric)?; self.add_route(gateway, interface, PRIMARY_METRIC)?;
Ok(()) Ok(())
} }
@@ -88,20 +91,17 @@ impl RouteManager {
self.set_primary_route(primary_gateway, primary_interface)?; self.set_primary_route(primary_gateway, primary_interface)?;
// Set secondary route with metric 20 (lower priority) // Set secondary route with metric 20 (lower priority)
let secondary_metric = 20; self.add_route(secondary_gateway, secondary_interface, SECONDARY_METRIC)?;
self.add_route(secondary_gateway, secondary_interface, secondary_metric)?;
Ok(()) Ok(())
} }
pub fn add_failover_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> { pub fn add_failover_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> {
let failover_metric = 5; // Higher priority than both primary (10) and secondary (20) self.add_route(gateway, interface, FAILOVER_METRIC)?;
self.add_route(gateway, interface, failover_metric)?;
Ok(()) Ok(())
} }
pub fn remove_failover_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> { pub fn remove_failover_route(&mut self, gateway: Ipv4Addr, interface: String) -> Result<()> {
let failover_metric = 5; self.remove_route(gateway, &interface, FAILOVER_METRIC)?;
self.remove_route(gateway, &interface, failover_metric)?;
Ok(()) Ok(())
} }