mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-13 07:53:45 +02:00
A lot of VL1 work including Node, Peer, etc., and wrap secrets in a container to zero them on drop.
This commit is contained in:
parent
da5dcc9d9b
commit
f989690785
28 changed files with 572 additions and 133 deletions
39
network-hypervisor/Cargo.lock
generated
39
network-hypervisor/Cargo.lock
generated
|
@ -190,6 +190,12 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
|
@ -363,6 +369,12 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -374,6 +386,31 @@ name = "serde"
|
|||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
|
@ -551,6 +588,8 @@ dependencies = [
|
|||
"lz4_flex",
|
||||
"parking_lot",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"urlencoding",
|
||||
"winapi",
|
||||
"x25519-dalek",
|
||||
|
|
|
@ -20,6 +20,8 @@ urlencoding = "^2"
|
|||
lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] }
|
||||
dashmap = "^4"
|
||||
parking_lot = "^0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
[target."cfg(not(windows))".dependencies]
|
||||
libc = "^0"
|
||||
|
|
|
@ -15,8 +15,10 @@ fn hash_int_le(sha: &mut crate::crypto::hash::SHA512, i: u64) {
|
|||
/// SPACE_COST must be a multiple of 64. This is checked with an assertion.
|
||||
/// DELTA is usually 3.
|
||||
pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8], salt: &[u8]) -> [u8; crate::crypto::hash::SHA512_HASH_SIZE] {
|
||||
assert_ne!(SPACE_COST, 0);
|
||||
assert_eq!((SPACE_COST % 64), 0);
|
||||
debug_assert_ne!(SPACE_COST, 0);
|
||||
debug_assert_ne!(TIME_COST, 0);
|
||||
debug_assert_ne!(DELTA, 0);
|
||||
debug_assert_eq!((SPACE_COST % 64), 0);
|
||||
|
||||
let mut buf: [u8; SPACE_COST] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
let zero64 = [0_u8; 8];
|
||||
|
|
|
@ -3,6 +3,9 @@ use std::io::Write;
|
|||
|
||||
use ed25519_dalek::Digest;
|
||||
|
||||
use crate::crypto::random::SecureRandom;
|
||||
use crate::crypto::secret::Secret;
|
||||
|
||||
pub const C25519_PUBLIC_KEY_SIZE: usize = 32;
|
||||
pub const C25519_SECRET_KEY_SIZE: usize = 32;
|
||||
pub const C25519_SHARED_SECRET_SIZE: usize = 32;
|
||||
|
@ -15,8 +18,8 @@ pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
|
|||
|
||||
impl C25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn generate() -> C25519KeyPair {
|
||||
let sk = x25519_dalek::StaticSecret::new(rand_core::OsRng);
|
||||
pub fn generate(_transient: bool) -> C25519KeyPair {
|
||||
let sk = x25519_dalek::StaticSecret::new(SecureRandom::get());
|
||||
let pk = x25519_dalek::PublicKey::from(&sk);
|
||||
C25519KeyPair(sk, pk)
|
||||
}
|
||||
|
@ -40,17 +43,17 @@ impl C25519KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> [u8; C25519_SECRET_KEY_SIZE] {
|
||||
self.0.to_bytes()
|
||||
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.to_bytes())
|
||||
}
|
||||
|
||||
/// Execute ECDH agreement and return a raw (un-hashed) shared secret key.
|
||||
#[inline(always)]
|
||||
pub fn agree(&self, their_public: &[u8]) -> [u8; C25519_SHARED_SECRET_SIZE] {
|
||||
pub fn agree(&self, their_public: &[u8]) -> Secret<{ C25519_SHARED_SECRET_SIZE }> {
|
||||
let pk: [u8; 32] = their_public.try_into().unwrap();
|
||||
let pk = x25519_dalek::PublicKey::from(pk);
|
||||
let sec = self.0.diffie_hellman(&pk);
|
||||
sec.to_bytes()
|
||||
Secret(sec.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,8 +62,8 @@ pub struct Ed25519KeyPair(ed25519_dalek::Keypair);
|
|||
|
||||
impl Ed25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn generate() -> Ed25519KeyPair {
|
||||
let mut rng = rand_core::OsRng::default();
|
||||
pub fn generate(_transient: bool) -> Ed25519KeyPair {
|
||||
let mut rng = SecureRandom::get();
|
||||
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
|
||||
}
|
||||
|
||||
|
@ -88,8 +91,8 @@ impl Ed25519KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> [u8; ED25519_SECRET_KEY_SIZE] {
|
||||
self.0.secret.to_bytes()
|
||||
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.secret.to_bytes())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::crypto::hash::SHA384;
|
||||
use crate::crypto::hash::{SHA384, SHA384_HASH_SIZE};
|
||||
use crate::crypto::secret::Secret;
|
||||
|
||||
/// Derive a key using KBKDF prefaced by the bytes 'ZT' for use in ZeroTier.
|
||||
/// This is a fixed cost key derivation function used to derive sub-keys from a single original
|
||||
/// shared secret for different uses, such as the K0/K1 in AES-GMAC-SIV.
|
||||
/// Key must be 384 bits in length.
|
||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> [u8; 48] {
|
||||
debug_assert!(key.len() == 48);
|
||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<{ SHA384_HASH_SIZE }> {
|
||||
debug_assert!(key.len() == SHA384_HASH_SIZE);
|
||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf
|
||||
SHA384::hmac(key, &[(iter >> 24) as u8, (iter >> 16) as u8, (iter >> 8) as u8, iter as u8, b'Z', b'T', label, 0, context, 0, 0, 0x01, 0x80])
|
||||
Secret(SHA384::hmac(key, &[(iter >> 24) as u8, (iter >> 16) as u8, (iter >> 8) as u8, iter as u8, b'Z', b'T', label, 0, context, 0, 0, 0x01, 0x80]))
|
||||
}
|
||||
|
|
|
@ -5,5 +5,25 @@ pub mod salsa;
|
|||
pub mod poly1305;
|
||||
pub mod balloon;
|
||||
pub mod kbkdf;
|
||||
pub mod random;
|
||||
pub mod secret;
|
||||
|
||||
pub use aes_gmac_siv;
|
||||
use std::convert::Infallible;
|
||||
|
||||
static mut SALT64: u64 = 0;
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
// We always run gcrypt in "FIPS mode," but it doesn't count as fully compliant unless it's a FIPS-certified library.
|
||||
let _ = gcrypt::init_fips_mode(|_| -> Result<(), Infallible> { Ok(()) });
|
||||
|
||||
while SALT64 == 0 {
|
||||
let mut tmp = 0_u64;
|
||||
gcrypt::rand::randomize(gcrypt::rand::Level::Strong, &mut *((&mut tmp as *mut u64).cast::<[u8; 8]>()));
|
||||
SALT64 = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn salt64() -> u64 { unsafe { SALT64 } }
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::str::FromStr;
|
|||
use std::convert::TryInto;
|
||||
|
||||
use gcrypt::sexp::SExpression;
|
||||
use crate::crypto::secret::Secret;
|
||||
|
||||
pub const P521_PUBLIC_KEY_SIZE: usize = 132;
|
||||
pub const P521_SECRET_KEY_SIZE: usize = 66;
|
||||
|
@ -67,7 +68,7 @@ pub struct P521KeyPair {
|
|||
public_key: P521PublicKey,
|
||||
secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression
|
||||
secret_key_for_ecdh: SExpression, // the same secret key as a "data" S-expression for the weird gcrypt ECDH interface
|
||||
secret_key_bytes: [u8; P521_SECRET_KEY_SIZE],
|
||||
secret_key_bytes: Secret<{ P521_SECRET_KEY_SIZE }>,
|
||||
}
|
||||
|
||||
impl P521KeyPair {
|
||||
|
@ -99,10 +100,10 @@ impl P521KeyPair {
|
|||
},
|
||||
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #{}#)(d #{}#)))", crate::util::hex::to_string(pk), crate::util::hex::to_string(sk)).as_str()).unwrap(),
|
||||
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(sk)).as_str()).unwrap(),
|
||||
secret_key_bytes: [0_u8; P521_SECRET_KEY_SIZE],
|
||||
secret_key_bytes: Secret::default(),
|
||||
};
|
||||
kp.public_key.public_key_bytes[((P521_PUBLIC_KEY_SIZE + 1) - pk.len())..P521_PUBLIC_KEY_SIZE].copy_from_slice(&pk[1..]);
|
||||
kp.secret_key_bytes[(P521_SECRET_KEY_SIZE - sk.len())..P521_SECRET_KEY_SIZE].copy_from_slice(sk);
|
||||
kp.secret_key_bytes.0[(P521_SECRET_KEY_SIZE - sk.len())..P521_SECRET_KEY_SIZE].copy_from_slice(sk);
|
||||
return Some(kp);
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +125,7 @@ impl P521KeyPair {
|
|||
public_key: public_key.unwrap(),
|
||||
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #04{}#)(d #{}#)))", crate::util::hex::to_string(public_bytes), crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(),
|
||||
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::util::hex::to_string(secret_bytes)).as_str()).unwrap(),
|
||||
secret_key_bytes: secret_bytes.try_into().unwrap(),
|
||||
secret_key_bytes: Secret::from_bytes(secret_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -143,7 +144,7 @@ impl P521KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_key_bytes(&self) -> &[u8; P521_SECRET_KEY_SIZE] {
|
||||
pub fn secret_key_bytes(&self) -> &Secret<{ P521_SECRET_KEY_SIZE }> {
|
||||
&self.secret_key_bytes
|
||||
}
|
||||
|
||||
|
@ -168,10 +169,10 @@ impl P521KeyPair {
|
|||
}
|
||||
|
||||
/// Execute ECDH key agreement, returning a raw (un-hashed) shared secret.
|
||||
pub fn agree(&self, other_public: &P521PublicKey) -> Option<[u8; P521_ECDH_SHARED_SECRET_SIZE]> {
|
||||
pub fn agree(&self, other_public: &P521PublicKey) -> Option<Secret<{ P521_ECDH_SHARED_SECRET_SIZE }>> {
|
||||
gcrypt::pkey::encrypt(&other_public.public_key, &self.secret_key_for_ecdh).map_or(None, |k| {
|
||||
k.find_token("s").map_or(None, |s| s.get_bytes(1).map_or(None, |sb| {
|
||||
Some(sb[1..].try_into().unwrap())
|
||||
Some(Secret(sb[1..].try_into().unwrap()))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
@ -248,7 +249,7 @@ mod tests {
|
|||
panic!("ECDH secrets do not match");
|
||||
}
|
||||
|
||||
let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes()).unwrap();
|
||||
let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes().as_ref()).unwrap();
|
||||
let sig = kp3.sign(&[3_u8]).unwrap();
|
||||
if !kp.public_key().verify(&[3_u8], &sig) {
|
||||
panic!("ECDSA verify failed (from key reconstructed from bytes)");
|
||||
|
|
|
@ -27,4 +27,10 @@ impl Poly1305 {
|
|||
let _ = self.0.get_mac(&mut mac);
|
||||
mac
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn finish_into(&mut self, mac: &mut [u8]) {
|
||||
debug_assert_eq!(mac.len(), 16);
|
||||
let _ = self.0.get_mac(mac);
|
||||
}
|
||||
}
|
||||
|
|
41
network-hypervisor/src/crypto/random.rs
Normal file
41
network-hypervisor/src/crypto/random.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use rand_core::{RngCore, Error};
|
||||
use rand_core::CryptoRng;
|
||||
use gcrypt::rand::{Level, randomize};
|
||||
|
||||
pub struct SecureRandom;
|
||||
|
||||
impl SecureRandom {
|
||||
#[inline(always)]
|
||||
pub fn get() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for SecureRandom {
|
||||
#[inline(always)]
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut tmp = 0_u32;
|
||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() });
|
||||
tmp
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut tmp = 0_u64;
|
||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() });
|
||||
tmp
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
randomize(Level::Strong, dest);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
randomize(Level::Strong, dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoRng for SecureRandom {}
|
63
network-hypervisor/src/crypto/secret.rs
Normal file
63
network-hypervisor/src/crypto/secret.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::convert::TryInto;
|
||||
use std::ptr::write_volatile;
|
||||
|
||||
/// Container for secrets that clears them on drop.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Secret<const L: usize>(pub [u8; L]);
|
||||
|
||||
impl<const L: usize> Secret<L> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self([0_u8; L])
|
||||
}
|
||||
|
||||
/// Copy bytes into secret, will panic if size does not match.
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self(b.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Drop for Secret<L> {
|
||||
fn drop(&mut self) {
|
||||
let p = self.0.as_mut_ptr();
|
||||
for i in 0..L {
|
||||
unsafe { write_volatile(p.offset(i as isize), 0_u8) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Default for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self([0_u8; L])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> AsRef<[u8]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> AsRef<[u8; L]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8; L] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> AsMut<[u8]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> AsMut<[u8; L]> for Secret<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8; L] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
|
@ -16,3 +16,19 @@ impl Debug for InvalidFormatError {
|
|||
}
|
||||
|
||||
impl Error for InvalidFormatError {}
|
||||
|
||||
pub struct InvalidParameterError(pub(crate) &'static str);
|
||||
|
||||
impl Display for InvalidParameterError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "InvalidParameterError: {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InvalidParameterError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "InvalidParameterError: {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidParameterError {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod hex;
|
||||
pub(crate) mod pool;
|
||||
pub mod pool;
|
||||
|
||||
pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];
|
||||
|
||||
|
@ -53,3 +53,24 @@ pub(crate) fn integer_load_be_u32(d: &[u8]) -> u32 {
|
|||
pub(crate) fn integer_load_be_u64(d: &[u8]) -> u64 {
|
||||
(d[0] as u64) << 56 | (d[1] as u64) << 48 | (d[2] as u64) << 40 | (d[3] as u64) << 32 | (d[4] as u64) << 24 | (d[5] as u64) << 16 | (d[6] as u64) << 8 | (d[7] as u64)
|
||||
}
|
||||
|
||||
/// Mix bits in a 64-bit integer.
|
||||
/// https://nullprogram.com/blog/2018/07/31/
|
||||
pub(crate) fn hash64(mut x: u64) -> u64 {
|
||||
x ^= x.wrapping_shr(30);
|
||||
x = x.wrapping_mul(0xbf58476d1ce4e5b9);
|
||||
x ^= x.wrapping_shr(27);
|
||||
x = x.wrapping_mul(0x94d049bb133111eb);
|
||||
x ^ x.wrapping_shr(31)
|
||||
}
|
||||
|
||||
/// Mix bits in 32-bit integer.
|
||||
/// https://nullprogram.com/blog/2018/07/31/
|
||||
#[inline(always)]
|
||||
pub(crate) fn hash32(mut x: u32) -> u32 {
|
||||
x ^= x.wrapping_shr(16);
|
||||
x = x.wrapping_mul(0x7feb352d);
|
||||
x ^= x.wrapping_shr(15);
|
||||
x = x.wrapping_mul(0x846ca68b);
|
||||
x ^ x.wrapping_shr(16)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::mem::size_of;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::mem::size_of;
|
||||
|
||||
/// Trait for objects that can be used with Pool.
|
||||
pub trait Reusable: Default + Sized {
|
||||
|
@ -17,12 +17,15 @@ struct PoolEntry<O: Reusable> {
|
|||
type PoolInner<O> = Mutex<Vec<*mut PoolEntry<O>>>;
|
||||
|
||||
/// Container for pooled objects that have been checked out of the pool.
|
||||
///
|
||||
/// When this is dropped the object is returned to the pool or if the pool or is
|
||||
/// dropped if the pool has been dropped. There is also an into_raw() and from_raw()
|
||||
/// functionality that allows conversion to/from naked pointers to O for
|
||||
/// interoperation with C/C++ APIs.
|
||||
///
|
||||
/// Note that pooled objects are not clonable. If you want to share them use Rc<>
|
||||
/// or Arc<>.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct Pooled<O: Reusable>(*mut PoolEntry<O>);
|
||||
|
||||
impl<O: Reusable> Pooled<O> {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::mem::size_of;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::util::pool::Reusable;
|
||||
|
||||
const OVERFLOW_ERR_MSG: &'static str = "overflow";
|
||||
|
||||
/// Annotates a type as containing only primitive types like integers and arrays.
|
||||
|
@ -21,6 +23,13 @@ impl<const L: usize> Default for Buffer<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Reusable for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn reset(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Buffer<L> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
|
@ -55,7 +64,7 @@ impl<const L: usize> Buffer<L> {
|
|||
|
||||
/// Erase contents and zero size.
|
||||
#[inline(always)]
|
||||
pub fn reset(&mut self) {
|
||||
pub fn clear(&mut self) {
|
||||
self.0 = 0;
|
||||
self.1.fill(0);
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// This just defines a ConcurrentMap type, selecting standard locked HashMap for smaller systems
|
||||
// or DashMap on larger ones where it would be faster. It also defines some wrappers for read
|
||||
// and write locking that do nothing for DashMap and return a lock guard for RwLock<HashMap<>>.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
#[allow(unused_imports)]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||
use dashmap::DashMap;
|
||||
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64")))]
|
||||
pub type ConcurrentMap<K, V> = RwLock<HashMap<K, V>>;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||
pub type ConcurrentMap<K, V> = DashMap<K, V>;
|
||||
|
||||
/// Wrapper to get a read lock guard on a concurrent map.
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64")))]
|
||||
#[inline(always)]
|
||||
pub fn read<K, V>(m: &Arc<ConcurrentMap<K, V>>) -> RwLockReadGuard<HashMap<K, V>> {
|
||||
m.read().unwrap()
|
||||
}
|
||||
|
||||
/// Wrapper to get a read lock guard on a concurrent map.
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||
#[inline(always)]
|
||||
pub fn read<K, V>(m: &Arc<ConcurrentMap<K, V>>) -> &ConcurrentMap<K, V> {
|
||||
m.as_ref()
|
||||
}
|
||||
|
||||
/// Wrapper to get a write lock guard on a concurrent map.
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64")))]
|
||||
#[inline(always)]
|
||||
pub fn write<K, V>(m: &Arc<ConcurrentMap<K, V>>) -> RwLockWriteGuard<HashMap<K, V>> {
|
||||
m.write().unwrap()
|
||||
}
|
||||
|
||||
/// Wrapper to get a write lock guard on a concurrent map.
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||
#[inline(always)]
|
||||
pub fn write<K, V>(m: &Arc<ConcurrentMap<K, V>>) -> &ConcurrentMap<K, V> {
|
||||
m.as_ref()
|
||||
}
|
|
@ -45,6 +45,13 @@ pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
|||
/// Minimum size of a fragment.
|
||||
pub const FRAGMENT_SIZE_MIN: usize = 16;
|
||||
|
||||
/// Maximum allowed number of fragments.
|
||||
pub const FRAGMENT_COUNT_MAX: usize = 16;
|
||||
|
||||
/// Maximum number of fragmented packets in flight from a peer.
|
||||
/// Usually there should only be one at a time, so this is overkill.
|
||||
pub const PEER_DEFRAGMENT_MAX_PACKETS_IN_FLIGHT: usize = 4;
|
||||
|
||||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
||||
|
||||
|
|
|
@ -95,22 +95,18 @@ impl Dictionary {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_str(&mut self, k: &str, v: &str) {
|
||||
let _ = self.0.insert(String::from(k), v.as_bytes().to_vec());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_u64(&mut self, k: &str, v: u64) {
|
||||
let _ = self.0.insert(String::from(k), crate::util::hex::to_vec_u64(v, true));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_bytes(&mut self, k: &str, v: Vec<u8>) {
|
||||
let _ = self.0.insert(String::from(k), v);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_bool(&mut self, k: &str, v: bool) {
|
||||
let _ = self.0.insert(String::from(k), (if v { [b'1'] } else { [b'0'] }).to_vec());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::vl1::{Address, MAC};
|
||||
use crate::vl1::inetaddress::InetAddress;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
const TYPE_NIL: u8 = 0;
|
||||
const TYPE_ZEROTIER: u8 = 1;
|
||||
|
@ -50,6 +51,7 @@ impl Default for Endpoint {
|
|||
}
|
||||
|
||||
impl Endpoint {
|
||||
#[inline(always)]
|
||||
pub fn ep_type(&self) -> Type {
|
||||
match self {
|
||||
Endpoint::Nil => Type::Nil,
|
||||
|
|
|
@ -9,9 +9,10 @@ use crate::vl1::Address;
|
|||
use crate::vl1::buffer::Buffer;
|
||||
use crate::crypto::c25519::{C25519_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, ED25519_SECRET_KEY_SIZE, C25519KeyPair, Ed25519KeyPair, ED25519_SIGNATURE_SIZE};
|
||||
use crate::crypto::p521::{P521KeyPair, P521PublicKey, P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE};
|
||||
use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE};
|
||||
use crate::crypto::hash::{SHA384, SHA512, SHA512_HASH_SIZE, SHA384_HASH_SIZE};
|
||||
use crate::crypto::balloon;
|
||||
use crate::crypto::salsa::Salsa;
|
||||
use crate::crypto::secret::Secret;
|
||||
use crate::error::InvalidFormatError;
|
||||
|
||||
// Memory parameter for V0 address derivation work function.
|
||||
|
@ -88,11 +89,11 @@ impl Identity {
|
|||
panic!("unable to allocate memory for V0 identity generation");
|
||||
}
|
||||
|
||||
let ed25519 = Ed25519KeyPair::generate();
|
||||
let ed25519 = Ed25519KeyPair::generate(false);
|
||||
let ed25519_pub_bytes = ed25519.public_bytes();
|
||||
let mut sha = SHA512::new();
|
||||
loop {
|
||||
let c25519 = C25519KeyPair::generate();
|
||||
let c25519 = C25519KeyPair::generate(false);
|
||||
let c25519_pub_bytes = c25519.public_bytes();
|
||||
|
||||
sha.update(&c25519_pub_bytes);
|
||||
|
@ -123,8 +124,8 @@ impl Identity {
|
|||
}
|
||||
|
||||
fn generate_p521() -> Identity {
|
||||
let c25519 = C25519KeyPair::generate();
|
||||
let ed25519 = Ed25519KeyPair::generate();
|
||||
let c25519 = C25519KeyPair::generate(false);
|
||||
let ed25519 = Ed25519KeyPair::generate(false);
|
||||
let p521_ecdh = P521KeyPair::generate(false).unwrap();
|
||||
let p521_ecdsa = P521KeyPair::generate(false).unwrap();
|
||||
|
||||
|
@ -191,11 +192,11 @@ impl Identity {
|
|||
sha.update((*p521).1.public_key_bytes());
|
||||
});
|
||||
self.secrets.as_ref().map(|secrets| {
|
||||
sha.update(&secrets.c25519.secret_bytes());
|
||||
sha.update(&secrets.ed25519.secret_bytes());
|
||||
sha.update(&secrets.c25519.secret_bytes().as_ref());
|
||||
sha.update(&secrets.ed25519.secret_bytes().as_ref());
|
||||
secrets.v1.as_ref().map(|p521_secrets| {
|
||||
sha.update((*p521_secrets).0.secret_key_bytes());
|
||||
sha.update((*p521_secrets).1.secret_key_bytes());
|
||||
sha.update((*p521_secrets).0.secret_key_bytes().as_ref());
|
||||
sha.update((*p521_secrets).1.secret_key_bytes().as_ref());
|
||||
});
|
||||
});
|
||||
sha.finish()
|
||||
|
@ -243,14 +244,14 @@ impl Identity {
|
|||
/// If both keys are type 1, key agreement is done with NIST P-521. Otherwise it's done
|
||||
/// with Curve25519. None is returned if there is an error such as this identity missing
|
||||
/// its secrets or a key being invalid.
|
||||
pub fn agree(&self, other_identity: &Identity) -> Option<[u8; 48]> {
|
||||
pub fn agree(&self, other_identity: &Identity) -> Option<Secret<48>> {
|
||||
self.secrets.as_ref().map_or(None, |secrets| {
|
||||
let c25519_secret = SHA384::hash(&secrets.c25519.agree(&other_identity.c25519));
|
||||
let c25519_secret = || Secret::<48>(SHA384::hash(&secrets.c25519.agree(&other_identity.c25519).as_ref()));
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
Some(c25519_secret)
|
||||
Some(c25519_secret())
|
||||
}, |p521_secret| {
|
||||
other_identity.v1.as_ref().map_or_else(|| {
|
||||
Some(c25519_secret)
|
||||
Some(c25519_secret())
|
||||
}, |other_p521_public| {
|
||||
p521_secret.0.agree(&other_p521_public.0).map_or(None, |p521_secret| {
|
||||
//
|
||||
|
@ -270,7 +271,7 @@ impl Identity {
|
|||
// as the stronger of the two algorithms. This should make the FIPS people happy and the
|
||||
// people who are paranoid about NIST curves happy.
|
||||
//
|
||||
Some(SHA384::hmac(&c25519_secret, &p521_secret))
|
||||
Some(Secret(SHA384::hmac(c25519_secret().as_ref(), p521_secret.as_ref())))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -356,10 +357,10 @@ impl Identity {
|
|||
if secrets.v1.is_some() {
|
||||
let p521_secrets = secrets.v1.as_ref().unwrap();
|
||||
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u8)?;
|
||||
buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?;
|
||||
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?;
|
||||
buf.append_bytes_fixed((*p521_secrets).0.secret_key_bytes())?;
|
||||
buf.append_bytes_fixed((*p521_secrets).1.secret_key_bytes())?;
|
||||
buf.append_bytes_fixed(&secrets.c25519.secret_bytes().as_ref())?;
|
||||
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes().as_ref())?;
|
||||
buf.append_bytes_fixed((*p521_secrets).0.secret_key_bytes().as_ref())?;
|
||||
buf.append_bytes_fixed((*p521_secrets).1.secret_key_bytes().as_ref())?;
|
||||
}
|
||||
} else {
|
||||
buf.append_u8(0)?; // 0 secret bytes if not adding any
|
||||
|
@ -371,8 +372,8 @@ impl Identity {
|
|||
if include_private && self.secrets.is_some() {
|
||||
let secrets = self.secrets.as_ref().unwrap();
|
||||
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8)?;
|
||||
buf.append_bytes_fixed(&secrets.c25519.secret_bytes())?;
|
||||
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes())?;
|
||||
buf.append_bytes_fixed(&secrets.c25519.secret_bytes().as_ref())?;
|
||||
buf.append_bytes_fixed(&secrets.ed25519.secret_bytes().as_ref())?;
|
||||
} else {
|
||||
buf.append_u8(0)?; // 0 secret bytes if not adding any
|
||||
}
|
||||
|
@ -479,14 +480,14 @@ impl Identity {
|
|||
pub fn to_secret_string(&self) -> String {
|
||||
self.secrets.as_ref().map_or_else(|| self.to_string(), |secrets| {
|
||||
secrets.v1.as_ref().map_or_else(|| {
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(&secrets.c25519.secret_bytes()), crate::util::hex::to_string(&secrets.ed25519.secret_bytes()))
|
||||
format!("{}:{}{}", self.to_string(), crate::util::hex::to_string(secrets.c25519.secret_bytes().as_ref()), crate::util::hex::to_string(secrets.ed25519.secret_bytes().as_ref()))
|
||||
}, |p521_secret| {
|
||||
let mut secret_key_blob: Vec<u8> = Vec::new();
|
||||
secret_key_blob.reserve(C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE);
|
||||
let _ = secret_key_blob.write_all(&secrets.c25519.secret_bytes());
|
||||
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes());
|
||||
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes());
|
||||
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes());
|
||||
let _ = secret_key_blob.write_all(&secrets.c25519.secret_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(&secrets.ed25519.secret_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(p521_secret.0.secret_key_bytes().as_ref());
|
||||
let _ = secret_key_blob.write_all(p521_secret.1.secret_key_bytes().as_ref());
|
||||
format!("{}:{}", self.to_string(), base64::encode_config(secret_key_blob.as_slice(), base64::URL_SAFE_NO_PAD))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -225,24 +225,6 @@ impl InetAddress {
|
|||
}
|
||||
}
|
||||
|
||||
/// Fills in the InetAddress specific parts of a path lookup key.
|
||||
/// This assumes that the key's default contents are zero bits and does not clear unused regions.
|
||||
pub(crate) fn fill_path_lookup_key(&self, k: &mut [u64; 4]) {
|
||||
unsafe {
|
||||
match self.sa.sa_family as u8 {
|
||||
AF_INET => {
|
||||
k[1] |= self.sin.sin_port as u64 | 0x40000; // OR because most significant 32 bits contain endpoint info
|
||||
k[2] = self.sin.sin_addr.s_addr as u64;
|
||||
}
|
||||
AF_INET6 => {
|
||||
k[1] |= self.sin6.sin6_port as u64 | 0x60000; // OR because most significant 32 bits contain endpoint info
|
||||
copy_nonoverlapping((&(self.sin6.sin6_addr) as *const in6_addr).cast::<u8>(), k.as_mut_ptr().cast::<u8>().offset(16), 16);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IP port.
|
||||
#[inline(always)]
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
|
@ -256,6 +238,24 @@ impl InetAddress {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn local_lookup_key(&self) -> u128 {
|
||||
unsafe {
|
||||
match self.sa.sa_family as u8 {
|
||||
AF_INET => {
|
||||
((self.sin.sin_addr.s_addr as u64).wrapping_shl(16) | self.sin.sin_port as u64) as u128
|
||||
}
|
||||
AF_INET6 => {
|
||||
let mut tmp: [u64; 2] = MaybeUninit::uninit().assume_init();
|
||||
copy_nonoverlapping((&self.sin6.sin6_addr as *const in6_addr).cast::<u8>(), tmp.as_mut_ptr().cast::<u8>(), 16);
|
||||
tmp[1] = tmp[1].wrapping_add((self.sin6.sin6_port as u64) ^ crate::crypto::salt64());
|
||||
(*tmp.as_ptr().cast::<u128>()).wrapping_mul(0x0fc94e3bf4e9ab32866458cd56f5e605)
|
||||
}
|
||||
_ => 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this IP address's scope as per RFC documents and what is advertised via BGP.
|
||||
pub fn scope(&self) -> IpScope {
|
||||
unsafe {
|
||||
|
|
1
network-hypervisor/src/vl1/locator.rs
Normal file
1
network-hypervisor/src/vl1/locator.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub struct Locator;
|
|
@ -1,15 +1,14 @@
|
|||
pub(crate) mod concurrentmap;
|
||||
pub(crate) mod constants;
|
||||
pub(crate) mod headers;
|
||||
pub(crate) mod protocol;
|
||||
pub(crate) mod buffer;
|
||||
pub(crate) mod node;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod peer;
|
||||
pub(crate) mod state;
|
||||
|
||||
pub mod constants;
|
||||
pub mod identity;
|
||||
pub mod inetaddress;
|
||||
pub mod endpoint;
|
||||
pub mod locator;
|
||||
|
||||
mod dictionary;
|
||||
mod address;
|
||||
|
@ -20,3 +19,7 @@ pub use mac::MAC;
|
|||
pub use identity::Identity;
|
||||
pub use endpoint::Endpoint;
|
||||
pub use dictionary::Dictionary;
|
||||
pub use inetaddress::InetAddress;
|
||||
pub use locator::Locator;
|
||||
pub use peer::Peer;
|
||||
pub use path::Path;
|
||||
|
|
|
@ -1,12 +1,181 @@
|
|||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::marker::PhantomData;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::vl1::{Address, Identity};
|
||||
use crate::vl1::concurrentmap::ConcurrentMap;
|
||||
use crate::crypto::random::SecureRandom;
|
||||
use crate::error::InvalidParameterError;
|
||||
use crate::util::pool::{Pool, Pooled};
|
||||
use crate::vl1::{Address, Identity, Endpoint, Locator};
|
||||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::constants::{PACKET_SIZE_MAX, FRAGMENT_COUNT_MAX};
|
||||
use crate::vl1::path::Path;
|
||||
use crate::vl1::peer::Peer;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use dashmap::DashMap;
|
||||
|
||||
/// Standard packet buffer type including pool container.
|
||||
pub type PacketBuffer = Pooled<Buffer<{ PACKET_SIZE_MAX }>>;
|
||||
|
||||
/// Callback interface and call context for calls to the node (for VL1).
|
||||
/// Every non-trivial call takes a reference to this, which it passes all the way through
|
||||
/// the call stack. This can be used to call back into the caller to send packets, get or
|
||||
/// store data, report events, etc.
|
||||
pub trait VL1CallerInterface {
|
||||
/// Node is up and ready for operation.
|
||||
fn event_node_is_up(&self);
|
||||
|
||||
/// Node is shutting down.
|
||||
fn event_node_is_down(&self);
|
||||
|
||||
/// A root signaled an identity collision.
|
||||
/// This should cause the external code to shut down this node, delete its identity, and recreate.
|
||||
fn event_identity_collision(&self);
|
||||
|
||||
/// Node has gone online or offline.
|
||||
fn event_online_status_change(&self, online: bool);
|
||||
|
||||
/// A USER_MESSAGE packet was received.
|
||||
fn event_user_message(&self, source: &Identity, message_type: u64, message: &[u8]);
|
||||
|
||||
/// Load this node's identity from the data store.
|
||||
fn load_identity(&self) -> Option<&[u8]>;
|
||||
|
||||
/// Save this node's identity.
|
||||
/// Note that this is only called on first startup (after up) and after identity_changed.
|
||||
fn save_identity(&self, id: &Identity, public: &[u8], secret: &[u8]);
|
||||
|
||||
/// Load this node's latest locator.
|
||||
fn load_locator(&self) -> Option<&[u8]>;
|
||||
|
||||
/// Save this node's latest locator.
|
||||
fn save_locator(&self, locator: &[u8]);
|
||||
|
||||
/// Load a peer's latest saved state. (A remote peer, not this one.)
|
||||
fn load_peer(&self, address: Address) -> Option<&[u8]>;
|
||||
|
||||
/// Save a peer's state.
|
||||
/// The state contains the identity, so there's no need to save that separately.
|
||||
/// It's just supplied for the address and if the external code wants it.
|
||||
fn save_peer(&self, id: &Identity, peer: &[u8]);
|
||||
|
||||
/// Load network configuration.
|
||||
fn load_network_config(&self, id: u64) -> Option<&[u8]>;
|
||||
|
||||
/// Save network configuration.
|
||||
fn save_network_config(&self, id: u64, config: &[u8]);
|
||||
|
||||
/// Called to send a packet over the physical network (virtual -> physical).
|
||||
///
|
||||
/// This may return false if the send definitely failed, and may return true if the send
|
||||
/// succeeded or may have succeeded (in the case of UDP and similar).
|
||||
///
|
||||
/// If local socket and/or local interface are None, the sending code should make its
|
||||
/// own decision about what local socket or interface to use. It may send on a random
|
||||
/// one, the best fit, or all at once.
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Called to look up a path to a known node.
|
||||
/// If a path is found, this returns a tuple of an endpoint and optional local socket and local
|
||||
/// interface IDs. If these are None they will be None when this is sent with wire_send.
|
||||
fn get_path_hints(&self, id: &Identity) -> Option<&[(&Endpoint, Option<i64>, Option<i64>)]>;
|
||||
|
||||
/// Called to get the current time in milliseconds from the system monotonically increasing clock.
|
||||
fn time_ticks(&self) -> i64;
|
||||
|
||||
/// Called to get the current time in milliseconds since epoch from the real-time clock.
|
||||
fn time_clock(&self) -> i64;
|
||||
}
|
||||
|
||||
pub struct Node {
|
||||
identity: Identity,
|
||||
paths: ConcurrentMap<[u64; 4], Arc<Path>>,
|
||||
peers: ConcurrentMap<Address, Arc<Peer>>,
|
||||
locator: Mutex<Option<Locator>>,
|
||||
paths_by_inaddr: DashMap<u128, Arc<Path>>,
|
||||
peers: DashMap<Address, Arc<Peer>>,
|
||||
peer_vec: Mutex<Vec<Arc<Peer>>>, // for rapid iteration through all peers
|
||||
buffer_pool: Pool<Buffer<{ PACKET_SIZE_MAX }>>,
|
||||
secure_prng: SecureRandom,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
/// Create a new Node.
|
||||
/// If the auto-generate identity type is not None, a new identity will be generated if
|
||||
/// no identity is currently stored in the data store.
|
||||
pub fn new<CI: VL1CallerInterface>(ci: &CI, auto_generate_identity_type: Option<crate::vl1::identity::Type>) -> Result<Self, InvalidParameterError> {
|
||||
crate::crypto::init(); // make sure this is initialized, okay to call more than once
|
||||
|
||||
let id = {
|
||||
let id_str = ci.load_identity();
|
||||
if id_str.is_none() {
|
||||
if auto_generate_identity_type.is_none() {
|
||||
return Err(InvalidParameterError("no identity found and auto-generate not specified"));
|
||||
} else {
|
||||
let id = Identity::generate(auto_generate_identity_type.unwrap());
|
||||
ci.save_identity(&id, id.to_string().as_bytes(), id.to_secret_string().as_bytes());
|
||||
id
|
||||
}
|
||||
} else {
|
||||
let id_str = String::from_utf8_lossy(id_str.unwrap());
|
||||
let id = Identity::from_str(id_str.as_ref());
|
||||
if id.is_err() {
|
||||
return Err(InvalidParameterError("invalid identity"));
|
||||
} else {
|
||||
id.unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
identity: id,
|
||||
locator: Mutex::new(None),
|
||||
paths_by_inaddr: DashMap::new(),
|
||||
peers: DashMap::new(),
|
||||
peer_vec: Mutex::new(Vec::new()),
|
||||
buffer_pool: Pool::new(64),
|
||||
secure_prng: SecureRandom::get(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get address, short for .identity().address()
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
self.identity.address()
|
||||
}
|
||||
|
||||
/// Get identity, which includes secret keys.
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> &Identity {
|
||||
&self.identity
|
||||
}
|
||||
|
||||
/// Get a reusable packet buffer.
|
||||
/// The buffer will automatically be returned to the pool if it is dropped.
|
||||
#[inline(always)]
|
||||
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
||||
self.buffer_pool.get()
|
||||
}
|
||||
|
||||
/// Run background tasks and return desired delay until next call in milliseconds.
|
||||
/// This should only be called once at a time. It technically won't hurt anything to
|
||||
/// call concurrently but it will waste CPU cycles.
|
||||
pub fn do_background_tasks<CI: VL1CallerInterface>(&self, ci: &CI) -> Duration {
|
||||
Duration::from_millis(1000)
|
||||
}
|
||||
|
||||
/// Called when a packet is received on the physical wire.
|
||||
pub fn wire_receive<CI: VL1CallerInterface>(&self, ci: &CI, endpoint: &Endpoint, local_socket: i64, local_interface: i64, data: PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Node {}
|
||||
|
||||
unsafe impl Sync for Node {}
|
||||
|
|
|
@ -1 +1,38 @@
|
|||
pub struct Path;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use crate::vl1::Endpoint;
|
||||
|
||||
pub struct Path {
|
||||
pub(crate) endpoint: Endpoint,
|
||||
pub(crate) local_socket: i64,
|
||||
pub(crate) local_interface: i64,
|
||||
last_send_time_ticks: AtomicI64,
|
||||
last_receive_time_ticks: AtomicI64,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
#[inline(always)]
|
||||
pub fn new(endpoint: Endpoint, local_socket: i64, local_interface: i64) -> Self {
|
||||
Self {
|
||||
endpoint,
|
||||
local_socket,
|
||||
local_interface,
|
||||
last_send_time_ticks: AtomicI64::new(0),
|
||||
last_receive_time_ticks: AtomicI64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn last_send_time_ticks(&self) -> i64 {
|
||||
self.last_send_time_ticks.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn send_receive_time_ticks(&self) -> i64 {
|
||||
self.last_receive_time_ticks.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Path {}
|
||||
|
||||
unsafe impl Sync for Path {}
|
||||
|
|
|
@ -1 +1,41 @@
|
|||
pub struct Peer;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8};
|
||||
|
||||
use crate::vl1::protocol::PacketID;
|
||||
use crate::vl1::node::PacketBuffer;
|
||||
use crate::vl1::constants::{FRAGMENT_COUNT_MAX, PEER_DEFRAGMENT_MAX_PACKETS_IN_FLIGHT};
|
||||
use crate::vl1::{Identity, Path};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
struct FragmentedPacket {
|
||||
pub id: PacketID,
|
||||
pub frag: [Option<PacketBuffer>; FRAGMENT_COUNT_MAX],
|
||||
}
|
||||
|
||||
pub struct Peer {
|
||||
// This peer's identity.
|
||||
identity: Identity,
|
||||
|
||||
// Primary static secret resulting from key agreement with identity.
|
||||
identity_static_secret: [u8; 48],
|
||||
|
||||
// Outgoing packet IV counter used to generate packet IDs to this peer.
|
||||
packet_iv_counter: AtomicU64,
|
||||
|
||||
// Paths sorted in ascending order of quality / preference.
|
||||
paths: Mutex<Vec<Arc<Path>>>,
|
||||
|
||||
// Incoming fragmented packet defragment buffer.
|
||||
fragmented_packets: Mutex<[FragmentedPacket; PEER_DEFRAGMENT_MAX_PACKETS_IN_FLIGHT]>,
|
||||
|
||||
// Last send and receive time in millisecond ticks (not wall clock).
|
||||
last_send_time_ticks: AtomicI64,
|
||||
last_receive_time_ticks: AtomicI64,
|
||||
|
||||
// Most recent remote version (most to least significant bytes: major, minor, revision, build)
|
||||
remote_version: AtomicU64,
|
||||
|
||||
// Most recent remote protocol version
|
||||
remote_protocol_version: AtomicU8,
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::vl1::Address;
|
|||
/// [u8; 8] fields in that their endianness is "wire" endian. If for some reason
|
||||
/// packet IDs need to be portably compared or shared across systems they should
|
||||
/// be treated as bytes not integers.
|
||||
type PacketID = u64;
|
||||
pub type PacketID = u64;
|
||||
|
||||
/// ZeroTier unencrypted outer header
|
||||
/// This is the header for a complete packet. If the fragmented flag is set, it will
|
||||
|
@ -113,7 +113,7 @@ impl FragmentHeader {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
use crate::vl1::headers::{PacketHeader, FragmentHeader};
|
||||
use crate::vl1::protocol::{PacketHeader, FragmentHeader};
|
||||
use crate::vl1::constants::{PACKET_HEADER_SIZE, FRAGMENT_SIZE_MIN};
|
||||
|
||||
#[test]
|
|
@ -188,7 +188,7 @@ impl MacFethTap {
|
|||
// Set sysctl for max if_fake MTU. This is allowed to fail since this sysctl doesn't
|
||||
// exist on older versions of MacOS (and isn't required there). 16000 is larger than
|
||||
// anything ZeroTier supports. OS max is 16384 - some overhead.
|
||||
let _ = Command::new(SYSCTL).arg("net.link.fake.max_mtu").arg("16000").spawn().map(|mut c| { let _ = c.wait(); });
|
||||
let _ = Command::new(SYSCTL).arg("net.link.fake.max_mtu").arg("10000").spawn().map(|mut c| { let _ = c.wait(); });
|
||||
|
||||
// Create pair of feth interfaces and create MacFethDevice struct.
|
||||
let cmd = Command::new(IFCONFIG).arg(&device_name).arg("create").spawn();
|
||||
|
|
Loading…
Add table
Reference in a new issue