From f989690785f184c800525be120570094e208d488 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Jul 2021 21:39:21 -0400 Subject: [PATCH] A lot of VL1 work including Node, Peer, etc., and wrap secrets in a container to zero them on drop. --- network-hypervisor/Cargo.lock | 39 ++++ network-hypervisor/Cargo.toml | 2 + network-hypervisor/src/crypto/balloon.rs | 6 +- network-hypervisor/src/crypto/c25519.rs | 23 ++- network-hypervisor/src/crypto/kbkdf.rs | 9 +- network-hypervisor/src/crypto/mod.rs | 20 ++ network-hypervisor/src/crypto/p521.rs | 17 +- network-hypervisor/src/crypto/poly1305.rs | 6 + network-hypervisor/src/crypto/random.rs | 41 ++++ network-hypervisor/src/crypto/secret.rs | 63 +++++++ network-hypervisor/src/error.rs | 16 ++ network-hypervisor/src/util/mod.rs | 23 ++- network-hypervisor/src/util/pool.rs | 7 +- network-hypervisor/src/vl1/buffer.rs | 11 +- network-hypervisor/src/vl1/concurrentmap.rs | 44 ----- network-hypervisor/src/vl1/constants.rs | 7 + network-hypervisor/src/vl1/dictionary.rs | 4 - network-hypervisor/src/vl1/endpoint.rs | 4 +- network-hypervisor/src/vl1/identity.rs | 51 ++--- network-hypervisor/src/vl1/inetaddress.rs | 36 ++-- network-hypervisor/src/vl1/locator.rs | 1 + network-hypervisor/src/vl1/mod.rs | 11 +- network-hypervisor/src/vl1/node.rs | 177 +++++++++++++++++- network-hypervisor/src/vl1/path.rs | 39 +++- network-hypervisor/src/vl1/peer.rs | 42 ++++- .../src/vl1/{headers.rs => protocol.rs} | 4 +- network-hypervisor/src/vl1/state.rs | 0 service/src/vnic/mac_feth_tap.rs | 2 +- 28 files changed, 572 insertions(+), 133 deletions(-) create mode 100644 network-hypervisor/src/crypto/random.rs create mode 100644 network-hypervisor/src/crypto/secret.rs delete mode 100644 network-hypervisor/src/vl1/concurrentmap.rs create mode 100644 network-hypervisor/src/vl1/locator.rs rename network-hypervisor/src/vl1/{headers.rs => protocol.rs} (97%) delete mode 100644 network-hypervisor/src/vl1/state.rs diff --git a/network-hypervisor/Cargo.lock b/network-hypervisor/Cargo.lock index fd7164217..0f6aa8391 100644 --- a/network-hypervisor/Cargo.lock +++ b/network-hypervisor/Cargo.lock @@ -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", diff --git a/network-hypervisor/Cargo.toml b/network-hypervisor/Cargo.toml index bf8852908..7b3aed876 100644 --- a/network-hypervisor/Cargo.toml +++ b/network-hypervisor/Cargo.toml @@ -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" diff --git a/network-hypervisor/src/crypto/balloon.rs b/network-hypervisor/src/crypto/balloon.rs index 396c44244..dd2b16e84 100644 --- a/network-hypervisor/src/crypto/balloon.rs +++ b/network-hypervisor/src/crypto/balloon.rs @@ -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(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]; diff --git a/network-hypervisor/src/crypto/c25519.rs b/network-hypervisor/src/crypto/c25519.rs index a6505dcc0..64b88f67d 100644 --- a/network-hypervisor/src/crypto/c25519.rs +++ b/network-hypervisor/src/crypto/c25519.rs @@ -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)] diff --git a/network-hypervisor/src/crypto/kbkdf.rs b/network-hypervisor/src/crypto/kbkdf.rs index c234a7435..8ee330f52 100644 --- a/network-hypervisor/src/crypto/kbkdf.rs +++ b/network-hypervisor/src/crypto/kbkdf.rs @@ -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])) } diff --git a/network-hypervisor/src/crypto/mod.rs b/network-hypervisor/src/crypto/mod.rs index 4cb86c978..2504ecc73 100644 --- a/network-hypervisor/src/crypto/mod.rs +++ b/network-hypervisor/src/crypto/mod.rs @@ -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 } } diff --git a/network-hypervisor/src/crypto/p521.rs b/network-hypervisor/src/crypto/p521.rs index bfb7a1d41..a6536b842 100644 --- a/network-hypervisor/src/crypto/p521.rs +++ b/network-hypervisor/src/crypto/p521.rs @@ -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> { 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)"); diff --git a/network-hypervisor/src/crypto/poly1305.rs b/network-hypervisor/src/crypto/poly1305.rs index a5f49cb9f..e00691a5b 100644 --- a/network-hypervisor/src/crypto/poly1305.rs +++ b/network-hypervisor/src/crypto/poly1305.rs @@ -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); + } } diff --git a/network-hypervisor/src/crypto/random.rs b/network-hypervisor/src/crypto/random.rs new file mode 100644 index 000000000..8dc8fa505 --- /dev/null +++ b/network-hypervisor/src/crypto/random.rs @@ -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 {} diff --git a/network-hypervisor/src/crypto/secret.rs b/network-hypervisor/src/crypto/secret.rs new file mode 100644 index 000000000..165acde0d --- /dev/null +++ b/network-hypervisor/src/crypto/secret.rs @@ -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(pub [u8; L]); + +impl Secret { + #[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 Drop for Secret { + 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 Default for Secret { + #[inline(always)] + fn default() -> Self { + Self([0_u8; L]) + } +} + +impl AsRef<[u8]> for Secret { + #[inline(always)] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8; L]> for Secret { + #[inline(always)] + fn as_ref(&self) -> &[u8; L] { + &self.0 + } +} + +impl AsMut<[u8]> for Secret { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl AsMut<[u8; L]> for Secret { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u8; L] { + &mut self.0 + } +} diff --git a/network-hypervisor/src/error.rs b/network-hypervisor/src/error.rs index 41ed41d0c..530266c66 100644 --- a/network-hypervisor/src/error.rs +++ b/network-hypervisor/src/error.rs @@ -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 {} diff --git a/network-hypervisor/src/util/mod.rs b/network-hypervisor/src/util/mod.rs index 651cf1e7c..61620111e 100644 --- a/network-hypervisor/src/util/mod.rs +++ b/network-hypervisor/src/util/mod.rs @@ -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) +} diff --git a/network-hypervisor/src/util/pool.rs b/network-hypervisor/src/util/pool.rs index af200f818..b4c98730c 100644 --- a/network-hypervisor/src/util/pool.rs +++ b/network-hypervisor/src/util/pool.rs @@ -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 { type PoolInner = Mutex>>; /// 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(*mut PoolEntry); impl Pooled { diff --git a/network-hypervisor/src/vl1/buffer.rs b/network-hypervisor/src/vl1/buffer.rs index b54dce595..e847bc797 100644 --- a/network-hypervisor/src/vl1/buffer.rs +++ b/network-hypervisor/src/vl1/buffer.rs @@ -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 Default for Buffer { } } +impl Reusable for Buffer { + #[inline(always)] + fn reset(&mut self) { + self.clear(); + } +} + impl Buffer { #[inline(always)] pub fn new() -> Self { @@ -55,7 +64,7 @@ impl Buffer { /// Erase contents and zero size. #[inline(always)] - pub fn reset(&mut self) { + pub fn clear(&mut self) { self.0 = 0; self.1.fill(0); } diff --git a/network-hypervisor/src/vl1/concurrentmap.rs b/network-hypervisor/src/vl1/concurrentmap.rs deleted file mode 100644 index 89dfb8b92..000000000 --- a/network-hypervisor/src/vl1/concurrentmap.rs +++ /dev/null @@ -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>. - -#[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 = RwLock>; - -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "powerpc64"))] -pub type ConcurrentMap = DashMap; - -/// 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(m: &Arc>) -> RwLockReadGuard> { - 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(m: &Arc>) -> &ConcurrentMap { - 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(m: &Arc>) -> RwLockWriteGuard> { - 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(m: &Arc>) -> &ConcurrentMap { - m.as_ref() -} diff --git a/network-hypervisor/src/vl1/constants.rs b/network-hypervisor/src/vl1/constants.rs index d8af89490..2aecccd5e 100644 --- a/network-hypervisor/src/vl1/constants.rs +++ b/network-hypervisor/src/vl1/constants.rs @@ -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; diff --git a/network-hypervisor/src/vl1/dictionary.rs b/network-hypervisor/src/vl1/dictionary.rs index f32830cd5..945ec78b7 100644 --- a/network-hypervisor/src/vl1/dictionary.rs +++ b/network-hypervisor/src/vl1/dictionary.rs @@ -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) { 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()); } diff --git a/network-hypervisor/src/vl1/endpoint.rs b/network-hypervisor/src/vl1/endpoint.rs index 87aa6b26c..b5d424388 100644 --- a/network-hypervisor/src/vl1/endpoint.rs +++ b/network-hypervisor/src/vl1/endpoint.rs @@ -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, diff --git a/network-hypervisor/src/vl1/identity.rs b/network-hypervisor/src/vl1/identity.rs index 9daf17db6..c6b7bcade 100644 --- a/network-hypervisor/src/vl1/identity.rs +++ b/network-hypervisor/src/vl1/identity.rs @@ -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> { 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 = 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)) }) }) diff --git a/network-hypervisor/src/vl1/inetaddress.rs b/network-hypervisor/src/vl1/inetaddress.rs index 1823e387f..5dc302057 100644 --- a/network-hypervisor/src/vl1/inetaddress.rs +++ b/network-hypervisor/src/vl1/inetaddress.rs @@ -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::(), k.as_mut_ptr().cast::().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::(), tmp.as_mut_ptr().cast::(), 16); + tmp[1] = tmp[1].wrapping_add((self.sin6.sin6_port as u64) ^ crate::crypto::salt64()); + (*tmp.as_ptr().cast::()).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 { diff --git a/network-hypervisor/src/vl1/locator.rs b/network-hypervisor/src/vl1/locator.rs new file mode 100644 index 000000000..73ce7c705 --- /dev/null +++ b/network-hypervisor/src/vl1/locator.rs @@ -0,0 +1 @@ +pub struct Locator; diff --git a/network-hypervisor/src/vl1/mod.rs b/network-hypervisor/src/vl1/mod.rs index cdd965977..d8f9d1b41 100644 --- a/network-hypervisor/src/vl1/mod.rs +++ b/network-hypervisor/src/vl1/mod.rs @@ -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; diff --git a/network-hypervisor/src/vl1/node.rs b/network-hypervisor/src/vl1/node.rs index 2add36943..e4e59413c 100644 --- a/network-hypervisor/src/vl1/node.rs +++ b/network-hypervisor/src/vl1/node.rs @@ -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>; + +/// 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, local_interface: Option, 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, local_interface: Option) -> 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, Option)]>; + + /// 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>, - peers: ConcurrentMap>, + locator: Mutex>, + paths_by_inaddr: DashMap>, + peers: DashMap>, + peer_vec: Mutex>>, // for rapid iteration through all peers + buffer_pool: Pool>, + 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: &CI, auto_generate_identity_type: Option) -> Result { + 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(&self, ci: &CI) -> Duration { + Duration::from_millis(1000) + } + + /// Called when a packet is received on the physical wire. + pub fn wire_receive(&self, ci: &CI, endpoint: &Endpoint, local_socket: i64, local_interface: i64, data: PacketBuffer) { + } +} + +unsafe impl Send for Node {} + +unsafe impl Sync for Node {} diff --git a/network-hypervisor/src/vl1/path.rs b/network-hypervisor/src/vl1/path.rs index d596f9735..a972c4bac 100644 --- a/network-hypervisor/src/vl1/path.rs +++ b/network-hypervisor/src/vl1/path.rs @@ -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 {} diff --git a/network-hypervisor/src/vl1/peer.rs b/network-hypervisor/src/vl1/peer.rs index 177ec4940..ed0149515 100644 --- a/network-hypervisor/src/vl1/peer.rs +++ b/network-hypervisor/src/vl1/peer.rs @@ -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; 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>>, + + // 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, +} diff --git a/network-hypervisor/src/vl1/headers.rs b/network-hypervisor/src/vl1/protocol.rs similarity index 97% rename from network-hypervisor/src/vl1/headers.rs rename to network-hypervisor/src/vl1/protocol.rs index be02d5b9e..875835488 100644 --- a/network-hypervisor/src/vl1/headers.rs +++ b/network-hypervisor/src/vl1/protocol.rs @@ -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] diff --git a/network-hypervisor/src/vl1/state.rs b/network-hypervisor/src/vl1/state.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/service/src/vnic/mac_feth_tap.rs b/service/src/vnic/mac_feth_tap.rs index a24963e46..3a54ffe3c 100644 --- a/service/src/vnic/mac_feth_tap.rs +++ b/service/src/vnic/mac_feth_tap.rs @@ -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();