There is now a test root up that anyone can use, and some other stuff.

This commit is contained in:
Adam Ierymenko 2022-07-05 15:05:31 -04:00
parent f45d6e1eec
commit 69fa840652
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
10 changed files with 170 additions and 51 deletions

View file

@ -1,11 +0,0 @@
{
"name": "local-parellels-test",
"url": "http://root.zerotier.com/root.zerotier.com.json",
"revision": 1,
"members": [ {
"identity": "bc47f54ab2:0:cfb92160bab1da37f31247ded76d8327c00c4d3e49d8a424c6ba16fe3e77b949ab782426584b0169e7b38f7679ea24f38cea637a7a93a9272bfcb0ff461c1e97",
"endpoints": [ "udp:10.211.55.3/9993" ],
"signature": [ 1, 21, 160, 59, 81, 229, 164, 176, 225, 189, 66, 211, 25, 209, 30, 130, 254, 218, 121, 84, 129, 109, 18, 0, 156, 97, 56, 40, 165, 114, 211, 89, 25, 114, 144, 93, 237, 206, 52, 219, 60, 173, 178, 40, 22, 76, 42, 215, 158, 82, 12, 218, 128, 28, 158, 167, 108, 68, 80, 6, 211, 53, 70, 22, 5 ],
"priority": 0
} ]
}

View file

@ -0,0 +1,11 @@
{
"name": "test-root",
"url": "http://root.zerotier.com/test-root.bin",
"revision": 1,
"members": [ {
"identity": "bc47f54ab2:0:cfb92160bab1da37f31247ded76d8327c00c4d3e49d8a424c6ba16fe3e77b949ab782426584b0169e7b38f7679ea24f38cea637a7a93a9272bfcb0ff461c1e97",
"endpoints": [ "udp:207.148.9.48/19993" ],
"signature": [ 1, 231, 242, 54, 205, 73, 178, 134, 80, 36, 182, 157, 154, 217, 55, 250, 164, 102, 119, 132, 32, 231, 62, 56, 13, 49, 41, 211, 30, 226, 248, 44, 185, 105, 163, 239, 189, 86, 37, 175, 157, 241, 209, 154, 205, 120, 15, 98, 169, 9, 83, 175, 3, 77, 250, 187, 36, 26, 146, 113, 208, 10, 36, 205, 15 ],
"priority": 0
} ]
}

View file

@ -80,7 +80,7 @@ impl<I: Interface> NetworkHypervisor<I> {
pub fn add_update_default_root_set(&self) -> bool {
let mut buf: Buffer<4096> = Buffer::new();
//buf.set_to(include_bytes!("../default-rootset/root.zerotier.com.bin"));
buf.set_to(include_bytes!("../default-rootset/local-parallels-test.bin"));
buf.set_to(include_bytes!("../default-rootset/test-root.bin"));
let mut cursor = 0;
self.add_update_root_set(RootSet::unmarshal(&buf, &mut cursor).unwrap())
}

View file

@ -80,6 +80,10 @@ impl<const L: usize> Buffer<L> {
Self(0, MaybeUninit::uninit().assume_init())
}
pub const fn capacity(&self) -> usize {
Self::CAPACITY
}
pub fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
let l = b.len();
if l <= L {

View file

@ -622,6 +622,16 @@ impl<SI: SystemInterface> Node<SI> {
self.roots.read().roots.keys().any(|p| (**p).eq(peer))
}
pub(crate) fn remote_update_root_set(&self, received_from: &Identity, rs: RootSet) {
let mut roots = self.roots.write();
if let Some(entry) = roots.sets.get_mut(&rs.name) {
if entry.members.iter().any(|m| m.identity.eq(received_from)) && rs.should_replace(entry) {
*entry = rs;
roots.sets_modified = true;
}
}
}
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

@ -17,9 +17,11 @@ use crate::util::byte_array_range;
use crate::util::canonicalobject::CanonicalObject;
use crate::util::debug_event;
use crate::util::marshalable::Marshalable;
use crate::vl1::address::Address;
use crate::vl1::careof::CareOf;
use crate::vl1::node::*;
use crate::vl1::protocol::*;
use crate::vl1::rootset::RootSet;
use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret};
use crate::vl1::{Dictionary, Endpoint, Identity, Path};
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
@ -33,6 +35,7 @@ struct PeerPath<SI: SystemInterface> {
}
struct RemoteNodeInfo {
remote_instance_id: [u8; 16],
reported_local_endpoints: HashMap<Endpoint, i64>,
care_of: Option<CareOf>,
remote_version: u64,
@ -52,7 +55,7 @@ pub struct Peer<SI: SystemInterface> {
// Static shared secret computed from agreement with identity.
identity_symmetric_key: SymmetricSecret,
// Latest ephemeral secret or None if not yet negotiated.
// Latest ephemeral session key or None if no current session.
ephemeral_symmetric_key: RwLock<Option<EphemeralSymmetricSecret>>,
// Paths sorted in descending order of quality / preference.
@ -200,6 +203,7 @@ impl<SI: SystemInterface> Peer<SI> {
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_node_info: RwLock::new(RemoteNodeInfo {
remote_instance_id: [0_u8; 16],
reported_local_endpoints: HashMap::new(),
care_of: None,
remote_version: 0,
@ -369,7 +373,7 @@ impl<SI: SystemInterface> Peer<SI> {
/// via a root or some other route.
///
/// It encrypts and sets the MAC and cipher fields and packet ID and other things.
pub(crate) async fn send(&self, si: &SI, path: Option<&Arc<Path<SI>>>, node: &Node<SI>, time_ticks: i64, message_id: MessageId, packet: &mut PacketBuffer) -> bool {
pub(crate) async fn send(&self, si: &SI, path: Option<&Arc<Path<SI>>>, node: &Node<SI>, time_ticks: i64, packet: &mut PacketBuffer) -> bool {
let mut _path_arc = None;
let path = if let Some(path) = path {
path
@ -386,7 +390,7 @@ impl<SI: SystemInterface> Peer<SI> {
let flags_cipher_hops = if packet.len() > max_fragment_size { packet_constants::HEADER_FLAG_FRAGMENTED | security_constants::CIPHER_AES_GMAC_SIV } else { security_constants::CIPHER_AES_GMAC_SIV };
let mut aes_gmac_siv = if let Some(ephemeral_key) = self.ephemeral_symmetric_key.read().as_ref() { ephemeral_key.secret.aes_gmac_siv.get() } else { self.identity_symmetric_key.aes_gmac_siv.get() };
aes_gmac_siv.encrypt_init(&message_id.to_ne_bytes());
aes_gmac_siv.encrypt_init(&self.next_message_id().to_ne_bytes());
aes_gmac_siv.encrypt_set_aad(&get_packet_aad_bytes(self.identity.address, node.identity.address, flags_cipher_hops));
if let Ok(payload) = packet.as_bytes_starting_at_mut(packet_constants::HEADER_SIZE) {
aes_gmac_siv.encrypt_first_pass(payload);
@ -428,6 +432,19 @@ impl<SI: SystemInterface> Peer<SI> {
return false;
}
pub(crate) fn create_session_metadata(&self, node: &Node<SI>, direct_source_report: Option<&Endpoint>) -> Vec<u8> {
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());
if let Some(direct_source) = direct_source_report {
session_metadata.set_bytes(session_metadata::DIRECT_SOURCE, direct_source.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::ROOT_SET_UPDATES, my_root_sets);
}
session_metadata.to_bytes()
}
/// Send a HELLO to this peer.
///
/// If explicit_endpoint is not None the packet will be sent directly to this endpoint.
@ -476,8 +493,7 @@ impl<SI: SystemInterface> Peer<SI> {
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 = Dictionary::new();
let session_metadata = session_metadata.to_bytes();
let session_metadata = self.create_session_metadata(node, None);
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());
@ -616,16 +632,17 @@ impl<SI: SystemInterface> Peer<SI> {
}
verb &= packet_constants::VERB_MASK; // mask off flags
debug_event!(si, "[vl1] #{:0>16x} decrypted and authenticated, verb: {} ({:0>2x})", u64::from_be_bytes(packet_header.id), verbs::name(verb & packet_constants::VERB_MASK), verb as u32);
debug_event!(si, "[vl1] #{:0>16x} decrypted and authenticated, verb: {} ({:0>2x})", u64::from_be_bytes(packet_header.id), verbs::name(verb), verb as u32);
if match verb {
verbs::VL1_NOP => true,
verbs::VL1_HELLO => self.handle_incoming_hello(si, ph, node, time_ticks, message_id, source_path, packet_header.hops(), 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,
verbs::VL1_RENDEZVOUS => self.handle_incoming_rendezvous(si, node, time_ticks, source_path, &payload).await,
verbs::VL1_ECHO => self.handle_incoming_echo(si, node, time_ticks, source_path, &payload).await,
verbs::VL1_WHOIS => self.handle_incoming_whois(si, ph, node, time_ticks, message_id, &payload).await,
verbs::VL1_RENDEZVOUS => self.handle_incoming_rendezvous(si, node, time_ticks, message_id, source_path, &payload).await,
verbs::VL1_ECHO => self.handle_incoming_echo(si, ph, node, time_ticks, message_id, &payload).await,
verbs::VL1_SESSION_ACK => true, // TODO, for forward secrecy
verbs::VL1_PUSH_DIRECT_PATHS => self.handle_incoming_push_direct_paths(si, node, time_ticks, source_path, &payload).await,
verbs::VL1_USER_MESSAGE => self.handle_incoming_user_message(si, node, time_ticks, source_path, &payload).await,
_ => ph.handle_packet(self, &source_path, forward_secrecy, extended_authentication, verb, &payload).await,
@ -643,7 +660,7 @@ impl<SI: SystemInterface> Peer<SI> {
async fn handle_incoming_hello<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, source_path: &Arc<Path<SI>>, hops: u8, extended_authentication: bool, payload: &PacketBuffer) -> bool {
if !(ph.has_trust_relationship(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
debug_event!(si, "[vl1] dropping HELLO from {} due to lack of trust relationship", self.identity.address.to_string());
return true;
return true; // packet wasn't invalid, just ignored
}
let mut cursor = 0;
@ -673,8 +690,24 @@ impl<SI: SystemInterface> Peer<SI> {
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
if let Some(session_metadata) = Dictionary::from_bytes(session_metadata.as_slice()) {
if let Some(instance_id) = session_metadata.get_bytes(session_metadata::INSTANCE_ID) {
// For new nodes that send an instance ID, we can try to filter out any possible replaying of old
// packets. A replay of an old HELLO could be used to at least probe for the existence of a node at
// a given IP address and port by eliciting an OK(HELLO) reply.
if remote_node_info.remote_instance_id.eq(instance_id) {
if message_id.wrapping_sub(self.last_incoming_message_id.load(Ordering::Relaxed)) > PACKET_RESPONSE_COUNTER_DELTA_MAX
&& time_ticks.wrapping_sub(self.last_receive_time_ticks.load(Ordering::Relaxed)) < PEER_HELLO_INTERVAL_MAX
{
debug_event!(si, "[vl1] dropping HELLO from {} due to possible replay of old packet", self.identity.address.to_string());
return true;
}
} else {
remote_node_info.remote_instance_id.fill(0);
let l = instance_id.len().min(remote_node_info.remote_instance_id.len());
(&mut remote_node_info.remote_instance_id[..l]).copy_from_slice(&instance_id[..l]);
}
}
}
}
}
@ -695,32 +728,21 @@ impl<SI: SystemInterface> Peer<SI> {
f.1.version_minor = VERSION_MINOR;
f.1.version_revision = VERSION_REVISION.to_be_bytes();
}
if hello_fixed_headers.version_proto >= 20 {
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());
if hops == 0 {
session_metadata.set_bytes(session_metadata::DIRECT_SOURCE, 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();
let session_metadata = self.create_session_metadata(node, if hops == 0 { Some(&source_path.endpoint) } else { None });
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());
}
return self.send(si, Some(source_path), node, time_ticks, self.next_message_id(), &mut packet).await;
return self.send(si, Some(source_path), node, time_ticks, &mut packet).await;
}
}
}
return false;
}
#[allow(unused)]
async fn handle_incoming_error<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, forward_secrecy: bool, extended_authentication: bool, payload: &PacketBuffer) -> bool {
async fn handle_incoming_error<PH: InnerProtocolInterface>(&self, _si: &SI, ph: &PH, _node: &Node<SI>, _time_ticks: i64, source_path: &Arc<Path<SI>>, forward_secrecy: bool, extended_authentication: bool, payload: &PacketBuffer) -> bool {
let mut cursor = 0;
if let Ok(error_header) = payload.read_struct::<message_component_structs::ErrorHeader>(&mut cursor) {
let in_re_message_id: MessageId = u64::from_ne_bytes(error_header.in_re_message_id);
@ -739,7 +761,7 @@ impl<SI: SystemInterface> Peer<SI> {
&self,
si: &SI,
ph: &PH,
_node: &Node<SI>,
node: &Node<SI>,
time_ticks: i64,
source_path: &Arc<Path<SI>>,
hops: u8,
@ -762,6 +784,7 @@ impl<SI: SystemInterface> Peer<SI> {
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) {
let mut remote_node_info = self.remote_node_info.write();
if hops == 0 {
if let Some(reported_endpoint) = session_metadata.get_bytes(session_metadata::DIRECT_SOURCE) {
if let Some(reported_endpoint) = Endpoint::from_bytes(reported_endpoint) {
@ -774,19 +797,42 @@ impl<SI: SystemInterface> Peer<SI> {
}
}
}
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);
}
}
if let Some(instance_id) = session_metadata.get_bytes(session_metadata::INSTANCE_ID) {
remote_node_info.remote_instance_id.fill(0);
let l = instance_id.len().min(remote_node_info.remote_instance_id.len());
(&mut remote_node_info.remote_instance_id[..l]).copy_from_slice(&instance_id[..l]);
}
if let Some(root_set_updates) = session_metadata.get_bytes(session_metadata::ROOT_SET_UPDATES) {
let mut tmp = PacketBuffer::new();
if root_set_updates.len() <= tmp.capacity() {
tmp.set_to(root_set_updates);
let mut cursor = 0;
while cursor < tmp.len() {
if let Ok(rs) = RootSet::unmarshal(&tmp, &mut cursor) {
// This checks the origin node and only allows members of root sets to update them.
node.remote_update_root_set(&self.identity, rs);
} else {
break;
}
}
}
}
}
}
}
}
} else {
// V1 nodes just append the endpoint to which they sent the packet, and we can still use that.
if let Ok(reported_endpoint) = Endpoint::unmarshal(&payload, &mut cursor) {
if hops == 0 {
if hops == 0 {
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().reported_local_endpoints.insert(reported_endpoint, time_ticks).is_none() {
@ -805,7 +851,19 @@ impl<SI: SystemInterface> Peer<SI> {
}
}
verbs::VL1_WHOIS => {}
verbs::VL1_WHOIS => {
if node.is_peer_root(self) {
while cursor < payload.len() {
if let Ok(_whois_response) = Identity::unmarshal(payload, &mut cursor) {
// TODO
} else {
break;
}
}
} else {
return true; // not invalid, just ignored
}
}
_ => {
return ph.handle_ok(self, &source_path, forward_secrecy, extended_authentication, ok_header.in_re_verb, in_re_message_id, payload, &mut cursor).await;
@ -816,19 +874,60 @@ impl<SI: SystemInterface> Peer<SI> {
return false;
}
#[allow(unused)]
async fn handle_incoming_whois(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) -> bool {
false
async fn handle_incoming_whois<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
if node.this_node_is_root() || ph.has_trust_relationship(&self.identity) {
let mut packet = PacketBuffer::new();
packet.set_size(packet_constants::HEADER_SIZE);
{
let mut f: &mut message_component_structs::OkHeader = packet.append_struct_get_mut().unwrap();
f.verb = verbs::VL1_OK;
f.in_re_verb = verbs::VL1_WHOIS;
f.in_re_message_id = message_id.to_ne_bytes();
}
let mut cursor = 0;
while cursor < payload.len() {
if let Ok(zt_address) = Address::unmarshal(payload, &mut cursor) {
if let Some(peer) = node.peer(zt_address) {
if !peer.identity.marshal(&mut packet).is_ok() {
debug_event!(si, "unexpected error serializing an identity into a WHOIS packet response");
return false;
}
}
}
}
self.send(si, None, node, time_ticks, &mut packet).await
} else {
true // packet wasn't invalid, just ignored
}
}
#[allow(unused)]
async fn handle_incoming_rendezvous(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) -> bool {
false
async fn handle_incoming_rendezvous(&self, si: &SI, node: &Node<SI>, time_ticks: i64, message_id: MessageId, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) -> bool {
if node.is_peer_root(self) {}
return true;
}
#[allow(unused)]
async fn handle_incoming_echo(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) -> bool {
false
async fn handle_incoming_echo<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, message_id: MessageId, payload: &PacketBuffer) -> bool {
if ph.has_trust_relationship(&self.identity) || node.is_peer_root(self) {
let mut packet = PacketBuffer::new();
packet.set_size(packet_constants::HEADER_SIZE);
{
let mut f: &mut message_component_structs::OkHeader = packet.append_struct_get_mut().unwrap();
f.verb = verbs::VL1_OK;
f.in_re_verb = verbs::VL1_ECHO;
f.in_re_message_id = message_id.to_ne_bytes();
}
if packet.append_bytes(payload.as_bytes()).is_ok() {
self.send(si, None, node, time_ticks, &mut packet).await
} else {
false
}
} else {
debug_event!(si, "[vl1] dropping ECHO from {} due to lack of trust relationship", self.identity.address.to_string());
true // packet wasn't invalid, just ignored
}
}
#[allow(unused)]

View file

@ -77,6 +77,7 @@ pub mod verbs {
pub const VL1_WHOIS: u8 = 0x04;
pub const VL1_RENDEZVOUS: u8 = 0x05;
pub const VL1_ECHO: u8 = 0x08;
pub const VL1_SESSION_ACK: u8 = 0x0f;
pub const VL1_PUSH_DIRECT_PATHS: u8 = 0x10;
pub const VL1_USER_MESSAGE: u8 = 0x14;
@ -89,6 +90,7 @@ pub mod verbs {
VL1_WHOIS => "VL1_WHOIS",
VL1_RENDEZVOUS => "VL1_RENDEZVOUS",
VL1_ECHO => "VL1_ECHO",
VL1_SESSION_ACK => "VL1_SESSION_ACK",
VL1_PUSH_DIRECT_PATHS => "VL1_PUSH_DIRECT_PATHS",
VL1_USER_MESSAGE => "VL1_USER_MESSAGE",
_ => "???",
@ -248,7 +250,7 @@ pub mod session_metadata {
pub const CARE_OF: &'static str = "c";
/// One or more root sets to which THIS node is a member. Included only if this is a root.
pub const MY_ROOT_SETS: &'static str = "r";
pub const ROOT_SET_UPDATES: &'static str = "r";
}
/// Maximum number of packet hops allowed by the protocol.

View file

@ -5,6 +5,10 @@ authors = ["ZeroTier, Inc. <contact@zerotier.com>", "Adam Ierymenko <adam.ieryme
edition = "2021"
license = "MPL-2.0"
[[bin]]
name = "zerotier"
path = "src/main.rs"
[dependencies]
zerotier-network-hypervisor = { path = "../zerotier-network-hypervisor" }
zerotier-core-crypto = { path = "../zerotier-core-crypto" }