Skip to content

Commit

Permalink
feat(net): IP resolution in docker (#10681)
Browse files Browse the repository at this point in the history
Co-authored-by: Cody Wang <[email protected]>
  • Loading branch information
emhane and cody-wang-cb authored Sep 11, 2024
1 parent 3d7bcb0 commit 6764f7b
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 3 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ tower-http = "0.5"

# p2p
discv5 = "0.7.0"
if-addrs = "0.13"

# rpc
jsonrpsee = "0.24"
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/debug/execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
--to <TO>
The maximum block height
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/debug/in-memory-merkle.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
--retries <RETRIES>
The number of retries per request
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/debug/merkle.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
--retries <RETRIES>
The number of retries per request
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/debug/replay-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
--engine-api-store <PATH>
The path to read engine API messages from
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
RPC:
--http
Enable the HTTP-RPC server
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/p2p.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
Datadir:
--datadir <DATA_DIR>
The path to the data dir for all reth files and subdirectories.
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/stage/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Expand Down
5 changes: 5 additions & 0 deletions book/cli/reth/stage/unwind.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ Networking:
[default: 25600]
--net-if.experimental <IF_NAME>
Name of network interface used to communicate with peers.
If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
--offline
If this is enabled, then all stages except headers, bodies, and sender recovery will be unwound
Expand Down
1 change: 1 addition & 0 deletions crates/net/nat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ reqwest.workspace = true
serde_with = { workspace = true, optional = true }
thiserror.workspace = true
tokio = { workspace = true, features = ["time"] }
if-addrs.workspace = true

[dev-dependencies]
reth-tracing.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions crates/net/nat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

pub mod net_if;

pub use net_if::{NetInterfaceError, DEFAULT_NET_IF_NAME};

use std::{
fmt,
future::{poll_fn, Future},
Expand Down
55 changes: 55 additions & 0 deletions crates/net/nat/src/net_if.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! IP resolution on non-host Docker network.
#![cfg(not(target_os = "windows"))]

use std::{io, net::IpAddr};

/// The 'eth0' interface tends to be the default interface that docker containers use to
/// communicate with each other.
pub const DEFAULT_NET_IF_NAME: &str = "eth0";

/// Errors resolving network interface IP.
#[derive(Debug, thiserror::Error)]
pub enum NetInterfaceError {
/// Error reading OS interfaces.
#[error("failed to read OS interfaces: {0}")]
Io(io::Error),
/// No interface found with given name.
#[error("interface not found: {0}, found other interfaces: {1:?}")]
IFNotFound(String, Vec<String>),
}

/// Reads IP of OS interface with given name, if exists.
#[cfg(not(target_os = "windows"))]
pub fn resolve_net_if_ip(if_name: &str) -> Result<IpAddr, NetInterfaceError> {
match if_addrs::get_if_addrs() {
Ok(ifs) => {
let ip = ifs.iter().find(|i| i.name == if_name).map(|i| i.ip());
match ip {
Some(ip) => Ok(ip),
None => {
let ifs = ifs.into_iter().map(|i| i.name.as_str().into()).collect();
Err(NetInterfaceError::IFNotFound(if_name.into(), ifs))
}
}
}
Err(err) => Err(NetInterfaceError::Io(err)),
}
}

#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;

use super::*;

#[test]
fn read_docker_if_addr() {
const LOCALHOST_IF: [&str; 2] = ["lo0", "lo"];

let ip = resolve_net_if_ip(LOCALHOST_IF[0])
.unwrap_or_else(|_| resolve_net_if_ip(LOCALHOST_IF[1]).unwrap());

assert_eq!(ip, Ipv4Addr::LOCALHOST);
}
}
38 changes: 35 additions & 3 deletions crates/node/core/src/args/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use reth_discv5::{
discv5::ListenConfig, DEFAULT_COUNT_BOOTSTRAP_LOOKUPS, DEFAULT_DISCOVERY_V5_PORT,
DEFAULT_SECONDS_BOOTSTRAP_LOOKUP_INTERVAL, DEFAULT_SECONDS_LOOKUP_INTERVAL,
};
use reth_net_nat::NatResolver;
use reth_net_nat::{NatResolver, DEFAULT_NET_IF_NAME};
use reth_network::{
transactions::{
constants::{
Expand All @@ -35,6 +35,7 @@ use reth_network::{
};
use reth_network_peers::{mainnet_nodes, TrustedPeer};
use secp256k1::SecretKey;
use tracing::error;

use crate::version::P2P_CLIENT_VERSION;

Expand Down Expand Up @@ -148,9 +149,38 @@ pub struct NetworkArgs {
/// Max capacity of cache of hashes for transactions pending fetch.
#[arg(long = "max-tx-pending-fetch", value_name = "COUNT", default_value_t = DEFAULT_MAX_CAPACITY_CACHE_PENDING_FETCH, verbatim_doc_comment)]
pub max_capacity_cache_txns_pending_fetch: u32,

/// Name of network interface used to communicate with peers.
///
/// If flag is set, but no value is passed, the default interface for docker `eth0` is tried.
#[cfg(not(target_os = "windows"))]
#[arg(long = "net-if.experimental", conflicts_with = "addr", value_name = "IF_NAME")]
pub net_if: Option<String>,
}

impl NetworkArgs {
/// Returns the resolved IP address.
pub fn resolved_addr(&self) -> IpAddr {
#[cfg(not(target_os = "windows"))]
if let Some(ref if_name) = self.net_if {
let if_name = if if_name.is_empty() { DEFAULT_NET_IF_NAME } else { if_name };
return match reth_net_nat::net_if::resolve_net_if_ip(if_name) {
Ok(addr) => addr,
Err(err) => {
error!(target: "reth::cli",
if_name,
%err,
"Failed to read network interface IP"
);

DEFAULT_DISCOVERY_ADDR
}
}
}

self.addr
}

/// Returns the resolved bootnodes if any are provided.
pub fn resolved_bootnodes(&self) -> Option<Vec<NodeRecord>> {
self.bootnodes.clone().map(|bootnodes| {
Expand All @@ -176,6 +206,7 @@ impl NetworkArgs {
secret_key: SecretKey,
default_peers_file: PathBuf,
) -> NetworkConfigBuilder {
let addr = self.resolved_addr();
let chain_bootnodes = self
.resolved_bootnodes()
.unwrap_or_else(|| chain_spec.bootnodes().unwrap_or_else(mainnet_nodes));
Expand Down Expand Up @@ -224,11 +255,11 @@ impl NetworkArgs {
})
// apply discovery settings
.apply(|builder| {
let rlpx_socket = (self.addr, self.port).into();
let rlpx_socket = (addr, self.port).into();
self.discovery.apply_to_builder(builder, rlpx_socket, chain_bootnodes)
})
.listener_addr(SocketAddr::new(
self.addr, // set discovery port based on instance number
addr, // set discovery port based on instance number
self.port,
))
.discovery_addr(SocketAddr::new(
Expand Down Expand Up @@ -303,6 +334,7 @@ impl Default for NetworkArgs {
max_pending_pool_imports: DEFAULT_MAX_COUNT_PENDING_POOL_IMPORTS,
max_seen_tx_history: DEFAULT_MAX_COUNT_TRANSACTIONS_SEEN_BY_PEER,
max_capacity_cache_txns_pending_fetch: DEFAULT_MAX_CAPACITY_CACHE_PENDING_FETCH,
net_if: None,
}
}
}
Expand Down

0 comments on commit 6764f7b

Please sign in to comment.