From 5a4bd88903d88a42a91f682be0fb8e20c50b9638 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 16 Jan 2024 15:21:29 +0100 Subject: [PATCH 1/4] dockerfile: update rust version --- Dockerfile | 2 +- src/fw.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f22575b..0beb12a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.57-bullseye as builder +FROM rust:1.69-bullseye as builder RUN apt-get update && \ apt-get install -y libssl-dev pkg-config diff --git a/src/fw.rs b/src/fw.rs index a3e6dec..a320fa2 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; +use std::sync::Arc; use anyhow::{Context, Result}; use iptables; From 097a2a029d8e06355ff24a5dcdfb2fcabfca839c Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 16 Jan 2024 15:24:34 +0100 Subject: [PATCH 2/4] refactor stun_actor --- src/stun_actor.rs | 85 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/src/stun_actor.rs b/src/stun_actor.rs index 6740c83..3182662 100644 --- a/src/stun_actor.rs +++ b/src/stun_actor.rs @@ -9,11 +9,18 @@ use crate::config::{RuntimeConfigConsul, RuntimeConfigStun}; use crate::consul; pub struct StunActor { - node: String, consul: consul::Consul, - stun_server_v4: Option, - stun_server_v6: SocketAddr, refresh_time: Duration, + + autodiscovery_v4: StunAutodiscovery, + autodiscovery_v6: StunAutodiscovery, +} + +pub struct StunAutodiscovery { + consul_key: String, + is_ipv4: bool, + stun_server: Option, + last_result: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -34,56 +41,64 @@ impl StunActor { .unwrap_or(true)); assert!(stun_config.stun_server_v6.is_ipv6()); + let autodiscovery_v4 = StunAutodiscovery { + consul_key: format!("diplonat/autodiscovery/ipv4/{}", node), + is_ipv4: true, + stun_server: stun_config.stun_server_v4, + last_result: None, + }; + + let autodiscovery_v6 = StunAutodiscovery { + consul_key: format!("diplonat/autodiscovery/ipv6/{}", node), + is_ipv4: false, + stun_server: Some(stun_config.stun_server_v6), + last_result: None, + }; + Self { consul: consul::Consul::new(consul_config), - node: node.to_string(), - stun_server_v4: stun_config.stun_server_v4, - stun_server_v6: stun_config.stun_server_v6, + autodiscovery_v4, + autodiscovery_v6, refresh_time: stun_config.refresh_time, } } pub async fn listen(&mut self) -> Result<()> { loop { - let ipv4_result = match self.stun_server_v4 { - Some(stun_server_v4) => self.autodiscover_ip(stun_server_v4).await, - None => self.autodiscover_none_ipv4().await, - }; - if let Err(e) = ipv4_result { + if let Err(e) = self.autodiscovery_v4.do_iteration(&self.consul).await { error!("Unable to autodiscover IPv4 address: {}", e); } - if let Err(e) = self.autodiscover_ip(self.stun_server_v6).await { + + if let Err(e) = self.autodiscovery_v6.do_iteration(&self.consul).await { error!("Unable to autodiscover IPv6 address: {}", e); } tokio::time::sleep(self.refresh_time).await; } } +} - async fn autodiscover_ip(&self, stun_server: SocketAddr) -> Result<()> { - let binding_ip = match stun_server.is_ipv4() { +impl StunAutodiscovery { + async fn do_iteration(&self, consul: &consul::Consul) -> Result<()> { + let binding_ip = match self.is_ipv4 { true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), // 0.0.0.0 false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), // [::] }; let binding_addr = SocketAddr::new(binding_ip, 0); - let discovered_addr = get_mapped_addr(stun_server, binding_addr) - .await? - .map(|x| x.ip()); + let discovered_addr = match self.stun_server { + Some(stun_server) => { + assert_eq!(self.is_ipv4, stun_server.is_ipv4()); - let consul_key = match stun_server.is_ipv4() { - true => { - debug!("Autodiscovered IPv4: {:?}", discovered_addr); - format!("diplonat/autodiscovery/ipv4/{}", self.node) - } - false => { - debug!("Autodiscovered IPv6: {:?}", discovered_addr); - format!("diplonat/autodiscovery/ipv6/{}", self.node) + get_mapped_addr(stun_server, binding_addr) + .await? + .map(|x| x.ip()) } + None => None, }; - self.consul + consul .kv_put( - &consul_key, + &self.consul_key, serde_json::to_vec(&AutodiscoverResult { timestamp: timestamp(), address: discovered_addr, @@ -93,22 +108,6 @@ impl StunActor { Ok(()) } - - async fn autodiscover_none_ipv4(&self) -> Result<()> { - let consul_key = format!("diplonat/autodiscovery/ipv4/{}", self.node); - - self.consul - .kv_put( - &consul_key, - serde_json::to_vec(&AutodiscoverResult { - timestamp: timestamp(), - address: None, - })?, - ) - .await?; - - Ok(()) - } } async fn get_mapped_addr( From 6613ea347dc1ff946e7b4c0d39617679ff1b903a Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 16 Jan 2024 15:37:27 +0100 Subject: [PATCH 3/4] stun actor: if no address is found but an address existed <15m ago, keep it --- src/fw.rs | 1 - src/stun_actor.rs | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/fw.rs b/src/fw.rs index a320fa2..a3e6dec 100644 --- a/src/fw.rs +++ b/src/fw.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::sync::Arc; use anyhow::{Context, Result}; use iptables; diff --git a/src/stun_actor.rs b/src/stun_actor.rs index 3182662..4cca4f1 100644 --- a/src/stun_actor.rs +++ b/src/stun_actor.rs @@ -8,6 +8,11 @@ use serde::{Deserialize, Serialize}; use crate::config::{RuntimeConfigConsul, RuntimeConfigStun}; use crate::consul; +/// If autodiscovery returns None but an address was obtained less than +/// this number of seconds ago (here 15 minutes), we keep that address +/// in the Consul db instead of insterting a None. +const PERSIST_SOME_RESULT_DURATION_SECS: u64 = 900; + pub struct StunActor { consul: consul::Consul, refresh_time: Duration, @@ -78,7 +83,7 @@ impl StunActor { } impl StunAutodiscovery { - async fn do_iteration(&self, consul: &consul::Consul) -> Result<()> { + async fn do_iteration(&mut self, consul: &consul::Consul) -> Result<()> { let binding_ip = match self.is_ipv4 { true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), // 0.0.0.0 false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), // [::] @@ -96,16 +101,31 @@ impl StunAutodiscovery { None => None, }; + let now = timestamp(); + + if discovered_addr.is_none() { + if let Some(last_result) = &self.last_result { + if last_result.address.is_some() + && now - last_result.timestamp <= PERSIST_SOME_RESULT_DURATION_SECS + { + // Keep non-None result that was obtained before by not + // writing/taking into account None result. + return Ok(()); + } + } + } + + let current_result = AutodiscoverResult { + timestamp: now, + address: discovered_addr, + }; + consul - .kv_put( - &self.consul_key, - serde_json::to_vec(&AutodiscoverResult { - timestamp: timestamp(), - address: discovered_addr, - })?, - ) + .kv_put(&self.consul_key, serde_json::to_vec(¤t_result)?) .await?; + self.last_result = Some(current_result); + Ok(()) } } From 843104dad73bfdebb674d3c3ec82af225c20c493 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 16 Jan 2024 15:51:36 +0100 Subject: [PATCH 4/4] stun actor: add back log message to inform of autodiscovery result --- src/stun_actor.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/stun_actor.rs b/src/stun_actor.rs index 4cca4f1..bf4da73 100644 --- a/src/stun_actor.rs +++ b/src/stun_actor.rs @@ -120,6 +120,16 @@ impl StunAutodiscovery { address: discovered_addr, }; + let msg = format!( + "STUN autodiscovery result: {} -> {:?}", + self.consul_key, discovered_addr + ); + if self.last_result.as_ref().and_then(|x| x.address) != discovered_addr { + info!("{}", msg); + } else { + debug!("{}", msg); + } + consul .kv_put(&self.consul_key, serde_json::to_vec(¤t_result)?) .await?;