diff --git a/Cargo.lock b/Cargo.lock index cc84d52caf..12896aa06e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8846,6 +8846,7 @@ name = "reth-net-banlist" version = "1.9.2" dependencies = [ "alloy-primitives", + "ipnet", ] [[package]] @@ -9123,6 +9124,7 @@ dependencies = [ "eyre", "futures", "humantime", + "ipnet", "proptest", "rand 0.9.2", "reth-chainspec", @@ -9135,6 +9137,7 @@ dependencies = [ "reth-engine-local", "reth-engine-primitives", "reth-ethereum-forks", + "reth-net-banlist", "reth-net-nat", "reth-network", "reth-network-p2p", diff --git a/Cargo.toml b/Cargo.toml index 5bca6a0d5d..580b50f5f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -729,6 +729,9 @@ visibility = "0.1.1" walkdir = "2.3.3" vergen-git2 = "1.0.5" +# networking +ipnet = "2.11" + # [patch.crates-io] # alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" } # alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" } diff --git a/crates/net/banlist/Cargo.toml b/crates/net/banlist/Cargo.toml index 7afec48d48..f5f885da24 100644 --- a/crates/net/banlist/Cargo.toml +++ b/crates/net/banlist/Cargo.toml @@ -14,3 +14,6 @@ workspace = true [dependencies] # ethereum alloy-primitives.workspace = true + +# networking +ipnet.workspace = true diff --git a/crates/net/banlist/src/lib.rs b/crates/net/banlist/src/lib.rs index 31b779bc8d..402041ed2f 100644 --- a/crates/net/banlist/src/lib.rs +++ b/crates/net/banlist/src/lib.rs @@ -10,7 +10,7 @@ type PeerId = alloy_primitives::B512; -use std::{collections::HashMap, net::IpAddr, time::Instant}; +use std::{collections::HashMap, net::IpAddr, str::FromStr, time::Instant}; /// Determines whether or not the IP is globally routable. /// Should be replaced with [`IpAddr::is_global`](std::net::IpAddr::is_global) once it is stable. @@ -215,3 +215,161 @@ mod tests { assert!(!banlist.is_banned_ip(&ip)); } } + +/// IP filter for restricting network communication to specific IP ranges using CIDR notation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IpFilter { + /// List of allowed IP networks in CIDR notation. + /// If empty, all IPs are allowed. + allowed_networks: Vec, +} + +impl IpFilter { + /// Creates a new IP filter with the given CIDR networks. + /// + /// If the list is empty, all IPs will be allowed. + pub const fn new(allowed_networks: Vec) -> Self { + Self { allowed_networks } + } + + /// Creates an IP filter from a comma-separated list of CIDR networks. + /// + /// # Errors + /// + /// Returns an error if any of the CIDR strings cannot be parsed. + pub fn from_cidr_string(cidrs: &str) -> Result { + if cidrs.is_empty() { + return Ok(Self::allow_all()) + } + + let networks = cidrs + .split(',') + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .map(ipnet::IpNet::from_str) + .collect::, _>>()?; + + Ok(Self::new(networks)) + } + + /// Creates a filter that allows all IPs. + pub const fn allow_all() -> Self { + Self { allowed_networks: Vec::new() } + } + + /// Checks if the given IP address is allowed by this filter. + /// + /// Returns `true` if the filter is empty (allows all) or if the IP is within + /// any of the allowed networks. + pub fn is_allowed(&self, ip: &IpAddr) -> bool { + // If no restrictions are set, allow all IPs + if self.allowed_networks.is_empty() { + return true + } + + // Check if the IP is within any of the allowed networks + self.allowed_networks.iter().any(|net| net.contains(ip)) + } + + /// Returns `true` if this filter has restrictions (i.e., not allowing all IPs). + pub const fn has_restrictions(&self) -> bool { + !self.allowed_networks.is_empty() + } + + /// Returns the list of allowed networks. + pub fn allowed_networks(&self) -> &[ipnet::IpNet] { + &self.allowed_networks + } +} + +impl Default for IpFilter { + fn default() -> Self { + Self::allow_all() + } +} + +#[cfg(test)] +mod ip_filter_tests { + use super::*; + + #[test] + fn test_allow_all_filter() { + let filter = IpFilter::allow_all(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + assert!(filter.is_allowed(&IpAddr::from([10, 0, 0, 1]))); + assert!(filter.is_allowed(&IpAddr::from([8, 8, 8, 8]))); + assert!(!filter.has_restrictions()); + } + + #[test] + fn test_single_network_filter() { + let filter = IpFilter::from_cidr_string("192.168.0.0/16").unwrap(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 255, 255]))); + assert!(!filter.is_allowed(&IpAddr::from([192, 169, 1, 1]))); + assert!(!filter.is_allowed(&IpAddr::from([10, 0, 0, 1]))); + assert!(filter.has_restrictions()); + } + + #[test] + fn test_multiple_networks_filter() { + let filter = IpFilter::from_cidr_string("192.168.0.0/16,10.0.0.0/8").unwrap(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + assert!(filter.is_allowed(&IpAddr::from([10, 5, 10, 20]))); + assert!(filter.is_allowed(&IpAddr::from([10, 255, 255, 255]))); + assert!(!filter.is_allowed(&IpAddr::from([172, 16, 0, 1]))); + assert!(!filter.is_allowed(&IpAddr::from([8, 8, 8, 8]))); + } + + #[test] + fn test_ipv6_filter() { + let filter = IpFilter::from_cidr_string("2001:db8::/32").unwrap(); + let ipv6_in_range: IpAddr = "2001:db8::1".parse().unwrap(); + let ipv6_out_range: IpAddr = "2001:db9::1".parse().unwrap(); + + assert!(filter.is_allowed(&ipv6_in_range)); + assert!(!filter.is_allowed(&ipv6_out_range)); + } + + #[test] + fn test_mixed_ipv4_ipv6_filter() { + let filter = IpFilter::from_cidr_string("192.168.0.0/16,2001:db8::/32").unwrap(); + + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + let ipv6_in_range: IpAddr = "2001:db8::1".parse().unwrap(); + assert!(filter.is_allowed(&ipv6_in_range)); + + assert!(!filter.is_allowed(&IpAddr::from([10, 0, 0, 1]))); + let ipv6_out_range: IpAddr = "2001:db9::1".parse().unwrap(); + assert!(!filter.is_allowed(&ipv6_out_range)); + } + + #[test] + fn test_empty_string() { + let filter = IpFilter::from_cidr_string("").unwrap(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + assert!(!filter.has_restrictions()); + } + + #[test] + fn test_invalid_cidr() { + assert!(IpFilter::from_cidr_string("invalid").is_err()); + assert!(IpFilter::from_cidr_string("192.168.0.0/33").is_err()); + assert!(IpFilter::from_cidr_string("192.168.0.0,10.0.0.0").is_err()); + } + + #[test] + fn test_whitespace_handling() { + let filter = IpFilter::from_cidr_string(" 192.168.0.0/16 , 10.0.0.0/8 ").unwrap(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 1]))); + assert!(filter.is_allowed(&IpAddr::from([10, 0, 0, 1]))); + assert!(!filter.is_allowed(&IpAddr::from([172, 16, 0, 1]))); + } + + #[test] + fn test_single_ip_as_cidr() { + let filter = IpFilter::from_cidr_string("192.168.1.100/32").unwrap(); + assert!(filter.is_allowed(&IpAddr::from([192, 168, 1, 100]))); + assert!(!filter.is_allowed(&IpAddr::from([192, 168, 1, 101]))); + } +} diff --git a/crates/net/network-types/src/peers/config.rs b/crates/net/network-types/src/peers/config.rs index 1fe685b0e8..29e4499b40 100644 --- a/crates/net/network-types/src/peers/config.rs +++ b/crates/net/network-types/src/peers/config.rs @@ -7,7 +7,7 @@ use std::{ time::Duration, }; -use reth_net_banlist::BanList; +use reth_net_banlist::{BanList, IpFilter}; use reth_network_peers::{NodeRecord, TrustedPeer}; use tracing::info; @@ -166,6 +166,12 @@ pub struct PeersConfig { /// This acts as an IP based rate limit. #[cfg_attr(feature = "serde", serde(default, with = "humantime_serde"))] pub incoming_ip_throttle_duration: Duration, + /// IP address filter for restricting network connections to specific IP ranges. + /// + /// Similar to geth's --netrestrict flag. If configured, only connections to/from + /// IPs within the specified CIDR ranges will be allowed. + #[cfg_attr(feature = "serde", serde(skip))] + pub ip_filter: IpFilter, } impl Default for PeersConfig { @@ -184,6 +190,7 @@ impl Default for PeersConfig { basic_nodes: Default::default(), max_backoff_count: 5, incoming_ip_throttle_duration: INBOUND_IP_THROTTLE_DURATION, + ip_filter: IpFilter::default(), } } } @@ -301,6 +308,12 @@ impl PeersConfig { Ok(self.with_basic_nodes(nodes)) } + /// Configure the IP filter for restricting network connections to specific IP ranges. + pub fn with_ip_filter(mut self, ip_filter: IpFilter) -> Self { + self.ip_filter = ip_filter; + self + } + /// Returns settings for testing #[cfg(any(test, feature = "test-utils"))] pub fn test() -> Self { diff --git a/crates/net/network/src/peers.rs b/crates/net/network/src/peers.rs index e89b1695d9..757ef500b3 100644 --- a/crates/net/network/src/peers.rs +++ b/crates/net/network/src/peers.rs @@ -90,6 +90,8 @@ pub struct PeersManager { net_connection_state: NetworkConnectionState, /// How long to temporarily ban ip on an incoming connection attempt. incoming_ip_throttle_duration: Duration, + /// IP address filter for restricting network connections to specific IP ranges. + ip_filter: reth_net_banlist::IpFilter, } impl PeersManager { @@ -108,6 +110,7 @@ impl PeersManager { basic_nodes, max_backoff_count, incoming_ip_throttle_duration, + ip_filter, } = config; let (manager_tx, handle_rx) = mpsc::unbounded_channel(); let now = Instant::now(); @@ -161,6 +164,7 @@ impl PeersManager { max_backoff_count, net_connection_state: NetworkConnectionState::default(), incoming_ip_throttle_duration, + ip_filter, } } @@ -243,6 +247,12 @@ impl PeersManager { &mut self, addr: IpAddr, ) -> Result<(), InboundConnectionError> { + // Check if the IP is in the allowed ranges (netrestrict) + if !self.ip_filter.is_allowed(&addr) { + trace!(target: "net", ?addr, "Rejecting connection from IP not in allowed ranges"); + return Err(InboundConnectionError::IpBanned) + } + if self.ban_list.is_banned_ip(&addr) { return Err(InboundConnectionError::IpBanned) } @@ -749,7 +759,15 @@ impl PeersManager { addr: PeerAddr, fork_id: Option, ) { - if self.ban_list.is_banned(&peer_id, &addr.tcp().ip()) { + let ip_addr = addr.tcp().ip(); + + // Check if the IP is in the allowed ranges (netrestrict) + if !self.ip_filter.is_allowed(&ip_addr) { + trace!(target: "net", ?peer_id, ?ip_addr, "Skipping peer from IP not in allowed ranges"); + return + } + + if self.ban_list.is_banned(&peer_id, &ip_addr) { return } @@ -830,7 +848,15 @@ impl PeersManager { addr: PeerAddr, fork_id: Option, ) { - if self.ban_list.is_banned(&peer_id, &addr.tcp().ip()) { + let ip_addr = addr.tcp().ip(); + + // Check if the IP is in the allowed ranges (netrestrict) + if !self.ip_filter.is_allowed(&ip_addr) { + trace!(target: "net", ?peer_id, ?ip_addr, "Skipping outbound connection to IP not in allowed ranges"); + return + } + + if self.ban_list.is_banned(&peer_id, &ip_addr) { return } @@ -2899,4 +2925,106 @@ mod tests { let updated_peer = manager.peers.get(&peer_id).unwrap(); assert_eq!(updated_peer.addr.tcp().ip(), updated_ip); } + + #[tokio::test] + async fn test_ip_filter_blocks_inbound_connection() { + use reth_net_banlist::IpFilter; + use std::net::IpAddr; + + // Create a filter that only allows 192.168.0.0/16 + let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16").unwrap(); + let config = PeersConfig::test().with_ip_filter(ip_filter); + let mut peers = PeersManager::new(config); + + // Try to connect from an allowed IP + let allowed_ip: IpAddr = "192.168.1.100".parse().unwrap(); + assert!(peers.on_incoming_pending_session(allowed_ip).is_ok()); + + // Try to connect from a disallowed IP + let disallowed_ip: IpAddr = "10.0.0.1".parse().unwrap(); + assert!(peers.on_incoming_pending_session(disallowed_ip).is_err()); + } + + #[tokio::test] + async fn test_ip_filter_blocks_outbound_connection() { + use reth_net_banlist::IpFilter; + use std::net::SocketAddr; + + // Create a filter that only allows 192.168.0.0/16 + let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16").unwrap(); + let config = PeersConfig::test().with_ip_filter(ip_filter); + let mut peers = PeersManager::new(config); + + let peer_id = PeerId::new([1; 64]); + + // Try to add a peer with an allowed IP + let allowed_addr: SocketAddr = "192.168.1.100:30303".parse().unwrap(); + peers.add_peer(peer_id, PeerAddr::from_tcp(allowed_addr), None); + assert!(peers.peers.contains_key(&peer_id)); + + // Try to add a peer with a disallowed IP + let peer_id2 = PeerId::new([2; 64]); + let disallowed_addr: SocketAddr = "10.0.0.1:30303".parse().unwrap(); + peers.add_peer(peer_id2, PeerAddr::from_tcp(disallowed_addr), None); + assert!(!peers.peers.contains_key(&peer_id2)); + } + + #[tokio::test] + async fn test_ip_filter_ipv6() { + use reth_net_banlist::IpFilter; + use std::net::IpAddr; + + // Create a filter that only allows IPv6 range 2001:db8::/32 + let ip_filter = IpFilter::from_cidr_string("2001:db8::/32").unwrap(); + let config = PeersConfig::test().with_ip_filter(ip_filter); + let mut peers = PeersManager::new(config); + + // Try to connect from an allowed IPv6 address + let allowed_ip: IpAddr = "2001:db8::1".parse().unwrap(); + assert!(peers.on_incoming_pending_session(allowed_ip).is_ok()); + + // Try to connect from a disallowed IPv6 address + let disallowed_ip: IpAddr = "2001:db9::1".parse().unwrap(); + assert!(peers.on_incoming_pending_session(disallowed_ip).is_err()); + } + + #[tokio::test] + async fn test_ip_filter_multiple_ranges() { + use reth_net_banlist::IpFilter; + use std::net::IpAddr; + + // Create a filter that allows multiple ranges + let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16,10.0.0.0/8").unwrap(); + let config = PeersConfig::test().with_ip_filter(ip_filter); + let mut peers = PeersManager::new(config); + + // Try IPs from both allowed ranges + let ip1: IpAddr = "192.168.1.1".parse().unwrap(); + let ip2: IpAddr = "10.5.10.20".parse().unwrap(); + assert!(peers.on_incoming_pending_session(ip1).is_ok()); + assert!(peers.on_incoming_pending_session(ip2).is_ok()); + + // Try IP from disallowed range + let disallowed_ip: IpAddr = "172.16.0.1".parse().unwrap(); + assert!(peers.on_incoming_pending_session(disallowed_ip).is_err()); + } + + #[tokio::test] + async fn test_ip_filter_no_restriction() { + use reth_net_banlist::IpFilter; + use std::net::IpAddr; + + // Create a filter with no restrictions (allow all) + let ip_filter = IpFilter::allow_all(); + let config = PeersConfig::test().with_ip_filter(ip_filter); + let mut peers = PeersManager::new(config); + + // All IPs should be allowed + let ip1: IpAddr = "192.168.1.1".parse().unwrap(); + let ip2: IpAddr = "10.0.0.1".parse().unwrap(); + let ip3: IpAddr = "8.8.8.8".parse().unwrap(); + assert!(peers.on_incoming_pending_session(ip1).is_ok()); + assert!(peers.on_incoming_pending_session(ip2).is_ok()); + assert!(peers.on_incoming_pending_session(ip3).is_ok()); + } } diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index 1f66865cc0..45b362a0c6 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -31,6 +31,7 @@ reth-config = { workspace = true, features = ["serde"] } reth-discv4.workspace = true reth-discv5.workspace = true reth-net-nat.workspace = true +reth-net-banlist.workspace = true reth-network-peers.workspace = true reth-prune-types.workspace = true reth-stages-types.workspace = true @@ -55,7 +56,7 @@ serde.workspace = true strum = { workspace = true, features = ["derive"] } thiserror.workspace = true url.workspace = true - +ipnet.workspace = true # io dirs-next.workspace = true shellexpand.workspace = true diff --git a/crates/node/core/src/args/network.rs b/crates/node/core/src/args/network.rs index 5519102b9b..91aaffbad4 100644 --- a/crates/node/core/src/args/network.rs +++ b/crates/node/core/src/args/network.rs @@ -18,6 +18,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_banlist::IpFilter; use reth_net_nat::{NatResolver, DEFAULT_NET_IF_NAME}; use reth_network::{ transactions::{ @@ -205,6 +206,15 @@ pub struct NetworkArgs { /// Optional network ID to override the chain specification's network ID for P2P connections #[arg(long)] pub network_id: Option, + + /// Restrict network communication to the given IP networks (CIDR masks). + /// + /// Comma separated list of CIDR network specifications. + /// Only peers with IP addresses within these ranges will be allowed to connect. + /// + /// Example: --netrestrict "192.168.0.0/16,10.0.0.0/8" + #[arg(long, value_name = "NETRESTRICT")] + pub netrestrict: Option, } impl NetworkArgs { @@ -276,11 +286,13 @@ impl NetworkArgs { let peers_file = self.peers_file.clone().unwrap_or(default_peers_file); // Configure peer connections + let ip_filter = self.ip_filter().unwrap_or_default(); let peers_config = config .peers .clone() .with_max_inbound_opt(self.max_inbound_peers) - .with_max_outbound_opt(self.max_outbound_peers); + .with_max_outbound_opt(self.max_outbound_peers) + .with_ip_filter(ip_filter); // Configure basic network stack NetworkConfigBuilder::::new(secret_key) @@ -381,6 +393,17 @@ impl NetworkArgs { get_secret_key(&secret_key_path) } } + + /// Creates an IP filter from the netrestrict argument. + /// + /// Returns an error if the CIDR format is invalid. + pub fn ip_filter(&self) -> Result { + if let Some(netrestrict) = &self.netrestrict { + IpFilter::from_cidr_string(netrestrict) + } else { + Ok(IpFilter::allow_all()) + } + } } impl Default for NetworkArgs { @@ -416,6 +439,7 @@ impl Default for NetworkArgs { propagation_mode: TransactionPropagationMode::Sqrt, required_block_hashes: vec![], network_id: None, + netrestrict: None, } } } @@ -819,4 +843,68 @@ mod tests { // Verify the secret key matches the hex input assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), hex); } + + #[test] + fn parse_netrestrict_single_network() { + let args = + CommandParser::::parse_from(["reth", "--netrestrict", "192.168.0.0/16"]) + .args; + + assert_eq!(args.netrestrict, Some("192.168.0.0/16".to_string())); + + let ip_filter = args.ip_filter().unwrap(); + assert!(ip_filter.has_restrictions()); + assert!(ip_filter.is_allowed(&"192.168.1.1".parse().unwrap())); + assert!(!ip_filter.is_allowed(&"10.0.0.1".parse().unwrap())); + } + + #[test] + fn parse_netrestrict_multiple_networks() { + let args = CommandParser::::parse_from([ + "reth", + "--netrestrict", + "192.168.0.0/16,10.0.0.0/8", + ]) + .args; + + assert_eq!(args.netrestrict, Some("192.168.0.0/16,10.0.0.0/8".to_string())); + + let ip_filter = args.ip_filter().unwrap(); + assert!(ip_filter.has_restrictions()); + assert!(ip_filter.is_allowed(&"192.168.1.1".parse().unwrap())); + assert!(ip_filter.is_allowed(&"10.5.10.20".parse().unwrap())); + assert!(!ip_filter.is_allowed(&"172.16.0.1".parse().unwrap())); + } + + #[test] + fn parse_netrestrict_ipv6() { + let args = + CommandParser::::parse_from(["reth", "--netrestrict", "2001:db8::/32"]) + .args; + + let ip_filter = args.ip_filter().unwrap(); + assert!(ip_filter.has_restrictions()); + assert!(ip_filter.is_allowed(&"2001:db8::1".parse().unwrap())); + assert!(!ip_filter.is_allowed(&"2001:db9::1".parse().unwrap())); + } + + #[test] + fn netrestrict_not_set() { + let args = CommandParser::::parse_from(["reth"]).args; + assert_eq!(args.netrestrict, None); + + let ip_filter = args.ip_filter().unwrap(); + assert!(!ip_filter.has_restrictions()); + assert!(ip_filter.is_allowed(&"192.168.1.1".parse().unwrap())); + assert!(ip_filter.is_allowed(&"10.0.0.1".parse().unwrap())); + } + + #[test] + fn netrestrict_invalid_cidr() { + let args = + CommandParser::::parse_from(["reth", "--netrestrict", "invalid-cidr"]) + .args; + + assert!(args.ip_filter().is_err()); + } } diff --git a/docs/vocs/docs/pages/cli/reth/node.mdx b/docs/vocs/docs/pages/cli/reth/node.mdx index 4289a8f199..51e504fece 100644 --- a/docs/vocs/docs/pages/cli/reth/node.mdx +++ b/docs/vocs/docs/pages/cli/reth/node.mdx @@ -275,6 +275,13 @@ Networking: --network-id Optional network ID to override the chain specification's network ID for P2P connections + --netrestrict + Restrict network communication to the given IP networks (CIDR masks). + + Comma separated list of CIDR network specifications. Only peers with IP addresses within these ranges will be allowed to connect. + + Example: --netrestrict "192.168.0.0/16,10.0.0.0/8" + RPC: --http Enable the HTTP-RPC server diff --git a/docs/vocs/docs/pages/cli/reth/p2p/body.mdx b/docs/vocs/docs/pages/cli/reth/p2p/body.mdx index 667937fce4..8eca4be487 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/body.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/body.mdx @@ -221,6 +221,13 @@ Networking: --network-id Optional network ID to override the chain specification's network ID for P2P connections + --netrestrict + Restrict network communication to the given IP networks (CIDR masks). + + Comma separated list of CIDR network specifications. Only peers with IP addresses within these ranges will be allowed to connect. + + Example: --netrestrict "192.168.0.0/16,10.0.0.0/8" + Datadir: --datadir The path to the data dir for all reth files and subdirectories. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/header.mdx b/docs/vocs/docs/pages/cli/reth/p2p/header.mdx index 21b73022b3..ada244eca4 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/header.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/header.mdx @@ -221,6 +221,13 @@ Networking: --network-id Optional network ID to override the chain specification's network ID for P2P connections + --netrestrict + Restrict network communication to the given IP networks (CIDR masks). + + Comma separated list of CIDR network specifications. Only peers with IP addresses within these ranges will be allowed to connect. + + Example: --netrestrict "192.168.0.0/16,10.0.0.0/8" + Datadir: --datadir The path to the data dir for all reth files and subdirectories. diff --git a/docs/vocs/docs/pages/cli/reth/stage/run.mdx b/docs/vocs/docs/pages/cli/reth/stage/run.mdx index ba7514dc87..2417323f69 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/run.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/run.mdx @@ -351,6 +351,13 @@ Networking: --network-id Optional network ID to override the chain specification's network ID for P2P connections + --netrestrict + Restrict network communication to the given IP networks (CIDR masks). + + Comma separated list of CIDR network specifications. Only peers with IP addresses within these ranges will be allowed to connect. + + Example: --netrestrict "192.168.0.0/16,10.0.0.0/8" + Logging: --log.stdout.format The format to use for logs written to stdout