diff --git a/controller/src/controller.rs b/controller/src/controller.rs index dccb557fe..895fed827 100644 --- a/controller/src/controller.rs +++ b/controller/src/controller.rs @@ -8,7 +8,9 @@ use tokio::time::{Duration, Instant}; use zerotier_network_hypervisor::protocol; use zerotier_network_hypervisor::protocol::{PacketBuffer, DEFAULT_MULTICAST_LIMIT, ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU}; -use zerotier_network_hypervisor::vl1::{HostSystem, Identity, InnerProtocol, Node, PacketHandlerResult, Path, PathFilter, Peer}; +use zerotier_network_hypervisor::vl1::{ + debug_event, HostSystem, Identity, InnerProtocol, Node, PacketHandlerResult, Path, PathFilter, Peer, +}; use zerotier_network_hypervisor::vl2; use zerotier_network_hypervisor::vl2::networkconfig::*; use zerotier_network_hypervisor::vl2::v1::Revocation; @@ -138,7 +140,6 @@ impl Controller { Ok(()) }, ); - // TODO: log errors } } @@ -343,7 +344,7 @@ impl PathFilter for Controller {} impl InnerProtocol for Controller { fn handle_packet( &self, - _: &HostSystemImpl, + host_system: &HostSystemImpl, _: &Node, source: &Arc, source_path: &Arc, @@ -365,6 +366,14 @@ impl InnerProtocol for Controller { } let network_id = network_id.unwrap(); + debug_event!( + host_system, + "[vl2] NETWORK_CONFIG_REQUEST from {}({}) for {:0>16x}", + source.identity.address.to_string(), + source_path.endpoint.to_string(), + u64::from(network_id) + ); + let meta_data = if (cursor + 2) < payload.len() { let meta_data_len = payload.read_u16(&mut cursor); if meta_data_len.is_err() { @@ -404,6 +413,7 @@ impl InnerProtocol for Controller { let node_id = peer.identity.address; let node_fingerprint = Blob::from(peer.identity.fingerprint); let now = ms_since_epoch(); + let _host = self2.service.read().unwrap().clone().upgrade().unwrap(); let (result, config) = match self2.get_network_config(&peer.identity, network_id, now).await { Result::Ok((result, Some(config), revocations)) => { @@ -411,8 +421,8 @@ impl InnerProtocol for Controller { (result, Some(config)) } Result::Ok((result, None, _)) => (result, None), - Result::Err(_) => { - // TODO: log invalid request or internal error + Result::Err(e) => { + debug_event!(_host, "[vl2] ERROR getting network config: {}", e.to_string()); return; } }; @@ -492,3 +502,7 @@ impl Drop for Controller { } } } + +fn dump_network_config(nc: &NetworkConfig) { + println!("{}", serde_yaml::to_string(nc).unwrap()); +} diff --git a/controller/src/filedatabase.rs b/controller/src/filedatabase.rs index 5b0e21471..fb28daf13 100644 --- a/controller/src/filedatabase.rs +++ b/controller/src/filedatabase.rs @@ -86,7 +86,7 @@ impl FileDatabase { } fn network_path(&self, network_id: NetworkId) -> PathBuf { - self.base_path.join(format!("N{:06x}", network_id.network_no())).join("config.yaml") + self.base_path.join(format!("N{:06x}.yaml", network_id.network_no())) } fn member_path(&self, network_id: NetworkId, member_id: Address) -> PathBuf { @@ -130,12 +130,24 @@ impl Database for FileDatabase { let r = fs::read(self.network_path(id)).await; if let Ok(raw) = r { let mut network = serde_yaml::from_slice::(raw.as_slice())?; - self.get_controller_address() - .map(|a| network.id = network.id.change_network_controller(a)); - Ok(Some(network)) - //Ok(Some(serde_json::from_slice::(raw.as_slice())?)) + + // FileDatabase stores networks by their "network number" and automatically adapts their IDs + // if the controller's identity changes. This is done to make it easy to just clone networks, + // including storing them in "git." + if let Some(controller_address) = self.get_controller_address() { + let network_id_should_be = network.id.change_network_controller(controller_address); + if id != network_id_should_be { + return Ok(None); + } + if network.id != network_id_should_be { + network.id = network_id_should_be; + let _ = self.save_network(network.clone()).await; + } + } + + return Ok(Some(network)); } else { - Ok(None) + return Ok(None); } } diff --git a/controller/src/model/member.rs b/controller/src/model/member.rs index 393c5ba6c..9890ed530 100644 --- a/controller/src/model/member.rs +++ b/controller/src/model/member.rs @@ -8,8 +8,6 @@ use serde::{Deserialize, Serialize}; use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress}; use zerotier_network_hypervisor::vl2::NetworkId; -use crate::model::ObjectType; - #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Member { #[serde(rename = "address")] @@ -70,11 +68,6 @@ pub struct Member { #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] pub advertised: Option, - - /// API object type documentation field, not actually edited/used. - #[serde(skip_deserializing)] - #[serde(default = "ObjectType::member")] - pub objtype: ObjectType, } impl Member { @@ -92,7 +85,6 @@ impl Member { tags: HashMap::new(), sso_exempt: None, advertised: None, - objtype: ObjectType::Member, } } diff --git a/controller/src/model/mod.rs b/controller/src/model/mod.rs index 5172793c8..0767965cd 100644 --- a/controller/src/model/mod.rs +++ b/controller/src/model/mod.rs @@ -22,24 +22,6 @@ pub struct NetworkExport { pub members: HashMap, } -/// Static string included in JSON-serializable objects to indicate their object type through the API. -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum ObjectType { - #[serde(rename = "network")] - Network, - #[serde(rename = "member")] - Member, -} - -impl ObjectType { - fn network() -> ObjectType { - Self::Network - } - fn member() -> ObjectType { - Self::Member - } -} - #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] #[repr(u8)] pub enum AuthorizationResult { diff --git a/controller/src/model/network.rs b/controller/src/model/network.rs index dcecb0f2f..8ade9f470 100644 --- a/controller/src/model/network.rs +++ b/controller/src/model/network.rs @@ -11,7 +11,7 @@ use zerotier_network_hypervisor::vl2::rule::Rule; use zerotier_network_hypervisor::vl2::NetworkId; use crate::database::Database; -use crate::model::{Member, ObjectType}; +use crate::model::Member; pub const CREDENTIAL_WINDOW_SIZE_DEFAULT: i64 = 1000 * 60 * 60; @@ -62,14 +62,16 @@ pub struct Network { pub enable_broadcast: Option, /// Auto IP assignment mode(s) for IPv4 addresses. + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "v4AssignMode")] #[serde(default)] - pub v4_assign_mode: Ipv4AssignMode, + pub v4_assign_mode: Option, /// Auto IP assignment mode(s) for IPv6 addresses. + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "v6AssignMode")] #[serde(default)] - pub v6_assign_mode: Ipv6AssignMode, + pub v6_assign_mode: Option, /// IPv4 or IPv6 auto-assignment pools available, must be present to use 'zt' mode. #[serde(skip_serializing_if = "HashSet::is_empty")] @@ -78,11 +80,13 @@ pub struct Network { pub ip_assignment_pools: HashSet, /// IPv4 or IPv6 routes to advertise. + #[serde(rename = "ipRoutes")] #[serde(skip_serializing_if = "HashSet::is_empty")] #[serde(default)] pub ip_routes: HashSet, /// DNS records to push to members. + #[serde(default)] #[serde(skip_serializing_if = "HashMap::is_empty")] pub dns: HashMap>, @@ -116,14 +120,10 @@ pub struct Network { pub private: bool, /// If true this network will add not-authorized members for anyone who requests a config. + #[serde(rename = "learnMembers")] #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] pub learn_members: Option, - - /// Static object type field for use with API. - #[serde(skip_deserializing)] - #[serde(default = "ObjectType::network")] - pub objtype: ObjectType, } impl Hash for Network { @@ -150,7 +150,7 @@ impl Network { pub async fn check_zt_ip_assignments(&self, database: &DatabaseImpl, member: &mut Member) -> bool { let mut modified = false; - if self.v4_assign_mode.zt { + if self.v4_assign_mode.as_ref().map_or(false, |m| m.zt) { if !member.ip_assignments.iter().any(|ip| ip.is_ipv4()) { 'ip_search: for pool in self.ip_assignment_pools.iter() { if pool.ip_range_start.is_ipv4() && pool.ip_range_end.is_ipv4() { @@ -178,7 +178,7 @@ impl Network { } } - if self.v6_assign_mode.zt { + if self.v6_assign_mode.as_ref().map_or(false, |m| m.zt) { if !member.ip_assignments.iter().any(|ip| ip.is_ipv6()) { 'ip_search: for pool in self.ip_assignment_pools.iter() { if pool.ip_range_start.is_ipv6() && pool.ip_range_end.is_ipv6() { diff --git a/network-hypervisor/src/protocol.rs b/network-hypervisor/src/protocol.rs index 06aac3bf2..fa1efbbcb 100644 --- a/network-hypervisor/src/protocol.rs +++ b/network-hypervisor/src/protocol.rs @@ -535,9 +535,6 @@ pub struct ErrorHeader { pub error_code: u8, } -/// Maximum delta between the message ID of a sent packet and its response. -pub(crate) const PACKET_RESPONSE_COUNTER_DELTA_MAX: u64 = 256; - /// Frequency for WHOIS retries in milliseconds. pub(crate) const WHOIS_RETRY_INTERVAL: i64 = 2000; diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index d49654c48..b3cf1a9ef 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -26,17 +26,20 @@ pub use rootset::{Root, RootSet}; #[cfg(feature = "debug_events")] #[allow(unused_macros)] +#[macro_export] macro_rules! debug_event { ($si:expr, $fmt:expr $(, $($arg:tt)*)?) => { - $si.event(crate::vl1::Event::Debug(file!(), line!(), format!($fmt, $($($arg)*)?))); + use $crate::vl1::Event; + $si.event(Event::Debug(file!(), line!(), format!($fmt, $($($arg)*)?))); } } #[cfg(not(feature = "debug_events"))] #[allow(unused_macros)] +#[macro_export] macro_rules! debug_event { ($si:expr, $fmt:expr $(, $($arg:tt)*)?) => {}; } #[allow(unused_imports)] -pub(crate) use debug_event; +pub use debug_event; diff --git a/network-hypervisor/src/vl1/node.rs b/network-hypervisor/src/vl1/node.rs index 42d7a9af7..f5e3e7a4e 100644 --- a/network-hypervisor/src/vl1/node.rs +++ b/network-hypervisor/src/vl1/node.rs @@ -408,6 +408,7 @@ impl Node { root_spam_hello = !self.is_online(); } + /* debug_event!( host_system, "[vl1] do_background_tasks:{}{}{}{}{}{} ----", @@ -442,6 +443,7 @@ impl Node { "" } ); + */ if root_sync { if { @@ -711,7 +713,7 @@ impl Node { } } - debug_event!(host_system, "[vl1] do_background_tasks DONE ----"); + //debug_event!(host_system, "[vl1] do_background_tasks DONE ----"); INTERVAL } @@ -910,7 +912,6 @@ impl Node { waiting_packet: Option<(Weak, PooledPacketBuffer)>, time_ticks: i64, ) { - debug_event!(host_system, "[vl1] [v1] WHOIS {}", address.to_string()); { let mut whois_queue = self.whois_queue.lock().unwrap(); let qi = whois_queue.entry(address).or_insert_with(|| WhoisQueueItem { @@ -934,6 +935,16 @@ impl Node { /// Send a WHOIS query to the current best root. fn send_whois(&self, host_system: &HostSystemImpl, mut addresses: &[Address], time_ticks: i64) { debug_assert!(!addresses.is_empty()); + debug_event!(host_system, "[vl1] [v1] sending WHOIS for {}", { + let mut tmp = String::new(); + for a in addresses.iter() { + if !tmp.is_empty() { + tmp.push(','); + } + tmp.push_str(a.to_string().as_str()); + } + tmp + }); if let Some(root) = self.best_root() { while !addresses.is_empty() { if !root diff --git a/network-hypervisor/src/vl1/peer.rs b/network-hypervisor/src/vl1/peer.rs index cb622713e..a59c772e2 100644 --- a/network-hypervisor/src/vl1/peer.rs +++ b/network-hypervisor/src/vl1/peer.rs @@ -671,23 +671,24 @@ impl Peer { let mut cursor = 0; if let Ok(error_header) = payload.read_struct::(&mut cursor) { let in_re_message_id: MessageId = u64::from_be_bytes(error_header.in_re_message_id); - if self.message_id_counter.load(Ordering::Relaxed).wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX { - match error_header.in_re_verb { - _ => { - return inner.handle_error( - host_system, - node, - self, - &source_path, - source_hops, - message_id, - error_header.in_re_verb, - in_re_message_id, - error_header.error_code, - payload, - cursor, - ); - } + + // TODO: replay attack prevention filter + + match error_header.in_re_verb { + _ => { + return inner.handle_error( + host_system, + node, + self, + &source_path, + source_hops, + message_id, + error_header.in_re_verb, + in_re_message_id, + error_header.error_code, + payload, + cursor, + ); } } } @@ -709,86 +710,88 @@ impl Peer { let mut cursor = 0; if let Ok(ok_header) = payload.read_struct::(&mut cursor) { let in_re_message_id: MessageId = u64::from_ne_bytes(ok_header.in_re_message_id); - if self.message_id_counter.load(Ordering::Relaxed).wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX { - match ok_header.in_re_verb { - verbs::VL1_HELLO => { - if let Ok(_ok_hello_fixed_header_fields) = - payload.read_struct::(&mut cursor) - { - if source_hops == 0 { - debug_event!(host_system, "[vl1] {} OK(HELLO)", self.identity.address.to_string(),); - if let Ok(reported_endpoint) = Endpoint::unmarshal(&payload, &mut cursor) { + + // TODO: replay attack prevention filter + + match ok_header.in_re_verb { + verbs::VL1_HELLO => { + if let Ok(_ok_hello_fixed_header_fields) = + payload.read_struct::(&mut cursor) + { + if source_hops == 0 { + debug_event!(host_system, "[vl1] {} OK(HELLO)", self.identity.address.to_string(),); + if let Ok(reported_endpoint) = Endpoint::unmarshal(&payload, &mut cursor) { + #[cfg(debug_assertions)] + let reported_endpoint2 = reported_endpoint.clone(); + if self + .remote_node_info + .write() + .unwrap() + .reported_local_endpoints + .insert(reported_endpoint, time_ticks) + .is_none() + { #[cfg(debug_assertions)] - let reported_endpoint2 = reported_endpoint.clone(); - if self - .remote_node_info - .write() - .unwrap() - .reported_local_endpoints - .insert(reported_endpoint, time_ticks) - .is_none() - { - #[cfg(debug_assertions)] - debug_event!( - host_system, - "[vl1] {} reported new remote perspective, local endpoint: {}", - self.identity.address.to_string(), - reported_endpoint2.to_string() - ); - } - } - } - - if source_hops == 0 && !path_is_known { - self.learn_path(host_system, source_path, time_ticks); - } - - self.last_hello_reply_time_ticks.store(time_ticks, Ordering::Relaxed); - } - } - - verbs::VL1_WHOIS => { - if node.is_peer_root(self) { - while cursor < payload.len() { - let r = Identity::unmarshal(payload, &mut cursor); - if let Ok(received_identity) = r { debug_event!( host_system, - "[vl1] {} OK(WHOIS): new identity: {}", + "[vl1] {} reported new remote perspective, local endpoint: {}", self.identity.address.to_string(), - received_identity.to_string() + reported_endpoint2.to_string() ); - node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true); - } else { - debug_event!( - host_system, - "[vl1] {} OK(WHOIS): bad identity: {}", - self.identity.address.to_string(), - r.err().unwrap().to_string() - ); - return PacketHandlerResult::Error; } } - } else { - return PacketHandlerResult::Ok; // not invalid, just ignored } - } - _ => { - return inner.handle_ok( - host_system, - node, - self, - &source_path, - source_hops, - message_id, - ok_header.in_re_verb, - in_re_message_id, - payload, - cursor, - ); + if source_hops == 0 && !path_is_known { + self.learn_path(host_system, source_path, time_ticks); + } + + self.last_hello_reply_time_ticks.store(time_ticks, Ordering::Relaxed); } } + + verbs::VL1_WHOIS => { + debug_event!(host_system, "[vl1] OK(WHOIS)"); + if node.is_peer_root(self) { + while cursor < payload.len() { + let r = Identity::unmarshal(payload, &mut cursor); + if let Ok(received_identity) = r { + debug_event!( + host_system, + "[vl1] {} OK(WHOIS): received identity: {}", + self.identity.address.to_string(), + received_identity.to_string() + ); + node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true); + } else { + debug_event!( + host_system, + "[vl1] {} OK(WHOIS): received bad identity: {}", + self.identity.address.to_string(), + r.err().unwrap().to_string() + ); + return PacketHandlerResult::Error; + } + } + } else { + return PacketHandlerResult::Ok; // not invalid, just ignored + } + } + + _ => { + return inner.handle_ok( + host_system, + node, + self, + &source_path, + source_hops, + message_id, + ok_header.in_re_verb, + in_re_message_id, + payload, + cursor, + ); + } } } return PacketHandlerResult::Error; diff --git a/network-hypervisor/src/vl2/networkid.rs b/network-hypervisor/src/vl2/networkid.rs index 8325d7b66..ab0872d93 100644 --- a/network-hypervisor/src/vl2/networkid.rs +++ b/network-hypervisor/src/vl2/networkid.rs @@ -56,8 +56,7 @@ impl NetworkId { /// Consume this network ID and return one with the same network number but a different controller ID. pub fn change_network_controller(self, new_controller: Address) -> NetworkId { - let new_controller: u64 = new_controller.into(); - Self(NonZeroU64::new((self.network_no() as u64) | new_controller.wrapping_shr(24)).unwrap()) + Self(NonZeroU64::new((self.network_no() as u64) | u64::from(new_controller).wrapping_shl(24)).unwrap()) } /// Get the 24-bit local network identifier minus the 40-bit controller address portion. diff --git a/network-hypervisor/src/vl2/v1/certificateofmembership.rs b/network-hypervisor/src/vl2/v1/certificateofmembership.rs index bb12cf545..e11a360a5 100644 --- a/network-hypervisor/src/vl2/v1/certificateofmembership.rs +++ b/network-hypervisor/src/vl2/v1/certificateofmembership.rs @@ -85,7 +85,7 @@ impl CertificateOfMembership { let mut fp = v1_signee_hasher.finish(); fp[32..].fill(0); if let Some(signed_by) = signed_by { - fp[32..38].copy_from_slice(&signed_by.to_bytes()); + fp[32..37].copy_from_slice(&signed_by.to_bytes()); } fp } diff --git a/network-hypervisor/src/vl2/v1/certificateofownership.rs b/network-hypervisor/src/vl2/v1/certificateofownership.rs index bbdc6e61f..717fe25ae 100644 --- a/network-hypervisor/src/vl2/v1/certificateofownership.rs +++ b/network-hypervisor/src/vl2/v1/certificateofownership.rs @@ -63,7 +63,7 @@ impl CertificateOfOwnership { } fn internal_to_bytes(&self, for_sign: bool, signed_by: Address) -> Option> { - if self.things.len() > 0xffff || self.signature.len() != 96 { + if self.things.len() > 0xffff { return None; } let mut v = Vec::with_capacity(256);