mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
Merge branch 'tetanus' into replay-attack-fixes
This commit is contained in:
commit
29f6699924
24 changed files with 580 additions and 770 deletions
|
@ -31,11 +31,11 @@ const REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
|
|||
/// ZeroTier VL2 network controller packet handler, answers VL2 netconf queries.
|
||||
pub struct Controller {
|
||||
self_ref: Weak<Self>,
|
||||
service: RwLock<Weak<VL1Service<dyn Database, Self, Self>>>,
|
||||
service: RwLock<Weak<VL1Service<Self>>>,
|
||||
reaper: Reaper,
|
||||
runtime: tokio::runtime::Handle,
|
||||
database: Arc<dyn Database>,
|
||||
local_identity: Verified<Identity>,
|
||||
local_identity: Valid<Identity>,
|
||||
|
||||
/// Handler for MULTICAST_LIKE and MULTICAST_GATHER messages.
|
||||
multicast_authority: MulticastAuthority,
|
||||
|
@ -78,7 +78,7 @@ impl Controller {
|
|||
/// This must be called once the service that uses this handler is up or the controller
|
||||
/// won't actually do anything. The controller holds a weak reference to VL1Service so
|
||||
/// be sure it's not dropped.
|
||||
pub async fn start(&self, service: &Arc<VL1Service<dyn Database, Self, Self>>) {
|
||||
pub async fn start(&self, service: &Arc<VL1Service<Self>>) {
|
||||
*self.service.write().unwrap() = Arc::downgrade(service);
|
||||
|
||||
// Create database change listener.
|
||||
|
@ -256,7 +256,7 @@ impl Controller {
|
|||
/// reason is returned with None or an acceptance reason with a network configuration is returned.
|
||||
async fn authorize(
|
||||
self: &Arc<Self>,
|
||||
source_identity: &Verified<Identity>,
|
||||
source_identity: &Valid<Identity>,
|
||||
network_id: NetworkId,
|
||||
time_clock: i64,
|
||||
) -> Result<(AuthenticationResult, Option<NetworkConfig>), Box<dyn Error + Send + Sync>> {
|
||||
|
@ -371,13 +371,10 @@ impl Controller {
|
|||
|
||||
// Make sure these agree. It should be impossible to end up with a member that's authorized and
|
||||
// whose identity and identity fingerprint don't match.
|
||||
if !secure_eq(&member
|
||||
.identity
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.fingerprint,
|
||||
member.identity_fingerprint.as_ref().unwrap().as_bytes())
|
||||
{
|
||||
if !secure_eq(
|
||||
&member.identity.as_ref().unwrap().fingerprint,
|
||||
member.identity_fingerprint.as_ref().unwrap().as_bytes(),
|
||||
) {
|
||||
debug_assert!(false);
|
||||
return Ok((AuthenticationResult::RejectedDueToError, None));
|
||||
}
|
||||
|
@ -500,8 +497,24 @@ impl Controller {
|
|||
}
|
||||
}
|
||||
|
||||
impl InnerProtocol for Controller {
|
||||
fn handle_packet<HostSystemImpl: HostSystem + ?Sized>(
|
||||
impl InnerProtocolLayer for Controller {
|
||||
#[inline(always)]
|
||||
fn should_respond_to(&self, _: &Valid<Identity>) -> bool {
|
||||
// Controllers always have to establish sessions to process requests. We don't really know if
|
||||
// a member is relevant until we have looked up both the network and the member, since whether
|
||||
// or not to "learn" unknown members is a network level option.
|
||||
true
|
||||
}
|
||||
|
||||
fn has_trust_relationship(&self, id: &Valid<Identity>) -> bool {
|
||||
self.recently_authorized
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id.fingerprint)
|
||||
.map_or(false, |by_network| by_network.values().any(|t| *t > ms_monotonic()))
|
||||
}
|
||||
|
||||
fn handle_packet<HostSystemImpl: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
_: &Node,
|
||||
|
@ -515,7 +528,7 @@ impl InnerProtocol for Controller {
|
|||
) -> PacketHandlerResult {
|
||||
match verb {
|
||||
protocol::message_type::VL2_NETWORK_CONFIG_REQUEST => {
|
||||
if !host_system.should_respond_to(&source.identity) {
|
||||
if !self.should_respond_to(&source.identity) {
|
||||
return PacketHandlerResult::Ok; // handled and ignored
|
||||
}
|
||||
|
||||
|
@ -641,24 +654,6 @@ impl InnerProtocol for Controller {
|
|||
}
|
||||
}
|
||||
|
||||
impl VL1AuthProvider for Controller {
|
||||
#[inline(always)]
|
||||
fn should_respond_to(&self, _: &Verified<Identity>) -> bool {
|
||||
// Controllers always have to establish sessions to process requests. We don't really know if
|
||||
// a member is relevant until we have looked up both the network and the member, since whether
|
||||
// or not to "learn" unknown members is a network level option.
|
||||
true
|
||||
}
|
||||
|
||||
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool {
|
||||
self.recently_authorized
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id.fingerprint)
|
||||
.map_or(false, |by_network| by_network.values().any(|t| *t > ms_monotonic()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Controller {
|
||||
fn drop(&mut self) {
|
||||
for h in self.daemons.lock().unwrap().drain(..) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use zerotier_crypto::secure_eq;
|
||||
use zerotier_network_hypervisor::vl1::{Address, InetAddress, NodeStorage};
|
||||
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 +22,7 @@ pub enum Change {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Database: Sync + Send + NodeStorage + 'static {
|
||||
pub trait Database: Sync + Send + VL1DataStorage + '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>;
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use notify::{RecursiveMode, Watcher};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, NodeStorage, Verified};
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, Valid};
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
use zerotier_utils::io::{fs_restrict_permissions, read_limit};
|
||||
use zerotier_utils::reaper::Reaper;
|
||||
use zerotier_utils::tokio::fs;
|
||||
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};
|
||||
use crate::model::*;
|
||||
|
||||
const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
|
||||
const EVENT_HANDLER_TASK_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const EVENT_HANDLER_TASK_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
/// An in-filesystem database that permits live editing.
|
||||
///
|
||||
|
@ -34,7 +32,7 @@ const EVENT_HANDLER_TASK_TIMEOUT: Duration = Duration::from_secs(5);
|
|||
/// is different from V1 so it'll need a converter to use with V1 FileDb controller data.
|
||||
pub struct FileDatabase {
|
||||
base_path: PathBuf,
|
||||
controller_address: AtomicU64,
|
||||
local_identity: Valid<Identity>,
|
||||
change_sender: Sender<Change>,
|
||||
tasks: Reaper,
|
||||
cache: Cache,
|
||||
|
@ -53,9 +51,13 @@ 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(),
|
||||
controller_address: AtomicU64::new(0),
|
||||
local_identity,
|
||||
change_sender,
|
||||
tasks: Reaper::new(&runtime2),
|
||||
cache: Cache::new(),
|
||||
|
@ -65,7 +67,6 @@ impl FileDatabase {
|
|||
match event.kind {
|
||||
notify::EventKind::Create(_) | notify::EventKind::Modify(_) | notify::EventKind::Remove(_) => {
|
||||
if let Some(db) = db_weak.lock().unwrap().upgrade() {
|
||||
if let Some(controller_address) = db.get_controller_address() {
|
||||
db.clone().tasks.add(
|
||||
runtime.spawn(async move {
|
||||
if let Some(path0) = event.paths.first() {
|
||||
|
@ -118,18 +119,13 @@ impl FileDatabase {
|
|||
if deleted.is_some() {
|
||||
match record_type {
|
||||
RecordType::Network => {
|
||||
if let Some((network, members)) =
|
||||
db.cache.on_network_deleted(network_id)
|
||||
{
|
||||
let _ =
|
||||
db.change_sender.send(Change::NetworkDeleted(network, members));
|
||||
if let Some((network, members)) = db.cache.on_network_deleted(network_id) {
|
||||
let _ = db.change_sender.send(Change::NetworkDeleted(network, members));
|
||||
}
|
||||
}
|
||||
RecordType::Member => {
|
||||
if let Some(node_id) = node_id {
|
||||
if let Some(member) =
|
||||
db.cache.on_member_deleted(network_id, node_id)
|
||||
{
|
||||
if let Some(member) = db.cache.on_member_deleted(network_id, node_id) {
|
||||
let _ = db.change_sender.send(Change::MemberDeleted(member));
|
||||
}
|
||||
}
|
||||
|
@ -141,9 +137,7 @@ impl FileDatabase {
|
|||
if let Some(changed) = changed {
|
||||
match record_type {
|
||||
RecordType::Network => {
|
||||
if let Ok(Some(new_network)) =
|
||||
Self::load_object::<Network>(changed).await
|
||||
{
|
||||
if let Ok(Some(new_network)) = Self::load_object::<Network>(changed).await {
|
||||
match db.cache.on_network_updated(new_network.clone()) {
|
||||
(true, Some(old_network)) => {
|
||||
let _ = db
|
||||
|
@ -151,17 +145,15 @@ impl FileDatabase {
|
|||
.send(Change::NetworkChanged(old_network, new_network));
|
||||
}
|
||||
(true, None) => {
|
||||
let _ = db
|
||||
.change_sender
|
||||
.send(Change::NetworkCreated(new_network));
|
||||
let _ =
|
||||
db.change_sender.send(Change::NetworkCreated(new_network));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordType::Member => {
|
||||
if let Ok(Some(new_member)) = Self::load_object::<Member>(changed).await
|
||||
{
|
||||
if let Ok(Some(new_member)) = Self::load_object::<Member>(changed).await {
|
||||
match db.cache.on_member_updated(new_member.clone()) {
|
||||
(true, Some(old_member)) => {
|
||||
let _ = db
|
||||
|
@ -169,9 +161,8 @@ impl FileDatabase {
|
|||
.send(Change::MemberChanged(old_member, new_member));
|
||||
}
|
||||
(true, None) => {
|
||||
let _ = db
|
||||
.change_sender
|
||||
.send(Change::MemberCreated(new_member));
|
||||
let _ =
|
||||
db.change_sender.send(Change::MemberCreated(new_member));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -187,7 +178,6 @@ impl FileDatabase {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -215,20 +205,6 @@ impl FileDatabase {
|
|||
Ok(db)
|
||||
}
|
||||
|
||||
fn get_controller_address(&self) -> Option<Address> {
|
||||
let a = self.controller_address.load(Ordering::Relaxed);
|
||||
if a == 0 {
|
||||
if let Some(id) = self.load_node_identity() {
|
||||
self.controller_address.store(id.address.into(), Ordering::Relaxed);
|
||||
Some(id.address)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Address::from_u64(a)
|
||||
}
|
||||
}
|
||||
|
||||
fn network_path(&self, network_id: NetworkId) -> PathBuf {
|
||||
self.base_path.join(format!("N{:06x}", network_id.network_no())).join("config.yaml")
|
||||
}
|
||||
|
@ -274,25 +250,13 @@ impl Drop for FileDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
impl NodeStorage for FileDatabase {
|
||||
fn load_node_identity(&self) -> Option<Verified<Identity>> {
|
||||
let id_data = read_limit(self.base_path.join(IDENTITY_SECRET_FILENAME), 16384);
|
||||
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(Verified::assume_verified(id_data.unwrap()))
|
||||
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: &Verified<Identity>) {
|
||||
assert!(id.secret.is_some());
|
||||
let id_secret_str = id.to_secret_string();
|
||||
let secret_path = self.base_path.join(IDENTITY_SECRET_FILENAME);
|
||||
assert!(std::fs::write(&secret_path, id_secret_str.as_bytes()).is_ok());
|
||||
assert!(fs_restrict_permissions(&secret_path));
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
|
||||
save_node_identity(self.base_path.as_path(), id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,8 +264,7 @@ impl NodeStorage for FileDatabase {
|
|||
impl Database for FileDatabase {
|
||||
async fn list_networks(&self) -> Result<Vec<NetworkId>, Error> {
|
||||
let mut networks = Vec::new();
|
||||
if let Some(controller_address) = self.get_controller_address() {
|
||||
let controller_address_shift24 = u64::from(controller_address).wrapping_shl(24);
|
||||
let controller_address_shift24 = u64::from(self.local_identity.address).wrapping_shl(24);
|
||||
let mut dir = fs::read_dir(&self.base_path).await?;
|
||||
while let Ok(Some(ent)) = dir.next_entry().await {
|
||||
if ent.file_type().await.map_or(false, |t| t.is_dir()) {
|
||||
|
@ -318,7 +281,6 @@ impl Database for FileDatabase {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(networks)
|
||||
}
|
||||
|
||||
|
@ -328,14 +290,12 @@ impl Database for FileDatabase {
|
|||
// FileDatabase stores networks by their "network number" and automatically adapts their IDs
|
||||
// if the controller's identity changes. This is done to make it easy to just clone networks,
|
||||
// including storing them in "git."
|
||||
if let Some(controller_address) = self.get_controller_address() {
|
||||
let network_id_should_be = network.id.change_network_controller(controller_address);
|
||||
let network_id_should_be = network.id.change_network_controller(self.local_identity.address);
|
||||
if network.id != network_id_should_be {
|
||||
network.id = network_id_should_be;
|
||||
let _ = self.save_network(network.clone(), false).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(network)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,53 +2,41 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::{Arg, Command};
|
||||
|
||||
use zerotier_network_controller::database::Database;
|
||||
use zerotier_network_controller::filedatabase::FileDatabase;
|
||||
use zerotier_network_controller::Controller;
|
||||
|
||||
use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||
use zerotier_utils::exitcode;
|
||||
use zerotier_utils::tokio::runtime::Runtime;
|
||||
use zerotier_vl1_service::VL1Service;
|
||||
|
||||
async fn run(database: Arc<dyn Database>, runtime: &Runtime) -> i32 {
|
||||
let handler = Controller::new(database.clone(), runtime.handle().clone()).await;
|
||||
if handler.is_err() {
|
||||
eprintln!("FATAL: error initializing handler: {}", handler.err().unwrap().to_string());
|
||||
async fn run(database: Arc<impl Database>, runtime: &Runtime) -> i32 {
|
||||
match Controller::new(database.clone(), runtime.handle().clone()).await {
|
||||
Err(err) => {
|
||||
eprintln!("FATAL: error initializing handler: {}", err.to_string());
|
||||
exitcode::ERR_CONFIG
|
||||
} else {
|
||||
let handler = handler.unwrap();
|
||||
|
||||
let svc = VL1Service::new(
|
||||
database.clone(),
|
||||
handler.clone(),
|
||||
handler.clone(),
|
||||
zerotier_vl1_service::VL1Settings::default(),
|
||||
);
|
||||
|
||||
if svc.is_ok() {
|
||||
let svc = svc.unwrap();
|
||||
}
|
||||
Ok(handler) => match VL1Service::new(database.clone(), handler.clone(), zerotier_vl1_service::VL1Settings::default()) {
|
||||
Err(err) => {
|
||||
eprintln!("FATAL: error launching service: {}", err.to_string());
|
||||
exitcode::ERR_IOERR
|
||||
}
|
||||
Ok(svc) => {
|
||||
svc.node().init_default_roots();
|
||||
|
||||
handler.start(&svc).await;
|
||||
|
||||
zerotier_utils::wait_for_process_abort();
|
||||
println!("Terminate signal received, shutting down...");
|
||||
exitcode::OK
|
||||
} else {
|
||||
eprintln!("FATAL: error launching service: {}", svc.err().unwrap().to_string());
|
||||
exitcode::ERR_IOERR
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const REQUIRE_ONE_OF_ARGS: [&'static str; 2] = ["postgres", "filedb"];
|
||||
let global_args = Command::new("zerotier-controller")
|
||||
let global_args = clap::Command::new("zerotier-controller")
|
||||
.arg(
|
||||
Arg::new("filedb")
|
||||
clap::Arg::new("filedb")
|
||||
.short('f')
|
||||
.long("filedb")
|
||||
.takes_value(true)
|
||||
|
@ -58,7 +46,7 @@ fn main() {
|
|||
.required_unless_present_any(&REQUIRE_ONE_OF_ARGS),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("postgres")
|
||||
clap::Arg::new("postgres")
|
||||
.short('p')
|
||||
.long("postgres")
|
||||
.takes_value(true)
|
||||
|
|
|
@ -10,9 +10,9 @@ use tokio_postgres::types::Type;
|
|||
use tokio_postgres::{Client, Statement};
|
||||
|
||||
use zerotier_crypto::secure_eq;
|
||||
use zerotier_crypto::verified::Verified;
|
||||
use zerotier_crypto::typestate::Valid;
|
||||
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress, NodeStorage};
|
||||
use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress};
|
||||
use zerotier_network_hypervisor::vl2::networkconfig::IpRoute;
|
||||
use zerotier_network_hypervisor::vl2::rule::Rule;
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
|
@ -22,6 +22,7 @@ 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};
|
||||
|
@ -136,7 +137,7 @@ impl<'a> Drop for ConnectionHolder<'a> {
|
|||
|
||||
pub struct PostgresDatabase {
|
||||
local_controller_id_str: String,
|
||||
local_identity: Verified<Identity>,
|
||||
local_identity: Valid<Identity>,
|
||||
connections: Mutex<(Vec<Box<PostgresConnection>>, Sender<()>)>,
|
||||
postgres_path: String,
|
||||
runtime: Handle,
|
||||
|
@ -147,7 +148,7 @@ impl PostgresDatabase {
|
|||
runtime: Handle,
|
||||
postgres_path: String,
|
||||
num_connections: usize,
|
||||
local_identity: Verified<Identity>,
|
||||
local_identity: Valid<Identity>,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
assert!(num_connections > 0);
|
||||
let (sender, _) = channel(4096);
|
||||
|
@ -187,14 +188,13 @@ impl PostgresDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
impl NodeStorage for PostgresDatabase {
|
||||
fn load_node_identity(&self) -> Option<Verified<Identity>> {
|
||||
impl VL1DataStorage for PostgresDatabase {
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>> {
|
||||
Some(self.local_identity.clone())
|
||||
}
|
||||
|
||||
fn save_node_identity(&self, _: &Verified<Identity>) {
|
||||
eprintln!("FATAL: NodeStorage::save_node_identity() not implemented in PostgresDatabase, identity must be pregenerated");
|
||||
panic!();
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool {
|
||||
panic!("local identity saving not supported by PostgresDatabase")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ pub mod poly1305;
|
|||
pub mod random;
|
||||
pub mod salsa;
|
||||
pub mod secret;
|
||||
pub mod verified;
|
||||
pub mod typestate;
|
||||
pub mod x25519;
|
||||
|
||||
pub const ZEROES: [u8; 64] = [0_u8; 64];
|
||||
|
|
|
@ -4,31 +4,25 @@ use std::fmt::Debug;
|
|||
use std::hash::Hash;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// A zero-overhead typestate indicating that a credential has been verified as valid.
|
||||
///
|
||||
/// What this means is obviously specific to the credential.
|
||||
///
|
||||
/// The purpose of this is to make code more self-documenting and make it harder to accidentally
|
||||
/// use an unverified/unvalidated credential (or other security critical object) where a verified
|
||||
/// one is required.
|
||||
/// Typestate indicating that a credential or other object has been internally validated.
|
||||
#[repr(transparent)]
|
||||
pub struct Verified<T>(T);
|
||||
pub struct Valid<T>(T);
|
||||
|
||||
impl<T> AsRef<T> for Verified<T> {
|
||||
impl<T> AsRef<T> for Valid<T> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for Verified<T> {
|
||||
impl<T> AsMut<T> for Valid<T> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Verified<T> {
|
||||
impl<T> Deref for Valid<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -37,14 +31,14 @@ impl<T> Deref for Verified<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Verified<T> {
|
||||
impl<T> DerefMut for Valid<T> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Verified<T>
|
||||
impl<T> Clone for Valid<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
|
@ -54,7 +48,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Verified<T>
|
||||
impl<T> PartialEq for Valid<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
|
@ -64,9 +58,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Verified<T> where T: Eq {}
|
||||
impl<T> Eq for Valid<T> where T: Eq {}
|
||||
|
||||
impl<T> Ord for Verified<T>
|
||||
impl<T> Ord for Valid<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
|
@ -76,7 +70,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd for Verified<T>
|
||||
impl<T> PartialOrd for Valid<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
|
@ -86,7 +80,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Hash for Verified<T>
|
||||
impl<T> Hash for Valid<T>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
|
@ -96,7 +90,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Verified<T>
|
||||
impl<T> Debug for Valid<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
|
@ -106,7 +100,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Verified<T> {
|
||||
impl<T> Valid<T> {
|
||||
/// Strip the Verified typestate off this object.
|
||||
#[inline(always)]
|
||||
pub fn unwrap(self) -> T {
|
|
@ -9,11 +9,11 @@ use std::str::FromStr;
|
|||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use zerotier_crypto::{hash::*, secure_eq};
|
||||
use zerotier_crypto::p384::*;
|
||||
use zerotier_crypto::salsa::Salsa;
|
||||
use zerotier_crypto::secret::Secret;
|
||||
use zerotier_crypto::x25519::*;
|
||||
use zerotier_crypto::{hash::*, secure_eq};
|
||||
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::buffer::Buffer;
|
||||
|
@ -23,7 +23,7 @@ use zerotier_utils::{base64_decode_url_nopad, base64_encode_url_nopad, hex};
|
|||
|
||||
use crate::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::Verified;
|
||||
use crate::vl1::Valid;
|
||||
|
||||
/// Current maximum size for an identity signature.
|
||||
pub const IDENTITY_MAX_SIGNATURE_SIZE: usize = P384_ECDSA_SIGNATURE_SIZE + 1;
|
||||
|
@ -166,7 +166,7 @@ impl Identity {
|
|||
const FLAG_INCLUDES_SECRETS: u8 = 0x80;
|
||||
|
||||
/// Generate a new identity.
|
||||
pub fn generate() -> Verified<Self> {
|
||||
pub fn generate() -> Valid<Self> {
|
||||
// First generate an identity with just x25519 keys and derive its address.
|
||||
let mut sha = SHA512::new();
|
||||
let ed25519 = Ed25519KeyPair::generate();
|
||||
|
@ -206,7 +206,7 @@ impl Identity {
|
|||
assert!(id.upgrade().is_ok());
|
||||
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
|
||||
|
||||
Verified::assume_verified(id)
|
||||
Valid::assume_verified(id)
|
||||
}
|
||||
|
||||
/// Upgrade older x25519-only identities to hybrid identities with both x25519 and NIST P-384 curves.
|
||||
|
@ -290,7 +290,7 @@ impl Identity {
|
|||
/// Locally check the validity of this identity.
|
||||
///
|
||||
/// This is somewhat time consuming due to the memory-intensive work algorithm.
|
||||
pub fn validate(self) -> Option<Verified<Self>> {
|
||||
pub fn validate(self) -> Option<Valid<Self>> {
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(
|
||||
ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE,
|
||||
|
@ -321,7 +321,7 @@ impl Identity {
|
|||
zt_address_derivation_work_function(&mut digest);
|
||||
|
||||
return if digest[0] < IDENTITY_POW_THRESHOLD && Address::from_bytes(&digest[59..64]).map_or(false, |a| a == self.address) {
|
||||
Some(Verified::assume_verified(self))
|
||||
Some(Valid::assume_verified(self))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -345,7 +345,7 @@ impl Identity {
|
|||
/// For new identities with P-384 keys a hybrid agreement is performed using both X25519 and NIST P-384 ECDH.
|
||||
/// The final key is derived as HMAC(x25519 secret, p-384 secret) to yield a FIPS-compliant key agreement with
|
||||
/// the X25519 secret being used as a "salt" as far as FIPS is concerned.
|
||||
pub fn agree(&self, other: &Verified<Identity>) -> Option<Secret<64>> {
|
||||
pub fn agree(&self, other: &Valid<Identity>) -> Option<Secret<64>> {
|
||||
if let Some(secret) = self.secret.as_ref() {
|
||||
let c25519_secret: Secret<64> = Secret(SHA512::hash(&secret.x25519.agree(&other.x25519).0));
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ pub use event::Event;
|
|||
pub use identity::Identity;
|
||||
pub use inetaddress::InetAddress;
|
||||
pub use mac::MAC;
|
||||
pub use node::{DummyInnerProtocol, HostSystem, InnerProtocol, Node, NodeStorage, PacketHandlerResult, VL1AuthProvider};
|
||||
pub use node::{ApplicationLayer, InnerProtocolLayer, Node, PacketHandlerResult};
|
||||
pub use path::Path;
|
||||
pub use peer::Peer;
|
||||
pub use rootset::{Root, RootSet};
|
||||
|
||||
pub use zerotier_crypto::verified::Verified;
|
||||
pub use zerotier_crypto::typestate::Valid;
|
||||
|
||||
#[cfg(feature = "debug_events")]
|
||||
#[allow(unused_macros)]
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::vl1::identity::Identity;
|
|||
use crate::vl1::path::{Path, PathServiceResult};
|
||||
use crate::vl1::peer::Peer;
|
||||
use crate::vl1::rootset::RootSet;
|
||||
use crate::vl1::Verified;
|
||||
use crate::vl1::Valid;
|
||||
|
||||
use zerotier_crypto::random;
|
||||
use zerotier_utils::error::InvalidParameterError;
|
||||
|
@ -27,42 +27,11 @@ use zerotier_utils::marshalable::Marshalable;
|
|||
use zerotier_utils::ringbuffer::RingBuffer;
|
||||
use zerotier_utils::thing::Thing;
|
||||
|
||||
/// Trait providing VL1 authentication functions to determine which nodes we should talk to.
|
||||
/// Interface trait to be implemented by code that's using the ZeroTier network hypervisor.
|
||||
///
|
||||
/// This is included in HostSystem but is provided as a separate trait to make it easy for
|
||||
/// implementers of HostSystem to break this out and allow a user to specify it.
|
||||
pub trait VL1AuthProvider: Sync + Send {
|
||||
/// Check if this node should respond to messages from a given peer at all.
|
||||
///
|
||||
/// If this returns false, the node simply drops messages on the floor and refuses
|
||||
/// to init V2 sessions.
|
||||
fn should_respond_to(&self, id: &Verified<Identity>) -> bool;
|
||||
|
||||
/// Check if this node has any trust relationship with the provided identity.
|
||||
///
|
||||
/// This should return true if there is any special trust relationship such as mutual
|
||||
/// membership in a network or for controllers the peer's membership in any network
|
||||
/// they control.
|
||||
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool;
|
||||
}
|
||||
|
||||
/// Trait to be implemented by outside code to provide object storage to VL1
|
||||
///
|
||||
/// This is included in HostSystem but is provided as a separate trait to make it easy for
|
||||
/// implementers of HostSystem to break this out and allow a user to specify it.
|
||||
pub trait NodeStorage: Sync + Send {
|
||||
/// Load this node's identity from the data store.
|
||||
fn load_node_identity(&self) -> Option<Verified<Identity>>;
|
||||
|
||||
/// Save this node's identity to the data store.
|
||||
fn save_node_identity(&self, id: &Verified<Identity>);
|
||||
}
|
||||
|
||||
/// Trait implemented by external code to handle events and provide an interface to the system or application.
|
||||
pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
|
||||
/// Type for implementation of NodeStorage.
|
||||
type Storage: NodeStorage + ?Sized;
|
||||
|
||||
/// This is analogous to a C struct full of function pointers to callbacks along with some
|
||||
/// associated type definitions.
|
||||
pub trait ApplicationLayer: Sync + Send {
|
||||
/// Type for local system sockets.
|
||||
type LocalSocket: Sync + Send + Hash + PartialEq + Eq + Clone + ToString + Sized + 'static;
|
||||
|
||||
|
@ -72,8 +41,11 @@ pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
|
|||
/// A VL1 level event occurred.
|
||||
fn event(&self, event: Event);
|
||||
|
||||
/// Get a reference to the local storage implementation at this host.
|
||||
fn storage(&self) -> &Self::Storage;
|
||||
/// Load this node's identity from the data store.
|
||||
fn load_node_identity(&self) -> Option<Valid<Identity>>;
|
||||
|
||||
/// Save this node's identity to the data store, returning true on success.
|
||||
fn save_node_identity(&self, id: &Valid<Identity>) -> bool;
|
||||
|
||||
/// Get a pooled packet buffer for internal use.
|
||||
fn get_buffer(&self) -> PooledPacketBuffer;
|
||||
|
@ -110,12 +82,12 @@ pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
|
|||
///
|
||||
/// The default implementation always returns true.
|
||||
#[allow(unused_variables)]
|
||||
fn should_use_physical_path<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn should_use_physical_path<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
id: &Verified<Identity>,
|
||||
id: &Valid<Identity>,
|
||||
endpoint: &Endpoint,
|
||||
local_socket: Option<&HostSystemImpl::LocalSocket>,
|
||||
local_interface: Option<&HostSystemImpl::LocalInterface>,
|
||||
local_socket: Option<&Application::LocalSocket>,
|
||||
local_interface: Option<&Application::LocalInterface>,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
@ -124,16 +96,10 @@ pub trait HostSystem: VL1AuthProvider + NodeStorage + 'static {
|
|||
///
|
||||
/// The default implementation always returns None.
|
||||
#[allow(unused_variables)]
|
||||
fn get_path_hints<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn get_path_hints<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
id: &Verified<Identity>,
|
||||
) -> Option<
|
||||
Vec<(
|
||||
Endpoint,
|
||||
Option<HostSystemImpl::LocalSocket>,
|
||||
Option<HostSystemImpl::LocalInterface>,
|
||||
)>,
|
||||
> {
|
||||
id: &Valid<Identity>,
|
||||
) -> Option<Vec<(Endpoint, Option<Application::LocalSocket>, Option<Application::LocalInterface>)>> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -163,14 +129,32 @@ pub enum PacketHandlerResult {
|
|||
/// This is implemented by Switch in VL2. It's usually not used outside of VL2 in the core but
|
||||
/// it could also be implemented for testing or "off label" use of VL1 to carry different protocols.
|
||||
#[allow(unused)]
|
||||
pub trait InnerProtocol: Sync + Send {
|
||||
pub trait InnerProtocolLayer: Sync + Send {
|
||||
/// Check if this node should respond to messages from a given peer at all.
|
||||
///
|
||||
/// The default implementation always returns true.
|
||||
fn should_respond_to(&self, id: &Valid<Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Check if this node has any trust relationship with the provided identity.
|
||||
///
|
||||
/// This should return true if there is any special trust relationship. It controls things
|
||||
/// like sharing of detailed P2P connectivity data, which should be limited to peers with
|
||||
/// some privileged relationship like mutual membership in a network.
|
||||
///
|
||||
/// The default implementation always returns true.
|
||||
fn has_trust_relationship(&self, id: &Valid<Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Handle a packet, returning true if it was handled by the next layer.
|
||||
///
|
||||
/// Do not attempt to handle OK or ERROR. Instead implement handle_ok() and handle_error().
|
||||
/// The default version returns NotHandled.
|
||||
fn handle_packet<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_packet<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -185,9 +169,9 @@ pub trait InnerProtocol: Sync + Send {
|
|||
|
||||
/// Handle errors, returning true if the error was recognized.
|
||||
/// The default version returns NotHandled.
|
||||
fn handle_error<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_error<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -204,9 +188,9 @@ pub trait InnerProtocol: Sync + Send {
|
|||
|
||||
/// Handle an OK, returning true if the OK was recognized.
|
||||
/// The default version returns NotHandled.
|
||||
fn handle_ok<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_ok<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -221,12 +205,9 @@ pub trait InnerProtocol: Sync + Send {
|
|||
}
|
||||
}
|
||||
|
||||
/// How often to check the root cluster definitions against the root list and update.
|
||||
const ROOT_SYNC_INTERVAL_MS: i64 = 1000;
|
||||
|
||||
struct RootInfo {
|
||||
/// Root sets to which we are a member.
|
||||
sets: HashMap<String, Verified<RootSet>>,
|
||||
sets: HashMap<String, Valid<RootSet>>,
|
||||
|
||||
/// Root peers and their statically defined endpoints (from root sets).
|
||||
roots: HashMap<Arc<Peer>, Vec<Endpoint>>,
|
||||
|
@ -242,7 +223,9 @@ struct RootInfo {
|
|||
online: bool,
|
||||
}
|
||||
|
||||
/// Interval gate objects used ot fire off background tasks, see do_background_tasks().
|
||||
/// How often to check the root cluster definitions against the root list and update.
|
||||
const ROOT_SYNC_INTERVAL_MS: i64 = 1000;
|
||||
|
||||
#[derive(Default)]
|
||||
struct BackgroundTaskIntervals {
|
||||
root_sync: IntervalGate<{ ROOT_SYNC_INTERVAL_MS }>,
|
||||
|
@ -253,7 +236,6 @@ struct BackgroundTaskIntervals {
|
|||
whois_queue_retry: IntervalGate<{ WHOIS_RETRY_INTERVAL }>,
|
||||
}
|
||||
|
||||
/// WHOIS requests and any packets that are waiting on them to be decrypted and authenticated.
|
||||
struct WhoisQueueItem {
|
||||
v1_proto_waiting_packets: RingBuffer<(Weak<Path>, PooledPacketBuffer), WHOIS_MAX_WAITING_PACKETS>,
|
||||
last_retry_time: i64,
|
||||
|
@ -261,24 +243,21 @@ struct WhoisQueueItem {
|
|||
}
|
||||
|
||||
const PATH_MAP_SIZE: usize = std::mem::size_of::<HashMap<[u8; std::mem::size_of::<Endpoint>() + 128], Arc<Path>>>();
|
||||
type PathMap<HostSystemImpl> = HashMap<PathKey<'static, 'static, HostSystemImpl>, Arc<Path>>;
|
||||
type PathMap<LocalSocket> = HashMap<PathKey<'static, 'static, LocalSocket>, Arc<Path>>;
|
||||
|
||||
/// A ZeroTier VL1 node that can communicate securely with the ZeroTier peer-to-peer network.
|
||||
pub struct Node {
|
||||
/// A random ID generated to identify this particular running instance.
|
||||
///
|
||||
/// This can be used to implement multi-homing by allowing remote nodes to distinguish instances
|
||||
/// that share an identity.
|
||||
pub instance_id: [u8; 16],
|
||||
|
||||
/// This node's identity and permanent keys.
|
||||
pub identity: Verified<Identity>,
|
||||
pub identity: Valid<Identity>,
|
||||
|
||||
/// Interval latches for periodic background tasks.
|
||||
intervals: Mutex<BackgroundTaskIntervals>,
|
||||
|
||||
/// Canonicalized network paths, held as Weak<> to be automatically cleaned when no longer in use.
|
||||
paths: RwLock<Thing<PATH_MAP_SIZE>>, // holds a PathMap<> but as a Thing<> to hide HostSystemImpl template parameter
|
||||
paths: RwLock<Thing<PATH_MAP_SIZE>>, // holds a PathMap<> but as a Thing<> to hide ApplicationLayer template parameter
|
||||
|
||||
/// Peers with which we are currently communicating.
|
||||
peers: RwLock<HashMap<Address, Arc<Peer>>>,
|
||||
|
@ -294,20 +273,20 @@ pub struct Node {
|
|||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new<HostSystemImpl: HostSystem + ?Sized>(
|
||||
host_system: &HostSystemImpl,
|
||||
pub fn new<Application: ApplicationLayer + ?Sized>(
|
||||
app: &Application,
|
||||
auto_generate_identity: bool,
|
||||
auto_upgrade_identity: bool,
|
||||
) -> Result<Self, InvalidParameterError> {
|
||||
let mut id = {
|
||||
let id = host_system.storage().load_node_identity();
|
||||
let id = app.load_node_identity();
|
||||
if id.is_none() {
|
||||
if !auto_generate_identity {
|
||||
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
|
||||
} else {
|
||||
let id = Identity::generate();
|
||||
host_system.event(Event::IdentityAutoGenerated(id.as_ref().clone()));
|
||||
host_system.storage().save_node_identity(&id);
|
||||
app.event(Event::IdentityAutoGenerated(id.as_ref().clone()));
|
||||
app.save_node_identity(&id);
|
||||
id
|
||||
}
|
||||
} else {
|
||||
|
@ -318,18 +297,18 @@ impl Node {
|
|||
if auto_upgrade_identity {
|
||||
let old = id.clone();
|
||||
if id.upgrade()? {
|
||||
host_system.storage().save_node_identity(&id);
|
||||
host_system.event(Event::IdentityAutoUpgraded(old.unwrap(), id.as_ref().clone()));
|
||||
app.save_node_identity(&id);
|
||||
app.event(Event::IdentityAutoUpgraded(old.unwrap(), id.as_ref().clone()));
|
||||
}
|
||||
}
|
||||
|
||||
debug_event!(host_system, "[vl1] loaded identity {}", id.to_string());
|
||||
debug_event!(app, "[vl1] loaded identity {}", id.to_string());
|
||||
|
||||
Ok(Self {
|
||||
instance_id: random::get_bytes_secure(),
|
||||
identity: id,
|
||||
intervals: Mutex::new(BackgroundTaskIntervals::default()),
|
||||
paths: RwLock::new(Thing::new(PathMap::<HostSystemImpl>::new())),
|
||||
paths: RwLock::new(Thing::new(PathMap::<Application::LocalSocket>::new())),
|
||||
peers: RwLock::new(HashMap::new()),
|
||||
roots: RwLock::new(RootInfo {
|
||||
sets: HashMap::new(),
|
||||
|
@ -343,31 +322,37 @@ impl Node {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> {
|
||||
self.peers.read().unwrap().get(&a).cloned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_online(&self) -> bool {
|
||||
self.roots.read().unwrap().online
|
||||
}
|
||||
|
||||
/// Get the current "best" root from among this node's trusted roots.
|
||||
#[inline]
|
||||
pub fn best_root(&self) -> Option<Arc<Peer>> {
|
||||
self.best_root.read().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Check whether a peer is a root according to any root set trusted by this node.
|
||||
#[inline]
|
||||
pub fn is_peer_root(&self, peer: &Peer) -> bool {
|
||||
self.roots.read().unwrap().roots.keys().any(|p| p.identity.eq(&peer.identity))
|
||||
}
|
||||
|
||||
/// Returns true if this node is a member of a root set (that it knows about).
|
||||
#[inline]
|
||||
pub fn this_node_is_root(&self) -> bool {
|
||||
self.roots.read().unwrap().this_root_sets.is_some()
|
||||
}
|
||||
|
||||
/// Add a new root set or update the existing root set if the new root set is newer and otherwise matches.
|
||||
pub fn add_update_root_set(&self, rs: Verified<RootSet>) -> bool {
|
||||
#[inline]
|
||||
pub fn add_update_root_set(&self, rs: Valid<RootSet>) -> bool {
|
||||
let mut roots = self.roots.write().unwrap();
|
||||
if let Some(entry) = roots.sets.get_mut(&rs.name) {
|
||||
if rs.should_replace(entry) {
|
||||
|
@ -385,11 +370,13 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Returns whether or not this node has any root sets defined.
|
||||
#[inline]
|
||||
pub fn has_roots_defined(&self) -> bool {
|
||||
self.roots.read().unwrap().sets.iter().any(|rs| !rs.1.members.is_empty())
|
||||
}
|
||||
|
||||
/// Initialize with default roots if there are no roots defined, otherwise do nothing.
|
||||
#[inline]
|
||||
pub fn init_default_roots(&self) -> bool {
|
||||
if !self.has_roots_defined() {
|
||||
self.add_update_root_set(RootSet::zerotier_default())
|
||||
|
@ -399,69 +386,28 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Get the root sets that this node trusts.
|
||||
#[inline]
|
||||
pub fn root_sets(&self) -> Vec<RootSet> {
|
||||
self.roots.read().unwrap().sets.values().cloned().map(|s| s.unwrap()).collect()
|
||||
}
|
||||
|
||||
pub fn do_background_tasks<HostSystemImpl: HostSystem + ?Sized>(&self, host_system: &HostSystemImpl) -> Duration {
|
||||
pub fn do_background_tasks<Application: ApplicationLayer + ?Sized>(&self, app: &Application) -> Duration {
|
||||
const INTERVAL_MS: i64 = 1000;
|
||||
const INTERVAL: Duration = Duration::from_millis(INTERVAL_MS as u64);
|
||||
let time_ticks = host_system.time_ticks();
|
||||
let time_ticks = app.time_ticks();
|
||||
|
||||
let (root_sync, root_hello, mut root_spam_hello, peer_service, path_service, whois_queue_retry) = {
|
||||
let (root_sync, root_hello, root_spam_hello, peer_service, path_service, whois_queue_retry) = {
|
||||
let mut intervals = self.intervals.lock().unwrap();
|
||||
(
|
||||
intervals.root_sync.gate(time_ticks),
|
||||
intervals.root_hello.gate(time_ticks),
|
||||
intervals.root_spam_hello.gate(time_ticks),
|
||||
intervals.root_spam_hello.gate(time_ticks) && !self.is_online(),
|
||||
intervals.peer_service.gate(time_ticks),
|
||||
intervals.path_service.gate(time_ticks),
|
||||
intervals.whois_queue_retry.gate(time_ticks),
|
||||
)
|
||||
};
|
||||
|
||||
// We only "spam" (try to contact roots more often) if we are offline.
|
||||
if root_spam_hello {
|
||||
root_spam_hello = !self.is_online();
|
||||
}
|
||||
|
||||
/*
|
||||
debug_event!(
|
||||
host_system,
|
||||
"[vl1] do_background_tasks:{}{}{}{}{}{} ----",
|
||||
if root_sync {
|
||||
" root_sync"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if root_hello {
|
||||
" root_hello"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if root_spam_hello {
|
||||
" root_spam_hello"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if peer_service {
|
||||
" peer_service"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if path_service {
|
||||
" path_service"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if whois_queue_retry {
|
||||
" whois_queue_retry"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
*/
|
||||
|
||||
if root_sync {
|
||||
if {
|
||||
let mut roots = self.roots.write().unwrap();
|
||||
|
@ -472,7 +418,7 @@ impl Node {
|
|||
false
|
||||
}
|
||||
} {
|
||||
debug_event!(host_system, "[vl1] root sets modified, synchronizing internal data structures");
|
||||
debug_event!(app, "[vl1] root sets modified, synchronizing internal data structures");
|
||||
|
||||
let (mut old_root_identities, address_collisions, new_roots, bad_identities, my_root_sets) = {
|
||||
let roots = self.roots.read().unwrap();
|
||||
|
@ -513,7 +459,7 @@ impl Node {
|
|||
if m.endpoints.is_some() && !address_collisions.contains(&m.identity.address) && !m.identity.eq(&self.identity)
|
||||
{
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] examining root {} with {} endpoints",
|
||||
m.identity.address.to_string(),
|
||||
m.endpoints.as_ref().map_or(0, |e| e.len())
|
||||
|
@ -522,8 +468,7 @@ impl Node {
|
|||
if let Some(peer) = peers.get(&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, Verified::assume_verified(m.identity.clone()), time_ticks)
|
||||
{
|
||||
if let Some(peer) = Peer::new(&self.identity, Valid::assume_verified(m.identity.clone()), time_ticks) {
|
||||
drop(peers);
|
||||
new_roots.insert(
|
||||
self.peers
|
||||
|
@ -546,13 +491,13 @@ impl Node {
|
|||
};
|
||||
|
||||
for c in address_collisions.iter() {
|
||||
host_system.event(Event::SecurityWarning(format!(
|
||||
app.event(Event::SecurityWarning(format!(
|
||||
"address/identity collision in root sets! address {} collides across root sets or with an existing peer and is being ignored as a root!",
|
||||
c.to_string()
|
||||
)));
|
||||
}
|
||||
for i in bad_identities.iter() {
|
||||
host_system.event(Event::SecurityWarning(format!(
|
||||
app.event(Event::SecurityWarning(format!(
|
||||
"bad identity detected for address {} in at least one root set, ignoring (error creating peer object)",
|
||||
i.address.to_string()
|
||||
)));
|
||||
|
@ -566,7 +511,7 @@ impl Node {
|
|||
let mut roots = self.roots.write().unwrap();
|
||||
roots.roots = new_roots;
|
||||
roots.this_root_sets = my_root_sets;
|
||||
host_system.event(Event::UpdatedRoots(old_root_identities, new_root_identities));
|
||||
app.event(Event::UpdatedRoots(old_root_identities, new_root_identities));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,7 +537,7 @@ impl Node {
|
|||
let mut best_root = self.best_root.write().unwrap();
|
||||
if let Some(best_root) = best_root.as_mut() {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] selected new best root: {} (replaced {})",
|
||||
best.identity.address.to_string(),
|
||||
best_root.identity.address.to_string()
|
||||
|
@ -600,7 +545,7 @@ impl Node {
|
|||
*best_root = best.clone();
|
||||
} else {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] selected new best root: {} (was empty)",
|
||||
best.identity.address.to_string()
|
||||
);
|
||||
|
@ -610,7 +555,7 @@ impl Node {
|
|||
} else {
|
||||
if let Some(old_best) = self.best_root.write().unwrap().take() {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] selected new best root: NONE (replaced {})",
|
||||
old_best.identity.address.to_string()
|
||||
);
|
||||
|
@ -622,12 +567,12 @@ impl Node {
|
|||
if !roots.online {
|
||||
drop(roots);
|
||||
self.roots.write().unwrap().online = true;
|
||||
host_system.event(Event::Online(true));
|
||||
app.event(Event::Online(true));
|
||||
}
|
||||
} else if roots.online {
|
||||
drop(roots);
|
||||
self.roots.write().unwrap().online = false;
|
||||
host_system.event(Event::Online(false));
|
||||
app.event(Event::Online(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -648,14 +593,14 @@ impl Node {
|
|||
for (root, endpoints) in roots.iter() {
|
||||
for ep in endpoints.iter() {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"sending HELLO to root {} (root interval: {})",
|
||||
root.identity.address.to_string(),
|
||||
ROOT_HELLO_INTERVAL
|
||||
);
|
||||
let root = root.clone();
|
||||
let ep = ep.clone();
|
||||
root.send_hello(host_system, self, Some(&ep));
|
||||
root.send_hello(app, self, Some(&ep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,7 +612,7 @@ impl Node {
|
|||
{
|
||||
let roots = self.roots.read().unwrap();
|
||||
for (a, peer) in self.peers.read().unwrap().iter() {
|
||||
if !peer.service(host_system, self, time_ticks) && !roots.roots.contains_key(peer) {
|
||||
if !peer.service(app, self, time_ticks) && !roots.roots.contains_key(peer) {
|
||||
dead_peers.push(*a);
|
||||
}
|
||||
}
|
||||
|
@ -682,8 +627,8 @@ impl Node {
|
|||
let mut need_keepalive = Vec::new();
|
||||
|
||||
// First check all paths in read mode to avoid blocking the entire node.
|
||||
for (k, path) in self.paths.read().unwrap().get::<PathMap<HostSystemImpl>>().iter() {
|
||||
if host_system.local_socket_is_valid(k.local_socket()) {
|
||||
for (k, path) in self.paths.read().unwrap().get::<PathMap<Application::LocalSocket>>().iter() {
|
||||
if app.local_socket_is_valid(k.local_socket()) {
|
||||
match path.service(time_ticks) {
|
||||
PathServiceResult::Ok => {}
|
||||
PathServiceResult::Dead => dead_paths.push(k.to_copied()),
|
||||
|
@ -696,16 +641,20 @@ impl Node {
|
|||
|
||||
// Lock in write mode and remove dead paths, doing so piecemeal to again avoid blocking.
|
||||
for dp in dead_paths.iter() {
|
||||
self.paths.write().unwrap().get_mut::<PathMap<HostSystemImpl>>().remove(dp);
|
||||
self.paths
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut::<PathMap<Application::LocalSocket>>()
|
||||
.remove(dp);
|
||||
}
|
||||
|
||||
// Finally run keepalive sends as a batch.
|
||||
let keepalive_buf = [time_ticks as u8]; // just an arbitrary byte, no significance
|
||||
for p in need_keepalive.iter() {
|
||||
host_system.wire_send(
|
||||
app.wire_send(
|
||||
&p.endpoint,
|
||||
Some(p.local_socket::<HostSystemImpl>()),
|
||||
Some(p.local_interface::<HostSystemImpl>()),
|
||||
Some(p.local_socket::<Application>()),
|
||||
Some(p.local_interface::<Application>()),
|
||||
&keepalive_buf,
|
||||
0,
|
||||
);
|
||||
|
@ -727,26 +676,25 @@ impl Node {
|
|||
need_whois
|
||||
};
|
||||
if !need_whois.is_empty() {
|
||||
self.send_whois(host_system, need_whois.as_slice(), time_ticks);
|
||||
self.send_whois(app, need_whois.as_slice(), time_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
//debug_event!(host_system, "[vl1] do_background_tasks DONE ----");
|
||||
INTERVAL
|
||||
}
|
||||
|
||||
pub fn handle_incoming_physical_packet<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
pub fn handle_incoming_physical_packet<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
source_endpoint: &Endpoint,
|
||||
source_local_socket: &HostSystemImpl::LocalSocket,
|
||||
source_local_interface: &HostSystemImpl::LocalInterface,
|
||||
source_local_socket: &Application::LocalSocket,
|
||||
source_local_interface: &Application::LocalInterface,
|
||||
time_ticks: i64,
|
||||
mut packet: PooledPacketBuffer,
|
||||
) {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} -> #{} {}->{} length {} (on socket {}@{})",
|
||||
source_endpoint.to_string(),
|
||||
packet
|
||||
|
@ -779,15 +727,14 @@ impl Node {
|
|||
|
||||
if dest == self.identity.address {
|
||||
let fragment_header = &*fragment_header; // discard mut
|
||||
let path =
|
||||
self.canonical_path::<HostSystemImpl>(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
||||
let path = self.canonical_path::<Application>(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
||||
path.log_receive_anything(time_ticks);
|
||||
|
||||
if fragment_header.is_fragment() {
|
||||
#[cfg(debug_assertions)]
|
||||
let fragment_header_id = u64::from_be_bytes(fragment_header.id);
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] [v1] #{:0>16x} fragment {} of {} received",
|
||||
u64::from_be_bytes(fragment_header.id),
|
||||
fragment_header.fragment_no(),
|
||||
|
@ -803,14 +750,14 @@ impl Node {
|
|||
) {
|
||||
if let Some(frag0) = assembled_packet.frags[0].as_ref() {
|
||||
#[cfg(debug_assertions)]
|
||||
debug_event!(host_system, "[vl1] [v1] #{:0>16x} packet fully assembled!", fragment_header_id);
|
||||
debug_event!(app, "[vl1] [v1] #{:0>16x} packet fully assembled!", fragment_header_id);
|
||||
|
||||
if let Ok(packet_header) = frag0.struct_at::<v1::PacketHeader>(0) {
|
||||
if let Some(source) = Address::from_bytes(&packet_header.src) {
|
||||
if let Some(peer) = self.peer(source) {
|
||||
peer.v1_proto_receive(
|
||||
self,
|
||||
host_system,
|
||||
app,
|
||||
inner,
|
||||
time_ticks,
|
||||
&path,
|
||||
|
@ -821,7 +768,7 @@ impl Node {
|
|||
} else {
|
||||
// If WHOIS is needed we need to go ahead and combine the packet so it can be cached
|
||||
// for later processing when a WHOIS reply comes back.
|
||||
let mut combined_packet = host_system.get_buffer();
|
||||
let mut combined_packet = app.get_buffer();
|
||||
let mut ok = combined_packet.append_bytes(frag0.as_bytes()).is_ok();
|
||||
for i in 1..assembled_packet.have {
|
||||
if let Some(f) = assembled_packet.frags[i as usize].as_ref() {
|
||||
|
@ -832,7 +779,7 @@ impl Node {
|
|||
}
|
||||
}
|
||||
if ok {
|
||||
self.whois(host_system, source, Some((Arc::downgrade(&path), combined_packet)), time_ticks);
|
||||
self.whois(app, source, Some((Arc::downgrade(&path), combined_packet)), time_ticks);
|
||||
}
|
||||
}
|
||||
} // else source address invalid
|
||||
|
@ -840,17 +787,13 @@ impl Node {
|
|||
} // else reassembly failed (in a way that shouldn't be possible)
|
||||
} // else packet not fully assembled yet
|
||||
} else if let Ok(packet_header) = packet.struct_at::<v1::PacketHeader>(0) {
|
||||
debug_event!(
|
||||
host_system,
|
||||
"[vl1] [v1] #{:0>16x} is unfragmented",
|
||||
u64::from_be_bytes(packet_header.id)
|
||||
);
|
||||
debug_event!(app, "[vl1] [v1] #{:0>16x} is unfragmented", u64::from_be_bytes(packet_header.id));
|
||||
|
||||
if let Some(source) = Address::from_bytes(&packet_header.src) {
|
||||
if let Some(peer) = self.peer(source) {
|
||||
peer.v1_proto_receive(self, host_system, inner, time_ticks, &path, packet_header, packet.as_ref(), &[]);
|
||||
peer.v1_proto_receive(self, app, inner, time_ticks, &path, packet_header, packet.as_ref(), &[]);
|
||||
} else {
|
||||
self.whois(host_system, source, Some((Arc::downgrade(&path), packet)), time_ticks);
|
||||
self.whois(app, source, Some((Arc::downgrade(&path), packet)), time_ticks);
|
||||
}
|
||||
}
|
||||
} // else not fragment and header incomplete
|
||||
|
@ -866,7 +809,7 @@ impl Node {
|
|||
{
|
||||
debug_packet_id = u64::from_be_bytes(fragment_header.id);
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] [v1] #{:0>16x} forwarding packet fragment to {}",
|
||||
debug_packet_id,
|
||||
dest.to_string()
|
||||
|
@ -874,7 +817,7 @@ impl Node {
|
|||
}
|
||||
if fragment_header.increment_hops() > v1::FORWARD_MAX_HOPS {
|
||||
#[cfg(debug_assertions)]
|
||||
debug_event!(host_system, "[vl1] [v1] #{:0>16x} discarded: max hops exceeded!", debug_packet_id);
|
||||
debug_event!(app, "[vl1] [v1] #{:0>16x} discarded: max hops exceeded!", debug_packet_id);
|
||||
return;
|
||||
}
|
||||
} else if let Ok(packet_header) = packet.struct_mut_at::<v1::PacketHeader>(0) {
|
||||
|
@ -882,7 +825,7 @@ impl Node {
|
|||
{
|
||||
debug_packet_id = u64::from_be_bytes(packet_header.id);
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] [v1] #{:0>16x} forwarding packet to {}",
|
||||
debug_packet_id,
|
||||
dest.to_string()
|
||||
|
@ -891,7 +834,7 @@ impl Node {
|
|||
if packet_header.increment_hops() > v1::FORWARD_MAX_HOPS {
|
||||
#[cfg(debug_assertions)]
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] [v1] #{:0>16x} discarded: max hops exceeded!",
|
||||
u64::from_be_bytes(packet_header.id)
|
||||
);
|
||||
|
@ -903,10 +846,10 @@ impl Node {
|
|||
|
||||
if let Some(peer) = self.peer(dest) {
|
||||
if let Some(forward_path) = peer.direct_path() {
|
||||
host_system.wire_send(
|
||||
app.wire_send(
|
||||
&forward_path.endpoint,
|
||||
Some(forward_path.local_socket::<HostSystemImpl>()),
|
||||
Some(forward_path.local_interface::<HostSystemImpl>()),
|
||||
Some(forward_path.local_socket::<Application>()),
|
||||
Some(forward_path.local_interface::<Application>()),
|
||||
packet.as_bytes(),
|
||||
0,
|
||||
);
|
||||
|
@ -914,7 +857,7 @@ impl Node {
|
|||
peer.last_forward_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
debug_event!(host_system, "[vl1] [v1] #{:0>16x} forwarded successfully", debug_packet_id);
|
||||
debug_event!(app, "[vl1] [v1] #{:0>16x} forwarded successfully", debug_packet_id);
|
||||
}
|
||||
}
|
||||
} // else not for this node and shouldn't be forwarded
|
||||
|
@ -923,9 +866,9 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Enqueue and send a WHOIS query for a given address, adding the supplied packet (if any) to the list to be processed on reply.
|
||||
fn whois<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn whois<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
address: Address,
|
||||
waiting_packet: Option<(Weak<Path>, PooledPacketBuffer)>,
|
||||
time_ticks: i64,
|
||||
|
@ -947,13 +890,13 @@ impl Node {
|
|||
qi.retry_count += 1;
|
||||
}
|
||||
}
|
||||
self.send_whois(host_system, &[address], time_ticks);
|
||||
self.send_whois(app, &[address], time_ticks);
|
||||
}
|
||||
|
||||
/// Send a WHOIS query to the current best root.
|
||||
fn send_whois<HostSystemImpl: HostSystem + ?Sized>(&self, host_system: &HostSystemImpl, mut addresses: &[Address], time_ticks: i64) {
|
||||
fn send_whois<Application: ApplicationLayer + ?Sized>(&self, app: &Application, mut addresses: &[Address], time_ticks: i64) {
|
||||
debug_assert!(!addresses.is_empty());
|
||||
debug_event!(host_system, "[vl1] [v1] sending WHOIS for {}", {
|
||||
debug_event!(app, "[vl1] [v1] sending WHOIS for {}", {
|
||||
let mut tmp = String::new();
|
||||
for a in addresses.iter() {
|
||||
if !tmp.is_empty() {
|
||||
|
@ -966,7 +909,7 @@ impl Node {
|
|||
if let Some(root) = self.best_root() {
|
||||
while !addresses.is_empty() {
|
||||
if !root
|
||||
.send(host_system, self, None, time_ticks, |packet| -> Result<(), Infallible> {
|
||||
.send(app, self, None, time_ticks, |packet| -> Result<(), Infallible> {
|
||||
assert!(packet.append_u8(message_type::VL1_WHOIS).is_ok());
|
||||
while !addresses.is_empty() && (packet.len() + ADDRESS_SIZE) <= UDP_DEFAULT_MTU {
|
||||
assert!(packet.append_bytes_fixed(&addresses[0].to_bytes()).is_ok());
|
||||
|
@ -983,10 +926,10 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Called by Peer when an identity is received from another node, e.g. via OK(WHOIS).
|
||||
pub(crate) fn handle_incoming_identity<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
pub(crate) fn handle_incoming_identity<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
received_identity: Identity,
|
||||
time_ticks: i64,
|
||||
authoritative: bool,
|
||||
|
@ -996,7 +939,7 @@ impl Node {
|
|||
let mut whois_queue = self.whois_queue.lock().unwrap();
|
||||
if let Some(qi) = whois_queue.get_mut(&received_identity.address) {
|
||||
let address = received_identity.address;
|
||||
if host_system.should_respond_to(&received_identity) {
|
||||
if inner.should_respond_to(&received_identity) {
|
||||
let mut peers = self.peers.write().unwrap();
|
||||
if let Some(peer) = peers.get(&address).cloned().or_else(|| {
|
||||
Peer::new(&self.identity, received_identity, time_ticks)
|
||||
|
@ -1007,7 +950,7 @@ impl Node {
|
|||
for p in qi.v1_proto_waiting_packets.iter() {
|
||||
if let Some(path) = p.0.upgrade() {
|
||||
if let Ok(packet_header) = p.1.struct_at::<v1::PacketHeader>(0) {
|
||||
peer.v1_proto_receive(self, host_system, inner, time_ticks, &path, packet_header, &p.1, &[]);
|
||||
peer.v1_proto_receive(self, app, inner, time_ticks, &path, packet_header, &p.1, &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1023,7 +966,8 @@ impl Node {
|
|||
///
|
||||
/// This will only replace an existing root set with a newer one. It won't add a new root set, which must be
|
||||
/// done by an authorized user or administrator not just by a root.
|
||||
pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Verified<RootSet>) {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn on_remote_update_root_set(&self, received_from: &Identity, rs: Valid<RootSet>) {
|
||||
let mut roots = self.roots.write().unwrap();
|
||||
if let Some(entry) = roots.sets.get_mut(&rs.name) {
|
||||
if entry.members.iter().any(|m| m.identity.eq(received_from)) && rs.should_replace(entry) {
|
||||
|
@ -1034,25 +978,28 @@ impl Node {
|
|||
}
|
||||
|
||||
/// Get the canonical Path object corresponding to an endpoint.
|
||||
pub(crate) fn canonical_path<HostSystemImpl: HostSystem + ?Sized>(
|
||||
pub(crate) fn canonical_path<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
ep: &Endpoint,
|
||||
local_socket: &HostSystemImpl::LocalSocket,
|
||||
local_interface: &HostSystemImpl::LocalInterface,
|
||||
local_socket: &Application::LocalSocket,
|
||||
local_interface: &Application::LocalInterface,
|
||||
time_ticks: i64,
|
||||
) -> Arc<Path> {
|
||||
let paths = self.paths.read().unwrap();
|
||||
if let Some(path) = paths.get::<PathMap<HostSystemImpl>>().get(&PathKey::Ref(ep, local_socket)) {
|
||||
if let Some(path) = paths
|
||||
.get::<PathMap<Application::LocalSocket>>()
|
||||
.get(&PathKey::Ref(ep, local_socket))
|
||||
{
|
||||
path.clone()
|
||||
} else {
|
||||
drop(paths);
|
||||
self.paths
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut::<PathMap<HostSystemImpl>>()
|
||||
.get_mut::<PathMap<Application::LocalSocket>>()
|
||||
.entry(PathKey::Copied(ep.clone(), local_socket.clone()))
|
||||
.or_insert_with(|| {
|
||||
Arc::new(Path::new::<HostSystemImpl>(
|
||||
Arc::new(Path::new::<Application>(
|
||||
ep.clone(),
|
||||
local_socket.clone(),
|
||||
local_interface.clone(),
|
||||
|
@ -1064,14 +1011,14 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// Key used to look up paths in a hash map
|
||||
/// This supports copied keys for storing and refs for fast lookup without having to copy anything.
|
||||
enum PathKey<'a, 'b, HostSystemImpl: HostSystem + ?Sized> {
|
||||
Copied(Endpoint, HostSystemImpl::LocalSocket),
|
||||
Ref(&'a Endpoint, &'b HostSystemImpl::LocalSocket),
|
||||
/// Key used to look up paths in a hash map efficiently.
|
||||
enum PathKey<'a, 'b, LocalSocket: Hash + PartialEq + Eq + Clone> {
|
||||
Copied(Endpoint, LocalSocket),
|
||||
Ref(&'a Endpoint, &'b LocalSocket),
|
||||
}
|
||||
|
||||
impl<HostSystemImpl: HostSystem + ?Sized> Hash for PathKey<'_, '_, HostSystemImpl> {
|
||||
impl<LocalSocket: Hash + PartialEq + Eq + Clone> Hash for PathKey<'_, '_, LocalSocket> {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Copied(ep, ls) => {
|
||||
|
@ -1086,7 +1033,8 @@ impl<HostSystemImpl: HostSystem + ?Sized> Hash for PathKey<'_, '_, HostSystemImp
|
|||
}
|
||||
}
|
||||
|
||||
impl<HostSystemImpl: HostSystem + ?Sized> PartialEq for PathKey<'_, '_, HostSystemImpl> {
|
||||
impl<LocalSocket: Hash + PartialEq + Eq + Clone> PartialEq for PathKey<'_, '_, LocalSocket> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Copied(ep1, ls1), Self::Copied(ep2, ls2)) => ep1.eq(ep2) && ls1.eq(ls2),
|
||||
|
@ -1097,11 +1045,11 @@ impl<HostSystemImpl: HostSystem + ?Sized> PartialEq for PathKey<'_, '_, HostSyst
|
|||
}
|
||||
}
|
||||
|
||||
impl<HostSystemImpl: HostSystem + ?Sized> Eq for PathKey<'_, '_, HostSystemImpl> {}
|
||||
impl<LocalSocket: Hash + PartialEq + Eq + Clone> Eq for PathKey<'_, '_, LocalSocket> {}
|
||||
|
||||
impl<HostSystemImpl: HostSystem + ?Sized> PathKey<'_, '_, HostSystemImpl> {
|
||||
impl<LocalSocket: Hash + PartialEq + Eq + Clone> PathKey<'_, '_, LocalSocket> {
|
||||
#[inline(always)]
|
||||
fn local_socket(&self) -> &HostSystemImpl::LocalSocket {
|
||||
fn local_socket(&self) -> &LocalSocket {
|
||||
match self {
|
||||
Self::Copied(_, ls) => ls,
|
||||
Self::Ref(_, ls) => *ls,
|
||||
|
@ -1109,28 +1057,10 @@ impl<HostSystemImpl: HostSystem + ?Sized> PathKey<'_, '_, HostSystemImpl> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_copied(&self) -> PathKey<'static, 'static, HostSystemImpl> {
|
||||
fn to_copied(&self) -> PathKey<'static, 'static, LocalSocket> {
|
||||
match self {
|
||||
Self::Copied(ep, ls) => PathKey::<'static, 'static, HostSystemImpl>::Copied(ep.clone(), ls.clone()),
|
||||
Self::Ref(ep, ls) => PathKey::<'static, 'static, HostSystemImpl>::Copied((*ep).clone(), (*ls).clone()),
|
||||
Self::Copied(ep, ls) => PathKey::<'static, 'static, LocalSocket>::Copied(ep.clone(), ls.clone()),
|
||||
Self::Ref(ep, ls) => PathKey::<'static, 'static, LocalSocket>::Copied((*ep).clone(), (*ls).clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy no-op inner protocol for debugging and testing.
|
||||
#[derive(Default)]
|
||||
pub struct DummyInnerProtocol;
|
||||
|
||||
impl InnerProtocol for DummyInnerProtocol {}
|
||||
|
||||
impl VL1AuthProvider for DummyInnerProtocol {
|
||||
#[inline(always)]
|
||||
fn should_respond_to(&self, _: &Verified<Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has_trust_relationship(&self, _: &Verified<Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@ pub struct Path {
|
|||
}
|
||||
|
||||
impl Path {
|
||||
pub(crate) fn new<HostSystemImpl: HostSystem + ?Sized>(
|
||||
pub(crate) fn new<Application: ApplicationLayer + ?Sized>(
|
||||
endpoint: Endpoint,
|
||||
local_socket: HostSystemImpl::LocalSocket,
|
||||
local_interface: HostSystemImpl::LocalInterface,
|
||||
local_socket: Application::LocalSocket,
|
||||
local_interface: Application::LocalInterface,
|
||||
time_ticks: i64,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -55,12 +55,12 @@ impl Path {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn local_socket<HostSystemImpl: HostSystem + ?Sized>(&self) -> &HostSystemImpl::LocalSocket {
|
||||
pub(crate) fn local_socket<Application: ApplicationLayer + ?Sized>(&self) -> &Application::LocalSocket {
|
||||
self.local_socket.get()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn local_interface<HostSystemImpl: HostSystem + ?Sized>(&self) -> &HostSystemImpl::LocalInterface {
|
||||
pub(crate) fn local_interface<Application: ApplicationLayer + ?Sized>(&self) -> &Application::LocalInterface {
|
||||
self.local_interface.get()
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,14 @@ use crate::protocol::*;
|
|||
use crate::vl1::address::Address;
|
||||
use crate::vl1::debug_event;
|
||||
use crate::vl1::node::*;
|
||||
use crate::vl1::Verified;
|
||||
use crate::vl1::Valid;
|
||||
use crate::vl1::{Endpoint, Identity, Path};
|
||||
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||
|
||||
pub(crate) const SERVICE_INTERVAL_MS: i64 = 10000;
|
||||
|
||||
pub struct Peer {
|
||||
pub identity: Verified<Identity>,
|
||||
pub identity: Valid<Identity>,
|
||||
|
||||
v1_proto_static_secret: v1::SymmetricSecret,
|
||||
paths: Mutex<Vec<PeerPath>>,
|
||||
|
@ -53,7 +53,7 @@ struct RemoteNodeInfo {
|
|||
}
|
||||
|
||||
/// Sort a list of paths by quality or priority, with best paths first.
|
||||
fn prioritize_paths<HostSystemImpl: HostSystem + ?Sized>(paths: &mut Vec<PeerPath>) {
|
||||
fn prioritize_paths<Application: ApplicationLayer + ?Sized>(paths: &mut Vec<PeerPath>) {
|
||||
paths.sort_unstable_by(|a, b| a.last_receive_time_ticks.cmp(&b.last_receive_time_ticks).reverse());
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ impl Peer {
|
|||
///
|
||||
/// This only returns None if this_node_identity does not have its secrets or if some
|
||||
/// fatal error occurs performing key agreement between the two identities.
|
||||
pub(crate) fn new(this_node_identity: &Verified<Identity>, id: Verified<Identity>, time_ticks: i64) -> Option<Self> {
|
||||
pub(crate) fn new(this_node_identity: &Valid<Identity>, id: Valid<Identity>, time_ticks: i64) -> Option<Self> {
|
||||
this_node_identity.agree(&id).map(|static_secret| -> Self {
|
||||
Self {
|
||||
identity: id,
|
||||
|
@ -136,7 +136,7 @@ impl Peer {
|
|||
return None;
|
||||
}
|
||||
|
||||
fn learn_path<HostSystemImpl: HostSystem + ?Sized>(&self, host_system: &HostSystemImpl, new_path: &Arc<Path>, time_ticks: i64) {
|
||||
fn learn_path<Application: ApplicationLayer + ?Sized>(&self, app: &Application, new_path: &Arc<Path>, time_ticks: i64) {
|
||||
let mut paths = self.paths.lock().unwrap();
|
||||
|
||||
// TODO: check path filter
|
||||
|
@ -155,7 +155,7 @@ impl Peer {
|
|||
Endpoint::IpUdp(existing_ip) => {
|
||||
if existing_ip.ip_bytes().eq(new_ip.ip_bytes()) {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} replacing path {} with {} (same IP, different port)",
|
||||
self.identity.address.to_string(),
|
||||
p.endpoint.to_string(),
|
||||
|
@ -163,7 +163,7 @@ impl Peer {
|
|||
);
|
||||
pi.path = Arc::downgrade(new_path);
|
||||
pi.last_receive_time_ticks = time_ticks;
|
||||
prioritize_paths::<HostSystemImpl>(&mut paths);
|
||||
prioritize_paths::<Application>(&mut paths);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ impl Peer {
|
|||
|
||||
// Learn new path if it's not a duplicate or should not replace an existing path.
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} learned new path: {}",
|
||||
self.identity.address.to_string(),
|
||||
new_path.endpoint.to_string()
|
||||
|
@ -192,7 +192,7 @@ impl Peer {
|
|||
path: Arc::downgrade(new_path),
|
||||
last_receive_time_ticks: time_ticks,
|
||||
});
|
||||
prioritize_paths::<HostSystemImpl>(&mut paths);
|
||||
prioritize_paths::<Application>(&mut paths);
|
||||
}
|
||||
|
||||
/// Get the next sequential message ID for use with the V1 transport protocol.
|
||||
|
@ -202,7 +202,7 @@ impl Peer {
|
|||
}
|
||||
|
||||
/// Called every SERVICE_INTERVAL_MS by the background service loop in Node.
|
||||
pub(crate) fn service<HostSystemImpl: HostSystem + ?Sized>(&self, _: &HostSystemImpl, _: &Node, time_ticks: i64) -> bool {
|
||||
pub(crate) fn service<Application: ApplicationLayer + ?Sized>(&self, _: &Application, _: &Node, time_ticks: i64) -> bool {
|
||||
// Prune dead paths and sort in descending order of quality.
|
||||
{
|
||||
let mut paths = self.paths.lock().unwrap();
|
||||
|
@ -210,7 +210,7 @@ impl Peer {
|
|||
if paths.capacity() > 16 {
|
||||
paths.shrink_to_fit();
|
||||
}
|
||||
prioritize_paths::<HostSystemImpl>(&mut paths);
|
||||
prioritize_paths::<Application>(&mut paths);
|
||||
}
|
||||
|
||||
// Prune dead entries from the map of reported local endpoints (e.g. externally visible IPs).
|
||||
|
@ -223,19 +223,19 @@ impl Peer {
|
|||
}
|
||||
|
||||
/// Send a prepared and encrypted packet using the V1 protocol with fragmentation if needed.
|
||||
fn v1_proto_internal_send<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn v1_proto_internal_send<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
endpoint: &Endpoint,
|
||||
local_socket: Option<&HostSystemImpl::LocalSocket>,
|
||||
local_interface: Option<&HostSystemImpl::LocalInterface>,
|
||||
local_socket: Option<&Application::LocalSocket>,
|
||||
local_interface: Option<&Application::LocalInterface>,
|
||||
max_fragment_size: usize,
|
||||
packet: PooledPacketBuffer,
|
||||
) {
|
||||
let packet_size = packet.len();
|
||||
if packet_size > max_fragment_size {
|
||||
let bytes = packet.as_bytes();
|
||||
host_system.wire_send(endpoint, local_socket, local_interface, &bytes[0..UDP_DEFAULT_MTU], 0);
|
||||
app.wire_send(endpoint, local_socket, local_interface, &bytes[0..UDP_DEFAULT_MTU], 0);
|
||||
let mut pos = UDP_DEFAULT_MTU;
|
||||
|
||||
let overrun_size = (packet_size - UDP_DEFAULT_MTU) as u32;
|
||||
|
@ -259,7 +259,7 @@ impl Peer {
|
|||
let fragment_size = v1::FRAGMENT_HEADER_SIZE + chunk_size;
|
||||
tmp_buf[..v1::FRAGMENT_HEADER_SIZE].copy_from_slice(header.as_bytes());
|
||||
tmp_buf[v1::FRAGMENT_HEADER_SIZE..fragment_size].copy_from_slice(&bytes[pos..next_pos]);
|
||||
host_system.wire_send(endpoint, local_socket, local_interface, &tmp_buf[..fragment_size], 0);
|
||||
app.wire_send(endpoint, local_socket, local_interface, &tmp_buf[..fragment_size], 0);
|
||||
pos = next_pos;
|
||||
if pos < packet_size {
|
||||
chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - v1::HEADER_SIZE);
|
||||
|
@ -268,7 +268,7 @@ impl Peer {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
host_system.wire_send(endpoint, local_socket, local_interface, packet.as_bytes(), 0);
|
||||
app.wire_send(endpoint, local_socket, local_interface, packet.as_bytes(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,9 +281,9 @@ impl Peer {
|
|||
/// The builder function must append the verb (with any verb flags) and packet payload. If it returns
|
||||
/// an error, the error is returned immediately and the send is aborted. None is returned if the send
|
||||
/// function itself fails for some reason such as no paths being available.
|
||||
pub fn send<HostSystemImpl: HostSystem + ?Sized, R, E, BuilderFunction: FnOnce(&mut PacketBuffer) -> Result<R, E>>(
|
||||
pub fn send<Application: ApplicationLayer + ?Sized, R, E, BuilderFunction: FnOnce(&mut PacketBuffer) -> Result<R, E>>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
path: Option<&Arc<Path>>,
|
||||
time_ticks: i64,
|
||||
|
@ -303,7 +303,7 @@ impl Peer {
|
|||
|
||||
let max_fragment_size = path.endpoint.max_fragment_size();
|
||||
|
||||
let mut packet = host_system.get_buffer();
|
||||
let mut packet = app.get_buffer();
|
||||
if !self.is_v2() {
|
||||
// For the V1 protocol, leave room for for the header in the buffer.
|
||||
packet.set_size(v1::HEADER_SIZE);
|
||||
|
@ -371,10 +371,10 @@ impl Peer {
|
|||
}
|
||||
|
||||
self.v1_proto_internal_send(
|
||||
host_system,
|
||||
app,
|
||||
&path.endpoint,
|
||||
Some(path.local_socket::<HostSystemImpl>()),
|
||||
Some(path.local_interface::<HostSystemImpl>()),
|
||||
Some(path.local_socket::<Application>()),
|
||||
Some(path.local_interface::<Application>()),
|
||||
max_fragment_size,
|
||||
packet,
|
||||
);
|
||||
|
@ -393,9 +393,9 @@ impl Peer {
|
|||
/// Unlike other messages HELLO is sent partially in the clear and always with the long-lived
|
||||
/// static identity key. Authentication in old versions is via Poly1305 and in new versions
|
||||
/// via HMAC-SHA512.
|
||||
pub(crate) fn send_hello<HostSystemImpl: HostSystem + ?Sized>(
|
||||
pub(crate) fn send_hello<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
explicit_endpoint: Option<&Endpoint>,
|
||||
) -> bool {
|
||||
|
@ -412,9 +412,9 @@ impl Peer {
|
|||
};
|
||||
|
||||
let max_fragment_size = destination.max_fragment_size();
|
||||
let time_ticks = host_system.time_ticks();
|
||||
let time_ticks = app.time_ticks();
|
||||
|
||||
let mut packet = host_system.get_buffer();
|
||||
let mut packet = app.get_buffer();
|
||||
{
|
||||
let message_id = self.v1_proto_next_message_id();
|
||||
|
||||
|
@ -447,7 +447,7 @@ impl Peer {
|
|||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"HELLO -> {} @ {} ({} bytes)",
|
||||
self.identity.address.to_string(),
|
||||
destination.to_string(),
|
||||
|
@ -457,16 +457,16 @@ impl Peer {
|
|||
|
||||
if let Some(p) = path.as_ref() {
|
||||
self.v1_proto_internal_send(
|
||||
host_system,
|
||||
app,
|
||||
destination,
|
||||
Some(p.local_socket::<HostSystemImpl>()),
|
||||
Some(p.local_interface::<HostSystemImpl>()),
|
||||
Some(p.local_socket::<Application>()),
|
||||
Some(p.local_interface::<Application>()),
|
||||
max_fragment_size,
|
||||
packet,
|
||||
);
|
||||
p.log_send_anything(time_ticks);
|
||||
} else {
|
||||
self.v1_proto_internal_send(host_system, destination, None, None, max_fragment_size, packet);
|
||||
self.v1_proto_internal_send(app, destination, None, None, max_fragment_size, packet);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -478,11 +478,11 @@ impl Peer {
|
|||
/// those fragments after the main packet header and first chunk.
|
||||
///
|
||||
/// This returns true if the packet decrypted and passed authentication.
|
||||
pub(crate) fn v1_proto_receive<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
pub(crate) fn v1_proto_receive<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
node: &Node,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
time_ticks: i64,
|
||||
source_path: &Arc<Path>,
|
||||
packet_header: &v1::PacketHeader,
|
||||
|
@ -503,11 +503,7 @@ impl Peer {
|
|||
message_id2
|
||||
} else {
|
||||
// Packet failed to decrypt using either ephemeral or permanent key, reject.
|
||||
debug_event!(
|
||||
host_system,
|
||||
"[vl1] #{:0>16x} failed authentication",
|
||||
u64::from_be_bytes(packet_header.id)
|
||||
);
|
||||
debug_event!(app, "[vl1] #{:0>16x} failed authentication", u64::from_be_bytes(packet_header.id));
|
||||
return PacketHandlerResult::Error;
|
||||
};
|
||||
|
||||
|
@ -539,7 +535,7 @@ impl Peer {
|
|||
|
||||
verb &= v1::VERB_MASK; // mask off flags
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] #{:0>16x} decrypted and authenticated, verb: {} ({:0>2x})",
|
||||
u64::from_be_bytes(packet_header.id),
|
||||
message_type::name(verb),
|
||||
|
@ -548,11 +544,9 @@ impl Peer {
|
|||
|
||||
return match verb {
|
||||
message_type::VL1_NOP => PacketHandlerResult::Ok,
|
||||
message_type::VL1_HELLO => {
|
||||
self.handle_incoming_hello(host_system, inner, node, time_ticks, message_id, source_path, &payload)
|
||||
}
|
||||
message_type::VL1_HELLO => self.handle_incoming_hello(app, inner, node, time_ticks, message_id, source_path, &payload),
|
||||
message_type::VL1_ERROR => self.handle_incoming_error(
|
||||
host_system,
|
||||
app,
|
||||
inner,
|
||||
node,
|
||||
time_ticks,
|
||||
|
@ -562,7 +556,7 @@ impl Peer {
|
|||
&payload,
|
||||
),
|
||||
message_type::VL1_OK => self.handle_incoming_ok(
|
||||
host_system,
|
||||
app,
|
||||
inner,
|
||||
node,
|
||||
time_ticks,
|
||||
|
@ -572,28 +566,16 @@ impl Peer {
|
|||
path_is_known,
|
||||
&payload,
|
||||
),
|
||||
message_type::VL1_WHOIS => self.handle_incoming_whois(host_system, inner, node, time_ticks, message_id, &payload),
|
||||
message_type::VL1_WHOIS => self.handle_incoming_whois(app, inner, node, time_ticks, message_id, &payload),
|
||||
message_type::VL1_RENDEZVOUS => {
|
||||
self.handle_incoming_rendezvous(host_system, node, time_ticks, message_id, source_path, &payload)
|
||||
self.handle_incoming_rendezvous(app, node, time_ticks, message_id, source_path, &payload)
|
||||
}
|
||||
message_type::VL1_ECHO => self.handle_incoming_echo(host_system, inner, node, time_ticks, message_id, &payload),
|
||||
message_type::VL1_ECHO => self.handle_incoming_echo(app, inner, node, time_ticks, message_id, &payload),
|
||||
message_type::VL1_PUSH_DIRECT_PATHS => {
|
||||
self.handle_incoming_push_direct_paths(host_system, node, time_ticks, source_path, &payload)
|
||||
self.handle_incoming_push_direct_paths(app, node, time_ticks, source_path, &payload)
|
||||
}
|
||||
message_type::VL1_USER_MESSAGE => {
|
||||
self.handle_incoming_user_message(host_system, node, time_ticks, source_path, &payload)
|
||||
}
|
||||
_ => inner.handle_packet(
|
||||
host_system,
|
||||
node,
|
||||
self,
|
||||
&source_path,
|
||||
packet_header.hops(),
|
||||
message_id,
|
||||
verb,
|
||||
&payload,
|
||||
1,
|
||||
),
|
||||
message_type::VL1_USER_MESSAGE => self.handle_incoming_user_message(app, node, time_ticks, source_path, &payload),
|
||||
_ => inner.handle_packet(app, node, self, &source_path, packet_header.hops(), message_id, verb, &payload, 1),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -601,19 +583,19 @@ impl Peer {
|
|||
return PacketHandlerResult::Error;
|
||||
}
|
||||
|
||||
fn handle_incoming_hello<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
fn handle_incoming_hello<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
message_id: MessageId,
|
||||
source_path: &Arc<Path>,
|
||||
payload: &PacketBuffer,
|
||||
) -> PacketHandlerResult {
|
||||
if !(host_system.should_respond_to(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
|
||||
if !(inner.should_respond_to(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] dropping HELLO from {} due to lack of trust relationship",
|
||||
self.identity.address.to_string()
|
||||
);
|
||||
|
@ -634,12 +616,7 @@ impl Peer {
|
|||
);
|
||||
}
|
||||
|
||||
self.send(
|
||||
host_system,
|
||||
node,
|
||||
Some(source_path),
|
||||
time_ticks,
|
||||
|packet| -> Result<(), Infallible> {
|
||||
self.send(app, node, Some(source_path), time_ticks, |packet| -> Result<(), Infallible> {
|
||||
let f: &mut (OkHeader, v1::message_component_structs::OkHelloFixedHeaderFields) =
|
||||
packet.append_struct_get_mut().unwrap();
|
||||
f.0.verb = message_type::VL1_OK;
|
||||
|
@ -651,8 +628,7 @@ impl Peer {
|
|||
f.1.version_minor = VERSION_MINOR;
|
||||
f.1.version_revision = VERSION_REVISION.to_be_bytes();
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
return PacketHandlerResult::Ok;
|
||||
}
|
||||
|
@ -662,10 +638,10 @@ impl Peer {
|
|||
return PacketHandlerResult::Error;
|
||||
}
|
||||
|
||||
fn handle_incoming_error<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
fn handle_incoming_error<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
node: &Node,
|
||||
_time_ticks: i64,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -682,7 +658,7 @@ impl Peer {
|
|||
match error_header.in_re_verb {
|
||||
_ => {
|
||||
return inner.handle_error(
|
||||
host_system,
|
||||
app,
|
||||
node,
|
||||
self,
|
||||
&source_path,
|
||||
|
@ -700,10 +676,10 @@ impl Peer {
|
|||
return PacketHandlerResult::Error;
|
||||
}
|
||||
|
||||
fn handle_incoming_ok<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
fn handle_incoming_ok<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -724,7 +700,7 @@ impl Peer {
|
|||
payload.read_struct::<v1::message_component_structs::OkHelloFixedHeaderFields>(&mut cursor)
|
||||
{
|
||||
if source_hops == 0 {
|
||||
debug_event!(host_system, "[vl1] {} OK(HELLO)", self.identity.address.to_string(),);
|
||||
debug_event!(app, "[vl1] {} OK(HELLO)", self.identity.address.to_string(),);
|
||||
if let Ok(reported_endpoint) = Endpoint::unmarshal(&payload, &mut cursor) {
|
||||
#[cfg(debug_assertions)]
|
||||
let reported_endpoint2 = reported_endpoint.clone();
|
||||
|
@ -738,7 +714,7 @@ impl Peer {
|
|||
{
|
||||
#[cfg(debug_assertions)]
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} reported new remote perspective, local endpoint: {}",
|
||||
self.identity.address.to_string(),
|
||||
reported_endpoint2.to_string()
|
||||
|
@ -748,7 +724,7 @@ impl Peer {
|
|||
}
|
||||
|
||||
if source_hops == 0 && !path_is_known {
|
||||
self.learn_path(host_system, source_path, time_ticks);
|
||||
self.learn_path(app, source_path, time_ticks);
|
||||
}
|
||||
|
||||
self.last_hello_reply_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
|
@ -756,21 +732,21 @@ impl Peer {
|
|||
}
|
||||
|
||||
message_type::VL1_WHOIS => {
|
||||
debug_event!(host_system, "[vl1] OK(WHOIS)");
|
||||
debug_event!(app, "[vl1] OK(WHOIS)");
|
||||
if node.is_peer_root(self) {
|
||||
while cursor < payload.len() {
|
||||
let r = Identity::unmarshal(payload, &mut cursor);
|
||||
if let Ok(received_identity) = r {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} OK(WHOIS): received identity: {}",
|
||||
self.identity.address.to_string(),
|
||||
received_identity.to_string()
|
||||
);
|
||||
node.handle_incoming_identity(host_system, inner, received_identity, time_ticks, true);
|
||||
node.handle_incoming_identity(app, inner, received_identity, time_ticks, true);
|
||||
} else {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] {} OK(WHOIS): received bad identity: {}",
|
||||
self.identity.address.to_string(),
|
||||
r.err().unwrap().to_string()
|
||||
|
@ -785,7 +761,7 @@ impl Peer {
|
|||
|
||||
_ => {
|
||||
return inner.handle_ok(
|
||||
host_system,
|
||||
app,
|
||||
node,
|
||||
self,
|
||||
&source_path,
|
||||
|
@ -802,20 +778,20 @@ impl Peer {
|
|||
return PacketHandlerResult::Error;
|
||||
}
|
||||
|
||||
fn handle_incoming_whois<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
fn handle_incoming_whois<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
message_id: MessageId,
|
||||
payload: &PacketBuffer,
|
||||
) -> PacketHandlerResult {
|
||||
if node.this_node_is_root() || host_system.should_respond_to(&self.identity) {
|
||||
if node.this_node_is_root() || inner.should_respond_to(&self.identity) {
|
||||
let mut addresses = payload.as_bytes();
|
||||
while addresses.len() >= ADDRESS_SIZE {
|
||||
if !self
|
||||
.send(host_system, node, None, time_ticks, |packet| {
|
||||
.send(app, node, None, time_ticks, |packet| {
|
||||
while addresses.len() >= ADDRESS_SIZE && (packet.len() + Identity::MAX_MARSHAL_SIZE) <= UDP_DEFAULT_MTU {
|
||||
if let Some(zt_address) = Address::from_bytes(&addresses[..ADDRESS_SIZE]) {
|
||||
if let Some(peer) = node.peer(zt_address) {
|
||||
|
@ -835,9 +811,9 @@ impl Peer {
|
|||
return PacketHandlerResult::Ok;
|
||||
}
|
||||
|
||||
fn handle_incoming_rendezvous<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_incoming_rendezvous<Application: ApplicationLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
message_id: MessageId,
|
||||
|
@ -848,17 +824,17 @@ impl Peer {
|
|||
return PacketHandlerResult::Ok;
|
||||
}
|
||||
|
||||
fn handle_incoming_echo<HostSystemImpl: HostSystem + ?Sized, InnerProtocolImpl: InnerProtocol + ?Sized>(
|
||||
fn handle_incoming_echo<Application: ApplicationLayer + ?Sized, Inner: InnerProtocolLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
inner: &InnerProtocolImpl,
|
||||
app: &Application,
|
||||
inner: &Inner,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
message_id: MessageId,
|
||||
payload: &PacketBuffer,
|
||||
) -> PacketHandlerResult {
|
||||
if host_system.should_respond_to(&self.identity) || node.is_peer_root(self) {
|
||||
self.send(host_system, node, None, time_ticks, |packet| {
|
||||
if inner.should_respond_to(&self.identity) || node.is_peer_root(self) {
|
||||
self.send(app, node, None, time_ticks, |packet| {
|
||||
let mut f: &mut OkHeader = packet.append_struct_get_mut().unwrap();
|
||||
f.verb = message_type::VL1_OK;
|
||||
f.in_re_verb = message_type::VL1_ECHO;
|
||||
|
@ -867,7 +843,7 @@ impl Peer {
|
|||
});
|
||||
} else {
|
||||
debug_event!(
|
||||
host_system,
|
||||
app,
|
||||
"[vl1] dropping ECHO from {} due to lack of trust relationship",
|
||||
self.identity.address.to_string()
|
||||
);
|
||||
|
@ -875,9 +851,9 @@ impl Peer {
|
|||
return PacketHandlerResult::Ok;
|
||||
}
|
||||
|
||||
fn handle_incoming_push_direct_paths<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_incoming_push_direct_paths<Application: ApplicationLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -886,9 +862,9 @@ impl Peer {
|
|||
PacketHandlerResult::Ok
|
||||
}
|
||||
|
||||
fn handle_incoming_user_message<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_incoming_user_message<Application: ApplicationLayer + ?Sized>(
|
||||
self: &Arc<Self>,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
time_ticks: i64,
|
||||
source_path: &Arc<Path>,
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::io::Write;
|
|||
use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE};
|
||||
use crate::vl1::Endpoint;
|
||||
|
||||
use zerotier_crypto::verified::Verified;
|
||||
use zerotier_crypto::typestate::Valid;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::buffer::Buffer;
|
||||
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
|
||||
|
@ -91,7 +91,7 @@ impl RootSet {
|
|||
}
|
||||
|
||||
/// Get the ZeroTier default root set, which contains roots run by ZeroTier Inc.
|
||||
pub fn zerotier_default() -> Verified<Self> {
|
||||
pub fn zerotier_default() -> Valid<Self> {
|
||||
let mut cursor = 0;
|
||||
let rs = include_bytes!("../../default-rootset/root.zerotier.com.bin");
|
||||
//let rs = include_bytes!("../../default-rootset/test-root.bin");
|
||||
|
@ -107,7 +107,7 @@ impl RootSet {
|
|||
}
|
||||
|
||||
/// Verify signatures present in this root cluster definition.
|
||||
pub fn verify(self) -> Option<Verified<Self>> {
|
||||
pub fn verify(self) -> Option<Valid<Self>> {
|
||||
if self.members.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ impl RootSet {
|
|||
}
|
||||
}
|
||||
|
||||
return Some(Verified::assume_verified(self));
|
||||
return Some(Valid::assume_verified(self));
|
||||
}
|
||||
|
||||
/// Add a member to this definition, replacing any current entry with this address.
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||
|
||||
use crate::protocol;
|
||||
use crate::protocol::PacketBuffer;
|
||||
use crate::vl1::{Address, HostSystem, Identity, Node, PacketHandlerResult, Peer, MAC};
|
||||
use crate::vl1::{Address, ApplicationLayer, Identity, Node, PacketHandlerResult, Peer, MAC};
|
||||
use crate::vl2::{MulticastGroup, NetworkId};
|
||||
|
||||
use zerotier_utils::buffer::OutOfBoundsError;
|
||||
|
@ -84,7 +84,7 @@ impl MulticastAuthority {
|
|||
}
|
||||
|
||||
/// Call for VL2_MULTICAST_GATHER packets.
|
||||
pub fn handle_vl2_multicast_gather<HostSystemImpl: HostSystem + ?Sized, Authenticator: Fn(NetworkId, &Identity) -> bool>(
|
||||
pub fn handle_vl2_multicast_gather<HostSystemImpl: ApplicationLayer + ?Sized, Authenticator: Fn(NetworkId, &Identity) -> bool>(
|
||||
&self,
|
||||
auth: Authenticator,
|
||||
time_ticks: i64,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::io::Write;
|
||||
|
||||
use zerotier_crypto::verified::Verified;
|
||||
use zerotier_crypto::typestate::Valid;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -26,7 +26,7 @@ impl Revocation {
|
|||
threshold: i64,
|
||||
target: Address,
|
||||
issued_to: Address,
|
||||
signer: &Verified<Identity>,
|
||||
signer: &Valid<Identity>,
|
||||
fast_propagate: bool,
|
||||
) -> Option<Self> {
|
||||
let mut r = Self {
|
||||
|
|
|
@ -3,17 +3,25 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::protocol::PacketBuffer;
|
||||
use crate::vl1::{HostSystem, InnerProtocol, Node, PacketHandlerResult, Path, Peer};
|
||||
use crate::vl1::{ApplicationLayer, InnerProtocolLayer, Node, PacketHandlerResult, Path, Peer};
|
||||
|
||||
pub trait SwitchInterface: Sync + Send {}
|
||||
|
||||
pub struct Switch {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
impl InnerProtocol for Switch {
|
||||
fn handle_packet<HostSystemImpl: HostSystem + ?Sized>(
|
||||
impl InnerProtocolLayer for Switch {
|
||||
fn should_respond_to(&self, id: &zerotier_crypto::typestate::Valid<crate::vl1::Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn has_trust_relationship(&self, id: &zerotier_crypto::typestate::Valid<crate::vl1::Identity>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_packet<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -26,9 +34,9 @@ impl InnerProtocol for Switch {
|
|||
PacketHandlerResult::NotHandled
|
||||
}
|
||||
|
||||
fn handle_error<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_error<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
@ -43,9 +51,9 @@ impl InnerProtocol for Switch {
|
|||
PacketHandlerResult::NotHandled
|
||||
}
|
||||
|
||||
fn handle_ok<HostSystemImpl: HostSystem + ?Sized>(
|
||||
fn handle_ok<Application: ApplicationLayer + ?Sized>(
|
||||
&self,
|
||||
host_system: &HostSystemImpl,
|
||||
app: &Application,
|
||||
node: &Node,
|
||||
source: &Arc<Peer>,
|
||||
source_path: &Arc<Path>,
|
||||
|
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use zerotier_crypto::hash::SHA384;
|
||||
use zerotier_crypto::secure_eq;
|
||||
use zerotier_crypto::verified::Verified;
|
||||
use zerotier_crypto::typestate::Valid;
|
||||
use zerotier_utils::arrayvec::ArrayVec;
|
||||
use zerotier_utils::blob::Blob;
|
||||
use zerotier_utils::error::InvalidParameterError;
|
||||
|
@ -171,10 +171,13 @@ impl CertificateOfMembership {
|
|||
}
|
||||
|
||||
/// Verify this certificate of membership.
|
||||
pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option<Verified<Self>> {
|
||||
if secure_eq(&Self::v1_proto_issued_to_fingerprint(expect_issued_to), &self.issued_to_fingerprint.as_bytes()[..32]) {
|
||||
pub fn verify(self, issuer: &Identity, expect_issued_to: &Identity) -> Option<Valid<Self>> {
|
||||
if secure_eq(
|
||||
&Self::v1_proto_issued_to_fingerprint(expect_issued_to),
|
||||
&self.issued_to_fingerprint.as_bytes()[..32],
|
||||
) {
|
||||
if issuer.verify(&self.v1_proto_get_qualifier_bytes(), self.signature.as_bytes()) {
|
||||
return Some(Verified::assume_verified(self));
|
||||
return Some(Valid::assume_verified(self));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
|
|
|
@ -15,6 +15,7 @@ use clap::error::{ContextKind, ContextValue};
|
|||
#[allow(unused_imports)]
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
|
||||
use zerotier_network_hypervisor::vl1::InnerProtocolLayer;
|
||||
use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||
use zerotier_utils::exitcode;
|
||||
use zerotier_vl1_service::datadir::DataDir;
|
||||
|
@ -209,14 +210,9 @@ fn main() {
|
|||
Some(("service", _)) => {
|
||||
drop(global_args); // free unnecessary heap before starting service as we're done with CLI args
|
||||
if let Ok(_tokio_runtime) = zerotier_utils::tokio::runtime::Builder::new_multi_thread().enable_all().build() {
|
||||
let test_inner = Arc::new(zerotier_network_hypervisor::vl1::DummyInnerProtocol::default());
|
||||
let test_inner = Arc::new(DummyInnerLayer);
|
||||
let datadir = open_datadir(&flags);
|
||||
let svc = VL1Service::new(
|
||||
datadir,
|
||||
test_inner.clone(),
|
||||
test_inner,
|
||||
zerotier_vl1_service::VL1Settings::default(),
|
||||
);
|
||||
let svc = VL1Service::new(datadir, test_inner, zerotier_vl1_service::VL1Settings::default());
|
||||
if svc.is_ok() {
|
||||
let svc = svc.unwrap();
|
||||
svc.node().init_default_roots();
|
||||
|
@ -254,3 +250,7 @@ fn main() {
|
|||
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
|
||||
struct DummyInnerLayer;
|
||||
|
||||
impl InnerProtocolLayer for DummyInnerLayer {}
|
||||
|
|
|
@ -8,10 +8,12 @@ use serde::de::DeserializeOwned;
|
|||
use serde::Serialize;
|
||||
|
||||
use zerotier_crypto::random::next_u32_secure;
|
||||
use zerotier_network_hypervisor::vl1::{Identity, NodeStorage, Verified};
|
||||
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";
|
||||
|
@ -20,15 +22,8 @@ pub const CONFIG_FILENAME: &'static str = "local.conf";
|
|||
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 48;
|
||||
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
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> NodeStorage for DataDir<Config> {
|
||||
fn load_node_identity(&self) -> Option<Verified<Identity>> {
|
||||
let id_data = read_limit(self.base_path.join(IDENTITY_SECRET_FILENAME), 4096);
|
||||
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;
|
||||
}
|
||||
|
@ -36,18 +31,34 @@ impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + Deseri
|
|||
if id_data.is_err() {
|
||||
return None;
|
||||
}
|
||||
Some(Verified::assume_verified(id_data.unwrap()))
|
||||
}
|
||||
Some(Valid::assume_verified(id_data.unwrap()))
|
||||
}
|
||||
|
||||
fn save_node_identity(&self, id: &Verified<Identity>) {
|
||||
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 = self.base_path.join(IDENTITY_SECRET_FILENAME);
|
||||
// TODO: handle errors
|
||||
let _ = std::fs::write(&secret_path, id_secret_str.as_bytes());
|
||||
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));
|
||||
let _ = std::fs::write(self.base_path.join(IDENTITY_PUBLIC_FILENAME), id_public_str.as_bytes());
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,21 +13,22 @@ use std::hash::Hash;
|
|||
pub struct LocalInterface(u128);
|
||||
|
||||
impl LocalInterface {
|
||||
#[inline]
|
||||
#[cfg(unix)]
|
||||
pub fn from_unix_interface_name(name: &str) -> Self {
|
||||
let mut tmp = [0_u8; 16];
|
||||
let mut tmp = 0u128.to_ne_bytes();
|
||||
let nb = name.as_bytes();
|
||||
let l = nb.len();
|
||||
assert!(l <= 16); // do any *nix OSes have device names longer than 16 bytes?
|
||||
tmp[..l].copy_from_slice(&nb[..l]);
|
||||
Self(u128::from_be_bytes(tmp))
|
||||
Self(u128::from_ne_bytes(tmp))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for LocalInterface {
|
||||
#[cfg(unix)]
|
||||
fn to_string(&self) -> String {
|
||||
let b = self.0.to_be_bytes();
|
||||
let b = self.0.to_ne_bytes();
|
||||
let mut l = 0;
|
||||
for bb in b.iter() {
|
||||
if *bb > 0 {
|
||||
|
@ -41,6 +42,6 @@ impl ToString for LocalInterface {
|
|||
|
||||
#[cfg(windows)]
|
||||
fn to_string(&self) -> String {
|
||||
zerotier_core_crypto::hex::to_string(&self.0.to_be_bytes())
|
||||
zerotier_core_crypto::hex::to_string(&self.0.to_ne_bytes())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::sys::udp::BoundUdpSocket;
|
||||
|
||||
static LOCAL_SOCKET_UNIQUE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Local socket wrapper to provide to the core.
|
||||
///
|
||||
/// This implements very fast hash and equality in terms of an arbitrary unique ID assigned at
|
||||
/// construction and holds a weak reference to the bound socket so dead sockets will silently
|
||||
/// cease to exist or work. This also means that this code can check the weak count to determine
|
||||
/// if the core is currently holding/using a socket for any reason.
|
||||
/// This wraps a bound UDP socket in weak form so sockets that are released by the UDP
|
||||
/// binding engine can be "garbage collected" by the core.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct LocalSocket(pub(crate) Weak<BoundUdpSocket>, usize);
|
||||
pub struct LocalSocket(Weak<BoundUdpSocket>);
|
||||
|
||||
impl LocalSocket {
|
||||
#[inline]
|
||||
pub fn new(s: &Arc<BoundUdpSocket>) -> Self {
|
||||
Self(Arc::downgrade(s), LOCAL_SOCKET_UNIQUE_ID_COUNTER.fetch_add(1, Ordering::SeqCst))
|
||||
Self(Arc::downgrade(s))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.0.strong_count() > 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn socket(&self) -> Option<Arc<BoundUdpSocket>> {
|
||||
self.0.upgrade()
|
||||
}
|
||||
|
@ -36,7 +33,7 @@ impl LocalSocket {
|
|||
impl PartialEq for LocalSocket {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.1 == other.1
|
||||
self.0.ptr_eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +42,7 @@ impl Eq for LocalSocket {}
|
|||
impl Hash for LocalSocket {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.1.hash(state)
|
||||
self.0.as_ptr().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ fn socket_read_concurrency() -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait UdpPacketHandler: Send + Sync + 'static {
|
||||
pub trait UdpPacketHandler: Send + Sync {
|
||||
fn incoming_udp_packet(
|
||||
self: &Arc<Self>,
|
||||
time_ticks: i64,
|
||||
|
@ -189,7 +189,7 @@ impl BoundUdpPort {
|
|||
/// The caller can check the 'sockets' member variable after calling to determine which if any bindings were
|
||||
/// successful. Any errors that occurred are returned as tuples of (interface, address, error). The second vector
|
||||
/// returned contains newly bound sockets.
|
||||
pub fn update_bindings<UdpPacketHandlerImpl: UdpPacketHandler + ?Sized>(
|
||||
pub fn update_bindings<UdpPacketHandlerImpl: UdpPacketHandler + ?Sized + 'static>(
|
||||
&mut self,
|
||||
interface_prefix_blacklist: &HashSet<String>,
|
||||
cidr_blacklist: &HashSet<InetAddress>,
|
||||
|
|
|
@ -20,21 +20,22 @@ 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,
|
||||
/// talks to the physical network, manages the vl1 node, and presents a templated interface for
|
||||
/// 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<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> {
|
||||
pub struct VL1Service<Inner: InnerProtocolLayer + ?Sized + 'static> {
|
||||
state: RwLock<VL1ServiceMutableState>,
|
||||
storage: Arc<NodeStorageImpl>,
|
||||
vl1_auth_provider: Arc<VL1AuthProviderImpl>,
|
||||
inner: Arc<InnerProtocolImpl>,
|
||||
vl1_data_storage: Arc<dyn VL1DataStorage>,
|
||||
inner: Arc<Inner>,
|
||||
buffer_pool: Arc<PacketBufferPool>,
|
||||
node_container: Option<Node>, // never None, set in new()
|
||||
}
|
||||
|
@ -46,18 +47,8 @@ struct VL1ServiceMutableState {
|
|||
running: bool,
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
pub fn new(
|
||||
storage: Arc<NodeStorageImpl>,
|
||||
vl1_auth_provider: Arc<VL1AuthProviderImpl>,
|
||||
inner: Arc<InnerProtocolImpl>,
|
||||
settings: VL1Settings,
|
||||
) -> Result<Arc<Self>, Box<dyn Error>> {
|
||||
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 {
|
||||
state: RwLock::new(VL1ServiceMutableState {
|
||||
daemons: Vec::with_capacity(2),
|
||||
|
@ -65,8 +56,7 @@ impl<
|
|||
settings,
|
||||
running: true,
|
||||
}),
|
||||
storage,
|
||||
vl1_auth_provider,
|
||||
vl1_data_storage,
|
||||
inner,
|
||||
buffer_pool: Arc::new(PacketBufferPool::new(
|
||||
std::thread::available_parallelism().map_or(2, |c| c.get() + 2),
|
||||
|
@ -189,12 +179,7 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> UdpPacketHandler for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
impl<Inner: InnerProtocolLayer + ?Sized + 'static> UdpPacketHandler for VL1Service<Inner> {
|
||||
#[inline(always)]
|
||||
fn incoming_udp_packet(
|
||||
self: &Arc<Self>,
|
||||
|
@ -215,16 +200,11 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> HostSystem for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
type Storage = NodeStorageImpl;
|
||||
impl<Inner: InnerProtocolLayer + ?Sized + 'static> ApplicationLayer for VL1Service<Inner> {
|
||||
type LocalSocket = crate::LocalSocket;
|
||||
type LocalInterface = crate::LocalInterface;
|
||||
|
||||
#[inline]
|
||||
fn event(&self, event: Event) {
|
||||
println!("{}", event.to_string());
|
||||
match event {
|
||||
|
@ -237,9 +217,14 @@ impl<
|
|||
socket.is_valid()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn storage(&self) -> &Self::Storage {
|
||||
self.storage.as_ref()
|
||||
#[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]
|
||||
|
@ -247,6 +232,7 @@ impl<
|
|||
self.buffer_pool.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wire_send(
|
||||
&self,
|
||||
endpoint: &Endpoint,
|
||||
|
@ -259,7 +245,7 @@ impl<
|
|||
Endpoint::IpUdp(address) => {
|
||||
// This is the fast path -- the socket is known to the core so just send it.
|
||||
if let Some(s) = local_socket {
|
||||
if let Some(s) = s.0.upgrade() {
|
||||
if let Some(s) = s.socket() {
|
||||
s.send(address, data, packet_ttl);
|
||||
} else {
|
||||
return;
|
||||
|
@ -321,46 +307,7 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> NodeStorage for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn load_node_identity(&self) -> Option<Verified<Identity>> {
|
||||
self.storage.load_node_identity()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn save_node_identity(&self, id: &Verified<Identity>) {
|
||||
self.storage.save_node_identity(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> VL1AuthProvider for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn should_respond_to(&self, id: &Verified<Identity>) -> bool {
|
||||
self.vl1_auth_provider.should_respond_to(id)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has_trust_relationship(&self, id: &Verified<Identity>) -> bool {
|
||||
self.vl1_auth_provider.has_trust_relationship(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
NodeStorageImpl: NodeStorage + ?Sized + 'static,
|
||||
VL1AuthProviderImpl: VL1AuthProvider + ?Sized + 'static,
|
||||
InnerProtocolImpl: InnerProtocol + ?Sized + 'static,
|
||||
> Drop for VL1Service<NodeStorageImpl, VL1AuthProviderImpl, InnerProtocolImpl>
|
||||
{
|
||||
impl<Inner: InnerProtocolLayer + ?Sized + 'static> Drop for VL1Service<Inner> {
|
||||
fn drop(&mut self) {
|
||||
let mut state = self.state.write().unwrap();
|
||||
state.running = false;
|
||||
|
|
|
@ -83,7 +83,7 @@ pub enum ReceiveResult<'a, H: ApplicationLayer> {
|
|||
|
||||
/// Packet is valid and a new session was created.
|
||||
///
|
||||
/// The session will have already been gated by the `accept_new_session()` method in the Host trait.
|
||||
/// The session will have already been gated by the accept_new_session() method in ApplicationLayer.
|
||||
OkNewSession(Session<H>),
|
||||
|
||||
/// Packet superficially appears valid but was ignored e.g. as a duplicate.
|
||||
|
|
Loading…
Add table
Reference in a new issue