public IP address autodiscovery #20

Merged
lx merged 10 commits from stun into main 2023-04-21 09:56:22 +00:00
5 changed files with 25 additions and 24 deletions
Showing only changes of commit f410230240 - Show all commits

View file

@ -26,7 +26,7 @@ fn all_valid_options() -> HashMap<String, String> {
); );
opts.insert( opts.insert(
"DIPLONAT_PRIVATE_IP".to_string(), "DIPLONAT_PRIVATE_IP".to_string(),
"172.123.43.555".to_string(), "172.123.43.55".to_string(),
); );
opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "10".to_string()); opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "10".to_string());
opts.insert( opts.insert(
@ -118,7 +118,7 @@ fn ok_from_iter_all_valid_options() {
); );
assert_eq!(rt_config.firewall.refresh_time, refresh_time); assert_eq!(rt_config.firewall.refresh_time, refresh_time);
assert_eq!( assert_eq!(
&rt_config.igd.private_ip.unwrap(), &rt_config.igd.private_ip.unwrap().to_string(),
opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap() opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap()
); );
assert_eq!(rt_config.igd.expiration_time, expiration_time); assert_eq!(rt_config.igd.expiration_time, expiration_time);

View file

@ -1,9 +1,9 @@
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::time::Duration; use std::time::Duration;
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Context, Result};
use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul}; use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul};
@ -31,7 +31,7 @@ pub struct RuntimeConfigFirewall {
#[derive(Debug)] #[derive(Debug)]
pub struct RuntimeConfigIgd { pub struct RuntimeConfigIgd {
pub private_ip: Option<String>, pub private_ip: Option<Ipv4Addr>,
pub expiration_time: Duration, pub expiration_time: Duration,
pub refresh_time: Duration, pub refresh_time: Duration,
} }
@ -137,7 +137,12 @@ impl RuntimeConfigFirewall {
impl RuntimeConfigIgd { impl RuntimeConfigIgd {
pub(super) fn new(opts: &ConfigOptsBase) -> Result<Self> { pub(super) fn new(opts: &ConfigOptsBase) -> Result<Self> {
let private_ip = opts.private_ip.clone(); let private_ip = opts
.private_ip
.as_ref()
.map(|x| x.parse())
.transpose()
.context("parse private_ip")?;
let expiration_time = Duration::from_secs( let expiration_time = Duration::from_secs(
opts.expiration_time opts.expiration_time
.unwrap_or(super::EXPIRATION_TIME) .unwrap_or(super::EXPIRATION_TIME)

View file

@ -23,7 +23,7 @@ impl Diplonat {
let fw = FirewallActor::new(rt_cfg.firewall.refresh_time, &ca.rx_open_ports).await?; let fw = FirewallActor::new(rt_cfg.firewall.refresh_time, &ca.rx_open_ports).await?;
let ia = IgdActor::new( let ia = IgdActor::new(
rt_cfg.igd.private_ip.as_ref().map(String::as_str), rt_cfg.igd.private_ip,
rt_cfg.igd.refresh_time, rt_cfg.igd.refresh_time,
rt_cfg.igd.expiration_time, rt_cfg.igd.expiration_time,
&ca.rx_open_ports, &ca.rx_open_ports,

View file

@ -11,7 +11,7 @@ pub fn setup(ipt: &iptables::IPTables) -> Result<()> {
// ensure we start from a clean state without any rule already set // ensure we start from a clean state without any rule already set
cleanup(ipt)?; cleanup(ipt)?;
info!("{}: creating DIPLONAT chain using", ipt.cmd); info!("{}: creating DIPLONAT chain", ipt.cmd);
ipt.new_chain("filter", "DIPLONAT") ipt.new_chain("filter", "DIPLONAT")
.context("Failed to create new chain")?; .context("Failed to create new chain")?;
ipt.insert_unique("filter", "INPUT", "-j DIPLONAT", 1) ipt.insert_unique("filter", "INPUT", "-j DIPLONAT", 1)

View file

@ -1,4 +1,4 @@
use std::net::SocketAddrV4; use std::net::{Ipv4Addr, SocketAddrV4};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use igd::{aio::*, PortMappingProtocol}; use igd::{aio::*, PortMappingProtocol};
@ -17,12 +17,12 @@ pub struct IgdActor {
gateway: Gateway, gateway: Gateway,
refresh: Duration, refresh: Duration,
expire: Duration, expire: Duration,
private_ip: String, private_ip: Ipv4Addr,
} }
impl IgdActor { impl IgdActor {
pub async fn new( pub async fn new(
priv_ip: Option<&str>, priv_ip: Option<Ipv4Addr>,
refresh: Duration, refresh: Duration,
expire: Duration, expire: Duration,
rxp: &watch::Receiver<messages::PublicExposedPorts>, rxp: &watch::Receiver<messages::PublicExposedPorts>,
@ -34,7 +34,7 @@ impl IgdActor {
let private_ip = if let Some(ip) = priv_ip { let private_ip = if let Some(ip) = priv_ip {
info!("Using private IP from config: {}", ip); info!("Using private IP from config: {}", ip);
ip.to_string() ip
} else { } else {
info!("Trying to automatically detect private IP"); info!("Trying to automatically detect private IP");
let gwa = gw.addr.ip().octets(); let gwa = gw.addr.ip().octets();
@ -47,18 +47,17 @@ impl IgdActor {
), ),
}; };
#[allow(unused_parens)] #[allow(unused_parens)]
let public_ip = get_if_addrs::get_if_addrs()? let private_ip = get_if_addrs::get_if_addrs()?
.into_iter() .into_iter()
.map(|i| i.addr.ip()) .map(|i| i.addr.ip())
.filter(|a| match a { .filter_map(|a| match a {
std::net::IpAddr::V4(a4) => (a4.octets()[..cmplen] == gwa[..cmplen]), std::net::IpAddr::V4(a4) if a4.octets()[..cmplen] == gwa[..cmplen] => Some(a4),
_ => false, _ => None,
}) })
.next() .next()
.expect("No interface has an IP on same subnet as gateway") .expect("No interface has an IP on same subnet as gateway");
.to_string(); info!("Autodetected private IP: {}", private_ip);
info!("Found private IP: {}", public_ip); private_ip
public_ip
}; };
let ctx = Self { let ctx = Self {
@ -104,10 +103,7 @@ impl IgdActor {
for (proto, list) in actions.iter() { for (proto, list) in actions.iter() {
for port in *list { for port in *list {
let service_str = format!("{}:{}", self.private_ip, port); let service = SocketAddrV4::new(self.private_ip, *port);
let service = service_str
.parse::<SocketAddrV4>()
.context("Invalid socket address")?;
self.gateway self.gateway
.add_port( .add_port(
*proto, *proto,