diff --git a/zerotier-network-hypervisor/src/util/canonicalobject.rs b/zerotier-network-hypervisor/src/util/canonicalobject.rs new file mode 100644 index 000000000..ab0683b05 --- /dev/null +++ b/zerotier-network-hypervisor/src/util/canonicalobject.rs @@ -0,0 +1,39 @@ +// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md. + +use std::sync::atomic::{AtomicUsize, Ordering}; + +use lazy_static::lazy_static; + +lazy_static! { + static ref CANONICAL_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); +} + +/// An object that implements equality such that each instance is globally unique in a runtime/process. +/// +/// This is used to make canonicalized objects like Path and Peer implement eq() accordingly. A unique +/// ID assigned internally from a counter is used instead of the object's location in memory because +/// technically objects can move in Rust. Canonical objects are encased in Arc<> and unlikely to do so, +/// but this is "correct." +#[repr(transparent)] +pub struct CanonicalObject(usize); + +impl CanonicalObject { + #[inline(always)] + pub fn new() -> Self { + Self(CANONICAL_ID_COUNTER.fetch_add(1, Ordering::SeqCst)) + } + + #[inline(always)] + pub fn canonical_instance_id(&self) -> usize { + self.0 + } +} + +impl PartialEq for CanonicalObject { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for CanonicalObject {} diff --git a/zerotier-network-hypervisor/src/util/mod.rs b/zerotier-network-hypervisor/src/util/mod.rs index bf999393d..a99d46bdb 100644 --- a/zerotier-network-hypervisor/src/util/mod.rs +++ b/zerotier-network-hypervisor/src/util/mod.rs @@ -1,6 +1,7 @@ // (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md. pub mod buffer; +pub(crate) mod canonicalobject; pub(crate) mod gate; pub mod marshalable; pub(crate) mod pool; diff --git a/zerotier-network-hypervisor/src/vl1/node.rs b/zerotier-network-hypervisor/src/vl1/node.rs index ad9deae8f..7b3dc6dbc 100644 --- a/zerotier-network-hypervisor/src/vl1/node.rs +++ b/zerotier-network-hypervisor/src/vl1/node.rs @@ -593,6 +593,10 @@ impl Node { self.best_root.read().clone() } + pub fn is_peer_root(&self, peer: &Peer) -> bool { + self.roots.read().roots.keys().any(|p| (**p).eq(peer)) + } + pub fn add_update_root_set(&self, rs: RootSet) -> bool { let mut roots = self.roots.write(); if let Some(entry) = roots.sets.get_mut(&rs.name) { diff --git a/zerotier-network-hypervisor/src/vl1/path.rs b/zerotier-network-hypervisor/src/vl1/path.rs index 3bfad9303..1b652b205 100644 --- a/zerotier-network-hypervisor/src/vl1/path.rs +++ b/zerotier-network-hypervisor/src/vl1/path.rs @@ -2,11 +2,11 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hasher}; -use std::sync::atomic::{AtomicI64, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicI64, Ordering}; -use lazy_static::lazy_static; use parking_lot::Mutex; +use crate::util::canonicalobject::CanonicalObject; use crate::vl1::endpoint::Endpoint; use crate::vl1::fragmentedpacket::FragmentedPacket; use crate::vl1::node::*; @@ -20,19 +20,15 @@ pub(crate) enum PathServiceResult { NeedsKeepalive, } -lazy_static! { - static ref INSTANCE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); -} - /// A remote endpoint paired with a local socket and a local interface. /// These are maintained in Node and canonicalized so that all unique paths have /// one and only one unique path object. That enables statistics to be tracked /// for them and uniform application of things like keepalives. pub struct Path { + pub(crate) canonical: CanonicalObject, pub endpoint: Endpoint, pub local_socket: SI::LocalSocket, pub local_interface: SI::LocalInterface, - pub(crate) internal_instance_id: usize, // arbitrary local ID that should be globally unique to a given path object instance last_send_time_ticks: AtomicI64, last_receive_time_ticks: AtomicI64, create_time_ticks: i64, @@ -42,10 +38,10 @@ pub struct Path { impl Path { pub fn new(endpoint: Endpoint, local_socket: SI::LocalSocket, local_interface: SI::LocalInterface, time_ticks: i64) -> Self { Self { + canonical: CanonicalObject::new(), endpoint, local_socket, local_interface, - internal_instance_id: INSTANCE_ID_COUNTER.fetch_add(1, Ordering::SeqCst), last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), create_time_ticks: time_ticks, @@ -107,6 +103,15 @@ impl Path { } } +impl PartialEq for Path { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + self.canonical.eq(&other.canonical) + } +} + +impl Eq for Path {} + #[repr(transparent)] struct PacketIdHasher(u64); diff --git a/zerotier-network-hypervisor/src/vl1/peer.rs b/zerotier-network-hypervisor/src/vl1/peer.rs index ef9940e66..9d5400cc2 100644 --- a/zerotier-network-hypervisor/src/vl1/peer.rs +++ b/zerotier-network-hypervisor/src/vl1/peer.rs @@ -14,6 +14,7 @@ use zerotier_core_crypto::salsa::Salsa; use zerotier_core_crypto::secret::Secret; use crate::util::byte_array_range; +use crate::util::canonicalobject::CanonicalObject; use crate::util::debug_event; use crate::util::marshalable::Marshalable; use crate::vl1::node::*; @@ -26,7 +27,7 @@ pub(crate) const SERVICE_INTERVAL_MS: i64 = security_constants::EPHEMERAL_SECRET struct PeerPath { path: Weak>, - path_internal_instance_id: usize, + canonical_instance_id: usize, last_receive_time_ticks: i64, } @@ -34,6 +35,8 @@ struct PeerPath { /// /// Equality and hashing is implemented in terms of the identity. pub struct Peer { + canonical: CanonicalObject, + // This peer's identity. pub(crate) identity: Identity, @@ -152,6 +155,7 @@ impl Peer { pub(crate) fn new(this_node_identity: &Identity, id: Identity, time_clock: i64, time_ticks: i64) -> Option> { this_node_identity.agree(&id).map(|static_secret| -> Self { Self { + canonical: CanonicalObject::new(), identity: id, identity_symmetric_key: SymmetricSecret::new(static_secret), ephemeral_symmetric_key: RwLock::new(None), @@ -310,7 +314,7 @@ impl Peer { let mut path_is_known = false; for p in self.paths.lock().iter_mut() { - if p.path_internal_instance_id == source_path.internal_instance_id { + if p.canonical_instance_id == source_path.canonical.canonical_instance_id() { p.last_receive_time_ticks = time_ticks; path_is_known = true; break; @@ -633,7 +637,7 @@ impl Peer { impl PartialEq for Peer { #[inline(always)] fn eq(&self, other: &Self) -> bool { - self.identity.eq(&other.identity) + self.canonical.eq(&other.canonical) } } @@ -642,6 +646,6 @@ impl Eq for Peer {} impl Hash for Peer { #[inline(always)] fn hash(&self, state: &mut H) { - self.identity.hash(state); + self.canonical.canonical_instance_id().hash(state); } } diff --git a/zerotier-system-service/src/service.rs b/zerotier-system-service/src/service.rs index c55884a33..a433234b7 100644 --- a/zerotier-system-service/src/service.rs +++ b/zerotier-system-service/src/service.rs @@ -21,7 +21,8 @@ use crate::localsocket::LocalSocket; use crate::udp::*; use crate::utils::{ms_monotonic, ms_since_epoch}; -const UDP_UPDATE_BINDINGS_INTERVAL_MS: Duration = Duration::from_millis(2500); +/// Interval between scans of system network interfaces to update port bindings. +const UDP_UPDATE_BINDINGS_INTERVAL_MS: Duration = Duration::from_millis(5000); /// ZeroTier system service, which presents virtual networks as VPN connections on Windows/macOS/Linux/BSD/etc. pub struct Service {