change(network): Configurable maximum connections per IP (#7013)

* Adds config field

* adds new generated config

* Lint

* fixes config_tests
This commit is contained in:
Arya 2023-06-20 01:11:45 -04:00 committed by GitHub
parent a588965b94
commit b40fc9b032
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 193 additions and 25 deletions

View File

@ -19,8 +19,8 @@ use zebra_chain::parameters::Network;
use crate::{
constants::{
DEFAULT_CRAWL_NEW_PEER_INTERVAL, DNS_LOOKUP_TIMEOUT, INBOUND_PEER_LIMIT_MULTIPLIER,
MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER,
DEFAULT_CRAWL_NEW_PEER_INTERVAL, DEFAULT_MAX_CONNS_PER_IP, DNS_LOOKUP_TIMEOUT,
INBOUND_PEER_LIMIT_MULTIPLIER, MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER,
},
protocol::external::{canonical_peer_addr, canonical_socket_addr},
BoxError, PeerSocketAddr,
@ -153,6 +153,12 @@ pub struct Config {
/// next connection attempt.
#[serde(with = "humantime_serde")]
pub crawl_new_peer_interval: Duration,
/// The maximum number of peer connections Zebra will keep for a given IP address
/// before it drops any additional peer connections with that IP.
///
/// The default and minimum value are 1.
pub max_connections_per_ip: usize,
}
impl Config {
@ -591,6 +597,7 @@ impl Default for Config {
// But Zebra should only make a small number of initial outbound connections,
// so that idle peers don't use too many connection slots.
peerset_initial_target_size: 25,
max_connections_per_ip: DEFAULT_MAX_CONNS_PER_IP,
}
}
}
@ -611,6 +618,7 @@ impl<'de> Deserialize<'de> for Config {
peerset_initial_target_size: usize,
#[serde(alias = "new_peer_interval", with = "humantime_serde")]
crawl_new_peer_interval: Duration,
max_connections_per_ip: Option<usize>,
}
impl Default for DConfig {
@ -624,16 +632,26 @@ impl<'de> Deserialize<'de> for Config {
cache_dir: config.cache_dir,
peerset_initial_target_size: config.peerset_initial_target_size,
crawl_new_peer_interval: config.crawl_new_peer_interval,
max_connections_per_ip: Some(DEFAULT_MAX_CONNS_PER_IP),
}
}
}
let config = DConfig::deserialize(deserializer)?;
let DConfig {
listen_addr,
network,
initial_mainnet_peers,
initial_testnet_peers,
cache_dir,
peerset_initial_target_size,
crawl_new_peer_interval,
max_connections_per_ip,
} = DConfig::deserialize(deserializer)?;
let listen_addr = match config.listen_addr.parse::<SocketAddr>() {
let listen_addr = match listen_addr.parse::<SocketAddr>() {
Ok(socket) => Ok(socket),
Err(_) => match config.listen_addr.parse::<IpAddr>() {
Ok(ip) => Ok(SocketAddr::new(ip, config.network.default_port())),
Err(_) => match listen_addr.parse::<IpAddr>() {
Ok(ip) => Ok(SocketAddr::new(ip, network.default_port())),
Err(err) => Err(de::Error::custom(format!(
"{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
))),
@ -642,12 +660,13 @@ impl<'de> Deserialize<'de> for Config {
Ok(Config {
listen_addr: canonical_socket_addr(listen_addr),
network: config.network,
initial_mainnet_peers: config.initial_mainnet_peers,
initial_testnet_peers: config.initial_testnet_peers,
cache_dir: config.cache_dir,
peerset_initial_target_size: config.peerset_initial_target_size,
crawl_new_peer_interval: config.crawl_new_peer_interval,
network,
initial_mainnet_peers,
initial_testnet_peers,
cache_dir,
peerset_initial_target_size,
crawl_new_peer_interval,
max_connections_per_ip: max_connections_per_ip.unwrap_or(DEFAULT_MAX_CONNS_PER_IP),
})
}
}

View File

@ -67,9 +67,11 @@ pub const INBOUND_PEER_LIMIT_MULTIPLIER: usize = 5;
/// See [`INBOUND_PEER_LIMIT_MULTIPLIER`] for details.
pub const OUTBOUND_PEER_LIMIT_MULTIPLIER: usize = 3;
/// The maximum number of peer connections Zebra will keep for a given IP address
/// The default maximum number of peer connections Zebra will keep for a given IP address
/// before it drops any additional peer connections with that IP.
pub const MAX_CONNS_PER_IP: usize = 1;
///
/// This will be used as Config.max_connections_per_ip if no value is provided.
pub const DEFAULT_MAX_CONNS_PER_IP: usize = 1;
/// The buffer size for the peer set.
///

View File

@ -254,7 +254,7 @@ where
last_peer_log: Option<Instant>,
/// The configured maximum number of peers that can be in the
/// peer set per IP, defaults to [`crate::constants::MAX_CONNS_PER_IP`]
/// peer set per IP, defaults to [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`]
max_conns_per_ip: usize,
}
@ -290,7 +290,8 @@ where
/// - `address_book`: when peer set is busy, it logs address book diagnostics.
/// - `minimum_peer_version`: endpoint to see the minimum peer protocol version in real time.
/// - `max_conns_per_ip`: configured maximum number of peers that can be in the
/// peer set per IP, defaults to [`crate::constants::MAX_CONNS_PER_IP`].
/// peer set per IP, defaults to the config value or to
/// [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`].
pub fn new(
config: &Config,
discover: D,
@ -328,7 +329,7 @@ where
last_peer_log: None,
address_metrics,
max_conns_per_ip: max_conns_per_ip.unwrap_or(crate::constants::MAX_CONNS_PER_IP),
max_conns_per_ip: max_conns_per_ip.unwrap_or(config.max_connections_per_ip),
}
}
@ -540,7 +541,7 @@ where
// # Security
//
// drop the new peer if there are already `MAX_CONNS_PER_IP` peers with
// drop the new peer if there are already `max_conns_per_ip` peers with
// the same IP address in the peer set.
if self.num_peers_with_ip(key.ip()) >= self.max_conns_per_ip {
std::mem::drop(svc);

View File

@ -12,7 +12,7 @@ use zebra_chain::{
use super::{PeerSetBuilder, PeerVersions};
use crate::{
constants::MAX_CONNS_PER_IP,
constants::DEFAULT_MAX_CONNS_PER_IP,
peer::{ClientRequest, MinimumPeerVersion},
peer_set::inventory_registry::InventoryStatus,
protocol::external::{types::Version, InventoryHash},
@ -145,7 +145,7 @@ fn peer_set_ready_multiple_connections() {
let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.max_conns_per_ip(max(3, MAX_CONNS_PER_IP))
.max_conns_per_ip(max(3, DEFAULT_MAX_CONNS_PER_IP))
.build();
// Get peerset ready
@ -178,7 +178,7 @@ fn peer_set_ready_multiple_connections() {
#[test]
fn peer_set_rejects_connections_past_per_ip_limit() {
const NUM_PEER_VERSIONS: usize = crate::constants::MAX_CONNS_PER_IP + 1;
const NUM_PEER_VERSIONS: usize = crate::constants::DEFAULT_MAX_CONNS_PER_IP + 1;
// Use three peers with the same version
let peer_version = Version::min_specified_for_upgrade(Network::Mainnet, NetworkUpgrade::Nu5);
@ -220,7 +220,7 @@ fn peer_set_rejects_connections_past_per_ip_limit() {
// Check we have the right amount of ready services
assert_eq!(
peer_ready.ready_services.len(),
crate::constants::MAX_CONNS_PER_IP
crate::constants::DEFAULT_MAX_CONNS_PER_IP
);
});
}
@ -259,7 +259,7 @@ fn peer_set_route_inv_empty_registry() {
let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.max_conns_per_ip(max(2, MAX_CONNS_PER_IP))
.max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP))
.build();
// Get peerset ready
@ -342,7 +342,7 @@ fn peer_set_route_inv_advertised_registry_order(advertised_first: bool) {
let (mut peer_set, mut peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.max_conns_per_ip(max(2, MAX_CONNS_PER_IP))
.max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP))
.build();
// Advertise some inventory
@ -450,7 +450,7 @@ fn peer_set_route_inv_missing_registry_order(missing_first: bool) {
let (mut peer_set, mut peer_set_guard) = PeerSetBuilder::new()
.with_discover(discovered_peers)
.with_minimum_peer_version(minimum_peer_version.clone())
.max_conns_per_ip(max(2, MAX_CONNS_PER_IP))
.max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP))
.build();
// Mark some inventory as missing

View File

@ -0,0 +1,75 @@
# Default configuration for zebrad.
#
# This file can be used as a skeleton for custom configs.
#
# Unspecified fields use default values. Optional fields are Some(field) if the
# field is present and None if it is absent.
#
# This file is generated as an example using zebrad's current defaults.
# You should set only the config options you want to keep, and delete the rest.
# Only a subset of fields are present in the skeleton, since optional values
# whose default is None are omitted.
#
# The config format (including a complete list of sections and fields) is
# documented here:
# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html
#
# zebrad attempts to load configs in the following order:
#
# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`;
# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent);
# 3. The default config.
[consensus]
checkpoint_sync = true
debug_skip_parameter_preload = false
[mempool]
eviction_memory_time = "1h"
tx_cost_limit = 80000000
[metrics]
[mining]
debug_like_zcashd = true
[network]
cache_dir = true
crawl_new_peer_interval = "1m 1s"
initial_mainnet_peers = [
"dnsseed.z.cash:8233",
"dnsseed.str4d.xyz:8233",
"mainnet.seeder.zfnd.org:8233",
"mainnet.is.yolo.money:8233",
]
initial_testnet_peers = [
"dnsseed.testnet.z.cash:18233",
"testnet.seeder.zfnd.org:18233",
"testnet.is.yolo.money:18233",
]
listen_addr = "0.0.0.0:8233"
max_connections_per_ip = 1
network = "Mainnet"
peerset_initial_target_size = 25
[rpc]
debug_force_finished_sync = false
parallel_cpu_threads = 0
[state]
cache_dir = "cache_dir"
delete_old_database = true
ephemeral = false
[sync]
checkpoint_verify_concurrency_limit = 1000
download_concurrency_limit = 50
full_verify_concurrency_limit = 20
parallel_cpu_threads = 0
[tracing]
buffer_limit = 128000
force_use_color = false
use_color = true
use_journald = false

View File

@ -0,0 +1,71 @@
# Default configuration for zebrad.
#
# This file can be used as a skeleton for custom configs.
#
# Unspecified fields use default values. Optional fields are Some(field) if the
# field is present and None if it is absent.
#
# This file is generated as an example using zebrad's current defaults.
# You should set only the config options you want to keep, and delete the rest.
# Only a subset of fields are present in the skeleton, since optional values
# whose default is None are omitted.
#
# The config format (including a complete list of sections and fields) is
# documented here:
# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html
#
# zebrad attempts to load configs in the following order:
#
# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`;
# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent);
# 3. The default config.
[consensus]
checkpoint_sync = true
debug_skip_parameter_preload = false
[mempool]
eviction_memory_time = "1h"
tx_cost_limit = 80000000
[metrics]
[network]
cache_dir = true
crawl_new_peer_interval = "1m 1s"
initial_mainnet_peers = [
"dnsseed.z.cash:8233",
"dnsseed.str4d.xyz:8233",
"mainnet.seeder.zfnd.org:8233",
"mainnet.is.yolo.money:8233",
]
initial_testnet_peers = [
"dnsseed.testnet.z.cash:18233",
"testnet.seeder.zfnd.org:18233",
"testnet.is.yolo.money:18233",
]
listen_addr = "0.0.0.0:8233"
max_connections_per_ip = 1
network = "Mainnet"
peerset_initial_target_size = 25
[rpc]
debug_force_finished_sync = false
parallel_cpu_threads = 1
[state]
cache_dir = "cache_dir"
delete_old_database = true
ephemeral = false
[sync]
checkpoint_verify_concurrency_limit = 1000
download_concurrency_limit = 50
full_verify_concurrency_limit = 20
parallel_cpu_threads = 0
[tracing]
buffer_limit = 128000
force_use_color = false
use_color = true
use_journald = false