Skip to content

Commit

Permalink
Automatic Firewall/NAT Detection (#271)
Browse files Browse the repository at this point in the history
* Initial work towards firewall checking

* Checking connectivity by awaiting incoming connections

* Correct timeout handling

* Testing

* Finished testing

* fmt

* clippy

* Inform peers of ENR update
  • Loading branch information
AgeManning authored Oct 29, 2024
1 parent a196ef7 commit ccc3a57
Show file tree
Hide file tree
Showing 6 changed files with 549 additions and 260 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ categories = ["network-programming", "asynchronous"]
exclude = [".gitignore", ".github/*"]

[dependencies]
enr = { version = "0.12", features = ["k256", "ed25519"] }
enr = { git = "https://github.com/sigp/enr", branch = "remove-fields", features = [
"k256",
"ed25519",
] } # enr = { version = "0.12", features = ["k256", "ed25519"] }
tokio = { version = "1", features = ["net", "sync", "macros", "rt"] }
libp2p-identity = { version = "0.2", features = [
"ed25519",
"secp256k1",
"ed25519",
"secp256k1",
], optional = true }
multiaddr = { version = "0.18", optional = true }
zeroize = { version = "1", features = ["zeroize_derive"] }
Expand Down
31 changes: 31 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ pub struct Config {
/// will last indefinitely. Default is 1 hour.
pub ban_duration: Option<Duration>,

/// Auto-discovering our IP address, is only one part in discovering our NAT/firewall
/// situation. We need to determine if we are behind a firewall that is preventing incoming
/// connections (this is especially true for IPv6 where all connections will report the same
/// external IP). To do this, Discv5 uses a heuristic, which is that after we set an address in
/// our ENR, we wait for this duration to see if we have any incoming connections. If we
/// receive a single INCOMING connection in this duration, we consider ourselves contactable,
/// until we update or change our IP address again. If we fail to receive an incoming
/// connection in this duration, we revoke our ENR address advertisement for 6 hours, before
/// trying again. This can be set to None, to always advertise and never revoke. The default is
/// Some(5 minutes).
pub auto_nat_listen_duration: Option<Duration>,

/// A custom executor which can spawn the discv5 tasks. This must be a tokio runtime, with
/// timing support. By default, the executor that created the discv5 struct will be used.
pub executor: Option<Box<dyn Executor + Send + Sync>>,
Expand Down Expand Up @@ -141,6 +153,7 @@ impl ConfigBuilder {
filter_max_bans_per_ip: Some(5),
permit_ban_list: PermitBanList::default(),
ban_duration: Some(Duration::from_secs(3600)), // 1 hour
auto_nat_listen_duration: Some(Duration::from_secs(300)), // 5 minutes
executor: None,
listen_config,
};
Expand Down Expand Up @@ -295,6 +308,24 @@ impl ConfigBuilder {
self
}

/// Auto-discovering our IP address, is only one part in discovering our NAT/firewall
/// situation. We need to determine if we are behind a firewall that is preventing incoming
/// connections (this is especially true for IPv6 where all connections will report the same
/// external IP). To do this, Discv5 uses a heuristic, which is that after we set an address in
/// our ENR, we wait for this duration to see if we have any incoming connections. If we
/// receive a single INCOMING connection in this duration, we consider ourselves contactable,
/// until we update or change our IP address again. If we fail to receive an incoming
/// connection in this duration, we revoke our ENR address advertisement for 6 hours, before
/// trying again. This can be set to None, to always advertise and never revoke. The default is
/// Some(5 minutes).
pub fn auto_nat_listen_duration(
&mut self,
auto_nat_listen_duration: Option<Duration>,
) -> &mut Self {
self.config.auto_nat_listen_duration = auto_nat_listen_duration;
self
}

/// A custom executor which can spawn the discv5 tasks. This must be a tokio runtime, with
/// timing support.
pub fn executor(&mut self, executor: Box<dyn Executor + Send + Sync>) -> &mut Self {
Expand Down
14 changes: 13 additions & 1 deletion src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

lazy_static! {
pub static ref METRICS: InternalMetrics = InternalMetrics::default();
Expand All @@ -16,6 +16,10 @@ pub struct InternalMetrics {
pub bytes_sent: AtomicUsize,
/// The number of bytes received.
pub bytes_recv: AtomicUsize,
/// Whether we consider ourselves contactable or not on ipv4.
pub ipv4_contactable: AtomicBool,
/// Whether we consider ourselves contactable or not on ipv6.
pub ipv6_contactable: AtomicBool,
}

impl Default for InternalMetrics {
Expand All @@ -26,6 +30,8 @@ impl Default for InternalMetrics {
unsolicited_requests_per_window: AtomicUsize::new(0),
bytes_sent: AtomicUsize::new(0),
bytes_recv: AtomicUsize::new(0),
ipv4_contactable: AtomicBool::new(false),
ipv6_contactable: AtomicBool::new(false),
}
}
}
Expand Down Expand Up @@ -55,6 +61,10 @@ pub struct Metrics {
pub bytes_sent: usize,
/// The number of bytes received.
pub bytes_recv: usize,
/// Whether we consider ourselves contactable or not.
pub ipv4_contactable: bool,
/// Whether we consider ourselves contactable or not.
pub ipv6_contactable: bool,
}

impl From<&METRICS> for Metrics {
Expand All @@ -67,6 +77,8 @@ impl From<&METRICS> for Metrics {
/ internal_metrics.moving_window as f64,
bytes_sent: internal_metrics.bytes_sent.load(Ordering::Relaxed),
bytes_recv: internal_metrics.bytes_recv.load(Ordering::Relaxed),
ipv4_contactable: internal_metrics.ipv4_contactable.load(Ordering::Relaxed),
ipv6_contactable: internal_metrics.ipv6_contactable.load(Ordering::Relaxed),
}
}
}
Loading

0 comments on commit ccc3a57

Please sign in to comment.