diff options
author | Florian Klink <flokli@flokli.de> | 2024-04-23 11:57:43 +0300 |
---|---|---|
committer | Florian Klink <flokli@flokli.de> | 2024-06-05 08:41:36 +0200 |
commit | a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4 (patch) | |
tree | a16e5b4d951c793bbd7f6ef62a66d15e7c4605c6 /src | |
parent | a2c1de646bce4a96cf8dc526f82bd88bcf3dde70 (diff) | |
download | garage-a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4.tar.gz garage-a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4.zip |
add rpc_public_addr_subnet config option
In case `rpc_public_addr` is not set, but autodiscovery is used, this
allows filtering the list of automatically discovered IPs to a specific
subnet.
For example, if nodes should pick *their* IP inside a specific subnet,
but you don't want to explicitly write the IP down (as it's dynamic, or
you want to share configs across nodes), you can use this option.
Diffstat (limited to 'src')
-rw-r--r-- | src/rpc/Cargo.toml | 1 | ||||
-rw-r--r-- | src/rpc/system.rs | 41 | ||||
-rw-r--r-- | src/util/config.rs | 4 |
3 files changed, 40 insertions, 6 deletions
diff --git a/src/rpc/Cargo.toml b/src/rpc/Cargo.toml index 43d5568e..4c8cafd9 100644 --- a/src/rpc/Cargo.toml +++ b/src/rpc/Cargo.toml @@ -24,6 +24,7 @@ bytes.workspace = true bytesize.workspace = true gethostname.workspace = true hex.workspace = true +ipnet.workspace = true tracing.workspace = true rand.workspace = true itertools.workspace = true diff --git a/src/rpc/system.rs b/src/rpc/system.rs index 0e78060b..d94d4eec 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -844,12 +844,20 @@ impl NodeStatus { } } -fn get_default_ip() -> Option<IpAddr> { +/// Obtain the list of currently available IP addresses on all non-loopback +/// interfaces, optionally filtering them to be inside a given IpNet. +fn get_default_ip(filter_ipnet: Option<ipnet::IpNet>) -> Option<IpAddr> { pnet_datalink::interfaces() - .iter() - .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()) - .and_then(|e| e.ips.first()) - .map(|a| a.ip()) + .into_iter() + // filter down and loopback interfaces + .filter(|i| i.is_up() && !i.is_loopback()) + // get all IPs + .flat_map(|e| e.ips) + // optionally, filter to be inside filter_ipnet + .find(|ipn| { + filter_ipnet.is_some_and(|ipnet| ipnet.contains(&ipn.ip())) || filter_ipnet.is_none() + }) + .map(|ipn| ipn.ip()) } fn get_rpc_public_addr(config: &Config) -> Option<SocketAddr> { @@ -877,7 +885,28 @@ fn get_rpc_public_addr(config: &Config) -> Option<SocketAddr> { } } None => { - let addr = get_default_ip().map(|ip| SocketAddr::new(ip, config.rpc_bind_addr.port())); + // `No rpc_public_addr` specified, try to discover one, optionally filtering by `rpc_public_addr_subnet`. + let filter_subnet: Option<ipnet::IpNet> = config + .rpc_public_addr_subnet + .as_ref() + .and_then(|filter_subnet_str| match filter_subnet_str.parse::<ipnet::IpNet>() { + Ok(filter_subnet) => { + let filter_subnet_trunc = filter_subnet.trunc(); + if filter_subnet_trunc != filter_subnet { + warn!("`rpc_public_addr_subnet` changed after applying netmask, continuing with {}", filter_subnet.trunc()); + } + Some(filter_subnet_trunc) + } + Err(e) => { + panic!( + "Cannot parse rpc_public_addr_subnet {} from config file: {}. Bailing out.", + filter_subnet_str, e + ); + } + }); + + let addr = get_default_ip(filter_subnet) + .map(|ip| SocketAddr::new(ip, config.rpc_bind_addr.port())); if let Some(a) = addr { warn!("Using autodetected rpc_public_addr: {}. Consider specifying it explicitly in configuration file if possible.", a); } diff --git a/src/util/config.rs b/src/util/config.rs index 028f8c68..59329c0b 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -85,6 +85,10 @@ pub struct Config { /// Public IP address of this node pub rpc_public_addr: Option<String>, + /// In case `rpc_public_addr` was not set, this can filter + /// the addresses announced to other peers to a specific subnet. + pub rpc_public_addr_subnet: Option<String>, + /// Timeout for Netapp's ping messagess pub rpc_ping_timeout_msec: Option<u64>, /// Timeout for Netapp RPC calls |