diff --git a/Cargo.lock b/Cargo.lock index c62e9fbc87..2e03edff9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4748,9 +4748,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libflate" @@ -5324,6 +5324,7 @@ dependencies = [ "libp2p", "libp2p-mplex", "lighthouse_version", + "local-ip-address", "logging", "lru", "lru_cache", @@ -5410,6 +5411,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "local-ip-address" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3669cf5561f8d27e8fc84cc15e58350e70f557d4d65f70e3154e54cd2f8e1782" +dependencies = [ + "libc", + "neli", + "thiserror 1.0.69", + "windows-sys 0.59.0", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -5806,6 +5819,31 @@ dependencies = [ "tempfile", ] +[[package]] +name = "neli" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" +dependencies = [ + "byteorder", + "libc", + "log", + "neli-proc-macros", +] + +[[package]] +name = "neli-proc-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" +dependencies = [ + "either", + "proc-macro2", + "quote", + "serde", + "syn 1.0.109", +] + [[package]] name = "netlink-packet-core" version = "0.7.0" diff --git a/beacon_node/lighthouse_network/Cargo.toml b/beacon_node/lighthouse_network/Cargo.toml index 485f32b37a..8b7bf119dd 100644 --- a/beacon_node/lighthouse_network/Cargo.toml +++ b/beacon_node/lighthouse_network/Cargo.toml @@ -19,6 +19,7 @@ fnv = { workspace = true } futures = { workspace = true } gossipsub = { workspace = true } hex = { workspace = true } +local-ip-address = "0.6" itertools = { workspace = true } libp2p-mplex = "0.42" lighthouse_version = { workspace = true } diff --git a/beacon_node/lighthouse_network/src/config.rs b/beacon_node/lighthouse_network/src/config.rs index 8a93b1185d..207c1edf15 100644 --- a/beacon_node/lighthouse_network/src/config.rs +++ b/beacon_node/lighthouse_network/src/config.rs @@ -6,6 +6,7 @@ use directory::{ DEFAULT_BEACON_NODE_DIR, DEFAULT_HARDCODED_NETWORK, DEFAULT_NETWORK_DIR, DEFAULT_ROOT_DIR, }; use libp2p::Multiaddr; +use local_ip_address::local_ipv6; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -265,6 +266,18 @@ impl Config { } } + /// A helper function to check if the local host has a globally routeable IPv6 address. If so, + /// returns true. + pub fn is_ipv6_supported() -> bool { + // If IPv6 is supported + let Ok(std::net::IpAddr::V6(local_ip)) = local_ipv6() else { + return false; + }; + + // If its globally routable, return true + is_global_ipv6(&local_ip) + } + pub fn listen_addrs(&self) -> &ListenAddress { &self.listen_addresses } diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index cecfcee868..55aaaab9b3 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -224,6 +224,13 @@ pub fn cli_app() -> Command { .action(ArgAction::Set) .display_order(0) ) + .arg( + Arg::new("disable-ipv6") + .long("disable-ipv6") + .help("If 0.0.0.0 is set as the IPv4 listening address and the host has a globally routeable IPv6 address, Lighthouse will listen on :: by default, in dual-stack mode. This flag prevents this.") + .action(ArgAction::SetTrue) + .display_order(0) + ) .arg( Arg::new("target-peers") .long("target-peers") diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 8d8a44a6fd..c9f2a44081 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -899,6 +899,7 @@ pub fn parse_listening_addresses( // parse the possible ips let mut maybe_ipv4 = None; let mut maybe_ipv6 = None; + for addr_str in listen_addresses_str { let addr = addr_str.parse::().map_err(|parse_error| { format!("Failed to parse listen-address ({addr_str}) as an Ip address: {parse_error}") @@ -926,6 +927,19 @@ pub fn parse_listening_addresses( } } + // If we have specified an IPv4 listen address and not an IPv6 address and the + // host has a globally routeable IPv6 address and the CLI doesn't expressly disable IPv6, + // then we also listen on IPv6. + // Note that we will only listen on all interfaces if the IPv4 counterpart is also listening on + // all interfaces, to prevent accidental exposure of ports. + if maybe_ipv4 == Some(Ipv4Addr::UNSPECIFIED) + && maybe_ipv6.is_none() + && !cli_args.get_flag("disable_ipv6") + && NetworkConfig::is_ipv6_supported() + { + maybe_ipv6 = Some(Ipv6Addr::UNSPECIFIED); + } + // parse the possible tcp ports let port = cli_args .get_one::("port")