diff --git a/zerotier-network-hypervisor/src/vl1/careof.rs b/zerotier-network-hypervisor/src/vl1/careof.rs index 6f8349fac..c8f2f46b3 100644 --- a/zerotier-network-hypervisor/src/vl1/careof.rs +++ b/zerotier-network-hypervisor/src/vl1/careof.rs @@ -1,6 +1,6 @@ // (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md. -use std::io::Write; +use std::io::{Read, Write}; use crate::vl1::identity::Identity; use crate::vl1::protocol::IDENTITY_FINGERPRINT_SIZE; @@ -34,7 +34,8 @@ impl CareOf { for f in self.fingerprints.iter() { let _ = v.write_all(f); } - let _ = varint::write(&mut v, 0); // reserved for future use + let _ = varint::write(&mut v, 0); // flags, reserved for future use + let _ = varint::write(&mut v, 0); // extra bytes, reserved for future use if include_signature { let _ = varint::write(&mut v, self.signature.len() as u64); let _ = v.write_all(self.signature.as_slice()); @@ -47,6 +48,40 @@ impl CareOf { self.to_bytes_internal(true) } + pub fn from_bytes(mut b: &[u8]) -> Option { + let mut f = move || -> std::io::Result { + let (timestamp, _) = varint::read(&mut b)?; + let mut care_of = CareOf { + timestamp: timestamp as i64, + fingerprints: Vec::new(), + signature: Vec::new(), + }; + let (fingerprint_count, _) = varint::read(&mut b)?; + for _ in 0..fingerprint_count { + let mut tmp = [0_u8; IDENTITY_FINGERPRINT_SIZE]; + b.read_exact(&mut tmp)?; + care_of.fingerprints.push(tmp); + } + let _ = varint::read(&mut b)?; // flags, currently ignored + let (extra_bytes, _) = varint::read(&mut b)?; + if extra_bytes > (b.len() as u64) { + return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "")); + } + b = &b[(extra_bytes as usize)..]; + let (signature_len, _) = varint::read(&mut b)?; + if signature_len > (b.len() as u64) { + return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "")); + } + let _ = care_of.signature.write_all(&b[..(signature_len as usize)]); + return Ok(care_of); + }; + if let Ok(care_of) = f() { + Some(care_of) + } else { + None + } + } + /// Sort, deduplicate, and sign this care-of packet. /// /// The supplied identitiy must contain its secret keys. False is returned if there is an error. diff --git a/zerotier-network-hypervisor/src/vl1/peer.rs b/zerotier-network-hypervisor/src/vl1/peer.rs index b147ea608..5f2945e82 100644 --- a/zerotier-network-hypervisor/src/vl1/peer.rs +++ b/zerotier-network-hypervisor/src/vl1/peer.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::mem::MaybeUninit; -use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicI64, AtomicU64, Ordering}; use std::sync::{Arc, Weak}; use parking_lot::{Mutex, RwLock}; @@ -18,6 +18,7 @@ use crate::util::byte_array_range; use crate::util::canonicalobject::CanonicalObject; use crate::util::debug_event; use crate::util::marshalable::Marshalable; +use crate::vl1::careof::CareOf; use crate::vl1::node::*; use crate::vl1::protocol::*; use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret}; @@ -32,6 +33,14 @@ struct PeerPath { last_receive_time_ticks: i64, } +struct RemoteNodeInfo { + reported_local_endpoints: HashMap, + care_of: Option, + remote_version: u64, + hello_extended_authentication: bool, + remote_protocol_version: u8, +} + /// A remote peer known to this node. /// /// Equality and hashing is implemented in terms of the identity. @@ -50,9 +59,6 @@ pub struct Peer { // Paths sorted in descending order of quality / preference. paths: Mutex>>, - // External addresses by this peer for the local node. - reported_local_endpoints: Mutex>, - // Statistics and times of events. last_send_time_ticks: AtomicI64, last_receive_time_ticks: AtomicI64, @@ -66,9 +72,8 @@ pub struct Peer { // Counter for assigning sequential message IDs. message_id_counter: AtomicU64, - // Remote peer version information. - remote_version: AtomicU64, - remote_protocol_version: AtomicU8, + // Other information reported by remote node. + remote_node_info: RwLock, } /// Attempt AEAD packet encryption and MAC validation. Returns message ID on success. @@ -186,7 +191,6 @@ impl Peer { identity_symmetric_key: SymmetricSecret::new(static_secret), ephemeral_symmetric_key: RwLock::new(None), paths: Mutex::new(Vec::with_capacity(4)), - reported_local_endpoints: Mutex::new(HashMap::new()), last_send_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_receive_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), last_forward_time_ticks: AtomicI64::new(crate::util::NEVER_HAPPENED_TICKS), @@ -194,8 +198,13 @@ impl Peer { create_time_ticks: time_ticks, random_ticks_offset: next_u64_secure(), message_id_counter: AtomicU64::new(((time_clock as u64) / 100).wrapping_shl(28) ^ next_u64_secure().wrapping_shr(36)), - remote_version: AtomicU64::new(0), - remote_protocol_version: AtomicU8::new(0), + remote_node_info: RwLock::new(RemoteNodeInfo { + reported_local_endpoints: HashMap::new(), + care_of: None, + remote_version: 0, + hello_extended_authentication: false, + remote_protocol_version: 0, + }), } }) } @@ -203,7 +212,7 @@ impl Peer { /// Get the remote version of this peer: major, minor, revision, and build. /// Returns None if it's not yet known. pub fn version(&self) -> Option<[u16; 4]> { - let rv = self.remote_version.load(Ordering::Relaxed); + let rv = self.remote_node_info.read().remote_version; if rv != 0 { Some([rv.wrapping_shr(48) as u16, rv.wrapping_shr(32) as u16, rv.wrapping_shr(16) as u16, rv as u16]) } else { @@ -213,7 +222,7 @@ impl Peer { /// Get the remote protocol version of this peer or None if not yet known. pub fn protocol_version(&self) -> Option { - let pv = self.remote_protocol_version.load(Ordering::Relaxed); + let pv = self.remote_node_info.read().remote_protocol_version; if pv != 0 { Some(pv) } else { @@ -305,7 +314,7 @@ impl Peer { paths.retain(|p| ((time_ticks - p.last_receive_time_ticks) < PEER_EXPIRATION_TIME) && (p.path.strong_count() > 0)); prioritize_paths(&mut paths); } - self.reported_local_endpoints.lock().retain(|_, ts| (time_ticks - *ts) < PEER_EXPIRATION_TIME); + self.remote_node_info.write().reported_local_endpoints.retain(|_, ts| (time_ticks - *ts) < PEER_EXPIRATION_TIME); (time_ticks - self.last_receive_time_ticks.load(Ordering::Relaxed).max(self.create_time_ticks)) < PEER_EXPIRATION_TIME } @@ -353,17 +362,6 @@ impl Peer { } } - fn create_session_metadata(&self, node: &Node, destination: &Endpoint) -> Vec { - let mut session_metadata = Dictionary::new(); - session_metadata.set_bytes(session_metadata::INSTANCE_ID, node.instance_id.to_vec()); - session_metadata.set_bytes(session_metadata::CARE_OF, node.care_of_bytes()); - session_metadata.set_bytes(session_metadata::SENT_TO, destination.to_buffer::<{ Endpoint::MAX_MARSHAL_SIZE }>().unwrap().as_bytes().to_vec()); - if let Some(my_root_sets) = node.my_root_sets() { - session_metadata.set_bytes(session_metadata::MY_ROOT_SETS, my_root_sets); - } - session_metadata.to_bytes() - } - /// Send a packet to this peer, returning true on (potential) success. /// /// This will go directly if there is an active path, or otherwise indirectly @@ -466,7 +464,8 @@ impl Peer { assert!(node.identity.marshal_with_options(&mut packet, Identity::ALGORITHM_ALL, false).is_ok()); // Create session meta-data and append length of this section. - let session_metadata = self.create_session_metadata(node, destination); + let session_metadata = Dictionary::new(); + let session_metadata = session_metadata.to_bytes(); let session_metadata_len = session_metadata.len() + 16; // plus nonce assert!(session_metadata_len <= 0xffff); // sanity check, should be impossible assert!(packet.append_u16(session_metadata_len as u16).is_ok()); @@ -610,7 +609,7 @@ impl Peer { return match verb { verbs::VL1_NOP => true, - verbs::VL1_HELLO => self.handle_incoming_hello(si, node, time_ticks, message_id, source_path, &payload).await, + verbs::VL1_HELLO => self.handle_incoming_hello(si, node, time_ticks, message_id, source_path, extended_authentication, &payload).await, verbs::VL1_ERROR => self.handle_incoming_error(si, ph, node, time_ticks, source_path, forward_secrecy, extended_authentication, &payload).await, verbs::VL1_OK => self.handle_incoming_ok(si, ph, node, time_ticks, source_path, packet_header.hops(), path_is_known, forward_secrecy, extended_authentication, &payload).await, verbs::VL1_WHOIS => self.handle_incoming_whois(si, node, time_ticks, source_path, &payload).await, @@ -625,31 +624,37 @@ impl Peer { return false; } - async fn handle_incoming_hello(&self, si: &SI, node: &Node, time_ticks: i64, message_id: MessageId, source_path: &Arc>, payload: &PacketBuffer) -> bool { + async fn handle_incoming_hello(&self, si: &SI, node: &Node, time_ticks: i64, message_id: MessageId, source_path: &Arc>, extended_authentication: bool, payload: &PacketBuffer) -> bool { let mut cursor = 0; if let Ok(hello_fixed_headers) = payload.read_struct::(&mut cursor) { - self.remote_protocol_version.store(hello_fixed_headers.version_proto, Ordering::Relaxed); - self.remote_version - .store((hello_fixed_headers.version_major as u64).wrapping_shl(48) | (hello_fixed_headers.version_minor as u64).wrapping_shl(32) | (u16::from_be_bytes(hello_fixed_headers.version_revision) as u64).wrapping_shl(16), Ordering::Relaxed); if let Ok(identity) = Identity::unmarshal(payload, &mut cursor) { if identity.eq(&self.identity) { - if hello_fixed_headers.version_proto >= 20 { - let mut session_metadata_len = payload.read_u16(&mut cursor).unwrap_or(0) as usize; - if session_metadata_len > 16 { - session_metadata_len -= 16; - if let Ok(nonce) = payload.read_bytes_fixed::<16>(&mut cursor) { - let mut nonce = nonce.clone(); - if let Ok(session_metadata) = payload.read_bytes(session_metadata_len, &mut cursor) { - let mut session_metadata = session_metadata.to_vec(); + { + let mut remote_node_info = self.remote_node_info.write(); - nonce[12] &= 0x7f; - let salted_key = Secret(hmac_sha384(&message_id.to_ne_bytes(), self.identity_symmetric_key.hello_private_section_key.as_bytes())); - let mut aes = AesCtr::new(&salted_key.as_bytes()[0..32]); - aes.init(&nonce); - aes.crypt_in_place(session_metadata.as_mut_slice()); + remote_node_info.remote_protocol_version = hello_fixed_headers.version_proto; + remote_node_info.hello_extended_authentication = extended_authentication; + remote_node_info.remote_version = + (hello_fixed_headers.version_major as u64).wrapping_shl(48) | (hello_fixed_headers.version_minor as u64).wrapping_shl(32) | (u16::from_be_bytes(hello_fixed_headers.version_revision) as u64).wrapping_shl(16); - if let Some(_session_metadata) = Dictionary::from_bytes(session_metadata.as_slice()) { - // TODO + if hello_fixed_headers.version_proto >= 20 { + let mut session_metadata_len = payload.read_u16(&mut cursor).unwrap_or(0) as usize; + if session_metadata_len > 16 { + session_metadata_len -= 16; + if let Ok(nonce) = payload.read_bytes_fixed::<16>(&mut cursor) { + let mut nonce = nonce.clone(); + if let Ok(session_metadata) = payload.read_bytes(session_metadata_len, &mut cursor) { + let mut session_metadata = session_metadata.to_vec(); + + nonce[12] &= 0x7f; + let salted_key = Secret(hmac_sha384(&message_id.to_ne_bytes(), self.identity_symmetric_key.hello_private_section_key.as_bytes())); + let mut aes = AesCtr::new(&salted_key.as_bytes()[0..32]); + aes.init(&nonce); + aes.crypt_in_place(session_metadata.as_mut_slice()); + + if let Some(_session_metadata) = Dictionary::from_bytes(session_metadata.as_slice()) { + // TODO + } } } } @@ -671,7 +676,14 @@ impl Peer { } if hello_fixed_headers.version_proto >= 20 { - let session_metadata = self.create_session_metadata(node, &source_path.endpoint); + let mut session_metadata = Dictionary::new(); + session_metadata.set_bytes(session_metadata::INSTANCE_ID, node.instance_id.to_vec()); + session_metadata.set_bytes(session_metadata::CARE_OF, node.care_of_bytes()); + session_metadata.set_bytes(session_metadata::SENT_TO, source_path.endpoint.to_buffer::<{ Endpoint::MAX_MARSHAL_SIZE }>().unwrap().as_bytes().to_vec()); + if let Some(my_root_sets) = node.my_root_sets() { + session_metadata.set_bytes(session_metadata::MY_ROOT_SETS, my_root_sets); + } + let session_metadata = session_metadata.to_bytes(); assert!(session_metadata.len() <= 0xffff); // sanity check, should be impossible assert!(packet.append_u16(session_metadata.len() as u16).is_ok()); assert!(packet.append_bytes(session_metadata.as_slice()).is_ok()); @@ -724,21 +736,36 @@ impl Peer { if let Ok(session_metadata_len) = payload.read_u16(&mut cursor) { if session_metadata_len > 0 { if let Ok(session_metadata) = payload.read_bytes(session_metadata_len as usize, &mut cursor) { - if let Some(_session_metadata) = Dictionary::from_bytes(session_metadata) { - // TODO + if let Some(session_metadata) = Dictionary::from_bytes(session_metadata) { + let mut remote_node_info = self.remote_node_info.write(); + if hops == 0 { + if let Some(reported_endpoint) = session_metadata.get_bytes(session_metadata::SENT_TO) { + if let Some(reported_endpoint) = Endpoint::from_bytes(reported_endpoint) { + let _ = remote_node_info.reported_local_endpoints.insert(reported_endpoint, time_ticks); + } + } + } + if let Some(care_of) = session_metadata.get_bytes(session_metadata::CARE_OF) { + if let Some(care_of) = CareOf::from_bytes(care_of) { + let _ = remote_node_info.care_of.insert(care_of); + } + } } } } } } else { if let Ok(reported_endpoint) = Endpoint::unmarshal(&payload, &mut cursor) { - let _ = self.reported_local_endpoints.lock().insert(reported_endpoint, time_ticks); + if hops == 0 { + let _ = self.remote_node_info.write().reported_local_endpoints.insert(reported_endpoint, time_ticks); + } } } if hops == 0 && !path_is_known { self.learn_path(si, source_path, time_ticks); } + self.last_hello_reply_time_ticks.store(time_ticks, Ordering::Relaxed); } }