Ipv6 bootnodes (#3752)
## Issue Addressed our bootnodes as of now support only ipv4. this makes it so that they support ipv6 ## Proposed Changes - Adds code necessary to update the bootnodes to run on dual stack nodes and therefore contact and store ipv6 nodes. - Adds some metrics about connectivity type of stored peers. It might have been nice to see some metrics over the sessions but that feels out of scope right now. ## Additional Info - some code quality improvements sneaked in since the changes seemed small - I think it depends on the OS, but enabling mapped addresses on an ipv6 node without dual stack support enabled could fail silently, making these nodes effectively ipv6 only. In the future I'll probably change this to use two sockets, which should fail loudly
This commit is contained in:
parent
3534c85e30
commit
b4f4c0d253
@ -14,6 +14,7 @@ use std::cmp::max;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::net::Ipv6Addr;
|
||||||
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
|
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -843,9 +844,11 @@ pub fn set_network_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cli_args.is_present("enr-match") {
|
if cli_args.is_present("enr-match") {
|
||||||
// set the enr address to localhost if the address is 0.0.0.0
|
// set the enr address to localhost if the address is unspecified
|
||||||
if config.listen_address == "0.0.0.0".parse::<IpAddr>().expect("valid ip addr") {
|
if config.listen_address == IpAddr::V4(Ipv4Addr::UNSPECIFIED) {
|
||||||
config.enr_address = Some("127.0.0.1".parse::<IpAddr>().expect("valid ip addr"));
|
config.enr_address = Some(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||||
|
} else if config.listen_address == IpAddr::V6(Ipv6Addr::UNSPECIFIED) {
|
||||||
|
config.enr_address = Some(IpAddr::V6(Ipv6Addr::LOCALHOST));
|
||||||
} else {
|
} else {
|
||||||
config.enr_address = Some(config.listen_address);
|
config.enr_address = Some(config.listen_address);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use beacon_node::{get_data_dir, set_network_config};
|
use beacon_node::{get_data_dir, set_network_config};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use eth2_network_config::Eth2NetworkConfig;
|
use eth2_network_config::Eth2NetworkConfig;
|
||||||
|
use lighthouse_network::discv5::enr::EnrBuilder;
|
||||||
|
use lighthouse_network::discv5::IpMode;
|
||||||
use lighthouse_network::discv5::{enr::CombinedKey, Discv5Config, Enr};
|
use lighthouse_network::discv5::{enr::CombinedKey, Discv5Config, Enr};
|
||||||
use lighthouse_network::{
|
use lighthouse_network::{
|
||||||
discovery::{create_enr_builder_from_config, load_enr_from_disk, use_or_load_enr},
|
discovery::{load_enr_from_disk, use_or_load_enr},
|
||||||
load_private_key, CombinedKeyExt, NetworkConfig,
|
load_private_key, CombinedKeyExt, NetworkConfig,
|
||||||
};
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
@ -70,6 +72,15 @@ impl<T: EthSpec> BootNodeConfig<T> {
|
|||||||
// the address to listen on
|
// the address to listen on
|
||||||
let listen_socket =
|
let listen_socket =
|
||||||
SocketAddr::new(network_config.listen_address, network_config.discovery_port);
|
SocketAddr::new(network_config.listen_address, network_config.discovery_port);
|
||||||
|
if listen_socket.is_ipv6() {
|
||||||
|
// create ipv6 sockets and enable ipv4 mapped addresses.
|
||||||
|
network_config.discv5_config.ip_mode = IpMode::Ip6 {
|
||||||
|
enable_mapped_addresses: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Set explicitly as ipv4 otherwise
|
||||||
|
network_config.discv5_config.ip_mode = IpMode::Ip4;
|
||||||
|
}
|
||||||
|
|
||||||
let private_key = load_private_key(&network_config, &logger);
|
let private_key = load_private_key(&network_config, &logger);
|
||||||
let local_key = CombinedKey::from_libp2p(&private_key)?;
|
let local_key = CombinedKey::from_libp2p(&private_key)?;
|
||||||
@ -104,7 +115,29 @@ impl<T: EthSpec> BootNodeConfig<T> {
|
|||||||
// Build the local ENR
|
// Build the local ENR
|
||||||
|
|
||||||
let mut local_enr = {
|
let mut local_enr = {
|
||||||
let mut builder = create_enr_builder_from_config(&network_config, false);
|
let mut builder = EnrBuilder::new("v4");
|
||||||
|
// Set the enr address if specified. Set also the port.
|
||||||
|
// NOTE: if the port is specified but the the address is not, the port won't be
|
||||||
|
// set since it can't be known if it's an ipv6 or ipv4 udp port.
|
||||||
|
if let Some(enr_address) = network_config.enr_address {
|
||||||
|
match enr_address {
|
||||||
|
std::net::IpAddr::V4(ipv4_addr) => {
|
||||||
|
builder.ip4(ipv4_addr);
|
||||||
|
if let Some(port) = network_config.enr_udp_port {
|
||||||
|
builder.udp4(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::net::IpAddr::V6(ipv6_addr) => {
|
||||||
|
builder.ip6(ipv6_addr);
|
||||||
|
if let Some(port) = network_config.enr_udp_port {
|
||||||
|
builder.udp6(port);
|
||||||
|
// We are enabling mapped addresses in the boot node in this case,
|
||||||
|
// so advertise an udp4 port as well.
|
||||||
|
builder.udp4(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If we know of the ENR field, add it to the initial construction
|
// If we know of the ENR field, add it to the initial construction
|
||||||
if let Some(enr_fork_bytes) = enr_fork {
|
if let Some(enr_fork_bytes) = enr_fork {
|
||||||
|
@ -9,53 +9,63 @@ use slog::info;
|
|||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
|
|
||||||
pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
||||||
|
let BootNodeConfig {
|
||||||
|
listen_socket,
|
||||||
|
boot_nodes,
|
||||||
|
local_enr,
|
||||||
|
local_key,
|
||||||
|
discv5_config,
|
||||||
|
..
|
||||||
|
} = config;
|
||||||
|
|
||||||
// Print out useful information about the generated ENR
|
// Print out useful information about the generated ENR
|
||||||
|
|
||||||
let enr_socket = config
|
let enr_v4_socket = local_enr.udp4_socket();
|
||||||
.local_enr
|
let enr_v6_socket = local_enr.udp6_socket();
|
||||||
.udp4_socket()
|
let eth2_field = local_enr
|
||||||
.expect("Enr has a UDP socket");
|
|
||||||
let eth2_field = config
|
|
||||||
.local_enr
|
|
||||||
.eth2()
|
.eth2()
|
||||||
.map(|fork_id| hex::encode(fork_id.fork_digest))
|
.map(|fork_id| hex::encode(fork_id.fork_digest))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
info!(log, "Configuration parameters"; "listening_address" => format!("{}:{}", config.listen_socket.ip(), config.listen_socket.port()), "broadcast_address" => format!("{}:{}",enr_socket.ip(), enr_socket.port()), "eth2" => eth2_field);
|
let pretty_v4_socket = enr_v4_socket.as_ref().map(|addr| addr.to_string());
|
||||||
|
let pretty_v6_socket = enr_v6_socket.as_ref().map(|addr| addr.to_string());
|
||||||
|
info!(
|
||||||
|
log, "Configuration parameters";
|
||||||
|
"listening_address" => %listen_socket,
|
||||||
|
"advertised_v4_address" => ?pretty_v4_socket,
|
||||||
|
"advertised_v6_address" => ?pretty_v6_socket,
|
||||||
|
"eth2" => eth2_field
|
||||||
|
);
|
||||||
|
|
||||||
info!(log, "Identity established"; "peer_id" => config.local_enr.peer_id().to_string(), "node_id" => config.local_enr.node_id().to_string());
|
info!(log, "Identity established"; "peer_id" => %local_enr.peer_id(), "node_id" => %local_enr.node_id());
|
||||||
|
|
||||||
// build the contactable multiaddr list, adding the p2p protocol
|
// build the contactable multiaddr list, adding the p2p protocol
|
||||||
info!(log, "Contact information"; "enr" => config.local_enr.to_base64());
|
info!(log, "Contact information"; "enr" => local_enr.to_base64());
|
||||||
info!(log, "Contact information"; "multiaddrs" => format!("{:?}", config.local_enr.multiaddr_p2p()));
|
info!(log, "Contact information"; "multiaddrs" => ?local_enr.multiaddr_p2p());
|
||||||
|
|
||||||
// construct the discv5 server
|
// construct the discv5 server
|
||||||
let mut discv5 = Discv5::new(
|
let mut discv5 = Discv5::new(local_enr.clone(), local_key, discv5_config).unwrap();
|
||||||
config.local_enr.clone(),
|
|
||||||
config.local_key,
|
|
||||||
config.discv5_config,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// If there are any bootnodes add them to the routing table
|
// If there are any bootnodes add them to the routing table
|
||||||
for enr in config.boot_nodes {
|
for enr in boot_nodes {
|
||||||
info!(
|
info!(
|
||||||
log,
|
log,
|
||||||
"Adding bootnode";
|
"Adding bootnode";
|
||||||
"address" => ?enr.udp4_socket(),
|
"ipv4_address" => ?enr.udp4_socket(),
|
||||||
"peer_id" => enr.peer_id().to_string(),
|
"ipv6_address" => ?enr.udp6_socket(),
|
||||||
"node_id" => enr.node_id().to_string()
|
"peer_id" => ?enr.peer_id(),
|
||||||
|
"node_id" => ?enr.node_id()
|
||||||
);
|
);
|
||||||
if enr != config.local_enr {
|
if enr != local_enr {
|
||||||
if let Err(e) = discv5.add_enr(enr) {
|
if let Err(e) = discv5.add_enr(enr) {
|
||||||
slog::warn!(log, "Failed adding ENR"; "error" => e.to_string());
|
slog::warn!(log, "Failed adding ENR"; "error" => ?e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the server
|
// start the server
|
||||||
if let Err(e) = discv5.start(config.listen_socket).await {
|
if let Err(e) = discv5.start(listen_socket).await {
|
||||||
slog::crit!(log, "Could not start discv5 server"; "error" => e.to_string());
|
slog::crit!(log, "Could not start discv5 server"; "error" => %e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +82,7 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
|||||||
let mut event_stream = match discv5.event_stream().await {
|
let mut event_stream = match discv5.event_stream().await {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
slog::crit!(log, "Failed to obtain event stream"; "error" => e.to_string());
|
slog::crit!(log, "Failed to obtain event stream"; "error" => %e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -81,9 +91,35 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
|||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = metric_interval.tick() => {
|
_ = metric_interval.tick() => {
|
||||||
|
// Get some ipv4/ipv6 stats to add in the metrics.
|
||||||
|
let mut ipv4_only_reachable: usize = 0;
|
||||||
|
let mut ipv6_only_reachable: usize= 0;
|
||||||
|
let mut ipv4_ipv6_reachable: usize = 0;
|
||||||
|
let mut unreachable_nodes: usize = 0;
|
||||||
|
for enr in discv5.kbuckets().iter_ref().filter_map(|entry| entry.status.is_connected().then_some(entry.node.value)) {
|
||||||
|
let declares_ipv4 = enr.udp4_socket().is_some();
|
||||||
|
let declares_ipv6 = enr.udp6_socket().is_some();
|
||||||
|
match (declares_ipv4, declares_ipv6) {
|
||||||
|
(true, true) => ipv4_ipv6_reachable += 1,
|
||||||
|
(true, false) => ipv4_only_reachable += 1,
|
||||||
|
(false, true) => ipv6_only_reachable += 1,
|
||||||
|
(false, false) => unreachable_nodes += 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// display server metrics
|
// display server metrics
|
||||||
let metrics = discv5.metrics();
|
let metrics = discv5.metrics();
|
||||||
info!(log, "Server metrics"; "connected_peers" => discv5.connected_peers(), "active_sessions" => metrics.active_sessions, "requests/s" => format!("{:.2}", metrics.unsolicited_requests_per_second));
|
info!(
|
||||||
|
log, "Server metrics";
|
||||||
|
"connected_peers" => discv5.connected_peers(),
|
||||||
|
"active_sessions" => metrics.active_sessions,
|
||||||
|
"requests/s" => format_args!("{:.2}", metrics.unsolicited_requests_per_second),
|
||||||
|
"ipv4_nodes" => ipv4_only_reachable,
|
||||||
|
"ipv6_nodes" => ipv6_only_reachable,
|
||||||
|
"ipv6_and_ipv4_nodes" => ipv4_ipv6_reachable,
|
||||||
|
"unreachable_nodes" => unreachable_nodes,
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
Some(event) = event_stream.recv() => {
|
Some(event) = event_stream.recv() => {
|
||||||
match event {
|
match event {
|
||||||
@ -95,7 +131,7 @@ pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
|||||||
Discv5Event::TalkRequest(_) => {} // Ignore
|
Discv5Event::TalkRequest(_) => {} // Ignore
|
||||||
Discv5Event::NodeInserted { .. } => {} // Ignore
|
Discv5Event::NodeInserted { .. } => {} // Ignore
|
||||||
Discv5Event::SocketUpdated(socket_addr) => {
|
Discv5Event::SocketUpdated(socket_addr) => {
|
||||||
info!(log, "External socket address updated"; "socket_addr" => format!("{:?}", socket_addr));
|
info!(log, "Advertised socket address updated"; "socket_addr" => %socket_addr);
|
||||||
}
|
}
|
||||||
Discv5Event::SessionEstablished{ .. } => {} // Ignore
|
Discv5Event::SessionEstablished{ .. } => {} // Ignore
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user