Implement canonical object equality in a canonical way.

This commit is contained in:
Adam Ierymenko 2022-06-23 17:31:19 -04:00
parent 32145fbe8d
commit 9cbbcb4495
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 67 additions and 13 deletions

View 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 {}

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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 {