mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
Implement canonical object equality in a canonical way.
This commit is contained in:
parent
32145fbe8d
commit
9cbbcb4495
6 changed files with 67 additions and 13 deletions
39
zerotier-network-hypervisor/src/util/canonicalobject.rs
Normal file
39
zerotier-network-hypervisor/src/util/canonicalobject.rs
Normal file
|
@ -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 {}
|
|
@ -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;
|
||||
|
|
|
@ -593,6 +593,10 @@ impl<SI: SystemInterface> Node<SI> {
|
|||
self.best_root.read().clone()
|
||||
}
|
||||
|
||||
pub fn is_peer_root(&self, peer: &Peer<SI>) -> 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) {
|
||||
|
|
|
@ -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<SI: SystemInterface> {
|
||||
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<SI: SystemInterface> {
|
|||
impl<SI: SystemInterface> Path<SI> {
|
||||
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<SI: SystemInterface> Path<SI> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<SI: SystemInterface> PartialEq for Path<SI> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.canonical.eq(&other.canonical)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SI: SystemInterface> Eq for Path<SI> {}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct PacketIdHasher(u64);
|
||||
|
||||
|
|
|
@ -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<SI: SystemInterface> {
|
||||
path: Weak<Path<SI>>,
|
||||
path_internal_instance_id: usize,
|
||||
canonical_instance_id: usize,
|
||||
last_receive_time_ticks: i64,
|
||||
}
|
||||
|
||||
|
@ -34,6 +35,8 @@ struct PeerPath<SI: SystemInterface> {
|
|||
///
|
||||
/// Equality and hashing is implemented in terms of the identity.
|
||||
pub struct Peer<SI: SystemInterface> {
|
||||
canonical: CanonicalObject,
|
||||
|
||||
// This peer's identity.
|
||||
pub(crate) identity: Identity,
|
||||
|
||||
|
@ -152,6 +155,7 @@ impl<SI: SystemInterface> Peer<SI> {
|
|||
pub(crate) fn new(this_node_identity: &Identity, id: Identity, time_clock: i64, time_ticks: i64) -> Option<Peer<SI>> {
|
||||
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<SI: SystemInterface> Peer<SI> {
|
|||
|
||||
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<SI: SystemInterface> Peer<SI> {
|
|||
impl<SI: SystemInterface> PartialEq for Peer<SI> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.identity.eq(&other.identity)
|
||||
self.canonical.eq(&other.canonical)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,6 +646,6 @@ impl<SI: SystemInterface> Eq for Peer<SI> {}
|
|||
impl<SI: SystemInterface> Hash for Peer<SI> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.identity.hash(state);
|
||||
self.canonical.canonical_instance_id().hash(state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue