STUN actor: try to avoid ip address flapping #21

Merged
lx merged 4 commits from stun-keep-values into main 2024-01-16 15:05:01 +00:00
Showing only changes of commit 097a2a029d - Show all commits

View file

@ -9,11 +9,18 @@ use crate::config::{RuntimeConfigConsul, RuntimeConfigStun};
use crate::consul; use crate::consul;
pub struct StunActor { pub struct StunActor {
node: String,
consul: consul::Consul, consul: consul::Consul,
stun_server_v4: Option<SocketAddr>,
stun_server_v6: SocketAddr,
refresh_time: Duration, refresh_time: Duration,
autodiscovery_v4: StunAutodiscovery,
autodiscovery_v6: StunAutodiscovery,
}
pub struct StunAutodiscovery {
consul_key: String,
is_ipv4: bool,
stun_server: Option<SocketAddr>,
last_result: Option<AutodiscoverResult>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -34,56 +41,64 @@ impl StunActor {
.unwrap_or(true)); .unwrap_or(true));
assert!(stun_config.stun_server_v6.is_ipv6()); 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 { Self {
consul: consul::Consul::new(consul_config), consul: consul::Consul::new(consul_config),
node: node.to_string(), autodiscovery_v4,
stun_server_v4: stun_config.stun_server_v4, autodiscovery_v6,
stun_server_v6: stun_config.stun_server_v6,
refresh_time: stun_config.refresh_time, refresh_time: stun_config.refresh_time,
} }
} }
pub async fn listen(&mut self) -> Result<()> { pub async fn listen(&mut self) -> Result<()> {
loop { loop {
let ipv4_result = match self.stun_server_v4 { if let Err(e) = self.autodiscovery_v4.do_iteration(&self.consul).await {
Some(stun_server_v4) => self.autodiscover_ip(stun_server_v4).await,
None => self.autodiscover_none_ipv4().await,
};
if let Err(e) = ipv4_result {
error!("Unable to autodiscover IPv4 address: {}", e); 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); error!("Unable to autodiscover IPv6 address: {}", e);
} }
tokio::time::sleep(self.refresh_time).await; tokio::time::sleep(self.refresh_time).await;
} }
} }
}
async fn autodiscover_ip(&self, stun_server: SocketAddr) -> Result<()> { impl StunAutodiscovery {
let binding_ip = match stun_server.is_ipv4() { 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 true => IpAddr::V4(Ipv4Addr::UNSPECIFIED), // 0.0.0.0
false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), // [::] false => IpAddr::V6(Ipv6Addr::UNSPECIFIED), // [::]
}; };
let binding_addr = SocketAddr::new(binding_ip, 0); let binding_addr = SocketAddr::new(binding_ip, 0);
let discovered_addr = get_mapped_addr(stun_server, binding_addr) let discovered_addr = match self.stun_server {
.await? Some(stun_server) => {
.map(|x| x.ip()); assert_eq!(self.is_ipv4, stun_server.is_ipv4());
let consul_key = match stun_server.is_ipv4() { get_mapped_addr(stun_server, binding_addr)
true => { .await?
debug!("Autodiscovered IPv4: {:?}", discovered_addr); .map(|x| x.ip())
format!("diplonat/autodiscovery/ipv4/{}", self.node)
}
false => {
debug!("Autodiscovered IPv6: {:?}", discovered_addr);
format!("diplonat/autodiscovery/ipv6/{}", self.node)
} }
None => None,
}; };
self.consul consul
.kv_put( .kv_put(
&consul_key, &self.consul_key,
serde_json::to_vec(&AutodiscoverResult { serde_json::to_vec(&AutodiscoverResult {
timestamp: timestamp(), timestamp: timestamp(),
address: discovered_addr, address: discovered_addr,
@ -93,22 +108,6 @@ impl StunActor {
Ok(()) 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( async fn get_mapped_addr(