Build fixes, defaults.

This commit is contained in:
Adam Ierymenko 2021-08-12 18:24:11 -04:00
parent 209aeefac3
commit 7f64dd34b8
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
8 changed files with 128 additions and 52 deletions

View file

@ -0,0 +1,9 @@
/// Default root set that uses ZeroTier's own global roots.
pub const ROOT_SET: [u8; 570] = [ 0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x78,0xcc,0x8e,0xf8,0xcb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x31,0xdc,0x40,0xa9,0xc7,0xb5,0xd2,0xf9,0x8e,0xd9,0x7b,0xf6,0x41,0x27,0x29,0x02,0xb6,0xb3,0x34,0x6f,0x56,0x16,0x11,0x45,0x82,0x44,0x55,0x85,0x78,0x79,0xb9,0x30,0xcb,0x01,0x51,0x15,0x49,0xf3,0x38,0x24,0xd8,0xd4,0x78,0x7d,0x77,0x23,0xda,0xc3,0x51,0x50,0x0b,0xe7,0xdf,0x5b,0x8f,0x72,0xdd,0x25,0x81,0xa5,0x0b,0x4a,0x36,0x01,0x46,0x85,0x95,0xbe,0x4d,0x5e,0xe6,0x3b,0x46,0xc2,0x9b,0x15,0x3c,0x43,0x8a,0x30,0xe0,0xa2,0xbf,0xba,0x1a,0x57,0xfc,0x98,0x7b,0x42,0x71,0xde,0x9c,0x53,0x6c,0x00,0x04,0x61,0xd2,0x94,0xb9,0xcb,0x00,0xe6,0x53,0xef,0x7a,0xd9,0x25,0x59,0x52,0xb7,0xc9,0xfc,0xa1,0x68,0x6d,0x3b,0x17,0xc6,0x10,0xb0,0x4e,0x6b,0x6c,0x82,0xd2,0xd3,0x7c,0xd3,0xa6,0xef,0xb2,0x56,0x3d,0x57,0x7f,0x81,0x22,0x24,0x37,0x62,0x02,0x09,0xe9,0x23,0x48,0xad,0x33,0x7b,0xd1,0x91,0xac,0x00,0xb7,0x49,0x2c,0xfd,0x55,0xce,0x0f,0xa0,0x36,0xd8,0xc5,0x62,0x83,0x00,0x02,0x04,0x32,0x07,0x49,0x22,0x27,0x09,0x06,0x20,0x01,0x49,0xf0,0xd0,0x02,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x77,0x8c,0xde,0x71,0x90,0x00,0x3f,0x66,0x81,0xa9,0x9e,0x5a,0xd1,0x89,0x5e,0x9f,0xba,0x33,0xe6,0x21,0x2d,0x44,0x54,0xe1,0x68,0xbc,0xec,0x71,0x12,0x10,0x1b,0xf0,0x00,0x95,0x6e,0xd8,0xe9,0x2e,0x42,0x89,0x2c,0xb6,0xf2,0xec,0x41,0x08,0x81,0xa8,0x4a,0xb1,0x9d,0xa5,0x0e,0x12,0x87,0xba,0x3d,0x92,0x6c,0x3a,0x1f,0x75,0x5c,0xcc,0xf2,0x99,0xa1,0x20,0x70,0x55,0x00,0x02,0x04,0x67,0xc3,0x67,0x42,0x27,0x09,0x06,0x26,0x05,0x98,0x80,0x04,0x00,0x00,0xc3,0x02,0x54,0xf2,0xbc,0xa1,0xf7,0x00,0x19,0x27,0x09,0x62,0xf8,0x65,0xae,0x71,0x00,0xe2,0x07,0x6c,0x57,0xde,0x87,0x0e,0x62,0x88,0xd7,0xd5,0xe7,0x40,0x44,0x08,0xb1,0x54,0x5e,0xfc,0xa3,0x7d,0x67,0xf7,0x7b,0x87,0xe9,0xe5,0x41,0x68,0xc2,0x5d,0x3e,0xf1,0xa9,0xab,0xf2,0x90,0x5e,0xa5,0xe7,0x85,0xc0,0x1d,0xff,0x23,0x88,0x7a,0xd4,0x23,0x2d,0x95,0xc7,0xa8,0xfd,0x2c,0x27,0x11,0x1a,0x72,0xbd,0x15,0x93,0x22,0xdc,0x00,0x02,0x04,0x32,0x07,0xfc,0x8a,0x27,0x09,0x06,0x20,0x01,0x49,0xf0,0xd0,0xdb,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x99,0x2f,0xcf,0x1d,0xb7,0x00,0x20,0x6e,0xd5,0x93,0x50,0xb3,0x19,0x16,0xf7,0x49,0xa1,0xf8,0x5d,0xff,0xb3,0xa8,0x78,0x7d,0xcb,0xf8,0x3b,0x8c,0x6e,0x94,0x48,0xd4,0xe3,0xea,0x0e,0x33,0x69,0x30,0x1b,0xe7,0x16,0xc3,0x60,0x93,0x44,0xa9,0xd1,0x53,0x38,0x50,0xfb,0x44,0x60,0xc5,0x0a,0xf4,0x33,0x22,0xbc,0xfc,0x8e,0x13,0xd3,0x30,0x1a,0x1f,0x10,0x03,0xce,0xb6,0x00,0x02,0x04,0xc3,0xb5,0xad,0x9f,0x27,0x09,0x06,0x2a,0x02,0x6e,0xa0,0xc0,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x09 ];
/// Default maximum payload size for UDP transport.
///
/// This is small enough to traverse numerous weird networks including PPPoE and Google Cloud's
/// weird exit MTU restriction, but is large enough that a standard 2800 byte frame creates only
/// two fragments.
pub const UDP_DEFAULT_MTU: usize = 1432;

View file

@ -3,6 +3,7 @@ pub mod util;
pub mod error;
pub mod vl1;
pub mod vl2;
pub mod defaults;
pub const VERSION_MAJOR: u8 = 1;
pub const VERSION_MINOR: u8 = 99;

View file

@ -1,5 +1,5 @@
use crate::vl1::node::PacketBuffer;
use crate::vl1::constants::FRAGMENT_COUNT_MAX;
use crate::vl1::protocol::FRAGMENT_COUNT_MAX;
/// Packet fragment re-assembler and container.
/// This is only used in the receive path.

View file

@ -594,10 +594,7 @@ impl FromStr for Identity {
impl PartialEq for Identity {
fn eq(&self, other: &Self) -> bool {
self.address.eq(&other.address) &&
self.c25519.eq(&other.c25519) &&
self.ed25519.eq(&other.ed25519) &&
self.v1.as_ref().map_or_else(|| other.v1.is_none(), |v1| other.v1.as_ref().map_or(false, |other_v1| (*v1).0.eq(&(*other_v1).0) && (*v1).1.eq(&(*other_v1).1)))
self.address.eq(&other.address) && self.c25519.eq(&other.c25519) && self.ed25519.eq(&other.ed25519) && self.v1.as_ref().map_or_else(|| other.v1.is_none(), |v1| other.v1.as_ref().map_or(false, |other_v1| (*v1).0.eq(&(*other_v1).0) && (*v1).1.eq(&(*other_v1).1)))
}
}

View file

@ -149,15 +149,6 @@ impl Locator {
signature: signature.to_vec(),
})
}
pub fn to_bytes(&self) -> Option<Vec<u8>> {
let mut buf: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
if self.marshal_internal(&mut buf, false).is_ok() {
Some(buf.as_bytes().to_vec())
} else {
None
}
}
}
impl Hash for Locator {

View file

@ -11,7 +11,7 @@ use crate::util::gate::IntervalGate;
use crate::util::pool::{Pool, Pooled};
use crate::vl1::{Address, Endpoint, Identity, Locator};
use crate::vl1::buffer::{Buffer, PooledBufferFactory};
use crate::vl1::constants::{PACKET_SIZE_MAX, FORWARD_MAX_HOPS};
use crate::vl1::constants::*;
use crate::vl1::path::Path;
use crate::vl1::peer::Peer;
use crate::vl1::protocol::*;
@ -67,7 +67,7 @@ pub trait VL1CallerInterface {
/// If packet TTL is non-zero it should be used to set the packet TTL for outgoing packets
/// for supported protocols such as UDP, but otherwise it can be ignored. It can also be
/// ignored if the platform does not support setting the TTL.
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>, data: PacketBuffer, packet_ttl: u8) -> bool;
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>, data: &[&[u8]], packet_ttl: u8) -> bool;
/// Called to check and see if a physical address should be used for ZeroTier traffic to a node.
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>) -> bool;
@ -268,7 +268,7 @@ impl Node {
let source = Address::from(&packet_header.src);
let peer = self.peer(source);
if peer.is_some() {
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, frag0.as_ref(), &assembled_packet.frags[1..(assembled_packet.have as usize)]);
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, frag0, &assembled_packet.frags[1..(assembled_packet.have as usize)]);
} else {
self.whois.query(self, ci, source, Some(QueuedPacket::Fragmented(assembled_packet)));
}

View file

@ -1,24 +1,28 @@
use std::convert::TryInto;
use std::mem::MaybeUninit;
use std::sync::Arc;
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
use parking_lot::Mutex;
use aes_gmac_siv::{AesGmacSiv, AesCtr};
use aes_gmac_siv::{AesCtr, AesGmacSiv};
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
use crate::crypto::c25519::C25519KeyPair;
use crate::crypto::hash::SHA384;
use crate::crypto::kbkdf::zt_kbkdf_hmac_sha384;
use crate::crypto::p521::P521KeyPair;
use crate::crypto::poly1305::Poly1305;
use crate::crypto::random::next_u64_secure;
use crate::crypto::salsa::Salsa;
use crate::crypto::secret::Secret;
use crate::defaults::UDP_DEFAULT_MTU;
use crate::util::pool::{Pool, PoolFactory};
use crate::vl1::{Identity, Path, Endpoint, InetAddress, Dictionary};
use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path};
use crate::vl1::buffer::Buffer;
use crate::vl1::constants::*;
use crate::vl1::node::*;
use crate::vl1::protocol::*;
use crate::{VERSION_PROTO, VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
use crate::crypto::hash::SHA384;
struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
@ -117,6 +121,7 @@ pub struct Peer {
/// is different the key will be wrong and MAC will fail.
///
/// This is only used for Salsa/Poly modes.
#[inline(always)]
fn salsa_derive_per_packet_key(key: &Secret<48>, header: &PacketHeader, packet_size: usize) -> Secret<48> {
let hb = header.as_bytes();
let mut k = key.clone();
@ -130,8 +135,9 @@ fn salsa_derive_per_packet_key(key: &Secret<48>, header: &PacketHeader, packet_s
}
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
#[inline(always)]
fn salsa_poly_create(secret: &PeerSecret, header: &PacketHeader, packet_size: usize) -> (Salsa, Poly1305) {
let key = salsa_derive_per_packet_key(&secret.secret, header, payload.len());
let key = salsa_derive_per_packet_key(&secret.secret, header, packet_size);
let mut salsa = Salsa::new(&key.0[0..32], header.id_bytes(), true).unwrap();
let mut poly1305_key = [0_u8; 32];
salsa.crypt_in_place(&mut poly1305_key);
@ -304,6 +310,54 @@ impl Peer {
self.paths.lock().last().map(|p| p.clone())
}
/// Send a packet as one or more UDP fragments.
///
/// Calling this with anything other than a UDP endpoint is invalid.
fn send_udp<CI: VL1CallerInterface>(&self, ci: &CI, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>, packet_id: PacketID, data: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
debug_assert!(matches!(endpoint, Endpoint::IpUdp(_)));
debug_assert!(data.len() <= PACKET_SIZE_MAX);
let packet_size = data.len();
if packet_size > UDP_DEFAULT_MTU {
let bytes = data.as_bytes();
if !ci.wire_send(endpoint, local_socket, local_interface, &[&bytes[0..UDP_DEFAULT_MTU]], 0) {
return false;
}
let mut pos = UDP_DEFAULT_MTU;
let fragment_count = (((packet_size - UDP_DEFAULT_MTU) as u32) / ((UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE) as u32)) + ((((packet_size - UDP_DEFAULT_MTU) as u32) % ((UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE) as u32)) != 0) as u32;
debug_assert!(fragment_count <= FRAGMENT_COUNT_MAX as u32);
let mut header = FragmentHeader {
id: packet_id,
dest: bytes[PACKET_DESTINATION_INDEX..PACKET_DESTINATION_INDEX + ADDRESS_SIZE].try_into().unwrap(),
fragment_indicator: FRAGMENT_INDICATOR,
total_and_fragment_no: ((fragment_count + 1) << 4) as u8,
reserved_hops: 0,
};
let mut chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE);
loop {
header.total_and_fragment_no += 1;
let next_pos = pos + chunk_size;
if !ci.wire_send(endpoint, local_socket, local_interface, &[header.as_bytes(), &bytes[pos..next_pos]], 0) {
return false;
}
pos = next_pos;
if pos < packet_size {
chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE);
} else {
break;
}
}
return true;
}
return ci.wire_send(endpoint, local_socket, local_interface, &[data.as_bytes()], 0);
}
/// Send a packet to this peer.
///
/// This will go directly if there is an active path, or otherwise indirectly
@ -332,24 +386,28 @@ impl Peer {
/// If try_new_endpoint is not None the packet will be sent directly to this endpoint.
/// Otherwise it will be sent via the best direct or indirect path.
pub(crate) fn send_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, try_new_endpoint: Option<Endpoint>) {
let (endpoint, path) = try_new_endpoint.as_ref().map_or_else(|| {
let path = if try_new_endpoint.is_none() {
self.best_path().map_or_else(|| {
node.root().map_or_else(|| (None, None), |root| {
root.best_path().map_or_else(|| (None, None), |bp| (Some(&bp.endpoint), Some(bp)))
node.root().map_or(None, |root| {
root.best_path().map_or(None, |bp| Some(bp))
})
}, |bp| (Some(&bp.endpoint), Some(bp)))
}, |ep| (Some(ep), None));
let _ = endpoint.map(|endpoint| {
let mut buf = node.get_packet_buffer();
}, |bp| Some(bp))
} else {
None
};
let _ = try_new_endpoint.as_ref().map_or_else(|| Some(&path.as_ref().unwrap().endpoint), |ep| Some(ep)).map(|endpoint| {
let mut packet: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
let this_peer_is_root = node.is_root(self);
debug_assert!(buf.append_and_init_struct(|header: &mut PacketHeader| {
header.id = self.next_packet_iv();
let packet_id = self.next_packet_iv();
debug_assert!(packet.append_and_init_struct(|header: &mut PacketHeader| {
header.id = packet_id;
header.dest = self.identity.address().to_bytes();
header.src = node.address().to_bytes();
header.flags_cipher_hops = CIPHER_NOCRYPT_POLY1305;
}).is_ok());
debug_assert!(buf.append_and_init_struct(|header: &mut message_component_structs::HelloFixedHeaderFields| {
debug_assert!(packet.append_and_init_struct(|header: &mut message_component_structs::HelloFixedHeaderFields| {
header.verb = VERB_VL1_HELLO | VERB_FLAG_HMAC;
header.version_proto = VERSION_PROTO;
header.version_major = VERSION_MAJOR;
@ -358,20 +416,24 @@ impl Peer {
header.timestamp = (ci.time_ticks() as u64).to_be();
}).is_ok());
debug_assert!(self.identity.marshal(&mut buf, false).is_ok());
debug_assert!(endpoint.marshal(&mut buf).is_ok());
debug_assert!(self.identity.marshal(&mut packet, false).is_ok());
debug_assert!(endpoint.marshal(&mut packet).is_ok());
let aes_ctr_iv_position = buf.len();
debug_assert!(buf.append_and_init_bytes_fixed(|iv: &mut [u8; 18]| {
crate::crypto::random::fill_bytes_secure(&mut buf[0..12]);
let aes_ctr_iv_position = packet.len();
debug_assert!(packet.append_and_init_bytes_fixed(|iv: &mut [u8; 18]| {
crate::crypto::random::fill_bytes_secure(&mut iv[0..12]);
todo!()
}));
let dictionary_position = buf.len();
}).is_ok());
let dictionary_position = packet.len();
let mut dict = Dictionary::new();
dict.set_u64(HELLO_DICT_KEY_INSTANCE_ID, node.instance_id);
dict.set_u64(HELLO_DICT_KEY_CLOCK, ci.time_clock() as u64);
let _ = node.locator().map(|loc| loc.to_bytes().map(|loc| dict.set_bytes(HELLO_DICT_KEY_LOCATOR, loc)));
let _ = self.ephemeral_pair.lock().map(|ephemeral_pair| {
let _ = node.locator().map(|loc| {
let mut tmp: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
debug_assert!(loc.marshal(&mut tmp).is_ok());
dict.set_bytes(HELLO_DICT_KEY_LOCATOR, tmp.as_bytes().to_vec());
});
let _ = self.ephemeral_pair.lock().as_ref().map(|ephemeral_pair| {
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_C25519, ephemeral_pair.c25519.public_bytes().to_vec());
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_P521, ephemeral_pair.p521.public_key_bytes().to_vec());
});
@ -379,7 +441,12 @@ impl Peer {
// If the peer is a root we include some extra information for diagnostic and statistics
// purposes such as the CPU type, bits, and OS info. This is not sent to other peers.
dict.set_str(HELLO_DICT_KEY_SYS_ARCH, std::env::consts::ARCH);
dict.set_u64(HELLO_DICT_KEY_SYS_BITS, (std::mem::size_of::<*const ()>() * 8) as u64);
#[cfg(target_pointer_width = "32")] {
dict.set_u64(HELLO_DICT_KEY_SYS_BITS, 32);
}
#[cfg(target_pointer_width = "64")] {
dict.set_u64(HELLO_DICT_KEY_SYS_BITS, 64);
}
dict.set_str(HELLO_DICT_KEY_OS_NAME, std::env::consts::OS);
}
let mut flags = String::new();
@ -390,20 +457,20 @@ impl Peer {
flags.push('w');
}
dict.set_str(HELLO_DICT_KEY_FLAGS, flags.as_str());
assert!(dict.write_to(&mut buf).is_ok());
debug_assert!(dict.write_to(&mut packet).is_ok());
let mut dict_aes = self.static_secret_hello_dictionary.lock();
dict_aes.init(&buf.as_bytes()[aes_ctr_iv_position..aes_ctr_iv_position + 12]);
dict_aes.crypt_in_place(&mut buf.as_bytes_mut()[dictionary_position..]);
dict_aes.init(&packet.as_bytes()[aes_ctr_iv_position..aes_ctr_iv_position + 12]);
dict_aes.crypt_in_place(&mut packet.as_bytes_mut()[dictionary_position..]);
drop(dict_aes);
assert!(buf.append_bytes_fixed(&SHA384::hmac(self.static_secret_packet_hmac.as_ref(), &buf.as_bytes()[PACKET_HEADER_SIZE + 1..])).is_ok());
debug_assert!(packet.append_bytes_fixed(&SHA384::hmac(self.static_secret_packet_hmac.as_ref(), &packet.as_bytes()[PACKET_HEADER_SIZE + 1..])).is_ok());
let (_, mut poly) = salsa_poly_create(secret, header, packet.len());
poly.update(buf.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
buf.as_bytes_mut()[HEADER_MAC_FIELD_INDEX..HEADER_MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
let (_, mut poly) = salsa_poly_create(&self.static_secret, packet.struct_at::<PacketHeader>(0).unwrap(), packet.len());
poly.update(packet.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
packet.as_bytes_mut()[HEADER_MAC_FIELD_INDEX..HEADER_MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
ci.wire_send(endpoint, path.map(|p| p.local_socket), path.map(|p| p.local_interface), buf, 0);
self.send_udp(ci, endpoint, path.as_ref().map(|p| p.local_socket), path.as_ref().map(|p| p.local_interface), packet_id, &packet);
});
}

View file

@ -231,6 +231,11 @@ impl FragmentHeader {
h
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] {
unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() }
}
#[inline(always)]
pub fn destination(&self) -> Address {
Address::from(&self.dest)
@ -238,6 +243,8 @@ impl FragmentHeader {
}
pub(crate) mod message_component_structs {
use crate::vl1::buffer::RawObject;
#[repr(packed)]
pub struct HelloFixedHeaderFields {
pub verb: u8,
@ -248,6 +255,8 @@ pub(crate) mod message_component_structs {
pub timestamp: u64,
}
unsafe impl RawObject for HelloFixedHeaderFields {}
#[repr(packed)]
pub struct OkHelloFixedHeaderFields {
pub timestamp_echo: u64,
@ -256,14 +265,16 @@ pub(crate) mod message_component_structs {
pub version_minor: u8,
pub version_revision: u16,
}
unsafe impl RawObject for OkHelloFixedHeaderFields {}
}
#[cfg(test)]
mod tests {
use std::mem::size_of;
use crate::vl1::constants::{FRAGMENT_HEADER_SIZE, PACKET_HEADER_SIZE};
use crate::vl1::protocol::{FragmentHeader, PacketHeader};
use crate::vl1::constants::*;
use crate::vl1::protocol::*;
#[test]
fn representation() {