aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport/libp2p/ignore.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 04:20:41 +0100
committertv <tv@krebsco.de>2026-10-31 04:20:41 +0100
commit235411a261441df6c0ddb1c4d7f99cf06b8f3c55 (patch)
tree8ccf4f4dfbdc985fd2196fca87ad9a2dd0d16ed3 /src/transport/libp2p/ignore.rs
parent803c2a528b25c5bf983130a9826bdcbfdc394ba6 (diff)
libp2p: explode!
Diffstat (limited to 'src/transport/libp2p/ignore.rs')
-rw-r--r--src/transport/libp2p/ignore.rs146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/transport/libp2p/ignore.rs b/src/transport/libp2p/ignore.rs
new file mode 100644
index 0000000..b14235e
--- /dev/null
+++ b/src/transport/libp2p/ignore.rs
@@ -0,0 +1,146 @@
+//! `--libp2p-ignore`: address ranges neither published nor dialled, nor connected over
+//! (`Ignoring`, a behaviour that denies connections there).
+
+use super::*;
+
+/// An address range (`10.244.0.0/16`, `42::/16`; a bare address is a range of one), for
+/// `Libp2pConfig::ignore`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct IpRange {
+ pub(super) network: std::net::IpAddr,
+ pub(super) prefix: u8,
+}
+
+impl FromStr for IpRange {
+ type Err = anyhow::Error;
+
+ fn from_str(text: &str) -> Result<Self> {
+ let (address, prefix) = match text.split_once('/') {
+ Some((address, prefix)) => (address, Some(prefix)),
+ None => (text, None),
+ };
+ let network: std::net::IpAddr = address
+ .parse()
+ .with_context(|| format!("{text:?} is no address range (e.g. 10.244.0.0/16)"))?;
+ let bits = if network.is_ipv4() { 32 } else { 128 };
+ let prefix = match prefix {
+ Some(prefix) => prefix
+ .parse::<u8>()
+ .ok()
+ .filter(|prefix| *prefix <= bits)
+ .with_context(|| format!("{text:?}: the prefix is 0 to {bits}"))?,
+ None => bits,
+ };
+ Ok(Self { network, prefix })
+ }
+}
+
+impl IpRange {
+ pub(super) fn contains(&self, ip: std::net::IpAddr) -> bool {
+ use std::net::IpAddr;
+ match (self.network, ip) {
+ (IpAddr::V4(network), IpAddr::V4(ip)) => {
+ let mask = u32::MAX
+ .checked_shl(32 - u32::from(self.prefix))
+ .unwrap_or(0);
+ u32::from(network) & mask == u32::from(ip) & mask
+ }
+ (IpAddr::V6(network), IpAddr::V6(ip)) => {
+ let mask = u128::MAX
+ .checked_shl(128 - u32::from(self.prefix))
+ .unwrap_or(0);
+ u128::from(network) & mask == u128::from(ip) & mask
+ }
+ _ => false,
+ }
+ }
+}
+
+/// Whether `address` is in one of `ranges`: a direct address there (one through a relay
+/// is judged by the connection to the relay).
+pub(super) fn ignored(ranges: &[IpRange], address: &Multiaddr) -> bool {
+ if ranges.is_empty() || is_relayed(address) {
+ return false;
+ }
+ let ip = match address.iter().next() {
+ Some(Protocol::Ip4(ip)) => std::net::IpAddr::V4(ip),
+ Some(Protocol::Ip6(ip)) => std::net::IpAddr::V6(ip),
+ _ => return false,
+ };
+ ranges.iter().any(|range| range.contains(ip))
+}
+
+/// Keeps connections off the address ranges `libp2p-ignore` names, whoever dialled:
+/// hole punching and identify bring addresses there too, and a peer may dial us there.
+pub(super) struct Ignoring {
+ pub(super) ranges: Vec<IpRange>,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("{0} is in a range left out (libp2p-ignore)")]
+pub(super) struct IgnoredAddress(Multiaddr);
+
+impl ::libp2p::swarm::NetworkBehaviour for Ignoring {
+ type ConnectionHandler = ::libp2p::swarm::dummy::ConnectionHandler;
+ type ToSwarm = std::convert::Infallible;
+
+ fn handle_pending_inbound_connection(
+ &mut self,
+ _connection_id: ConnectionId,
+ _local_addr: &Multiaddr,
+ remote_addr: &Multiaddr,
+ ) -> Result<(), ::libp2p::swarm::ConnectionDenied> {
+ if ignored(&self.ranges, remote_addr) {
+ return Err(::libp2p::swarm::ConnectionDenied::new(IgnoredAddress(
+ remote_addr.clone(),
+ )));
+ }
+ Ok(())
+ }
+
+ fn handle_established_inbound_connection(
+ &mut self,
+ _connection_id: ConnectionId,
+ _peer: PeerId,
+ _local_addr: &Multiaddr,
+ _remote_addr: &Multiaddr,
+ ) -> Result<::libp2p::swarm::THandler<Self>, ::libp2p::swarm::ConnectionDenied> {
+ Ok(::libp2p::swarm::dummy::ConnectionHandler)
+ }
+
+ fn handle_established_outbound_connection(
+ &mut self,
+ _connection_id: ConnectionId,
+ _peer: PeerId,
+ addr: &Multiaddr,
+ _role_override: ::libp2p::core::Endpoint,
+ _port_use: ::libp2p::core::transport::PortUse,
+ ) -> Result<::libp2p::swarm::THandler<Self>, ::libp2p::swarm::ConnectionDenied> {
+ if ignored(&self.ranges, addr) {
+ return Err(::libp2p::swarm::ConnectionDenied::new(IgnoredAddress(
+ addr.clone(),
+ )));
+ }
+ Ok(::libp2p::swarm::dummy::ConnectionHandler)
+ }
+
+ fn on_swarm_event(&mut self, _event: ::libp2p::swarm::FromSwarm) {}
+
+ fn on_connection_handler_event(
+ &mut self,
+ _peer_id: PeerId,
+ _connection_id: ConnectionId,
+ event: ::libp2p::swarm::THandlerOutEvent<Self>,
+ ) {
+ match event {}
+ }
+
+ fn poll(
+ &mut self,
+ _cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll<
+ ::libp2p::swarm::ToSwarm<Self::ToSwarm, ::libp2p::swarm::THandlerInEvent<Self>>,
+ > {
+ std::task::Poll::Pending
+ }
+}