diff --git a/controller/src/cache.rs b/controller/src/cache.rs index b8e2feb70..0a6e5e41e 100644 --- a/controller/src/cache.rs +++ b/controller/src/cache.rs @@ -43,10 +43,6 @@ impl Cache { Ok(()) } - pub fn list_cached_networks(&self) -> Vec { - self.by_nwid.read().unwrap().keys().cloned().collect() - } - /// Update a network if changed, returning whether or not any update was made and the old version if any. /// A value of (true, None) indicates that there was no network by that ID in which case it is added. pub fn on_network_updated(&self, network: Network) -> (bool, Option) { diff --git a/controller/src/controller.rs b/controller/src/controller.rs index 171f95db2..f80291776 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -183,8 +183,8 @@ impl Controller { } } - /// Send one or more revocation object(s) to a peer (V1 protocol only). - fn v1_proto_send_revocations(&self, peer: &Peer, mut revocations: Vec) { + /// Send one or more revocation object(s) to a peer. + fn send_revocations(&self, peer: &Peer, mut revocations: Vec) { if let Some(host_system) = self.service.read().unwrap().upgrade() { let time_ticks = ms_monotonic(); while !revocations.is_empty() { @@ -336,11 +336,9 @@ impl Controller { nc.name = network.name.clone(); nc.private = network.private; nc.timestamp = now; - nc.credential_ttl = credential_ttl; - nc.revision = Some(now as u64); - nc.mtu = network.mtu.unwrap_or(ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU as u16); nc.multicast_limit = network.multicast_limit.unwrap_or(DEFAULT_MULTICAST_LIMIT as u32); nc.multicast_like_expire = Some(protocol::VL2_DEFAULT_MULTICAST_LIKE_EXPIRE as u32); + nc.mtu = network.mtu.unwrap_or(ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU as u16); nc.routes = network.ip_routes; nc.static_ips = member.ip_assignments.clone(); nc.rules = network.rules; @@ -354,6 +352,8 @@ impl Controller { vl2::v1::CertificateOfMembership::new(&self.local_identity, network_id, &source_identity, now, credential_ttl) { let mut v1cred = V1Credentials { + revision: now as u64, + max_delta: credential_ttl, certificate_of_membership: com, certificates_of_ownership: Vec::new(), tags: HashMap::new(), @@ -383,7 +383,7 @@ impl Controller { // For anyone who has been deauthorized but is still in the window, send revocations. if let Ok(deauthed_members_still_in_window) = self .database - .list_members_deauthorized_after(network.id, now - credential_ttl) + .list_members_deauthorized_after(network.id, now - (credential_ttl as i64)) .await { if !deauthed_members_still_in_window.is_empty() { @@ -419,7 +419,7 @@ impl Controller { .unwrap() .entry(source_identity.fingerprint) .or_default() - .insert(network_id, ms_monotonic() + nc.credential_ttl); + .insert(network_id, ms_monotonic() + (credential_ttl as i64)); network_config = Some(nc); } @@ -497,7 +497,7 @@ impl InnerProtocol for Controller { //println!("{}", serde_yaml::to_string(&config).unwrap()); self2.send_network_config(source.as_ref(), &config, Some(message_id)); if let Some(revocations) = revocations { - self2.v1_proto_send_revocations(source.as_ref(), revocations); + self2.send_revocations(source.as_ref(), revocations); } (result, Some(config)) } diff --git a/controller/src/filedatabase.rs b/controller/src/filedatabase.rs index a014d6d19..3fd782e07 100644 --- a/controller/src/filedatabase.rs +++ b/controller/src/filedatabase.rs @@ -74,6 +74,7 @@ impl FileDatabase { if let Some((record_type, network_id, node_id)) = Self::record_type_from_path(controller_address, path0.as_path()) { + // Paths to objects that were deleted or changed. Changed includes adding new objects. let mut deleted = None; let mut changed = None; @@ -117,6 +118,7 @@ impl FileDatabase { } if deleted.is_some() { + println!("DELETED: {}", deleted.unwrap().as_os_str().to_string_lossy()); match record_type { RecordType::Network => { if let Some((network, mut members)) = db.cache.on_network_deleted(network_id) { @@ -138,6 +140,7 @@ impl FileDatabase { } if let Some(changed) = changed { + println!("CHANGED: {}", changed.as_os_str().to_string_lossy()); match record_type { RecordType::Network => { if let Ok(Some(new_network)) = Self::get_network_internal(changed).await { @@ -375,6 +378,7 @@ impl Database for FileDatabase { let mut member = Self::get_member_internal(&self.member_path(network_id, node_id)).await?; if let Some(member) = member.as_mut() { if member.network_id != network_id { + // Also auto-update member network IDs, see get_network(). member.network_id = network_id; self.save_member(member.clone()).await?; } @@ -385,7 +389,6 @@ impl Database for FileDatabase { async fn save_member(&self, obj: Member) -> Result<(), Box> { let base_member_path = self.member_path(obj.network_id, obj.node_id); let _ = fs::create_dir_all(base_member_path.parent().unwrap()).await; - //let _ = fs::write(base_member_path, to_json_pretty(&obj).as_bytes()).await?; let _ = fs::write(base_member_path, serde_yaml::to_string(&obj)?.as_bytes()).await?; Ok(()) } diff --git a/controller/src/model/network.rs b/controller/src/model/network.rs index b040e11a9..595caa6cb 100644 --- a/controller/src/model/network.rs +++ b/controller/src/model/network.rs @@ -13,7 +13,7 @@ use zerotier_network_hypervisor::vl2::NetworkId; use crate::database::Database; use crate::model::Member; -pub const CREDENTIAL_WINDOW_SIZE_DEFAULT: i64 = 1000 * 60 * 60; +pub const CREDENTIAL_WINDOW_SIZE_DEFAULT: u64 = 1000 * 60 * 60; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Ipv4AssignMode { @@ -105,7 +105,7 @@ pub struct Network { /// Usually this does not need to be changed. #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "credentialTtl")] - pub credential_ttl: Option, + pub credential_ttl: Option, /// Minimum supported ZeroTier protocol version for this network (default: undefined, up to members) #[serde(skip_serializing_if = "Option::is_none")] diff --git a/network-hypervisor/src/vl2/networkconfig.rs b/network-hypervisor/src/vl2/networkconfig.rs index 8f84b256b..d48f81fb7 100644 --- a/network-hypervisor/src/vl2/networkconfig.rs +++ b/network-hypervisor/src/vl2/networkconfig.rs @@ -41,17 +41,6 @@ pub struct NetworkConfig { /// Network configuration timestamp pub timestamp: i64, - /// TTL for credentials on this network (or window size for V1 nodes) - pub credential_ttl: i64, - - /// Network configuration revision number (V1) - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(default)] - pub revision: Option, - - /// L2 Ethernet MTU for this network. - pub mtu: u16, - /// Suggested horizon limit for multicast (not a hard limit, but 0 disables multicast) pub multicast_limit: u32, @@ -60,6 +49,9 @@ pub struct NetworkConfig { #[serde(default)] pub multicast_like_expire: Option, + /// L2 Ethernet MTU for this network. + pub mtu: u16, + /// ZeroTier-assigned L3 routes for this node. #[serde(skip_serializing_if = "HashSet::is_empty")] #[serde(default)] @@ -105,8 +97,6 @@ impl NetworkConfig { motd: String::new(), private: true, timestamp: 0, - credential_ttl: 0, - revision: None, mtu: 0, multicast_limit: 0, multicast_like_expire: None, @@ -143,8 +133,6 @@ impl NetworkConfig { }, ); d.set_u64(proto_v1_field_name::network_config::TIMESTAMP, self.timestamp as u64); - d.set_u64(proto_v1_field_name::network_config::MAX_DELTA, self.credential_ttl as u64); - d.set_u64(proto_v1_field_name::network_config::REVISION, self.revision.unwrap_or(0)); d.set_u64(proto_v1_field_name::network_config::MTU, self.mtu as u64); d.set_u64(proto_v1_field_name::network_config::MULTICAST_LIMIT, self.multicast_limit as u64); @@ -193,6 +181,9 @@ impl NetworkConfig { } if let Some(v1cred) = self.v1_credentials.as_ref() { + d.set_u64(proto_v1_field_name::network_config::REVISION, v1cred.revision); + d.set_u64(proto_v1_field_name::network_config::MAX_DELTA, v1cred.max_delta); + d.set_bytes( proto_v1_field_name::network_config::CERTIFICATE_OF_MEMBERSHIP, v1cred @@ -268,8 +259,6 @@ impl NetworkConfig { nc.timestamp = d .get_i64(proto_v1_field_name::network_config::TIMESTAMP) .ok_or(InvalidParameterError("missing timestamp"))?; - nc.credential_ttl = d.get_i64(proto_v1_field_name::network_config::MAX_DELTA).unwrap_or(0); - nc.revision = Some(d.get_u64(proto_v1_field_name::network_config::REVISION).unwrap_or(0)); nc.mtu = d .get_u64(proto_v1_field_name::network_config::MTU) .unwrap_or(crate::protocol::ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU as u64) as u16; @@ -327,6 +316,8 @@ impl NetworkConfig { } let mut v1cred = V1Credentials { + revision: d.get_u64(proto_v1_field_name::network_config::REVISION).unwrap_or(0), + max_delta: d.get_u64(proto_v1_field_name::network_config::MAX_DELTA).unwrap_or(0), certificate_of_membership: CertificateOfMembership::from_bytes( d.get_bytes(proto_v1_field_name::network_config::CERTIFICATE_OF_MEMBERSHIP) .ok_or(InvalidParameterError("missing certificate of membership"))?, @@ -446,6 +437,8 @@ pub struct SSOAuthConfiguration { /// networks that support older protocol versions. #[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct V1Credentials { + pub revision: u64, + pub max_delta: u64, pub certificate_of_membership: CertificateOfMembership, pub certificates_of_ownership: Vec, #[serde(skip_serializing_if = "HashMap::is_empty")] diff --git a/network-hypervisor/src/vl2/v1/certificateofmembership.rs b/network-hypervisor/src/vl2/v1/certificateofmembership.rs index b5a0cd89a..53c2db80b 100644 --- a/network-hypervisor/src/vl2/v1/certificateofmembership.rs +++ b/network-hypervisor/src/vl2/v1/certificateofmembership.rs @@ -25,7 +25,7 @@ use zerotier_utils::memory; pub struct CertificateOfMembership { pub network_id: NetworkId, pub timestamp: i64, - pub max_delta: i64, + pub max_delta: u64, pub issued_to: Address, pub issued_to_fingerprint: Blob<32>, pub signature: ArrayVec, @@ -34,7 +34,7 @@ pub struct CertificateOfMembership { impl CertificateOfMembership { /// Create a new signed certificate of membership. /// None is returned if an error occurs, such as the issuer missing its secrets. - pub fn new(issuer: &Identity, network_id: NetworkId, issued_to: &Identity, timestamp: i64, max_delta: i64) -> Option { + pub fn new(issuer: &Identity, network_id: NetworkId, issued_to: &Identity, timestamp: i64, max_delta: u64) -> Option { let mut com = CertificateOfMembership { network_id, timestamp, @@ -128,7 +128,7 @@ impl CertificateOfMembership { match qt { 0 => { timestamp = i64::from_be_bytes(q); - max_delta = qd as i64; + max_delta = qd; } 1 => { network_id = u64::from_be_bytes(q); diff --git a/utils/src/lib.rs b/utils/src/lib.rs index a08df3bfa..07cc68345 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -28,7 +28,7 @@ pub mod reaper; pub use tokio; /// A monotonic ticks value for "never happened" that should be lower than any initial value. -pub const NEVER_HAPPENED_TICKS: i64 = i64::MIN / 2; +pub const NEVER_HAPPENED_TICKS: i64 = i64::MIN; /// Get milliseconds since unix epoch. #[inline] @@ -44,15 +44,19 @@ pub fn ms_since_epoch() -> i64 { pub fn ms_monotonic() -> i64 { static STARTUP_INSTANT: std::sync::RwLock> = std::sync::RwLock::new(None); let si = *STARTUP_INSTANT.read().unwrap(); - let instant_zero = if let Some(si) = si { - si + if let Some(si) = si { + si.elapsed().as_millis() as i64 } else { - *STARTUP_INSTANT.write().unwrap().get_or_insert(std::time::Instant::now()) - }; - std::time::Instant::now().duration_since(instant_zero).as_millis() as i64 + STARTUP_INSTANT + .write() + .unwrap() + .get_or_insert(std::time::Instant::now()) + .elapsed() + .as_millis() as i64 + } } -/// Wait for a kill signal (e.g. SIGINT or OS-equivalent) and return when received. +/// Wait for a kill signal (e.g. SIGINT or OS-equivalent) sent to this process and return when received. #[cfg(unix)] pub fn wait_for_process_abort() { if let Ok(mut signals) = signal_hook::iterator::Signals::new(&[libc::SIGINT, libc::SIGTERM, libc::SIGQUIT]) { @@ -65,10 +69,10 @@ pub fn wait_for_process_abort() { _ => {} } } - std::thread::sleep(std::time::Duration::from_millis(500)); + std::thread::sleep(std::time::Duration::from_millis(100)); } } else { - panic!("unable to listen to OS signals"); + panic!("unable to listen for OS signals"); } } diff --git a/utils/src/thing.rs b/utils/src/thing.rs index 9f8445ee1..61f8d2819 100644 --- a/utils/src/thing.rs +++ b/utils/src/thing.rs @@ -13,6 +13,7 @@ use std::ptr::{drop_in_place, read, write}; /// This will panic if the capacity is too small. If that occurs, it must be enlarged. It will /// also panic if any of the accessors (other than the try_ versions) are used to try to get /// a type other than the one it was constructed with. +#[repr(C)] pub struct Thing { storage: [u8; CAPACITY], dropper: fn(*mut u8),