Просмотр исходного кода

net/hosts: keep a ringbuf of auto discovered addrs from the version exchange. Check new connections in filter_addresses() against this list to avoid self connections.

darkfi 1 год назад
Родитель
Сommit
4bf87ea395

+ 40 - 11
src/net/hosts.rs

@@ -16,21 +16,21 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use log::{debug, error, info, trace, warn};
+use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
+use smol::lock::RwLock as AsyncRwLock;
 use std::{
-    collections::HashMap,
+    collections::{HashMap, VecDeque},
     fmt, fs,
     fs::File,
+    net::Ipv6Addr,
     sync::{
         atomic::{AtomicBool, Ordering},
-        Arc, Mutex, RwLock,
+        Arc, Mutex as SyncMutex, RwLock,
     },
     time::{Instant, UNIX_EPOCH},
 };
-
-use log::{debug, error, info, trace, warn};
-use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
-use smol::lock::RwLock as AsyncRwLock;
-use url::Url;
+use url::{Host, Url};
 
 use super::{
     session::{SESSION_REFINE, SESSION_SEED},
@@ -97,7 +97,7 @@ pub type HostsPtr = Arc<Hosts>;
 /// Keeps track of hosts and their current state. Prevents race conditions
 /// where multiple threads are simultaneously trying to change the state of
 /// a given host.
-pub(in crate::net) type HostRegistry = Mutex<HashMap<Url, HostState>>;
+pub(in crate::net) type HostRegistry = SyncMutex<HashMap<Url, HostState>>;
 
 /// HostState is a set of mutually exclusive states that can be Insert,
 /// Refine, Move, Connect, Suspend or Connected or Free.
@@ -867,11 +867,14 @@ pub struct Hosts {
     pub(in crate::net) disconnect_publisher: PublisherPtr<Error>,
 
     /// Keeps track of the last time a connection was made.
-    pub(in crate::net) last_connection: Mutex<Instant>,
+    pub(in crate::net) last_connection: SyncMutex<Instant>,
 
     /// Marker for IPv6 availability
     pub(in crate::net) ipv6_available: AtomicBool,
 
+    /// Auto self discovered addresses. Used for filtering self connections.
+    auto_self_addrs: SyncMutex<VecDeque<Ipv6Addr>>,
+
     /// Pointer to configured P2P settings
     settings: Arc<AsyncRwLock<Settings>>,
 }
@@ -880,13 +883,14 @@ impl Hosts {
     /// Create a new hosts list
     pub(in crate::net) fn new(settings: Arc<AsyncRwLock<Settings>>) -> HostsPtr {
         Arc::new(Self {
-            registry: Mutex::new(HashMap::new()),
+            registry: SyncMutex::new(HashMap::new()),
             container: HostContainer::new(),
             store_publisher: Publisher::new(),
             channel_publisher: Publisher::new(),
             disconnect_publisher: Publisher::new(),
-            last_connection: Mutex::new(Instant::now()),
+            last_connection: SyncMutex::new(Instant::now()),
             ipv6_available: AtomicBool::new(true),
+            auto_self_addrs: SyncMutex::new(VecDeque::new()),
             settings,
         })
     }
@@ -1277,6 +1281,22 @@ impl Hosts {
                         continue 'addr_loop
                     }
                 }
+
+                // Cloning this vec means a linear scan and copy here, so just doing a linear
+                // scan here is faster.
+                let auto_self_addrs = self.auto_self_addrs.lock().unwrap();
+                match host {
+                    Host::Ipv6(host_addr) => {
+                        if auto_self_addrs.contains(&host_addr) {
+                            debug!(
+                                target: "net::hosts::filter_addresses",
+                                "[{}] is our own auto discovered addr. Skipping", addr_,
+                            );
+                            continue 'addr_loop
+                        }
+                    }
+                    _ => {}
+                }
             } else {
                 // On localnet, make sure ours ports don't enter the host set.
                 for ext in &settings.external_addrs {
@@ -1476,6 +1496,15 @@ impl Hosts {
 
         Ok(())
     }
+
+    pub(in crate::net) fn add_auto_addr(&self, addr: Ipv6Addr) {
+        let mut auto_addrs = self.auto_self_addrs.lock().unwrap();
+        auto_addrs.push_back(addr);
+        // This is a ringbuf so remove front elements
+        while auto_addrs.len() > 20 {
+            auto_addrs.pop_front().unwrap();
+        }
+    }
 }
 
 #[cfg(test)]

+ 13 - 1
src/net/message.rs

@@ -19,7 +19,8 @@
 use darkfi_serial::{
     async_trait, serialize_async, AsyncDecodable, AsyncEncodable, SerialDecodable, SerialEncodable,
 };
-use url::Url;
+use std::net::Ipv6Addr;
+use url::{Host, Url};
 
 /// Generic message template.
 pub trait Message: 'static + Send + Sync + AsyncDecodable + AsyncEncodable {
@@ -106,6 +107,17 @@ pub struct VersionMessage {
 }
 impl_p2p_message!(VersionMessage, "version");
 
+impl VersionMessage {
+    pub(in crate::net) fn get_ipv6_addr(&self) -> Option<Ipv6Addr> {
+        let host = self.connect_recv_addr.host()?;
+        // Check the reported address is Ipv6
+        match host {
+            Host::Ipv6(addr) => Some(addr),
+            _ => None,
+        }
+    }
+}
+
 /// Sends version information to inbound connection.
 /// Response to `VersionMessage`.
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]

+ 14 - 18
src/net/protocol/protocol_address.rs

@@ -228,10 +228,8 @@ impl ProtocolAddress {
 
         let mut external_addrs = self.settings.read().await.external_addrs.clone();
 
-        // Auto-advertise the node's inbound address using the address that
-        // was sent to use by the node in the version exchange.
         for external_addr in &mut external_addrs {
-            let _ = Self::patch_inbound(&self.channel, external_addr);
+            let _ = Self::patch_external_addr(&self.channel, external_addr);
         }
 
         if external_addrs.is_empty() {
@@ -265,21 +263,21 @@ impl ProtocolAddress {
         Ok(())
     }
 
-    /// If the inbound is an Ipv6 address, then replace it with the ip address reported to
-    /// us by the version exchange.
+    /// If the external_addr is set to `[::]` (unspecified), then replace it with the
+    /// ip address reported to us by the version exchange.
     ///
     /// Also used by ProtocolSeed.
-    pub(super) fn patch_inbound(channel: &Channel, inbound: &mut Url) -> Option<()> {
-        if inbound.scheme() != "tcp" && inbound.scheme() != "tcp+tls" {
+    pub(super) fn patch_external_addr(channel: &Channel, external_addr: &mut Url) -> Option<()> {
+        if external_addr.scheme() != "tcp" && external_addr.scheme() != "tcp+tls" {
             return None
         }
 
-        let inbound_host = inbound.host()?;
+        let external_addr_host = external_addr.host()?;
         // Is it an Ipv6 listener?
-        match inbound_host {
+        match external_addr_host {
             Host::Ipv6(addr) => {
-                // We are only interested if it's localhost
-                if !addr.is_loopback() {
+                // We are only interested if it's [::]
+                if !addr.is_unspecified() {
                     return None
                 }
             }
@@ -297,13 +295,11 @@ impl ProtocolAddress {
 
         // Get our auto-discovered IP
         let version = channel.get_version();
-        let discover_host = version.connect_recv_addr.host()?;
-        // Check the reported address is Ipv6
-        match discover_host {
-            Host::Ipv6(_) => {}
-            _ => return None,
-        };
-        inbound.set_host(version.connect_recv_addr.host_str()).ok()?;
+        // Check if the returned value in the version exchange is ipv6.
+        // Maybe this outbound is using another transport or ipv4.
+        let _ = version.get_ipv6_addr()?;
+
+        external_addr.set_host(version.connect_recv_addr.host_str()).ok()?;
         Some(())
     }
 }

+ 1 - 3
src/net/protocol/protocol_seed.rs

@@ -65,10 +65,8 @@ impl ProtocolSeed {
 
         let mut external_addrs = self.settings.read().await.external_addrs.clone();
 
-        // Auto-advertise the node's inbound address using the address that
-        // was sent to use by the node in the version exchange.
         for external_addr in &mut external_addrs {
-            let _ = ProtocolAddress::patch_inbound(&self.channel, external_addr);
+            let _ = ProtocolAddress::patch_external_addr(&self.channel, external_addr);
         }
 
         if external_addrs.is_empty() {

+ 4 - 0
src/net/protocol/protocol_version.rs

@@ -212,6 +212,10 @@ impl ProtocolVersion {
 
         // Receive version message
         let version = self.version_sub.receive().await?;
+        if let Some(ipv6_addr) = version.get_ipv6_addr() {
+            let hosts = self.channel.p2p().hosts();
+            hosts.add_auto_addr(ipv6_addr);
+        }
         self.channel.set_version(version).await;
 
         // Send verack