aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport/libp2p/book.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/libp2p/book.rs')
-rw-r--r--src/transport/libp2p/book.rs66
1 files changed, 64 insertions, 2 deletions
diff --git a/src/transport/libp2p/book.rs b/src/transport/libp2p/book.rs
index ceb9d27..b495719 100644
--- a/src/transport/libp2p/book.rs
+++ b/src/transport/libp2p/book.rs
@@ -48,6 +48,8 @@ pub(super) struct Book {
pub(super) announced: Vec<Multiaddr>,
/// `Libp2pConfig::ignore`: not published (but announced), not dialled.
pub(super) ignored: Vec<IpRange>,
+ /// `Libp2pConfig::prefer`: addresses there first (`preference`).
+ pub(super) preferred: Vec<IpRange>,
/// `Libp2pConfig::relay`: this node relays, and says so.
pub(super) serving: bool,
/// Relays named to pick (configured, listed), by peer: picked first.
@@ -265,6 +267,7 @@ impl Book {
}
}
direct.retain(|address| !first.contains(address));
+ direct.sort_by_key(|address| self.preferred_range(address));
first.extend(direct);
first.retain(|address| !ignored(&self.ignored, address));
first
@@ -469,9 +472,11 @@ impl Book {
/// `peer`, connected, identified itself at `listen` (`published`): its direct
/// addresses are those from now on, while connected (`remember`); those through
- /// relays and by name stay.
+ /// relays and by name stay. Kept in order of `preference`: of a peer with many,
+ /// the ones that matter stay (`ADDRESSES_PER_PEER`).
pub(super) fn identified(&mut self, peer: PeerId, listen: Vec<Multiaddr>) {
- let listen: Vec<Multiaddr> = listen.into_iter().map(without_peer).collect();
+ let mut listen: Vec<Multiaddr> = listen.into_iter().map(without_peer).collect();
+ listen.sort_by_key(|address| self.preference(address));
if let Some(known) = self.addresses.get_mut(&peer) {
known.retain(|address| !is_ip(address) || listen.contains(address));
}
@@ -483,6 +488,63 @@ impl Book {
}
}
+impl Book {
+ /// Where a peer's `address` comes, first first: in a range `libp2p-prefer` names
+ /// (by the range's place), then public, then through a relay or by name, then a
+ /// private one (another LAN, a container bridge).
+ fn preference(&self, address: &Multiaddr) -> (u8, usize) {
+ let range = self.preferred_range(address);
+ if range != usize::MAX {
+ return (0, range);
+ }
+ match ip_of(address).filter(|_| is_ip(address)) {
+ Some(ip) if is_public(ip) => (1, 0),
+ Some(_) => (3, 0),
+ None => (2, 0),
+ }
+ }
+
+ /// The place of the first range of `libp2p-prefer` that has `address`; for an
+ /// address none has, `usize::MAX`: after them.
+ fn preferred_range(&self, address: &Multiaddr) -> usize {
+ ip_of(address)
+ .filter(|_| is_ip(address))
+ .and_then(|ip| self.preferred.iter().position(|range| range.contains(ip)))
+ .unwrap_or(usize::MAX)
+ }
+}
+
+/// The IP address `address` starts with, if any.
+fn ip_of(address: &Multiaddr) -> Option<std::net::IpAddr> {
+ match address.iter().next() {
+ Some(Protocol::Ip4(ip)) => Some(ip.into()),
+ Some(Protocol::Ip6(ip)) => Some(ip.into()),
+ _ => None,
+ }
+}
+
+/// Whether `ip` can be reached from the internet, as far as its range tells.
+fn is_public(ip: std::net::IpAddr) -> bool {
+ use std::net::IpAddr;
+ match ip {
+ IpAddr::V4(ip) => {
+ // 100.64.0.0/10: carrier-grade NAT.
+ let shared = ip.octets()[0] == 100 && (ip.octets()[1] & 0xc0) == 64;
+ !(ip.is_private()
+ || ip.is_loopback()
+ || ip.is_link_local()
+ || ip.is_unspecified()
+ || shared)
+ }
+ IpAddr::V6(ip) => {
+ !(ip.is_loopback()
+ || ip.is_unspecified()
+ || ip.is_unique_local()
+ || ip.is_unicast_link_local())
+ }
+ }
+}
+
/// Whether `address` is a direct one at an IP address (not through a relay, nor a name).
pub(super) fn is_ip(address: &Multiaddr) -> bool {
matches!(