Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protocols/autonat: optionally use only global IPs #2618

Merged
merged 21 commits into from May 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3a214d7
protocols/autonat: add only_global_ips config
elenaf9 Apr 17, 2022
dedb309
protocols/autonat: fix tests and fmt
elenaf9 Apr 17, 2022
3df7e84
Merge branch 'master' into autonat/config-global-ips
elenaf9 Apr 26, 2022
a9e64da
Merge branch 'master' of github.com:libp2p/rust-libp2p into autonat/c…
elenaf9 May 4, 2022
099b258
autonat: literal copy of fns from std::net
elenaf9 May 4, 2022
f2a51f3
Merge branch 'master' of github.com:libp2p/rust-libp2p into autonat/c…
elenaf9 May 8, 2022
9c2d901
autonat: literal copy of fns from std Ipv6Addr
elenaf9 May 8, 2022
b0f9713
autonat: return DialRefused on relayed/ private ip
elenaf9 May 8, 2022
99fec62
autonat: test only_global_ips config
elenaf9 May 8, 2022
d3d9015
autonat: remove unneeded explicit `drop`
elenaf9 May 8, 2022
05d5f2d
autonat: remove outdated comment
elenaf9 May 8, 2022
5b3b909
autonat: add changelog entry
elenaf9 May 8, 2022
f7d47da
autonat: fix changelog entry
elenaf9 May 10, 2022
b5a2c46
Merge branch 'master' into autonat/config-global-ips
elenaf9 May 10, 2022
2954c1a
autonat: remove redundant check
elenaf9 May 10, 2022
7035422
autonat: return DialRefused if no dialable addrs
elenaf9 May 10, 2022
f027e09
Merge branch 'master' into autonat/config-global-ips
elenaf9 May 11, 2022
202e0ad
autonat: fix typo
elenaf9 May 19, 2022
1d6dbeb
Merge branch 'master' into autonat/config-global-ips
elenaf9 May 19, 2022
36665ee
Merge branch 'master' into autonat/config-global-ips
elenaf9 May 22, 2022
54c9681
protocols/autonat: fix typo in changelog
elenaf9 May 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
129 changes: 116 additions & 13 deletions protocols/autonat/src/behaviour.rs
Expand Up @@ -30,6 +30,7 @@ use futures_timer::Delay;
use instant::Instant;
use libp2p_core::{
connection::{ConnectionId, ListenerId},
multiaddr::Protocol,
ConnectedPoint, Endpoint, Multiaddr, PeerId,
};
use libp2p_request_response::{
Expand Down Expand Up @@ -77,6 +78,8 @@ pub struct Config {
pub throttle_clients_peer_max: usize,
/// Period for throttling clients requests.
pub throttle_clients_period: Duration,
/// Reject probes for clients that are observed at a non-global ip address.
pub only_global_ips: bool,
}

impl Default for Config {
Expand All @@ -93,6 +96,7 @@ impl Default for Config {
throttle_clients_global_max: 30,
throttle_clients_peer_max: 3,
throttle_clients_period: Duration::from_secs(1),
only_global_ips: true,
}
}
}
Expand Down Expand Up @@ -188,7 +192,8 @@ pub struct Behaviour {
ongoing_outbound: HashMap<RequestId, ProbeId>,

// Connected peers with the observed address of each connection.
// If the endpoint of a connection is relayed, the observed address is `None`.
// If the endpoint of a connection is relayed or not global (in case of Config::only_global_ips),
// the observed address is `None`.
connected: HashMap<PeerId, HashMap<ConnectionId, Option<Multiaddr>>>,

// Used servers in recent outbound probes that are throttled through Config::throttle_server_period.
Expand Down Expand Up @@ -313,12 +318,14 @@ impl NetworkBehaviour for Behaviour {
other_established,
);
let connections = self.connected.entry(*peer).or_default();
let addr = if endpoint.is_relayed() {
None
} else {
Some(endpoint.get_remote_address().clone())
};
connections.insert(*conn, addr);
let addr = endpoint.get_remote_address();
let observed_addr =
if !endpoint.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) {
Some(addr.clone())
} else {
None
};
connections.insert(*conn, observed_addr);

match endpoint {
ConnectedPoint::Dialer {
Expand Down Expand Up @@ -386,12 +393,14 @@ impl NetworkBehaviour for Behaviour {
return;
}
let connections = self.connected.get_mut(peer).expect("Peer is connected.");
let addr = if new.is_relayed() {
None
} else {
Some(new.get_remote_address().clone())
};
connections.insert(*conn, addr);
let addr = new.get_remote_address();
let observed_addr =
if !new.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) {
Some(addr.clone())
} else {
None
};
connections.insert(*conn, observed_addr);
}

fn inject_new_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) {
Expand Down Expand Up @@ -512,3 +521,97 @@ trait HandleInnerEvent {
event: RequestResponseEvent<DialRequest, DialResponse>,
) -> (VecDeque<Event>, Option<Action>);
}

trait GlobalIp {
fn is_global_ip(&self) -> bool;
}

impl GlobalIp for Multiaddr {
fn is_global_ip(&self) -> bool {
match self.iter().next() {
Some(Protocol::Ip4(a)) => a.is_global_ip(),
Some(Protocol::Ip6(a)) => a.is_global_ip(),
_ => false,
}
}
}

impl GlobalIp for std::net::Ipv4Addr {
// NOTE: The below logic is copied from `std::net::Ipv4Addr::is_global`, which is at the time of
// writing behind the unstable `ip` feature.
// See https://github.com/rust-lang/rust/issues/27709 for more info.
//
// TODO: Consider removing check check with the unstable methods `is_shared`, `is_reserved` and `is_benchmarking`? In case
// of the former the peer is still eligible for a dial-back, the latter cases should never occur in practice.
//
// TODO: Consider to only check for loopback, private ip and link-local. Can any other case ever happen in practice if we solely
// use this check for observed addresses?
elenaf9 marked this conversation as resolved.
Show resolved Hide resolved
fn is_global_ip(&self) -> bool {
// Check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two
// globally routable addresses in the 192.0.0.0/24 range.
if u32::from_be_bytes(self.octets()) == 0xc0000009
|| u32::from_be_bytes(self.octets()) == 0xc000000a
{
return true;
}

// Copied from the unstable method `std::net::Ipv4Addr::is_shared`.
let is_shared =
|| self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000);
elenaf9 marked this conversation as resolved.
Show resolved Hide resolved

// Copied from the unstable method `std::net::Ipv4Addr::is_reserved`.
//
// **Warning**: As IANA assigns new addresses, this logic will be
// updated. This may result in non-reserved addresses being
// treated as reserved in code that relies on an outdated version
// of this method.
let is_reserved = || self.octets()[0] & 240 == 240 && !self.is_broadcast();

// Copied from the unstable method `std::net::Ipv4Addr::is_benchmarking`.
let is_benchmarking = || self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18;

!self.is_private()
&& !self.is_loopback()
&& !self.is_link_local()
&& !self.is_broadcast()
&& !self.is_documentation()
&& !is_shared()
// addresses reserved for future protocols (`192.0.0.0/24`)
&& !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0)
&& !is_reserved()
&& !is_benchmarking()
// Make sure the address is not in 0.0.0.0/8
&& self.octets()[0] != 0
}
}

impl GlobalIp for std::net::Ipv6Addr {
// NOTE: The below logic is copied from `std::net::Ipv6Addr::is_global`, which is at the time of
// writing behind the unstable `ip` feature.
// See https://github.com/rust-lang/rust/issues/27709 for more info.
//
// Note that contrary to `Ipv4Addr::is_global_ip` this currently checks for global scope
// rather than global reachability.
// TODO: There is a PR to change this, but seems inactive. Should we help push this forward?
elenaf9 marked this conversation as resolved.
Show resolved Hide resolved
// See https://github.com/rust-lang/rust/pull/86634 and https://github.com/rust-lang/rust/issues/85604.
fn is_global_ip(&self) -> bool {
let is_unicast_global = || {
let is_unicast_link_local = (self.segments()[0] & 0xffc0) == 0xfe80;
let is_unique_local = (self.segments()[0] & 0xfe00) == 0xfc00;
let is_documentation = (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8);

!self.is_loopback() && !is_unicast_link_local && !is_unique_local && is_documentation
};

if !self.is_multicast() {
return is_unicast_global();
}
// Check scope of the multicast address.
// Copied from unstable method [`std::net::Ipv6Addr::multicast_scope`].
match self.segments()[0] & 0x000f {
14 => true, // Global multicast scope.
1..=5 | 8 => false, // Local multicast scope.
_ => is_unicast_global(), // Unknown multicast scope.
}
}
}
7 changes: 6 additions & 1 deletion protocols/autonat/src/behaviour/as_client.rs
Expand Up @@ -262,7 +262,12 @@ impl<'a> AsClient<'a> {
let mut servers: Vec<&PeerId> = self.servers.iter().collect();

if self.config.use_connected {
servers.extend(self.connected.iter().map(|(id, _)| id));
servers.extend(self.connected.iter().filter_map(|(id, addrs)| {
// Filter servers for which no qualified address is known.
// This is the case if the connection is relayed or the address is
// not global (in case of Config::only_global_ips).
addrs.values().any(|a| a.is_some()).then(|| id)
}));
}

servers.retain(|s| !self.throttled_servers.iter().any(|(id, _)| s == &id));
Expand Down
3 changes: 2 additions & 1 deletion protocols/autonat/tests/test_client.rs
Expand Up @@ -35,10 +35,11 @@ const MAX_CONFIDENCE: usize = 3;
const TEST_RETRY_INTERVAL: Duration = Duration::from_secs(1);
const TEST_REFRESH_INTERVAL: Duration = Duration::from_secs(2);

async fn init_swarm(config: Config) -> Swarm<Behaviour> {
async fn init_swarm(mut config: Config) -> Swarm<Behaviour> {
let keypair = Keypair::generate_ed25519();
let local_id = PeerId::from_public_key(&keypair.public());
let transport = development_transport(keypair).await.unwrap();
config.only_global_ips = false;
let behaviour = Behaviour::new(local_id, config);
Swarm::new(transport, behaviour, local_id)
}
Expand Down
3 changes: 2 additions & 1 deletion protocols/autonat/tests/test_server.rs
Expand Up @@ -34,10 +34,11 @@ use libp2p_core::{ConnectedPoint, Endpoint};
use libp2p_swarm::DialError;
use std::{num::NonZeroU32, time::Duration};

async fn init_swarm(config: Config) -> Swarm<Behaviour> {
async fn init_swarm(mut config: Config) -> Swarm<Behaviour> {
let keypair = Keypair::generate_ed25519();
let local_id = PeerId::from_public_key(&keypair.public());
let transport = development_transport(keypair).await.unwrap();
config.only_global_ips = false;
let behaviour = Behaviour::new(local_id, config);
Swarm::new(transport, behaviour, local_id)
}
Expand Down