mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-11 15:03:45 +02:00
A bunch of reorg, and add a safety valve to nuke the send key in ZSSP if it is hard-expired.
This commit is contained in:
parent
54d6fba6c5
commit
2649ce7571
13 changed files with 252 additions and 116 deletions
|
@ -11,3 +11,5 @@ path = "src/main.rs"
|
||||||
zerotier-crypto = { path = "../crypto" }
|
zerotier-crypto = { path = "../crypto" }
|
||||||
zerotier-utils = { path = "../utils" }
|
zerotier-utils = { path = "../utils" }
|
||||||
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
||||||
|
serde = { version = "^1", features = ["derive"], default-features = false }
|
||||||
|
serde_json = { version = "^1", features = ["std"], default-features = false }
|
||||||
|
|
1
controller/src/lib.rs
Normal file
1
controller/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod model;
|
127
controller/src/model.rs
Normal file
127
controller/src/model.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
||||||
|
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::Address;
|
||||||
|
use zerotier_network_hypervisor::vl1::InetAddress;
|
||||||
|
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||||
|
|
||||||
|
#[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)]
|
||||||
|
pub struct Ipv4AssignMode {
|
||||||
|
pub zt: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Ipv6AssignMode {
|
||||||
|
pub zt: bool,
|
||||||
|
pub rfc4193: bool,
|
||||||
|
#[serde(rename = "6plane")]
|
||||||
|
pub _6plane: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct IpAssignmentPool {
|
||||||
|
#[serde(rename = "ipRangeStart")]
|
||||||
|
ip_range_start: InetAddress,
|
||||||
|
#[serde(rename = "ipRangeEnd")]
|
||||||
|
ip_range_end: InetAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Network {
|
||||||
|
pub id: NetworkId,
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
#[serde(rename = "creationTime")]
|
||||||
|
pub creation_time: i64,
|
||||||
|
|
||||||
|
#[serde(rename = "multicastLimit")]
|
||||||
|
pub multicast_limit: u64,
|
||||||
|
#[serde(rename = "enableBroadcast")]
|
||||||
|
pub enable_broadcast: bool,
|
||||||
|
|
||||||
|
#[serde(rename = "v4AssignMode")]
|
||||||
|
pub v4_assign_mode: Ipv4AssignMode,
|
||||||
|
#[serde(rename = "v6AssignMode")]
|
||||||
|
pub v6_assign_mode: Ipv6AssignMode,
|
||||||
|
#[serde(rename = "ipAssignmentPools")]
|
||||||
|
pub ip_assignment_pools: Vec<IpAssignmentPool>,
|
||||||
|
|
||||||
|
#[serde(rename = "rulesSource")]
|
||||||
|
pub rules_source: String,
|
||||||
|
|
||||||
|
pub mtu: u16,
|
||||||
|
pub private: bool,
|
||||||
|
|
||||||
|
#[serde(default = "ObjectType::network")]
|
||||||
|
pub objtype: ObjectType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Network {
|
||||||
|
#[inline(always)]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Member {
|
||||||
|
#[serde(rename = "networkId")]
|
||||||
|
pub network_id: NetworkId,
|
||||||
|
pub address: Address,
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
#[serde(rename = "creationTime")]
|
||||||
|
pub creation_time: i64,
|
||||||
|
|
||||||
|
#[serde(rename = "ipAssignments")]
|
||||||
|
pub ip_assignments: Vec<InetAddress>,
|
||||||
|
#[serde(rename = "noAutoAssignIps")]
|
||||||
|
pub no_auto_assign_ips: bool,
|
||||||
|
|
||||||
|
#[serde(rename = "vMajor")]
|
||||||
|
pub version_major: u16,
|
||||||
|
#[serde(rename = "vMinor")]
|
||||||
|
pub version_minor: u16,
|
||||||
|
#[serde(rename = "vRev")]
|
||||||
|
pub version_revision: u16,
|
||||||
|
#[serde(rename = "vProto")]
|
||||||
|
pub version_protocol: u16,
|
||||||
|
|
||||||
|
pub authorized: bool,
|
||||||
|
#[serde(rename = "activeBridge")]
|
||||||
|
pub bridge: bool,
|
||||||
|
|
||||||
|
#[serde(rename = "ssoExempt")]
|
||||||
|
pub sso_exempt: bool,
|
||||||
|
|
||||||
|
#[serde(default = "ObjectType::member")]
|
||||||
|
pub objtype: ObjectType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Member {
|
||||||
|
#[inline(always)]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.network_id.hash(state);
|
||||||
|
self.address.hash(state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,12 +20,13 @@ extern "C" {
|
||||||
pub struct Secret<const L: usize>(pub [u8; L]);
|
pub struct Secret<const L: usize>(pub [u8; L]);
|
||||||
|
|
||||||
impl<const L: usize> Secret<L> {
|
impl<const L: usize> Secret<L> {
|
||||||
|
/// Create a new all-zero secret.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self([0_u8; L])
|
Self([0_u8; L])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy bytes into secret, will panic if size does not match.
|
/// Copy bytes into secret, will panic if the slice does not match the size of this secret.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bytes(b: &[u8]) -> Self {
|
pub fn from_bytes(b: &[u8]) -> Self {
|
||||||
Self(b.try_into().unwrap())
|
Self(b.try_into().unwrap())
|
||||||
|
@ -36,16 +37,26 @@ impl<const L: usize> Secret<L> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the first N bytes of this secret as a fixed length array.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn first_n<const N: usize>(&self) -> &[u8; N] {
|
pub fn first_n<const N: usize>(&self) -> &[u8; N] {
|
||||||
assert!(N <= L);
|
assert!(N <= L);
|
||||||
unsafe { &*self.0.as_ptr().cast() }
|
unsafe { &*self.0.as_ptr().cast() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone the first N bytes of this secret as another secret.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn first_n_clone<const N: usize>(&self) -> Secret<N> {
|
pub fn first_n_clone<const N: usize>(&self) -> Secret<N> {
|
||||||
Secret::<N>(self.first_n().clone())
|
Secret::<N>(self.first_n().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destroy the contents of this secret, ignoring normal Rust mutability constraints.
|
||||||
|
///
|
||||||
|
/// This can be used to force a secret to be forgotten under e.g. key lifetime exceeded or error conditions.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn nuke(&self) {
|
||||||
|
unsafe { OPENSSL_cleanse(std::mem::transmute(self.0.as_ptr().cast::<c_void>()), L) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> Drop for Secret<L> {
|
impl<const L: usize> Drop for Secret<L> {
|
||||||
|
|
|
@ -1604,6 +1604,11 @@ impl SessionKey {
|
||||||
.pop()
|
.pop()
|
||||||
.unwrap_or_else(|| Box::new(AesGcm::new(self.send_key.as_bytes(), true))))
|
.unwrap_or_else(|| Box::new(AesGcm::new(self.send_key.as_bytes(), true))))
|
||||||
} else {
|
} else {
|
||||||
|
// Not only do we return an error, but we also destroy the key.
|
||||||
|
let mut scp = self.send_cipher_pool.lock();
|
||||||
|
scp.clear();
|
||||||
|
self.send_key.nuke();
|
||||||
|
|
||||||
Err(Error::MaxKeyLifetimeExceeded)
|
Err(Error::MaxKeyLifetimeExceeded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,27 +26,3 @@ macro_rules! debug_event {
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use debug_event;
|
pub(crate) use debug_event;
|
||||||
|
|
||||||
/// Obtain a view into a byte array cast as another byte array.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn byte_array_range<const A: usize, const START: usize, const LEN: usize>(a: &[u8; A]) -> &[u8; LEN] {
|
|
||||||
assert!((START + LEN) <= A);
|
|
||||||
unsafe { &*a.as_ptr().add(START).cast::<[u8; LEN]>() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// View a flat (Copy) object as a byte array.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn flat_object_as_bytes<T: Copy>(t: &T) -> &[u8] {
|
|
||||||
unsafe { &*std::ptr::slice_from_raw_parts((t as *const T).cast::<u8>(), std::mem::size_of::<T>()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait that annotates a type as being alignment neutral, such as a packed struct of all bytes and byte arrays.
|
|
||||||
pub(crate) unsafe trait AlignmentNeutral: Copy {}
|
|
||||||
|
|
||||||
/// View a byte array as a flat (Copy) object.
|
|
||||||
/// To be safe this can only be used with annotated alignment-neutral structs.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn bytes_as_flat_object<T: Copy + AlignmentNeutral>(b: &[u8]) -> &T {
|
|
||||||
assert!(b.len() >= std::mem::size_of::<T>());
|
|
||||||
unsafe { &*b.as_ptr().cast() }
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ use zerotier_crypto::secret::Secret;
|
||||||
use zerotier_crypto::x25519::*;
|
use zerotier_crypto::x25519::*;
|
||||||
|
|
||||||
use zerotier_utils::hex;
|
use zerotier_utils::hex;
|
||||||
|
use zerotier_utils::memory::{as_byte_array, as_flat_object};
|
||||||
|
|
||||||
use crate::error::{InvalidFormatError, InvalidParameterError};
|
use crate::error::{InvalidFormatError, InvalidParameterError};
|
||||||
use crate::util::{bytes_as_flat_object, flat_object_as_bytes, AlignmentNeutral};
|
|
||||||
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_FINGERPRINT_SIZE, IDENTITY_POW_THRESHOLD};
|
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_FINGERPRINT_SIZE, IDENTITY_POW_THRESHOLD};
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
|
|
||||||
|
@ -394,7 +394,7 @@ impl Identity {
|
||||||
pub fn to_public_bytes(&self) -> IdentityBytes {
|
pub fn to_public_bytes(&self) -> IdentityBytes {
|
||||||
if let Some(p384) = self.p384.as_ref() {
|
if let Some(p384) = self.p384.as_ref() {
|
||||||
IdentityBytes::X25519P384Public(
|
IdentityBytes::X25519P384Public(
|
||||||
flat_object_as_bytes(&packed::V1 {
|
as_byte_array(&packed::V1 {
|
||||||
v0: packed::V0 {
|
v0: packed::V0 {
|
||||||
address: self.address.to_bytes(),
|
address: self.address.to_bytes(),
|
||||||
key_type: 0,
|
key_type: 0,
|
||||||
|
@ -410,12 +410,11 @@ impl Identity {
|
||||||
ecdsa_self_signature: p384.ecdsa_self_signature,
|
ecdsa_self_signature: p384.ecdsa_self_signature,
|
||||||
ed25519_self_signature: p384.ed25519_self_signature,
|
ed25519_self_signature: p384.ed25519_self_signature,
|
||||||
})
|
})
|
||||||
.try_into()
|
.clone(),
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
IdentityBytes::X25519Public(
|
IdentityBytes::X25519Public(
|
||||||
flat_object_as_bytes(&packed::V0 {
|
as_byte_array(&packed::V0 {
|
||||||
address: self.address.to_bytes(),
|
address: self.address.to_bytes(),
|
||||||
key_type: 0,
|
key_type: 0,
|
||||||
x25519: self.x25519,
|
x25519: self.x25519,
|
||||||
|
@ -424,8 +423,7 @@ impl Identity {
|
||||||
reserved: 0x03,
|
reserved: 0x03,
|
||||||
ext_len: [0; 2],
|
ext_len: [0; 2],
|
||||||
})
|
})
|
||||||
.try_into()
|
.clone(),
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +432,7 @@ impl Identity {
|
||||||
self.secret.as_ref().map(|s| {
|
self.secret.as_ref().map(|s| {
|
||||||
if let Some(p384) = s.p384.as_ref() {
|
if let Some(p384) = s.p384.as_ref() {
|
||||||
IdentityBytes::X25519P384Secret(
|
IdentityBytes::X25519P384Secret(
|
||||||
flat_object_as_bytes(&packed::V1S {
|
as_byte_array(&packed::V1S {
|
||||||
v0s: packed::V0S {
|
v0s: packed::V0S {
|
||||||
address: self.address.to_bytes(),
|
address: self.address.to_bytes(),
|
||||||
key_type: 0,
|
key_type: 0,
|
||||||
|
@ -454,12 +452,11 @@ impl Identity {
|
||||||
ecdh_secret: p384.ecdh.secret_key_bytes().0.clone(),
|
ecdh_secret: p384.ecdh.secret_key_bytes().0.clone(),
|
||||||
ecdsa_secret: p384.ecdsa.secret_key_bytes().0.clone(),
|
ecdsa_secret: p384.ecdsa.secret_key_bytes().0.clone(),
|
||||||
})
|
})
|
||||||
.try_into()
|
.clone(),
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
IdentityBytes::X25519Secret(
|
IdentityBytes::X25519Secret(
|
||||||
flat_object_as_bytes(&packed::V0S {
|
as_byte_array(&packed::V0S {
|
||||||
address: self.address.to_bytes(),
|
address: self.address.to_bytes(),
|
||||||
key_type: 0,
|
key_type: 0,
|
||||||
x25519: self.x25519,
|
x25519: self.x25519,
|
||||||
|
@ -470,8 +467,7 @@ impl Identity {
|
||||||
reserved: 0x03,
|
reserved: 0x03,
|
||||||
ext_len: [0; 2],
|
ext_len: [0; 2],
|
||||||
})
|
})
|
||||||
.try_into()
|
.clone(),
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -483,7 +479,7 @@ impl Identity {
|
||||||
pub fn from_bytes(bytes: &IdentityBytes) -> Option<Self> {
|
pub fn from_bytes(bytes: &IdentityBytes) -> Option<Self> {
|
||||||
let mut id = match bytes {
|
let mut id = match bytes {
|
||||||
IdentityBytes::X25519Public(b) => {
|
IdentityBytes::X25519Public(b) => {
|
||||||
let b: &packed::V0 = bytes_as_flat_object(b);
|
let b: &packed::V0 = as_flat_object(b);
|
||||||
if b.key_type == 0 && b.secret_length == 0 && b.reserved == 0x03 && u16::from_be_bytes(b.ext_len) == 0 {
|
if b.key_type == 0 && b.secret_length == 0 && b.reserved == 0x03 && u16::from_be_bytes(b.ext_len) == 0 {
|
||||||
Some(Self {
|
Some(Self {
|
||||||
address: Address::from_bytes_fixed(&b.address)?,
|
address: Address::from_bytes_fixed(&b.address)?,
|
||||||
|
@ -498,7 +494,7 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IdentityBytes::X25519Secret(b) => {
|
IdentityBytes::X25519Secret(b) => {
|
||||||
let b: &packed::V0S = bytes_as_flat_object(b);
|
let b: &packed::V0S = as_flat_object(b);
|
||||||
if b.key_type == 0
|
if b.key_type == 0
|
||||||
&& b.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
&& b.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
||||||
&& b.reserved == 0x03
|
&& b.reserved == 0x03
|
||||||
|
@ -521,7 +517,7 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IdentityBytes::X25519P384Public(b) => {
|
IdentityBytes::X25519P384Public(b) => {
|
||||||
let b: &packed::V1 = bytes_as_flat_object(b);
|
let b: &packed::V1 = as_flat_object(b);
|
||||||
if b.v0.key_type == 0
|
if b.v0.key_type == 0
|
||||||
&& b.v0.secret_length == 0
|
&& b.v0.secret_length == 0
|
||||||
&& b.v0.reserved == 0x03
|
&& b.v0.reserved == 0x03
|
||||||
|
@ -546,7 +542,7 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IdentityBytes::X25519P384Secret(b) => {
|
IdentityBytes::X25519P384Secret(b) => {
|
||||||
let b: &packed::V1S = bytes_as_flat_object(b);
|
let b: &packed::V1S = as_flat_object(b);
|
||||||
if b.v0s.key_type == 0
|
if b.v0s.key_type == 0
|
||||||
&& b.v0s.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
&& b.v0s.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
||||||
&& b.v0s.reserved == 0x03
|
&& b.v0s.reserved == 0x03
|
||||||
|
@ -587,7 +583,7 @@ impl Identity {
|
||||||
pub fn read_bytes<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
pub fn read_bytes<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||||
let mut buf = [0_u8; 512];
|
let mut buf = [0_u8; 512];
|
||||||
r.read_exact(&mut buf[..Self::BYTE_LENGTH_X25519_PUBLIC])?;
|
r.read_exact(&mut buf[..Self::BYTE_LENGTH_X25519_PUBLIC])?;
|
||||||
let x25519_public = bytes_as_flat_object::<packed::V0>(&buf);
|
let x25519_public: &packed::V0 = as_flat_object(&buf);
|
||||||
let ext_len = u16::from_be_bytes(x25519_public.ext_len) as usize;
|
let ext_len = u16::from_be_bytes(x25519_public.ext_len) as usize;
|
||||||
let obj_len = if x25519_public.secret_length == 0 {
|
let obj_len = if x25519_public.secret_length == 0 {
|
||||||
let obj_len = ext_len + Self::BYTE_LENGTH_X25519_PUBLIC;
|
let obj_len = ext_len + Self::BYTE_LENGTH_X25519_PUBLIC;
|
||||||
|
@ -910,11 +906,6 @@ mod packed {
|
||||||
pub ecdh_secret: [u8; P384_SECRET_KEY_SIZE],
|
pub ecdh_secret: [u8; P384_SECRET_KEY_SIZE],
|
||||||
pub ecdsa_secret: [u8; P384_SECRET_KEY_SIZE],
|
pub ecdsa_secret: [u8; P384_SECRET_KEY_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl AlignmentNeutral for V0 {}
|
|
||||||
unsafe impl AlignmentNeutral for V0S {}
|
|
||||||
unsafe impl AlignmentNeutral for V1 {}
|
|
||||||
unsafe impl AlignmentNeutral for V1S {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identity rendered as a flat byte array.
|
/// Identity rendered as a flat byte array.
|
||||||
|
|
|
@ -11,9 +11,9 @@ use zerotier_crypto::poly1305;
|
||||||
use zerotier_crypto::random::next_u64_secure;
|
use zerotier_crypto::random::next_u64_secure;
|
||||||
use zerotier_crypto::salsa::Salsa;
|
use zerotier_crypto::salsa::Salsa;
|
||||||
use zerotier_crypto::secret::Secret;
|
use zerotier_crypto::secret::Secret;
|
||||||
|
use zerotier_utils::memory::array_range;
|
||||||
|
|
||||||
use crate::util::buffer::BufferReader;
|
use crate::util::buffer::BufferReader;
|
||||||
use crate::util::byte_array_range;
|
|
||||||
use crate::util::debug_event;
|
use crate::util::debug_event;
|
||||||
use crate::util::marshalable::Marshalable;
|
use crate::util::marshalable::Marshalable;
|
||||||
use crate::vl1::address::Address;
|
use crate::vl1::address::Address;
|
||||||
|
@ -142,7 +142,7 @@ fn try_aead_decrypt(
|
||||||
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
|
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
|
||||||
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
|
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
|
||||||
// decrypted tag.
|
// decrypted tag.
|
||||||
Some(u64::from_ne_bytes(*byte_array_range::<16, 0, 8>(tag)))
|
Some(u64::from_ne_bytes(*array_range::<u8, 16, 0, 8>(tag)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -446,11 +446,11 @@ impl<SI: SystemInterface> Peer<SI> {
|
||||||
aes_gmac_siv.encrypt_second_pass_in_place(payload);
|
aes_gmac_siv.encrypt_second_pass_in_place(payload);
|
||||||
let tag = aes_gmac_siv.encrypt_second_pass_finish();
|
let tag = aes_gmac_siv.encrypt_second_pass_finish();
|
||||||
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||||
header.id = *byte_array_range::<16, 0, 8>(tag);
|
header.id = *array_range::<u8, 16, 0, 8>(tag);
|
||||||
header.dest = self.identity.address.to_bytes();
|
header.dest = self.identity.address.to_bytes();
|
||||||
header.src = node.identity.address.to_bytes();
|
header.src = node.identity.address.to_bytes();
|
||||||
header.flags_cipher_hops = flags_cipher_hops;
|
header.flags_cipher_hops = flags_cipher_hops;
|
||||||
header.mac = *byte_array_range::<16, 8, 8>(tag);
|
header.mac = *array_range::<u8, 16, 8, 8>(tag);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ path = "src/main.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
||||||
zerotier-crypto = { path = "../crypto" }
|
zerotier-crypto = { path = "../crypto" }
|
||||||
|
zerotier-utils = { path = "../utils" }
|
||||||
async-trait = "^0"
|
async-trait = "^0"
|
||||||
num-traits = "^0"
|
num-traits = "^0"
|
||||||
tokio = { version = "^1", features = ["fs", "io-util", "io-std", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "sync", "time"], default-features = false }
|
tokio = { version = "^1", features = ["fs", "io-util", "io-std", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "sync", "time"], default-features = false }
|
||||||
|
|
|
@ -12,6 +12,7 @@ use zerotier_network_hypervisor::vl2::*;
|
||||||
use zerotier_network_hypervisor::*;
|
use zerotier_network_hypervisor::*;
|
||||||
|
|
||||||
use zerotier_crypto::random;
|
use zerotier_crypto::random;
|
||||||
|
use zerotier_utils::{ms_monotonic, ms_since_epoch};
|
||||||
|
|
||||||
use tokio::time::Duration;
|
use tokio::time::Duration;
|
||||||
|
|
||||||
|
@ -19,7 +20,6 @@ use crate::datadir::DataDir;
|
||||||
use crate::localinterface::LocalInterface;
|
use crate::localinterface::LocalInterface;
|
||||||
use crate::localsocket::LocalSocket;
|
use crate::localsocket::LocalSocket;
|
||||||
use crate::udp::*;
|
use crate::udp::*;
|
||||||
use crate::utils::{ms_monotonic, ms_since_epoch};
|
|
||||||
|
|
||||||
/// Interval between scans of system network interfaces to update port bindings.
|
/// Interval between scans of system network interfaces to update port bindings.
|
||||||
const UDP_UPDATE_BINDINGS_INTERVAL_MS: Duration = Duration::from_millis(5000);
|
const UDP_UPDATE_BINDINGS_INTERVAL_MS: Duration = Duration::from_millis(5000);
|
||||||
|
|
|
@ -2,13 +2,10 @@
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
@ -19,20 +16,32 @@ use zerotier_network_hypervisor::vl1::Identity;
|
||||||
/// Default sanity limit parameter for read_limit() used throughout the service.
|
/// Default sanity limit parameter for read_limit() used throughout the service.
|
||||||
pub const DEFAULT_FILE_IO_READ_LIMIT: usize = 1048576;
|
pub const DEFAULT_FILE_IO_READ_LIMIT: usize = 1048576;
|
||||||
|
|
||||||
lazy_static! {
|
/// Convenience function to read up to limit bytes from a file.
|
||||||
static ref STARTUP_INSTANT: Instant = Instant::now();
|
///
|
||||||
|
/// If the file is larger than limit, the excess is not read.
|
||||||
|
pub async fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||||
|
let mut f = File::open(path).await?;
|
||||||
|
let bytes = f.metadata().await?.len().min(limit as u64) as usize;
|
||||||
|
let mut v: Vec<u8> = Vec::with_capacity(bytes);
|
||||||
|
v.resize(bytes, 0);
|
||||||
|
f.read_exact(v.as_mut_slice()).await?;
|
||||||
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get milliseconds since unix epoch.
|
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
|
||||||
#[inline(always)]
|
#[cfg(unix)]
|
||||||
pub fn ms_since_epoch() -> i64 {
|
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
|
||||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
|
unsafe {
|
||||||
}
|
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
||||||
|
libc::chmod(
|
||||||
/// Get milliseconds since an arbitrary time in the past, guaranteed to monotonically increase.
|
c_path.as_ptr(),
|
||||||
#[inline(always)]
|
if path.as_ref().is_dir() {
|
||||||
pub fn ms_monotonic() -> i64 {
|
0o700
|
||||||
Instant::now().duration_since(*STARTUP_INSTANT).as_millis() as i64
|
} else {
|
||||||
|
0o600
|
||||||
|
},
|
||||||
|
) == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
||||||
|
@ -143,34 +152,6 @@ pub fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to read up to limit bytes from a file.
|
|
||||||
///
|
|
||||||
/// If the file is larger than limit, the excess is not read.
|
|
||||||
pub async fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
|
||||||
let mut f = File::open(path).await?;
|
|
||||||
let bytes = f.metadata().await?.len().min(limit as u64) as usize;
|
|
||||||
let mut v: Vec<u8> = Vec::with_capacity(bytes);
|
|
||||||
v.resize(bytes, 0);
|
|
||||||
f.read_exact(v.as_mut_slice()).await?;
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
|
||||||
libc::chmod(
|
|
||||||
c_path.as_ptr(),
|
|
||||||
if path.as_ref().is_dir() {
|
|
||||||
0o700
|
|
||||||
} else {
|
|
||||||
0o600
|
|
||||||
},
|
|
||||||
) == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read an identity as either a literal or from a file.
|
/// Read an identity as either a literal or from a file.
|
||||||
pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||||
let parse_func = |s: &str| {
|
let parse_func = |s: &str| {
|
||||||
|
@ -201,22 +182,3 @@ pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity,
|
||||||
//pub fn c_strerror() -> String {
|
//pub fn c_strerror() -> String {
|
||||||
// unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() }
|
// unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::utils::ms_monotonic;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn monotonic_clock_sanity_check() {
|
|
||||||
let start = ms_monotonic();
|
|
||||||
std::thread::sleep(Duration::from_millis(500));
|
|
||||||
let end = ms_monotonic();
|
|
||||||
// per docs:
|
|
||||||
//
|
|
||||||
// The thread may sleep longer than the duration specified due to scheduling specifics or
|
|
||||||
// platform-dependent functionality. It will never sleep less.
|
|
||||||
//
|
|
||||||
assert!((end - start).abs() >= 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
||||||
|
|
||||||
pub mod arrayvec;
|
pub mod arrayvec;
|
||||||
pub mod gatherarray;
|
pub mod gatherarray;
|
||||||
pub mod hex;
|
pub mod hex;
|
||||||
|
@ -5,3 +7,43 @@ pub mod memory;
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
pub mod ringbuffermap;
|
pub mod ringbuffermap;
|
||||||
pub mod varint;
|
pub mod varint;
|
||||||
|
|
||||||
|
/// Get milliseconds since unix epoch.
|
||||||
|
pub fn ms_since_epoch() -> i64 {
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get milliseconds since an arbitrary time in the past, guaranteed to monotonically increase.
|
||||||
|
pub fn ms_monotonic() -> i64 {
|
||||||
|
static STARTUP_INSTANT: parking_lot::RwLock<Option<std::time::Instant>> = parking_lot::RwLock::new(None);
|
||||||
|
let si = *STARTUP_INSTANT.read();
|
||||||
|
let instant_zero = if let Some(si) = si {
|
||||||
|
si
|
||||||
|
} else {
|
||||||
|
*STARTUP_INSTANT.write().get_or_insert(std::time::Instant::now())
|
||||||
|
};
|
||||||
|
std::time::Instant::now().duration_since(instant_zero).as_millis() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::ms_monotonic;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn monotonic_clock_sanity_check() {
|
||||||
|
let start = ms_monotonic();
|
||||||
|
std::thread::sleep(Duration::from_millis(500));
|
||||||
|
let end = ms_monotonic();
|
||||||
|
// per docs:
|
||||||
|
//
|
||||||
|
// The thread may sleep longer than the duration specified due to scheduling specifics or
|
||||||
|
// platform-dependent functionality. It will never sleep less.
|
||||||
|
//
|
||||||
|
assert!((end - start).abs() >= 500);
|
||||||
|
assert!((end - start).abs() < 750);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
// Version for architectures that definitely don't care about unaligned memory access.
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
mod fast_int_memory_access {
|
mod fast_int_memory_access {
|
||||||
|
@ -66,6 +67,7 @@ mod fast_int_memory_access {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version for architectures that might care about unaligned memory access.
|
||||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
mod fast_int_memory_access {
|
mod fast_int_memory_access {
|
||||||
|
@ -122,10 +124,26 @@ mod fast_int_memory_access {
|
||||||
|
|
||||||
pub use fast_int_memory_access::*;
|
pub use fast_int_memory_access::*;
|
||||||
|
|
||||||
|
/// Obtain a view into an array cast as another array.
|
||||||
|
/// This will panic if the template parameters would result in out of bounds access.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn array_range<T: Copy, const S: usize, const START: usize, const LEN: usize>(a: &[T; S]) -> &[T; LEN] {
|
||||||
|
assert!((START + LEN) <= S);
|
||||||
|
unsafe { &*a.as_ptr().add(START).cast::<[T; LEN]>() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to a raw object as a byte array.
|
/// Get a reference to a raw object as a byte array.
|
||||||
/// The template parameter S must equal the size of the object in bytes or this will panic.
|
/// The template parameter S must equal the size of the object in bytes or this will panic.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_byte_array<T: Copy, const S: usize>(o: &T) -> &[u8; S] {
|
pub fn as_byte_array<T: Copy, const S: usize>(o: &T) -> &[u8; S] {
|
||||||
assert_eq!(S, size_of::<T>());
|
assert_eq!(S, size_of::<T>());
|
||||||
unsafe { &*(o as *const T).cast::<[u8; S]>() }
|
unsafe { &*(o as *const T).cast() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a byte array as a flat object.
|
||||||
|
///
|
||||||
|
/// WARNING: while this is technically safe, care must be taken if the object requires aligned access.
|
||||||
|
pub fn as_flat_object<T: Copy, const S: usize>(b: &[u8; S]) -> &T {
|
||||||
|
assert!(std::mem::size_of::<T>() <= S);
|
||||||
|
unsafe { &*b.as_ptr().cast() }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue