Build fixes, add more parsing and serialization to Identity.

This commit is contained in:
Adam Ierymenko 2023-03-24 22:45:51 -04:00
parent ec7fae71b4
commit ed309b9f18
18 changed files with 333 additions and 244 deletions

View file

@ -10,9 +10,8 @@ use tokio_postgres::types::Type;
use tokio_postgres::{Client, Statement};
use zerotier_crypto::secure_eq;
use zerotier_crypto::typestate::Valid;
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
use zerotier_network_hypervisor::vl2::rule::Rule;
use zerotier_network_hypervisor::vl2::{IpRoute, NetworkId};
@ -21,7 +20,6 @@ use zerotier_utils::tokio;
use zerotier_utils::tokio::runtime::Handle;
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
use zerotier_utils::tokio::task::JoinHandle;
use zerotier_vl1_service::VL1DataStorage;
use crate::database::*;
use crate::model::{IpAssignmentPool, Member, Network, RequestLogItem};
@ -130,15 +128,14 @@ impl<'a> Drop for ConnectionHolder<'a> {
}
pub struct PostgresDatabase {
local_controller_id_str: String,
local_identity: Valid<Identity>,
local_controller: Address,
connections: Mutex<(Vec<Box<PostgresConnection>>, Sender<()>)>,
postgres_path: String,
runtime: Handle,
}
impl PostgresDatabase {
pub async fn new(runtime: Handle, postgres_path: String, num_connections: usize, local_identity: Valid<Identity>) -> Result<Arc<Self>, Error> {
pub async fn new(runtime: Handle, postgres_path: String, num_connections: usize) -> Result<Arc<Self>, Error> {
assert!(num_connections > 0);
let (sender, _) = channel(4096);
let mut connections = Vec::with_capacity(num_connections);
@ -147,7 +144,6 @@ impl PostgresDatabase {
}
Ok(Arc::new(Self {
local_controller_id_str: local_identity.address.to_string(),
local_identity,
connections: Mutex::new((connections, sender)),
postgres_path,
runtime,
@ -177,16 +173,6 @@ impl PostgresDatabase {
}
}
impl VL1DataStorage for PostgresDatabase {
fn load_node_identity(&self) -> Option<Valid<Identity>> {
Some(self.local_identity.clone())
}
fn save_node_identity(&self, _id: &Valid<Identity>) -> bool {
panic!("local identity saving not supported by PostgresDatabase")
}
}
#[async_trait]
impl Database for PostgresDatabase {
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {

View file

@ -9,6 +9,7 @@ use tokio::time::{Duration, Instant};
use zerotier_crypto::secure_eq;
use zerotier_network_hypervisor::protocol;
use zerotier_network_hypervisor::protocol::{PacketBuffer, DEFAULT_MULTICAST_LIMIT, ZEROTIER_VIRTUAL_NETWORK_DEFAULT_MTU};
use zerotier_network_hypervisor::vl1::identity::{Identity, IdentitySecret};
use zerotier_network_hypervisor::vl1::*;
use zerotier_network_hypervisor::vl2;
use zerotier_network_hypervisor::vl2::multicastauthority::MulticastAuthority;
@ -37,7 +38,6 @@ pub struct Controller {
reaper: Reaper,
runtime: tokio::runtime::Handle,
database: Arc<dyn Database>,
local_identity: Valid<Identity>,
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
multicast_authority: MulticastAuthority,
@ -47,7 +47,7 @@ pub struct Controller {
/// Recently authorized network members and when that authorization expires (in monotonic ticks).
/// Note that this is not and should not be used for real authentication, just for locking up multicast info.
recently_authorized: RwLock<HashMap<[u8; Identity::FINGERPRINT_SIZE], HashMap<NetworkId, i64>>>,
recently_authorized: RwLock<HashMap<Address, HashMap<NetworkId, i64>>>,
}
impl Controller {
@ -63,7 +63,6 @@ impl Controller {
reaper: Reaper::new(&runtime),
runtime,
database: database.clone(),
local_identity: local_identity,
multicast_authority: MulticastAuthority::new(),
daemons: Mutex::new(Vec::with_capacity(2)),
recently_authorized: RwLock::new(HashMap::new()),
@ -232,7 +231,7 @@ impl Controller {
if member.node_id != *m {
if let Some(peer) = self.service.read().unwrap().upgrade().and_then(|s| s.node().peer(*m)) {
revocations.clear();
Revocation::new(member.network_id, time_clock, member.node_id, *m, &self.local_identity, false).map(|r| revocations.push(r));
Revocation::new(&member.network_id, time_clock, &member.node_id, m, &self.local_identity, false).map(|r| revocations.push(r));
self.send_revocations(&peer, &mut revocations);
}
}
@ -427,7 +426,7 @@ impl Controller {
// the overhead (bandwidth and CPU) of generating these.
if let Some(com) =
vl2::v1::CertificateOfMembership::new(&self.local_identity, network_id, &source_identity, time_clock, credential_ttl)
vl2::v1::CertificateOfMembership::new(&self.local_identity, &network_id, &source_identity, time_clock, credential_ttl)
{
let mut v1cred = V1Credentials {
revision: time_clock as u64,
@ -438,7 +437,7 @@ impl Controller {
};
if !nc.static_ips.is_empty() {
let mut coo = vl2::v1::CertificateOfOwnership::new(network_id, time_clock, source_identity.address);
let mut coo = vl2::v1::CertificateOfOwnership::new(&network_id, time_clock, &source_identity.address);
for ip in nc.static_ips.iter() {
coo.add_ip(ip);
}

View file

@ -4,7 +4,6 @@ use zerotier_crypto::secure_eq;
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::tokio::sync::broadcast::Receiver;
use zerotier_vl1_service::VL1DataStorage;
use crate::model::*;
@ -22,7 +21,7 @@ pub enum Change {
}
#[async_trait]
pub trait Database: Sync + Send + VL1DataStorage + 'static {
pub trait Database: Sync + Send + 'static {
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error>;
async fn get_network(&self, id: NetworkId) -> Result<Option<Network>, Error>;
async fn save_network(&self, obj: Network, generate_change_notification: bool) -> Result<(), Error>;

View file

@ -5,7 +5,7 @@ use async_trait::async_trait;
use notify::{RecursiveMode, Watcher};
use serde::de::DeserializeOwned;
use zerotier_network_hypervisor::vl1::{Address, Identity, Valid};
use zerotier_network_hypervisor::vl1::Address;
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::reaper::Reaper;
use zerotier_utils::tokio::fs;
@ -13,8 +13,6 @@ use zerotier_utils::tokio::runtime::Handle;
use zerotier_utils::tokio::sync::broadcast::{channel, Receiver, Sender};
use zerotier_utils::tokio::task::JoinHandle;
use zerotier_utils::tokio::time::{sleep, Duration, Instant};
use zerotier_vl1_service::datadir::{load_node_identity, save_node_identity};
use zerotier_vl1_service::VL1DataStorage;
use crate::cache::Cache;
use crate::database::{Change, Database, Error};
@ -32,7 +30,7 @@ const EVENT_HANDLER_TASK_TIMEOUT: Duration = Duration::from_secs(10);
/// is different from V1 so it'll need a converter to use with V1 FileDb controller data.
pub struct FileDatabase {
base_path: PathBuf,
local_identity: Valid<Identity>,
controller_address: Address,
change_sender: Sender<Change>,
tasks: Reaper,
cache: Cache,
@ -42,7 +40,7 @@ pub struct FileDatabase {
// TODO: should cache at least hashes and detect changes in the filesystem live.
impl FileDatabase {
pub async fn new<P: AsRef<Path>>(runtime: Handle, base_path: P) -> Result<Arc<Self>, Error> {
pub async fn new<P: AsRef<Path>>(runtime: Handle, base_path: P, controller_address: Address) -> Result<Arc<Self>, Error> {
let base_path: PathBuf = base_path.as_ref().into();
let (change_sender, _) = channel(256);
@ -50,13 +48,9 @@ impl FileDatabase {
let db_weak = db_weak_tmp.clone();
let runtime2 = runtime.clone();
let local_identity =
load_node_identity(base_path.as_path()).ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "identity.secret not found"))?;
let controller_address = local_identity.address;
let db = Arc::new(Self {
base_path: base_path.clone(),
local_identity,
controller_address: controller_address.clone(),
change_sender,
tasks: Reaper::new(&runtime2),
cache: Cache::new(),
@ -246,16 +240,6 @@ impl Drop for FileDatabase {
}
}
impl VL1DataStorage for FileDatabase {
fn load_node_identity(&self) -> Option<Valid<Identity>> {
load_node_identity(self.base_path.as_path())
}
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
save_node_identity(self.base_path.as_path(), id)
}
}
#[async_trait]
impl Database for FileDatabase {
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
@ -362,6 +346,7 @@ mod tests {
#[allow(unused_imports)]
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use zerotier_network_hypervisor::vl1::identity::Identity;
#[allow(unused)]
#[test]
@ -375,11 +360,14 @@ mod tests {
println!("test filedatabase is in: {}", test_dir.as_os_str().to_str().unwrap());
let _ = std::fs::remove_dir_all(&test_dir);
let controller_id = Identity::generate();
let controller_id = Identity::generate(false);
assert!(fs::create_dir_all(&test_dir).await.is_ok());
assert!(save_node_identity(test_dir.as_path(), &controller_id));
let db = Arc::new(FileDatabase::new(tokio_runtime.handle().clone(), test_dir).await.expect("new db"));
let db = Arc::new(
FileDatabase::new(tokio_runtime.handle().clone(), test_dir, controller_id.public.address.clone())
.await
.expect("new db"),
);
let change_count = Arc::new(AtomicUsize::new(0));

View file

@ -7,6 +7,5 @@ pub(crate) mod cache;
pub mod database;
pub mod filedatabase;
pub mod model;
pub mod postgresdatabase;
pub use controller::*;

View file

@ -5,7 +5,8 @@ use std::hash::Hash;
use serde::{Deserialize, Serialize};
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
use zerotier_network_hypervisor::vl1::identity::Identity;
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
use zerotier_network_hypervisor::vl2::NetworkId;
use zerotier_utils::blob::Blob;

View file

@ -16,7 +16,7 @@ use zerotier_crypto::secret::Secret;
use zerotier_crypto::typestate::Valid;
use zerotier_crypto::x25519::*;
use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::base24;
use zerotier_utils::base64;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
@ -123,9 +123,7 @@ impl Identity {
}
// Bits 40-384 of the address are filled from a SHA384 hash of all keys for a full length V2 address.
let mut address = secret.public.address.clone();
secret.public.populate_extended_address_bits(&mut address);
secret.public.address = address;
secret.public.address = secret.public.populate_extended_address_bits();
// For V2 identities we include two self signatures to ensure that all these different key pairs
// are properly bound together and can't be changed independently.
@ -148,7 +146,7 @@ impl Identity {
// First, check that the full SHA384 (bits 40-384) in the address is correct. Check first since this is fast.
let mut test_address = Address::new_uninitialized();
test_address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]);
self.populate_extended_address_bits(&mut test_address);
test_address = self.populate_extended_address_bits();
if self.address != test_address {
return None;
}
@ -190,8 +188,8 @@ impl Identity {
}
}
/// Populate bits 40-384 of the address with a hash of everything else.
fn populate_extended_address_bits(&self, address: &mut Address) {
/// Populate bits 40-384 of the address with a hash of everything else and return the completed address.
fn populate_extended_address_bits(&self) -> Address {
let mut sha = SHA384::new();
sha.update(&self.address.0[..PartialAddress::LEGACY_SIZE_BYTES]); // include short address in full hash
sha.update(&[Self::ALGORITHM_X25519
@ -207,7 +205,9 @@ impl Identity {
sha.update(p384.ecdsa.as_bytes());
}
let sha = sha.finish();
let mut address = self.address.clone();
address.0[PartialAddress::LEGACY_SIZE_BYTES..].copy_from_slice(&sha[..Address::SIZE_BYTES - PartialAddress::LEGACY_SIZE_BYTES]);
address
}
/// Encode for self-signing, used only with p384 keys enabled and panics otherwise.
@ -224,7 +224,6 @@ impl Identity {
/// Decode a byte serialized identity.
pub fn from_bytes(b: &[u8]) -> Result<Self, InvalidFormatError> {
let mut id;
let mut address = Address::new_uninitialized();
if b.len() == packed::V2_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == (Self::ALGORITHM_X25519 | Self::ALGORITHM_P384) {
let p: &packed::V2Public = memory::cast_to_struct(b);
id = Self {
@ -237,7 +236,7 @@ impl Identity {
p384_self_signature: p.p384_self_signature,
}),
};
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
} else if b.len() == packed::V1_PUBLIC_SIZE && b[PartialAddress::LEGACY_SIZE_BYTES] == Self::ALGORITHM_X25519 {
let p: &packed::V1Public = memory::cast_to_struct(b);
id = Self {
@ -245,12 +244,11 @@ impl Identity {
x25519: X25519 { ecdh: p.c25519, eddsa: p.ed25519 },
p384: None,
};
address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
id.address.0[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(&p.short_address);
} else {
return Err(InvalidFormatError);
}
id.populate_extended_address_bits(&mut address);
id.address = address;
id.address = id.populate_extended_address_bits();
return Ok(id);
}
@ -283,23 +281,23 @@ impl ToString for Identity {
fn to_string(&self) -> String {
if let Some(p384) = self.p384.as_ref() {
let mut s = String::with_capacity(1024);
base24::encode_into(self.address.as_bytes(), &mut s);
s.push_str(self.address.to_string().as_str());
s.push_str(":1:");
base24::encode_into(&self.x25519.ecdh, &mut s);
base64::encode_into(&self.x25519.ecdh, &mut s);
s.push(':');
base24::encode_into(&self.x25519.eddsa, &mut s);
base64::encode_into(&self.x25519.eddsa, &mut s);
s.push(':');
base24::encode_into(p384.ecdh.as_bytes(), &mut s);
base64::encode_into(p384.ecdh.as_bytes(), &mut s);
s.push(':');
base24::encode_into(p384.ecdsa.as_bytes(), &mut s);
base64::encode_into(p384.ecdsa.as_bytes(), &mut s);
s.push(':');
base24::encode_into(&p384.ed25519_self_signature, &mut s);
base64::encode_into(&p384.ed25519_self_signature, &mut s);
s.push(':');
base24::encode_into(&p384.p384_self_signature, &mut s);
base64::encode_into(&p384.p384_self_signature, &mut s);
s
} else {
format!(
"{}:0:{}:{}",
"{}:0:{}{}",
hex::to_string(self.address.legacy_bytes()),
hex::to_string(&self.x25519.ecdh),
hex::to_string(&self.x25519.eddsa)
@ -314,54 +312,56 @@ impl FromStr for Identity {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let ss: Vec<&str> = s.split(':').collect();
if ss.len() >= 2 {
if ss[1] == "1" && ss.len() == 8 {
if ss[1] == "1" && ss.len() >= 8 {
return Ok(Self {
address: Address::from_str(ss[0]).map_err(|_| InvalidFormatError)?,
x25519: X25519 {
ecdh: base24::decode(ss[2].as_bytes())
ecdh: base64::decode(ss[2].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
eddsa: base24::decode(ss[3].as_bytes())
eddsa: base64::decode(ss[3].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
},
p384: Some(P384 {
ecdh: P384PublicKey::from_bytes(base24::decode(ss[4].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
ecdh: P384PublicKey::from_bytes(base64::decode(ss[4].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
.ok_or(InvalidFormatError)?,
ecdsa: P384PublicKey::from_bytes(base24::decode(ss[5].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
ecdsa: P384PublicKey::from_bytes(base64::decode(ss[5].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice())
.ok_or(InvalidFormatError)?,
ed25519_self_signature: base24::decode(ss[6].as_bytes())
ed25519_self_signature: base64::decode(ss[6].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
p384_self_signature: base24::decode(ss[7].as_bytes())
p384_self_signature: base64::decode(ss[7].as_bytes())
.map_err(|_| InvalidFormatError)?
.try_into()
.map_err(|_| InvalidFormatError)?,
}),
});
} else if ss[1] == "0" && ss.len() == 4 {
let mut address = {
let legacy_address = hex::from_string(ss[0]);
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
return Err(InvalidFormatError);
}
let mut tmp = [0u8; Address::SIZE_BYTES];
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
Address(tmp)
};
} else if ss[1] == "0" && ss.len() >= 3 {
let ecdh_eddsa = hex::from_string(ss[2]);
if ecdh_eddsa.len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
return Err(InvalidFormatError);
}
let mut a = Self {
address: Address::new_uninitialized(),
address: {
let legacy_address = hex::from_string(ss[0]);
if legacy_address.len() != PartialAddress::LEGACY_SIZE_BYTES {
return Err(InvalidFormatError);
}
let mut tmp = [0u8; Address::SIZE_BYTES];
tmp[..PartialAddress::LEGACY_SIZE_BYTES].copy_from_slice(legacy_address.as_slice());
Address(tmp)
},
x25519: X25519 {
ecdh: hex::from_string(ss[2]).try_into().map_err(|_| InvalidFormatError)?,
eddsa: hex::from_string(ss[3]).try_into().map_err(|_| InvalidFormatError)?,
ecdh: ecdh_eddsa[..C25519_PUBLIC_KEY_SIZE].try_into().unwrap(),
eddsa: ecdh_eddsa[C25519_PUBLIC_KEY_SIZE..].try_into().unwrap(),
},
p384: None,
};
a.populate_extended_address_bits(&mut address);
a.address = address;
a.address = a.populate_extended_address_bits();
return Ok(a);
}
}
@ -401,53 +401,6 @@ impl Marshalable for Identity {
}
}
impl IdentitySecret {
pub fn sign(&self, data: &[u8]) -> ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }> {
let mut s = ArrayVec::new();
if let Some(p384) = self.p384.as_ref() {
s.push_slice(&p384.sign(data));
} else {
s.push_slice(&self.x25519.sign(data));
}
s
}
}
impl X25519Secret {
#[inline]
pub fn agree(&self, public: &Identity) -> Option<Secret<64>> {
Some(Secret(SHA512::hash(self.ecdh.agree(&public.x25519.ecdh).as_bytes())))
}
/// Sign with Ed25519 using the legacy signature format used by ZeroTier V1.
/// This just means the last 32 bytes of a 96-byte signature are the first 32 bytes of the
/// SHA512 hash. This isn't used even in V1 but was once used long ago to rapidly check
/// signatures as part of a different design. Some nodes still expect it to be there though.
#[inline(always)]
pub fn sign(&self, data: &[u8]) -> [u8; 96] {
self.eddsa.sign_zt(data)
}
}
impl P384Secret {
#[inline(always)]
pub fn sign(&self, data: &[u8]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
self.ecdsa.sign(data)
}
}
impl Eq for P384 {}
impl PartialEq for P384 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ecdh.as_bytes() == other.ecdh.as_bytes()
&& self.ecdsa.as_bytes() == other.ecdsa.as_bytes()
&& self.ed25519_self_signature == other.ed25519_self_signature
&& self.p384_self_signature == other.p384_self_signature
}
}
impl Serialize for Identity {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -505,6 +458,127 @@ impl<'de> Deserialize<'de> for Identity {
}
}
impl Eq for P384 {}
impl PartialEq for P384 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ecdh.as_bytes() == other.ecdh.as_bytes()
&& self.ecdsa.as_bytes() == other.ecdsa.as_bytes()
&& self.ed25519_self_signature == other.ed25519_self_signature
&& self.p384_self_signature == other.p384_self_signature
}
}
impl IdentitySecret {
pub fn sign(&self, data: &[u8]) -> ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }> {
let mut s = ArrayVec::new();
if let Some(p384) = self.p384.as_ref() {
s.push_slice(&p384.sign(data));
} else {
s.push_slice(&self.x25519.sign(data));
}
s
}
}
impl ToString for IdentitySecret {
fn to_string(&self) -> String {
let mut s = self.public.to_string();
if let Some(p384) = self.p384.as_ref() {
s.push(':');
base64::encode_into(self.x25519.ecdh.secret_bytes().as_bytes(), &mut s);
s.push(':');
base64::encode_into(self.x25519.eddsa.secret_bytes().as_bytes(), &mut s);
s.push(':');
base64::encode_into(p384.ecdh.secret_key_bytes().as_bytes(), &mut s);
s.push(':');
base64::encode_into(p384.ecdsa.secret_key_bytes().as_bytes(), &mut s);
} else {
s.push(':');
s.push_str(hex::to_string(self.x25519.ecdh.secret_bytes().as_bytes()).as_str());
s.push_str(hex::to_string(self.x25519.eddsa.secret_bytes().as_bytes()).as_str());
}
s
}
}
impl FromStr for IdentitySecret {
type Err = InvalidFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let public = Identity::from_str(s)?;
let ss: Vec<&str> = s.split(':').collect();
if ss.len() >= 2 {
if ss[1] == "1" && ss.len() >= 12 && public.p384.is_some() {
let x25519_ecdh = X25519KeyPair::from_bytes(
&public.x25519.ecdh,
base64::decode(ss[8].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
)
.ok_or(InvalidFormatError)?;
let x25519_eddsa = Ed25519KeyPair::from_bytes(
&public.x25519.ecdh,
base64::decode(ss[9].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
)
.ok_or(InvalidFormatError)?;
let p384_ecdh = P384KeyPair::from_bytes(
public.p384.as_ref().unwrap().ecdh.as_bytes(),
base64::decode(ss[10].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
)
.ok_or(InvalidFormatError)?;
let p384_ecdsa = P384KeyPair::from_bytes(
public.p384.as_ref().unwrap().ecdh.as_bytes(),
base64::decode(ss[11].as_bytes()).map_err(|_| InvalidFormatError)?.as_slice(),
)
.ok_or(InvalidFormatError)?;
return Ok(Self {
public: public.validate().ok_or(InvalidFormatError)?,
x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: x25519_eddsa },
p384: Some(P384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa }),
});
} else if ss[1] == "0" && ss.len() >= 4 && public.p384.is_none() {
let ecdh_eddsa_secrets = hex::from_string(ss[3]);
if ecdh_eddsa_secrets.len() != C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE {
return Err(InvalidFormatError);
}
let x25519_ecdh =
X25519KeyPair::from_bytes(&public.x25519.ecdh, &ecdh_eddsa_secrets[..C25519_SECRET_KEY_SIZE]).ok_or(InvalidFormatError)?;
let x25519_eddsa =
Ed25519KeyPair::from_bytes(&public.x25519.ecdh, &ecdh_eddsa_secrets[C25519_SECRET_KEY_SIZE..]).ok_or(InvalidFormatError)?;
return Ok(Self {
public: public.validate().ok_or(InvalidFormatError)?,
x25519: X25519Secret { ecdh: x25519_ecdh, eddsa: x25519_eddsa },
p384: None,
});
}
}
return Err(InvalidFormatError);
}
}
impl X25519Secret {
#[inline]
pub fn agree(&self, public: &Identity) -> Option<Secret<64>> {
Some(Secret(SHA512::hash(self.ecdh.agree(&public.x25519.ecdh).as_bytes())))
}
/// Sign with Ed25519 using the legacy signature format used by ZeroTier V1.
/// This just means the last 32 bytes of a 96-byte signature are the first 32 bytes of the
/// SHA512 hash. This isn't used even in V1 but was once used long ago to rapidly check
/// signatures as part of a different design. Some nodes still expect it to be there though.
#[inline(always)]
pub fn sign(&self, data: &[u8]) -> [u8; 96] {
self.eddsa.sign_zt(data)
}
}
impl P384Secret {
#[inline(always)]
pub fn sign(&self, data: &[u8]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
self.ecdsa.sign(data)
}
}
/// The actual serialization format for secret identities.
#[derive(Serialize, Deserialize)]
struct IdentitySecretForSerialization<'a> {
@ -572,7 +646,7 @@ impl<'de> Deserialize<'de> for IdentitySecret {
let e2 = || D::Error::custom("invalid key");
let e = |_e: TryFromSliceError| e2();
Ok(IdentitySecret {
public: Valid::mark_valid(Identity {
public: Identity {
address: Address::from_bytes(tmp.address).map_err(|_| e2())?,
x25519: X25519 {
ecdh: x25519_ecdh_public.try_into().map_err(e)?,
@ -588,7 +662,9 @@ impl<'de> Deserialize<'de> for IdentitySecret {
} else {
None
},
}),
}
.validate()
.ok_or_else(e2)?,
x25519: X25519Secret {
ecdh: X25519KeyPair::from_bytes(x25519_ecdh_public, x25519_ecdh_secret).ok_or_else(e2)?,
eddsa: Ed25519KeyPair::from_bytes(x25519_eddsa_public, x25519_eddsa_secret).ok_or_else(e2)?,

View file

@ -218,7 +218,7 @@ struct BackgroundTaskIntervals {
}
pub struct Node<Application: ApplicationLayer + ?Sized> {
pub(super) identity_secret: IdentitySecret,
pub identity: IdentitySecret,
intervals: Mutex<BackgroundTaskIntervals>,
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application>>>>,
pub(super) peers: PeerMap<Application>,
@ -229,7 +229,7 @@ pub struct Node<Application: ApplicationLayer + ?Sized> {
impl<Application: ApplicationLayer + ?Sized> Node<Application> {
pub fn new(identity_secret: IdentitySecret) -> Self {
Self {
identity_secret,
identity: identity_secret,
intervals: Mutex::new(BackgroundTaskIntervals::default()),
paths: RwLock::new(HashMap::new()),
peers: PeerMap::new(),
@ -244,11 +244,6 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
}
}
#[inline(always)]
pub fn identity(&self) -> &Valid<Identity> {
&self.identity_secret.public
}
#[inline(always)]
pub fn peer(&self, a: &Address) -> Option<Arc<Peer<Application>>> {
self.peers.get_exact(a)
@ -353,7 +348,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
for (_, rs) in roots.sets.iter() {
for m in rs.members.iter() {
if m.identity.eq(&self.identity_secret.public) {
if m.identity.eq(&self.identity.public) {
let _ = my_root_sets
.get_or_insert_with(|| Vec::new())
.write_all(rs.to_buffer::<{ RootSet::MAX_MARSHAL_SIZE }>().unwrap().as_bytes());
@ -367,7 +362,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
if let Some(peer) = self.peers.get_exact(&m.identity.address) {
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
} else {
if let Some(peer) = Peer::new(&self.identity_secret, Valid::mark_valid(m.identity.clone()), time_ticks) {
if let Some(peer) = Peer::new(&self.identity, Valid::mark_valid(m.identity.clone()), time_ticks) {
new_roots.insert(self.peers.add(Arc::new(peer)).0, m.endpoints.as_ref().unwrap().iter().cloned().collect());
} else {
bad_identities.push(m.identity.clone());
@ -584,7 +579,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
if let Ok(dest) = PartialAddress::from_legacy_address_bytes(&fragment_header.dest) {
// Packet is addressed to this node.
if dest.matches(&self.identity_secret.public.address) {
if dest.matches(&self.identity.public.address) {
let fragment_header = &*fragment_header; // discard mut
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface, time_ticks);
path.log_receive_anything(time_ticks);

View file

@ -326,7 +326,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
aes_gmac_siv.encrypt_init(&self.v1_proto_next_message_id().to_be_bytes());
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(
&self.identity.address,
&node.identity().address,
&node.identity.public.address,
flags_cipher_hops,
));
let payload = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE).unwrap();
@ -338,7 +338,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id.copy_from_slice(&tag[0..8]);
header.dest = *self.identity.address.legacy_bytes();
header.src = *node.identity().address.legacy_bytes();
header.src = *node.identity.public.address.legacy_bytes();
header.flags_cipher_hops = flags_cipher_hops;
header.mac.copy_from_slice(&tag[8..16]);
} else {
@ -355,7 +355,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id = self.v1_proto_next_message_id().to_be_bytes();
header.dest = *self.identity.address.legacy_bytes();
header.src = *node.identity().address.legacy_bytes();
header.src = *node.identity.public.address.legacy_bytes();
header.flags_cipher_hops = flags_cipher_hops;
header
},
@ -413,7 +413,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
let f: &mut (v1::PacketHeader, v1::message_component_structs::HelloFixedHeaderFields) = packet.append_struct_get_mut().unwrap();
f.0.id = message_id.to_ne_bytes();
f.0.dest = *self.identity.address.legacy_bytes();
f.0.src = *node.identity().address.legacy_bytes();
f.0.src = *node.identity.public.address.legacy_bytes();
f.0.flags_cipher_hops = v1::CIPHER_NOCRYPT_POLY1305;
f.1.verb = message_type::VL1_HELLO;
f.1.version_proto = PROTOCOL_VERSION;
@ -424,7 +424,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
}
debug_assert_eq!(packet.len(), 41);
assert!(node.identity().write_bytes(packet.as_mut(), !self.is_v2()).is_ok());
assert!(node.identity.public.write_bytes(packet.as_mut(), !self.is_v2()).is_ok());
let (_, poly1305_key) = v1_proto_salsa_poly_create(
&self.v1_proto_static_secret,

View file

@ -154,10 +154,10 @@ impl RootSet {
///
/// All current members must sign whether they are disabled (witnessing) or active. The verify()
/// method will return true when signing is complete.
pub fn sign(&mut self, member_identity: &Identity, member_identity_secret: &IdentitySecret) -> bool {
pub fn sign(&mut self, member_identity_secret: &IdentitySecret) -> bool {
let signature = member_identity_secret.sign(self.marshal_for_signing().as_bytes());
let unsigned_entry = self.members.iter().find_map(|m| {
if m.identity.eq(member_identity) {
if m.identity.eq(&member_identity_secret.public) {
Some(m.clone())
} else {
None
@ -165,7 +165,7 @@ impl RootSet {
});
if unsigned_entry.is_some() {
let unsigned_entry = unsigned_entry.unwrap();
self.members.retain(|m| !m.identity.eq(member_identity));
self.members.retain(|m| !m.identity.eq(&member_identity_secret.public));
let _ = self.members.push(Root {
identity: unsigned_entry.identity,
endpoints: unsigned_entry.endpoints,

View file

@ -65,7 +65,7 @@ impl<Application: ApplicationLayer + ?Sized> Whois<Application> {
}
}
if let Some(peer) = node.peers.get_or_add(&node.identity_secret, &identity, time_ticks) {
if let Some(peer) = node.peers.get_or_add(&node.identity, &identity, time_ticks) {
for qi in queued_items.iter() {
for pkt in qi.pending_v1_packets.iter() {
if let Some(source_path) = pkt.0.upgrade() {

View file

@ -12,7 +12,7 @@ use crate::vl2::NetworkId;
/// "Anti-credential" revoking a network member's permission to communicate on a network.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Revocation {
pub network_id: NetworkId,
pub network_id: u64, // legacy 64-bit network ID
pub threshold: i64,
pub target: u64, // legacy 40-bit address
pub issued_to: u64, // legacy 40-bit address
@ -22,7 +22,7 @@ pub struct Revocation {
impl Revocation {
pub fn new(
network_id: NetworkId,
network_id: &NetworkId,
threshold: i64,
target: &Address,
issued_to: &Address,
@ -31,7 +31,7 @@ impl Revocation {
fast_propagate: bool,
) -> Self {
let mut r = Self {
network_id,
network_id: network_id.to_legacy_u64(),
threshold,
target: target.legacy_u64(),
issued_to: issued_to.legacy_u64(),
@ -50,7 +50,7 @@ impl Revocation {
let _ = v.write_all(&[0; 4]);
let _ = v.write_all(&((self.threshold as u32) ^ (self.target as u32)).to_be_bytes()); // ID is arbitrary
let _ = v.write_all(&self.network_id.to_bytes());
let _ = v.write_all(&self.network_id.to_be_bytes());
let _ = v.write_all(&[0; 8]);
let _ = v.write_all(&self.threshold.to_be_bytes());
let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this

View file

@ -3,7 +3,7 @@
use std::path::Path;
use std::str::FromStr;
use zerotier_network_hypervisor::vl1::Identity;
use zerotier_network_hypervisor::vl1::identity::Identity;
use zerotier_utils::io::read_limit;
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].

View file

@ -105,7 +105,7 @@ mod tests {
use super::*;
#[test]
fn base24_encode_decode() {
fn encode_decode() {
let mut tmp = [0xffu8; 256];
for _ in 0..3 {
let mut s = String::with_capacity(1024);

115
utils/src/base64.rs Normal file
View file

@ -0,0 +1,115 @@
use crate::error::InvalidParameterError;
/// URL-safe base64 alphabet
const ALPHABET: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const ALPHABET_INV: [u8; 256] = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
];
pub fn encode_into(mut b: &[u8], s: &mut String) {
while b.len() >= 3 {
let bits = (b[0] as usize) | (b[1] as usize).wrapping_shl(8) | (b[2] as usize).wrapping_shl(16);
b = &b[3..];
let (i0, i1, i2, i3) = (bits & 63, bits.wrapping_shr(6) & 63, bits.wrapping_shr(12) & 63, bits.wrapping_shr(18));
s.push(ALPHABET[i0] as char);
s.push(ALPHABET[i1] as char);
s.push(ALPHABET[i2] as char);
s.push(ALPHABET[i3] as char);
}
if b.len() == 2 {
let bits = (b[0] as usize) | (b[1] as usize).wrapping_shl(8);
s.push(ALPHABET[bits & 63] as char);
s.push(ALPHABET[bits.wrapping_shr(6) & 63] as char);
s.push(ALPHABET[bits.wrapping_shr(12)] as char);
} else if b.len() == 1 {
let bits = b[0] as usize;
s.push(ALPHABET[bits & 63] as char);
s.push(ALPHABET[bits.wrapping_shr(6)] as char);
}
}
pub fn decode_into(mut s: &[u8], b: &mut Vec<u8>) -> Result<(), InvalidParameterError> {
while s.len() >= 4 {
let (i0, i1, i2, i3) = (
ALPHABET_INV[s[0] as usize],
ALPHABET_INV[s[1] as usize],
ALPHABET_INV[s[2] as usize],
ALPHABET_INV[s[3] as usize],
);
s = &s[4..];
if (i0 | i1 | i2 | i3) > 64 {
return Err(InvalidParameterError("invalid base64 string"));
}
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6) | (i2 as usize).wrapping_shl(12) | (i3 as usize).wrapping_shl(18);
b.push((bits & 0xff) as u8);
b.push((bits.wrapping_shr(8) & 0xff) as u8);
b.push((bits.wrapping_shr(16) & 0xff) as u8);
}
match s.len() {
1 => return Err(InvalidParameterError("invalid base64 string")),
2 => {
let (i0, i1) = (ALPHABET_INV[s[0] as usize], ALPHABET_INV[s[1] as usize]);
if (i0 | i1) > 64 {
return Err(InvalidParameterError("invalid base64 string"));
}
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6);
b.push((bits & 0xff) as u8);
}
3 => {
let (i0, i1, i2) = (ALPHABET_INV[s[0] as usize], ALPHABET_INV[s[1] as usize], ALPHABET_INV[s[2] as usize]);
if (i0 | i1 | i2) > 64 {
return Err(InvalidParameterError("invalid base64 string"));
}
let bits = (i0 as usize) | (i1 as usize).wrapping_shl(6) | (i2 as usize).wrapping_shl(12);
b.push((bits & 0xff) as u8);
b.push((bits.wrapping_shr(8) & 0xff) as u8);
}
_ => {}
}
Ok(())
}
pub fn encode(b: &[u8]) -> String {
let mut tmp = String::with_capacity(((b.len() / 3) * 4) + 3);
encode_into(b, &mut tmp);
tmp
}
pub fn decode(s: &[u8]) -> Result<Vec<u8>, InvalidParameterError> {
let mut tmp = Vec::with_capacity(((s.len() / 4) * 3) + 3);
decode_into(s, &mut tmp)?;
Ok(tmp)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_decode() {
let mut tmp = [0xffu8; 256];
for _ in 0..7 {
let mut s = String::with_capacity(1024);
let mut v: Vec<u8> = Vec::with_capacity(256);
for i in 1..256 {
s.clear();
encode_into(&tmp[..i], &mut s);
//println!("{}", s);
v.clear();
decode_into(s.as_str().as_bytes(), &mut v).expect("decode error");
assert!(v.as_slice().eq(&tmp[..i]));
}
for b in tmp.iter_mut() {
*b -= 13;
}
}
}
}

View file

@ -8,6 +8,7 @@
pub mod arrayvec;
pub mod base24;
pub mod base64;
pub mod blob;
pub mod buffer;
pub mod cast;

View file

@ -1,19 +1,15 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::{Arc, Mutex, RwLock};
use serde::de::DeserializeOwned;
use serde::Serialize;
use zerotier_crypto::random::next_u32_secure;
use zerotier_network_hypervisor::vl1::{Identity, Valid};
use zerotier_utils::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT};
use zerotier_utils::json::to_json_pretty;
use crate::vl1service::VL1DataStorage;
pub const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
pub const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
pub const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
@ -22,46 +18,12 @@ pub const CONFIG_FILENAME: &'static str = "local.conf";
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 48;
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
pub fn load_node_identity(base_path: &Path) -> Option<Valid<Identity>> {
let id_data = read_limit(base_path.join(IDENTITY_SECRET_FILENAME), 4096);
if id_data.is_err() {
return None;
}
let id_data = Identity::from_str(String::from_utf8_lossy(id_data.unwrap().as_slice()).as_ref());
if id_data.is_err() {
return None;
}
Some(Valid::mark_valid(id_data.unwrap()))
}
pub fn save_node_identity(base_path: &Path, id: &Valid<Identity>) -> bool {
assert!(id.secret.is_some());
let id_secret_str = id.to_secret_string();
let id_public_str = id.to_string();
let secret_path = base_path.join(IDENTITY_SECRET_FILENAME);
if std::fs::write(&secret_path, id_secret_str.as_bytes()).is_err() {
return false;
}
assert!(fs_restrict_permissions(&secret_path));
return std::fs::write(base_path.join(IDENTITY_PUBLIC_FILENAME), id_public_str.as_bytes()).is_ok();
}
pub struct DataDir<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> {
pub base_path: PathBuf,
config: RwLock<Arc<Config>>,
authtoken: Mutex<String>,
}
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> VL1DataStorage for DataDir<Config> {
fn load_node_identity(&self) -> Option<Valid<Identity>> {
load_node_identity(self.base_path.as_path())
}
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
save_node_identity(self.base_path.as_path(), id)
}
}
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> DataDir<Config> {
pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
let base_path = path.as_ref().to_path_buf();

View file

@ -9,6 +9,7 @@ use std::time::Duration;
use zerotier_crypto::random;
use zerotier_network_hypervisor::protocol::{PacketBufferFactory, PacketBufferPool};
use zerotier_network_hypervisor::vl1::identity::IdentitySecret;
use zerotier_network_hypervisor::vl1::*;
use zerotier_utils::{ms_monotonic, ms_since_epoch};
@ -20,12 +21,6 @@ use crate::LocalSocket;
/// Update UDP bindings every this many seconds.
const UPDATE_UDP_BINDINGS_EVERY_SECS: usize = 10;
/// Trait to implement to provide storage for VL1-related state information.
pub trait VL1DataStorage: Sync + Send {
fn load_node_identity(&self) -> Option<Valid<Identity>>;
fn save_node_identity(&self, id: &Valid<Identity>) -> bool;
}
/// VL1 service that connects to the physical network and hosts an inner protocol like ZeroTier VL2.
///
/// This is the "outward facing" half of a full ZeroTier stack on a normal system. It binds sockets,
@ -33,11 +28,10 @@ pub trait VL1DataStorage: Sync + Send {
/// whatever inner protocol implementation is using it. This would typically be VL2 but could be
/// a test harness or just the controller for a controller that runs stand-alone.
pub struct VL1Service<Inner: InnerProtocolLayer + ?Sized + 'static> {
pub node: Node<Self>,
state: RwLock<VL1ServiceMutableState>,
vl1_data_storage: Arc<dyn VL1DataStorage>,
inner: Arc<Inner>,
buffer_pool: Arc<PacketBufferPool>,
node_container: Option<Node<Self>>, // never None, set in new()
}
struct VL1ServiceMutableState {
@ -48,25 +42,21 @@ struct VL1ServiceMutableState {
}
impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
pub fn new(vl1_data_storage: Arc<dyn VL1DataStorage>, inner: Arc<Inner>, settings: VL1Settings) -> Result<Arc<Self>, Box<dyn Error>> {
let mut service = Self {
pub fn new(identity: IdentitySecret, inner: Arc<Inner>, settings: VL1Settings) -> Result<Arc<Self>, Box<dyn Error>> {
let service = Arc::new(Self {
node: Node::<Self>::new(identity),
state: RwLock::new(VL1ServiceMutableState {
daemons: Vec::with_capacity(2),
udp_sockets: HashMap::with_capacity(8),
settings,
running: true,
}),
vl1_data_storage,
inner,
buffer_pool: Arc::new(PacketBufferPool::new(
std::thread::available_parallelism().map_or(2, |c| c.get() + 2),
PacketBufferFactory::new(),
)),
node_container: None,
};
service.node_container.replace(Node::new(&service, true, false)?);
let service = Arc::new(service);
});
let mut daemons = Vec::new();
let s = service.clone();
@ -78,12 +68,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
Ok(service)
}
#[inline(always)]
pub fn node(&self) -> &Node<Self> {
debug_assert!(self.node_container.is_some());
unsafe { self.node_container.as_ref().unwrap_unchecked() }
}
pub fn bound_udp_ports(&self) -> Vec<u16> {
self.state.read().unwrap().udp_sockets.keys().cloned().collect()
}
@ -173,7 +157,7 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> VL1Service<Inner> {
self.update_udp_bindings();
}
udp_binding_check_every = udp_binding_check_every.wrapping_add(1);
std::thread::sleep(self.node().do_background_tasks(self.as_ref()));
std::thread::sleep(self.node.do_background_tasks(self.as_ref()));
}
}
}
@ -187,7 +171,7 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> UdpPacketHandler for VL1Servi
source_address: &InetAddress,
packet: zerotier_network_hypervisor::protocol::PooledPacketBuffer,
) {
self.node().handle_incoming_physical_packet(
self.node.handle_incoming_physical_packet(
self.as_ref(),
self.inner.as_ref(),
&Endpoint::IpUdp(source_address.clone()),
@ -216,22 +200,6 @@ impl<Inner: InnerProtocolLayer + ?Sized + 'static> ApplicationLayer for VL1Servi
socket.is_valid()
}
#[inline]
fn should_respond_to(&self, _: &Valid<Identity>) -> bool {
// TODO: provide a way for the user of VL1Service to control this
true
}
#[inline]
fn load_node_identity(&self) -> Option<Valid<Identity>> {
self.vl1_data_storage.load_node_identity()
}
#[inline]
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
self.vl1_data_storage.save_node_identity(id)
}
#[inline]
fn get_buffer(&self) -> zerotier_network_hypervisor::protocol::PooledPacketBuffer {
self.buffer_pool.get()