mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
An absolutely gigantic amount of refactoring to make the core and its service (or other users) interact in a way that is rustier and also better for performance. Also vastly simplifies some code.
This commit is contained in:
parent
f14efdcd3d
commit
9d5c2a925d
30 changed files with 1182 additions and 1117 deletions
|
@ -8,47 +8,21 @@
|
||||||
|
|
||||||
use crate::secret::Secret;
|
use crate::secret::Secret;
|
||||||
|
|
||||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
/*
|
||||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
* HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||||
|
*
|
||||||
|
* Iteration and context are always zero here. Preface is 'ZT'. Hash size is in bits. Integers
|
||||||
|
* larger than one byte are big-endian.
|
||||||
|
*
|
||||||
|
* See: https://csrc.nist.gov/publications/detail/sp/800-108/final (page 12)
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<48> {
|
/// Derive a key using HMAC-SHA384 and a single byte label, ZeroTier variant with "ZT" preface.
|
||||||
Secret(crate::hash::hmac_sha384(
|
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8) -> Secret<48> {
|
||||||
key,
|
Secret(crate::hash::hmac_sha384(key, &[0, 0, 0, 0, b'Z', b'T', label, 0, 0, 0, 0, 0x01, 0x80]))
|
||||||
&[
|
|
||||||
(iter >> 24) as u8,
|
|
||||||
(iter >> 16) as u8,
|
|
||||||
(iter >> 8) as u8,
|
|
||||||
iter as u8,
|
|
||||||
b'Z',
|
|
||||||
b'T', // can also be considered part of "label"
|
|
||||||
label,
|
|
||||||
0,
|
|
||||||
context,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0x01,
|
|
||||||
0x80, // 384 bits
|
|
||||||
],
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zt_kbkdf_hmac_sha512(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<64> {
|
/// Derive a key using HMAC-SHA512 and a single byte label, ZeroTier variant with "ZT" preface.
|
||||||
Secret(crate::hash::hmac_sha512(
|
pub fn zt_kbkdf_hmac_sha512(key: &[u8], label: u8) -> Secret<64> {
|
||||||
key,
|
Secret(crate::hash::hmac_sha512(key, &[0, 0, 0, 0, b'Z', b'T', label, 0, 0, 0, 0, 0x02, 0x00]))
|
||||||
&[
|
|
||||||
(iter >> 24) as u8,
|
|
||||||
(iter >> 16) as u8,
|
|
||||||
(iter >> 8) as u8,
|
|
||||||
iter as u8,
|
|
||||||
b'Z',
|
|
||||||
b'T', // can also be considered part of "label"
|
|
||||||
label,
|
|
||||||
0,
|
|
||||||
context,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0x01,
|
|
||||||
0x80, // 384 bits
|
|
||||||
],
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ panic = 'abort'
|
||||||
zerotier-core-crypto = { path = "../zerotier-core-crypto" }
|
zerotier-core-crypto = { path = "../zerotier-core-crypto" }
|
||||||
base64 = "^0"
|
base64 = "^0"
|
||||||
lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] }
|
lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] }
|
||||||
metrohash = "^1"
|
|
||||||
dashmap = "^5"
|
dashmap = "^5"
|
||||||
parking_lot = "^0"
|
parking_lot = "^0"
|
||||||
lazy_static = "^1"
|
lazy_static = "^1"
|
||||||
|
|
|
@ -16,53 +16,6 @@ pub mod vl1;
|
||||||
pub mod vl2;
|
pub mod vl2;
|
||||||
|
|
||||||
mod networkhypervisor;
|
mod networkhypervisor;
|
||||||
|
|
||||||
pub use networkhypervisor::{Interface, NetworkHypervisor};
|
pub use networkhypervisor::{Interface, NetworkHypervisor};
|
||||||
|
pub use vl1::protocol::{PacketBuffer, PooledPacketBuffer};
|
||||||
/// Standard packet buffer type including pool container.
|
|
||||||
pub type PacketBuffer = crate::util::pool::Pooled<crate::util::buffer::Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::PacketBufferFactory>;
|
|
||||||
|
|
||||||
/// Factory type to supply to a new PacketBufferPool.
|
|
||||||
pub type PacketBufferFactory = crate::util::buffer::PooledBufferFactory<{ crate::vl1::protocol::PACKET_SIZE_MAX }>;
|
|
||||||
|
|
||||||
/// Source for instances of PacketBuffer
|
|
||||||
pub type PacketBufferPool = crate::util::pool::Pool<crate::util::buffer::Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, crate::PacketBufferFactory>;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Protocol versions
|
|
||||||
*
|
|
||||||
* 1 - 0.2.0 ... 0.2.5
|
|
||||||
* 2 - 0.3.0 ... 0.4.5
|
|
||||||
* + Added signature and originating peer to multicast frame
|
|
||||||
* + Double size of multicast frame bloom filter
|
|
||||||
* 3 - 0.5.0 ... 0.6.0
|
|
||||||
* + Yet another multicast redesign
|
|
||||||
* + New crypto completely changes key agreement cipher
|
|
||||||
* 4 - 0.6.0 ... 1.0.6
|
|
||||||
* + BREAKING CHANGE: New identity format based on hashcash design
|
|
||||||
* 5 - 1.1.0 ... 1.1.5
|
|
||||||
* + Supports echo
|
|
||||||
* + Supports in-band world (root server definition) updates
|
|
||||||
* + Clustering! (Though this will work with protocol v4 clients.)
|
|
||||||
* + Otherwise backward compatible with protocol v4
|
|
||||||
* 6 - 1.1.5 ... 1.1.10
|
|
||||||
* + Network configuration format revisions including binary values
|
|
||||||
* 7 - 1.1.10 ... 1.1.17
|
|
||||||
* + Introduce trusted paths for local SDN use
|
|
||||||
* 8 - 1.1.17 ... 1.2.0
|
|
||||||
* + Multipart network configurations for large network configs
|
|
||||||
* + Tags and Capabilities
|
|
||||||
* + inline push of CertificateOfMembership deprecated
|
|
||||||
* 9 - 1.2.0 ... 1.2.14
|
|
||||||
* 10 - 1.4.0 ... 1.4.6
|
|
||||||
* + Contained early pre-alpha versions of multipath, which are deprecated
|
|
||||||
* 11 - 1.6.0 ... 2.0.0
|
|
||||||
* + Supports and prefers AES-GMAC-SIV symmetric crypto, backported.
|
|
||||||
*
|
|
||||||
* 20 - 2.0.0 ... CURRENT
|
|
||||||
* + Forward secrecy with cryptographic ratchet! Finally!!!
|
|
||||||
* + New identity format including both x25519 and NIST P-521 keys.
|
|
||||||
* + AES-GMAC-SIV, a FIPS-compliant SIV construction using AES.
|
|
||||||
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
|
|
||||||
* + HELLO and OK(HELLO) use a dictionary for better extensibilit.
|
|
||||||
*/
|
|
||||||
pub const VERSION_PROTO: u8 = 20;
|
|
||||||
|
|
|
@ -6,23 +6,22 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::num::NonZeroI64;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::error::InvalidParameterError;
|
use crate::error::InvalidParameterError;
|
||||||
|
use crate::vl1::protocol::PooledPacketBuffer;
|
||||||
use crate::vl1::{Address, Endpoint, Identity, Node, RootSet, SystemInterface};
|
use crate::vl1::{Address, Endpoint, Identity, Node, RootSet, SystemInterface};
|
||||||
use crate::vl2::{Switch, SwitchInterface};
|
use crate::vl2::{Switch, SwitchInterface};
|
||||||
use crate::PacketBuffer;
|
|
||||||
|
|
||||||
pub trait Interface: SystemInterface + SwitchInterface {}
|
pub trait Interface: SystemInterface + SwitchInterface {}
|
||||||
|
|
||||||
pub struct NetworkHypervisor {
|
pub struct NetworkHypervisor<I: Interface> {
|
||||||
vl1: Node,
|
vl1: Node<I>,
|
||||||
vl2: Switch,
|
vl2: Switch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkHypervisor {
|
impl<I: Interface> NetworkHypervisor<I> {
|
||||||
pub fn new<I: Interface>(ii: &I, auto_generate_identity: bool) -> Result<NetworkHypervisor, InvalidParameterError> {
|
pub fn new(ii: &I, auto_generate_identity: bool) -> Result<Self, InvalidParameterError> {
|
||||||
Ok(NetworkHypervisor {
|
Ok(NetworkHypervisor {
|
||||||
vl1: Node::new(ii, auto_generate_identity)?,
|
vl1: Node::new(ii, auto_generate_identity)?,
|
||||||
vl2: Switch::new(),
|
vl2: Switch::new(),
|
||||||
|
@ -30,7 +29,7 @@ impl NetworkHypervisor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
pub fn get_packet_buffer(&self) -> PooledPacketBuffer {
|
||||||
self.vl1.get_packet_buffer()
|
self.vl1.get_packet_buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +44,12 @@ impl NetworkHypervisor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn do_background_tasks<I: Interface>(&self, ii: &I) -> Duration {
|
pub fn do_background_tasks(&self, ii: &I) -> Duration {
|
||||||
self.vl1.do_background_tasks(ii)
|
self.vl1.do_background_tasks(ii)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn wire_receive<I: Interface>(&self, ii: &I, source_endpoint: &Endpoint, source_local_socket: Option<NonZeroI64>, source_local_interface: Option<NonZeroI64>, data: PacketBuffer) {
|
pub fn wire_receive(&self, ii: &I, source_endpoint: &Endpoint, source_local_socket: &I::LocalSocket, source_local_interface: &I::LocalInterface, data: PooledPacketBuffer) {
|
||||||
self.vl1.wire_receive(ii, &self.vl2, source_endpoint, source_local_socket, source_local_interface, data)
|
self.vl1.wire_receive(ii, &self.vl2, source_endpoint, source_local_socket, source_local_interface, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,23 +11,10 @@ use std::mem::{size_of, MaybeUninit};
|
||||||
|
|
||||||
use crate::util::pool::PoolFactory;
|
use crate::util::pool::PoolFactory;
|
||||||
|
|
||||||
/// Annotates a structure as containing only primitive types.
|
|
||||||
///
|
|
||||||
/// This means the structure is safe to copy in raw form, does not need to be dropped, and otherwise
|
|
||||||
/// contains nothing complex that requires any special handling. It also implies that it is safe to
|
|
||||||
/// access without concern for alignment on platforms on which this is an issue, or at least that
|
|
||||||
/// the implementer must take care to guard any unaligned access in appropriate ways. FlatBlob
|
|
||||||
/// structures are generally repr(C, packed) as well to make them deterministic across systems.
|
|
||||||
///
|
|
||||||
/// The Buffer has special methods allowing these structs to be read and written in place, which
|
|
||||||
/// would be unsafe without these concerns being flagged as not applicable.
|
|
||||||
pub unsafe trait FlatBlob: Sized {}
|
|
||||||
|
|
||||||
/// A safe bounds checked I/O buffer with extensions for convenient appending of RawObject types.
|
/// A safe bounds checked I/O buffer with extensions for convenient appending of RawObject types.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Buffer<const L: usize>(usize, [u8; L]);
|
pub struct Buffer<const L: usize>(usize, [u8; L]);
|
||||||
|
|
||||||
unsafe impl<const L: usize> FlatBlob for Buffer<L> {}
|
|
||||||
|
|
||||||
impl<const L: usize> Default for Buffer<L> {
|
impl<const L: usize> Default for Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -76,20 +63,21 @@ impl<const L: usize> Buffer<L> {
|
||||||
|
|
||||||
/// Create an empty zeroed buffer on the heap without intermediate stack allocation.
|
/// Create an empty zeroed buffer on the heap without intermediate stack allocation.
|
||||||
/// This can be used to allocate buffers too large for the stack.
|
/// This can be used to allocate buffers too large for the stack.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_boxed() -> Box<Self> {
|
pub fn new_boxed() -> Box<Self> {
|
||||||
unsafe { Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>()).cast()) }
|
unsafe { Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::<Self>()).cast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an empty buffer without internally zeroing its memory.
|
/// Create an empty buffer without internally zeroing its memory.
|
||||||
///
|
///
|
||||||
/// This is technically unsafe because unwritten memory in the buffer will have undefined contents.
|
/// This is unsafe because unwritten memory in the buffer will have undefined contents.
|
||||||
/// Otherwise it behaves exactly like new().
|
/// This means that some of the append_X_get_mut() functions may return mutable references to
|
||||||
|
/// undefined memory contents rather than zeroed memory.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn new_without_memzero() -> Self {
|
pub unsafe fn new_without_memzero() -> Self {
|
||||||
Self(0, MaybeUninit::uninit().assume_init())
|
Self(0, MaybeUninit::uninit().assume_init())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
|
pub fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
|
||||||
let l = b.len();
|
let l = b.len();
|
||||||
if l <= L {
|
if l <= L {
|
||||||
|
@ -138,7 +126,6 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.1[0..self.0].fill(0);
|
self.1[0..self.0].fill(0);
|
||||||
self.0 = 0;
|
self.0 = 0;
|
||||||
|
@ -189,7 +176,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
|
|
||||||
/// Append a structure and return a mutable reference to its memory.
|
/// Append a structure and return a mutable reference to its memory.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn append_struct_get_mut<T: FlatBlob>(&mut self) -> std::io::Result<&mut T> {
|
pub fn append_struct_get_mut<T: Copy>(&mut self) -> std::io::Result<&mut T> {
|
||||||
let ptr = self.0;
|
let ptr = self.0;
|
||||||
let end = ptr + size_of::<T>();
|
let end = ptr + size_of::<T>();
|
||||||
if end <= L {
|
if end <= L {
|
||||||
|
@ -226,6 +213,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn append_padding(&mut self, b: u8, count: usize) -> std::io::Result<()> {
|
pub fn append_padding(&mut self, b: u8, count: usize) -> std::io::Result<()> {
|
||||||
let ptr = self.0;
|
let ptr = self.0;
|
||||||
let end = ptr + count;
|
let end = ptr + count;
|
||||||
|
@ -238,6 +226,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
pub fn append_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||||
let ptr = self.0;
|
let ptr = self.0;
|
||||||
let end = ptr + buf.len();
|
let end = ptr + buf.len();
|
||||||
|
@ -250,6 +239,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn append_bytes_fixed<const S: usize>(&mut self, buf: &[u8; S]) -> std::io::Result<()> {
|
pub fn append_bytes_fixed<const S: usize>(&mut self, buf: &[u8; S]) -> std::io::Result<()> {
|
||||||
let ptr = self.0;
|
let ptr = self.0;
|
||||||
let end = ptr + S;
|
let end = ptr + S;
|
||||||
|
@ -318,19 +308,35 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a structure at a given position in the buffer.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn struct_at<T: FlatBlob>(&self, ptr: usize) -> std::io::Result<&T> {
|
pub fn bytes_fixed_at<const S: usize>(&self, ptr: usize) -> std::io::Result<&[u8; S]> {
|
||||||
if (ptr + size_of::<T>()) <= self.0 {
|
if (ptr + S) <= self.0 {
|
||||||
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().add(ptr).cast::<[u8; S]>()) }
|
||||||
} else {
|
} else {
|
||||||
Err(overflow_err())
|
Err(overflow_err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a structure at a given position in the buffer.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn struct_mut_at<T: FlatBlob>(&mut self, ptr: usize) -> std::io::Result<&mut T> {
|
pub fn bytes_fixed_mut_at<const S: usize>(&mut self, ptr: usize) -> std::io::Result<&mut [u8; S]> {
|
||||||
|
if (ptr + S) <= self.0 {
|
||||||
|
unsafe { Ok(&mut *self.1.as_mut_ptr().cast::<u8>().add(ptr).cast::<[u8; S]>()) }
|
||||||
|
} else {
|
||||||
|
Err(overflow_err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn struct_at<T: Copy>(&self, ptr: usize) -> std::io::Result<&T> {
|
||||||
|
if (ptr + size_of::<T>()) <= self.0 {
|
||||||
|
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().add(ptr).cast::<T>()) }
|
||||||
|
} else {
|
||||||
|
Err(overflow_err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn struct_mut_at<T: Copy>(&mut self, ptr: usize) -> std::io::Result<&mut T> {
|
||||||
if (ptr + size_of::<T>()) <= self.0 {
|
if (ptr + size_of::<T>()) <= self.0 {
|
||||||
unsafe { Ok(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
unsafe { Ok(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
||||||
} else {
|
} else {
|
||||||
|
@ -347,9 +353,8 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a structure at a given position in the buffer and advance the cursor.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_struct<T: FlatBlob>(&self, cursor: &mut usize) -> std::io::Result<&T> {
|
pub fn read_struct<T: Copy>(&self, cursor: &mut usize) -> std::io::Result<&T> {
|
||||||
let ptr = *cursor;
|
let ptr = *cursor;
|
||||||
let end = ptr + size_of::<T>();
|
let end = ptr + size_of::<T>();
|
||||||
debug_assert!(end <= L);
|
debug_assert!(end <= L);
|
||||||
|
@ -454,15 +459,6 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> PartialEq for Buffer<L> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.1[0..self.0].eq(&other.1[0..other.0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const L: usize> Eq for Buffer<L> {}
|
|
||||||
|
|
||||||
impl<const L: usize> Write for Buffer<L> {
|
impl<const L: usize> Write for Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::util::buffer::Buffer;
|
||||||
|
|
||||||
/// Must be larger than any object we want to use with to_bytes() or from_bytes().
|
/// Must be larger than any object we want to use with to_bytes() or from_bytes().
|
||||||
/// This hack can go away once Rust allows us to reference trait consts as generics.
|
/// This hack can go away once Rust allows us to reference trait consts as generics.
|
||||||
const TEMP_BUF_SIZE: usize = 131072;
|
const TEMP_BUF_SIZE: usize = 16384;
|
||||||
|
|
||||||
/// A super-lightweight zero-allocation serialization interface.
|
/// A super-lightweight zero-allocation serialization interface.
|
||||||
pub trait Marshalable: Sized {
|
pub trait Marshalable: Sized {
|
||||||
|
@ -48,7 +48,7 @@ pub trait Marshalable: Sized {
|
||||||
/// Marshal and convert to a Rust vector.
|
/// Marshal and convert to a Rust vector.
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
|
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
|
||||||
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
|
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new();
|
||||||
assert!(self.marshal(&mut tmp).is_ok());
|
assert!(self.marshal(&mut tmp).is_ok());
|
||||||
tmp.as_bytes().to_vec()
|
tmp.as_bytes().to_vec()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,13 @@ use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
/// Trait for objects that create and reset poolable objects.
|
/// Each pool requires a factory that creates and resets (for re-use) pooled objects.
|
||||||
pub trait PoolFactory<O> {
|
pub trait PoolFactory<O> {
|
||||||
fn create(&self) -> O;
|
fn create(&self) -> O;
|
||||||
fn reset(&self, obj: &mut O);
|
fn reset(&self, obj: &mut O);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
struct PoolEntry<O, F: PoolFactory<O>> {
|
struct PoolEntry<O, F: PoolFactory<O>> {
|
||||||
obj: O,
|
obj: O,
|
||||||
return_pool: Weak<PoolInner<O, F>>,
|
return_pool: Weak<PoolInner<O, F>>,
|
||||||
|
@ -102,10 +103,7 @@ impl<O, F: PoolFactory<O>> Drop for Pooled<O, F> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Return to pool if the pool still exists. Deallocate otherwise.
|
if let Some(p) = self.0.as_ref().return_pool.upgrade() {
|
||||||
let p = Weak::upgrade(&self.0.as_ref().return_pool);
|
|
||||||
if p.is_some() {
|
|
||||||
let p = p.unwrap_unchecked();
|
|
||||||
p.factory.reset(&mut self.0.as_mut().obj);
|
p.factory.reset(&mut self.0.as_mut().obj);
|
||||||
p.pool.lock().push(self.0);
|
p.pool.lock().push(self.0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,14 +127,16 @@ impl<O, F: PoolFactory<O>> Pool<O, F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a pooled object, or allocate one if the pool is empty.
|
/// Get a pooled object, or allocate one if the pool is empty.
|
||||||
#[inline(always)]
|
|
||||||
pub fn get(&self) -> Pooled<O, F> {
|
pub fn get(&self) -> Pooled<O, F> {
|
||||||
Pooled::<O, F>(self.0.pool.lock().pop().unwrap_or_else(|| unsafe {
|
Pooled::<O, F>(self.0.pool.lock().pop().unwrap_or_else(
|
||||||
NonNull::new_unchecked(Box::into_raw(Box::new(PoolEntry::<O, F> {
|
#[inline(always)]
|
||||||
obj: self.0.factory.create(),
|
|| unsafe {
|
||||||
return_pool: Arc::downgrade(&self.0),
|
NonNull::new_unchecked(Box::into_raw(Box::new(PoolEntry::<O, F> {
|
||||||
})))
|
obj: self.0.factory.create(),
|
||||||
}))
|
return_pool: Arc::downgrade(&self.0),
|
||||||
|
})))
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dispose of all pooled objects, freeing any memory they use.
|
/// Dispose of all pooled objects, freeing any memory they use.
|
||||||
|
@ -145,14 +145,8 @@ impl<O, F: PoolFactory<O>> Pool<O, F> {
|
||||||
/// objects will still be returned on drop unless the pool itself is dropped. This can
|
/// objects will still be returned on drop unless the pool itself is dropped. This can
|
||||||
/// be done to free some memory if there has been a spike in memory use.
|
/// be done to free some memory if there has been a spike in memory use.
|
||||||
pub fn purge(&self) {
|
pub fn purge(&self) {
|
||||||
let mut p = self.0.pool.lock();
|
for o in self.0.pool.lock().drain(..) {
|
||||||
loop {
|
drop(unsafe { Box::from_raw(o.as_ptr()) })
|
||||||
let o = p.pop();
|
|
||||||
if o.is_some() {
|
|
||||||
drop(unsafe { Box::from_raw(o.unwrap().as_ptr()) })
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,13 @@ pub struct Address(NonZeroU64);
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Get an address from a 64-bit integer or return None if it is zero or reserved.
|
/// Get an address from a 64-bit integer or return None if it is zero or reserved.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_u64(mut i: u64) -> Option<Address> {
|
pub fn from_u64(mut i: u64) -> Option<Address> {
|
||||||
i &= 0xffffffffff;
|
i &= 0xffffffffff;
|
||||||
NonZeroU64::new(i).and_then(|ii| if (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 { Some(Address(ii)) } else { None })
|
NonZeroU64::new(i).and_then(|ii| if (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 { Some(Address(ii)) } else { None })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_bytes(b: &[u8]) -> Option<Address> {
|
pub fn from_bytes(b: &[u8]) -> Option<Address> {
|
||||||
if b.len() >= ADDRESS_SIZE {
|
if b.len() >= ADDRESS_SIZE {
|
||||||
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
||||||
|
@ -40,12 +40,12 @@ impl Address {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_bytes_fixed(b: &[u8; ADDRESS_SIZE]) -> Option<Address> {
|
pub fn from_bytes_fixed(b: &[u8; ADDRESS_SIZE]) -> Option<Address> {
|
||||||
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn to_bytes(&self) -> [u8; ADDRESS_SIZE] {
|
pub fn to_bytes(&self) -> [u8; ADDRESS_SIZE] {
|
||||||
let i = self.0.get();
|
let i = self.0.get();
|
||||||
[(i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
[(i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
||||||
|
@ -60,12 +60,12 @@ impl Address {
|
||||||
impl Marshalable for Address {
|
impl Marshalable for Address {
|
||||||
const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE;
|
const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
buf.append_bytes(&self.0.get().to_be_bytes()[8 - ADDRESS_SIZE..])
|
buf.append_bytes(&self.0.get().to_be_bytes()[8 - ADDRESS_SIZE..])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
||||||
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a))
|
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::PacketBuffer;
|
|
||||||
|
|
||||||
/// Packet fragment re-assembler and container.
|
/// Packet fragment re-assembler and container.
|
||||||
///
|
///
|
||||||
|
@ -19,15 +18,14 @@ use crate::PacketBuffer;
|
||||||
/// the size of frags[] and the number of bits in 'have' and 'expecting'.
|
/// the size of frags[] and the number of bits in 'have' and 'expecting'.
|
||||||
pub(crate) struct FragmentedPacket {
|
pub(crate) struct FragmentedPacket {
|
||||||
pub ts_ticks: i64,
|
pub ts_ticks: i64,
|
||||||
pub frags: [Option<PacketBuffer>; PACKET_FRAGMENT_COUNT_MAX],
|
pub frags: [Option<PooledPacketBuffer>; packet_constants::FRAGMENT_COUNT_MAX],
|
||||||
pub have: u8,
|
pub have: u8,
|
||||||
pub expecting: u8,
|
pub expecting: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentedPacket {
|
impl FragmentedPacket {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn new(ts: i64) -> Self {
|
pub fn new(ts: i64) -> Self {
|
||||||
debug_assert_eq!(PACKET_FRAGMENT_COUNT_MAX, 8);
|
|
||||||
Self {
|
Self {
|
||||||
ts_ticks: ts,
|
ts_ticks: ts,
|
||||||
frags: [None, None, None, None, None, None, None, None],
|
frags: [None, None, None, None, None, None, None, None],
|
||||||
|
@ -37,8 +35,8 @@ impl FragmentedPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a fragment to this fragment set and return true if all fragments are present.
|
/// Add a fragment to this fragment set and return true if all fragments are present.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn add_fragment(&mut self, frag: PacketBuffer, no: u8, expecting: u8) -> bool {
|
pub fn add_fragment(&mut self, frag: PooledPacketBuffer, no: u8, expecting: u8) -> bool {
|
||||||
self.frags.get_mut(no as usize).map_or(false, |entry| {
|
self.frags.get_mut(no as usize).map_or(false, |entry| {
|
||||||
/*
|
/*
|
||||||
* This works by setting bit N in the 'have' bit mask and then setting X bits
|
* This works by setting bit N in the 'have' bit mask and then setting X bits
|
||||||
|
|
|
@ -33,30 +33,6 @@ use crate::util::pool::{Pool, PoolFactory, Pooled};
|
||||||
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
|
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD};
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
|
|
||||||
/// Curve25519 and Ed25519
|
|
||||||
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
|
||||||
|
|
||||||
/// NIST P-384 ECDH and ECDSA
|
|
||||||
pub const IDENTITY_ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
|
||||||
|
|
||||||
/// Bit mask to include all algorithms.
|
|
||||||
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
|
||||||
|
|
||||||
/// Current sanity limit for the size of a marshaled Identity
|
|
||||||
/// This is padded just a little up to 512 and can be increased if new key types are ever added.
|
|
||||||
pub(crate) const MAX_MARSHAL_SIZE: usize = 25
|
|
||||||
+ ADDRESS_SIZE
|
|
||||||
+ C25519_PUBLIC_KEY_SIZE
|
|
||||||
+ ED25519_PUBLIC_KEY_SIZE
|
|
||||||
+ C25519_SECRET_KEY_SIZE
|
|
||||||
+ ED25519_SECRET_KEY_SIZE
|
|
||||||
+ P384_PUBLIC_KEY_SIZE
|
|
||||||
+ P384_PUBLIC_KEY_SIZE
|
|
||||||
+ P384_SECRET_KEY_SIZE
|
|
||||||
+ P384_SECRET_KEY_SIZE
|
|
||||||
+ P384_ECDSA_SIGNATURE_SIZE
|
|
||||||
+ ED25519_SIGNATURE_SIZE;
|
|
||||||
|
|
||||||
/// Secret keys associated with NIST P-384 public keys.
|
/// Secret keys associated with NIST P-384 public keys.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IdentityP384Secret {
|
pub struct IdentityP384Secret {
|
||||||
|
@ -122,6 +98,15 @@ fn concat_arrays_4<const A: usize, const B: usize, const C: usize, const D: usiz
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity {
|
impl Identity {
|
||||||
|
/// Curve25519 and Ed25519
|
||||||
|
pub const ALGORITHM_X25519: u8 = 0x01;
|
||||||
|
|
||||||
|
/// NIST P-384 ECDH and ECDSA
|
||||||
|
pub const ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
||||||
|
|
||||||
|
/// Bit mask to include all algorithms.
|
||||||
|
pub const ALGORITHM_ALL: u8 = 0xff;
|
||||||
|
|
||||||
/// Generate a new identity.
|
/// Generate a new identity.
|
||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
// First generate an identity with just x25519 keys and derive its address.
|
// First generate an identity with just x25519 keys and derive its address.
|
||||||
|
@ -184,7 +169,7 @@ impl Identity {
|
||||||
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
||||||
let _ = self_sign_buf.write_all(&self.c25519);
|
let _ = self_sign_buf.write_all(&self.c25519);
|
||||||
let _ = self_sign_buf.write_all(&self.ed25519);
|
let _ = self_sign_buf.write_all(&self.ed25519);
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P384);
|
self_sign_buf.push(Self::ALGORITHM_EC_NIST_P384);
|
||||||
let _ = self_sign_buf.write_all(p384_ecdh.public_key_bytes());
|
let _ = self_sign_buf.write_all(p384_ecdh.public_key_bytes());
|
||||||
let _ = self_sign_buf.write_all(p384_ecdsa.public_key_bytes());
|
let _ = self_sign_buf.write_all(p384_ecdsa.public_key_bytes());
|
||||||
|
|
||||||
|
@ -226,12 +211,11 @@ impl Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a bit mask of algorithms present in this identity.
|
/// Get a bit mask of algorithms present in this identity.
|
||||||
#[inline(always)]
|
|
||||||
pub fn algorithms(&self) -> u8 {
|
pub fn algorithms(&self) -> u8 {
|
||||||
if self.p384.is_some() {
|
if self.p384.is_some() {
|
||||||
IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P384
|
Self::ALGORITHM_X25519 | Self::ALGORITHM_EC_NIST_P384
|
||||||
} else {
|
} else {
|
||||||
IDENTITY_ALGORITHM_X25519
|
Self::ALGORITHM_X25519
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +230,7 @@ impl Identity {
|
||||||
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
||||||
let _ = self_sign_buf.write_all(&self.c25519);
|
let _ = self_sign_buf.write_all(&self.c25519);
|
||||||
let _ = self_sign_buf.write_all(&self.ed25519);
|
let _ = self_sign_buf.write_all(&self.ed25519);
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P384);
|
self_sign_buf.push(Self::ALGORITHM_EC_NIST_P384);
|
||||||
let _ = self_sign_buf.write_all(p384.ecdh.as_bytes());
|
let _ = self_sign_buf.write_all(p384.ecdh.as_bytes());
|
||||||
let _ = self_sign_buf.write_all(p384.ecdsa.as_bytes());
|
let _ = self_sign_buf.write_all(p384.ecdsa.as_bytes());
|
||||||
|
|
||||||
|
@ -313,12 +297,12 @@ impl Identity {
|
||||||
} else {
|
} else {
|
||||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
||||||
tmp.push(0);
|
tmp.push(0);
|
||||||
if secret.p384.is_some() && (include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 {
|
if secret.p384.is_some() && (include_algorithms & Self::ALGORITHM_EC_NIST_P384) != 0 {
|
||||||
*tmp.first_mut().unwrap() |= IDENTITY_ALGORITHM_EC_NIST_P384;
|
*tmp.first_mut().unwrap() |= Self::ALGORITHM_EC_NIST_P384;
|
||||||
let _ = tmp.write_all(&secret.p384.as_ref().unwrap().ecdsa.sign(msg));
|
let _ = tmp.write_all(&secret.p384.as_ref().unwrap().ecdsa.sign(msg));
|
||||||
}
|
}
|
||||||
if (include_algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
if (include_algorithms & Self::ALGORITHM_X25519) != 0 {
|
||||||
*tmp.first_mut().unwrap() |= IDENTITY_ALGORITHM_X25519;
|
*tmp.first_mut().unwrap() |= Self::ALGORITHM_X25519;
|
||||||
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||||
}
|
}
|
||||||
if tmp.len() > 1 {
|
if tmp.len() > 1 {
|
||||||
|
@ -347,14 +331,14 @@ impl Identity {
|
||||||
let algorithms = signature[0];
|
let algorithms = signature[0];
|
||||||
signature = &signature[1..];
|
signature = &signature[1..];
|
||||||
let mut passed = false; // makes sure we can't pass with an empty signature!
|
let mut passed = false; // makes sure we can't pass with an empty signature!
|
||||||
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && signature.len() >= P384_ECDSA_SIGNATURE_SIZE && self.p384.is_some() {
|
if (algorithms & Self::ALGORITHM_EC_NIST_P384) != 0 && signature.len() >= P384_ECDSA_SIGNATURE_SIZE && self.p384.is_some() {
|
||||||
if !self.p384.as_ref().unwrap().ecdsa.verify(msg, &signature[..P384_ECDSA_SIGNATURE_SIZE]) {
|
if !self.p384.as_ref().unwrap().ecdsa.verify(msg, &signature[..P384_ECDSA_SIGNATURE_SIZE]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
signature = &signature[P384_ECDSA_SIGNATURE_SIZE..];
|
signature = &signature[P384_ECDSA_SIGNATURE_SIZE..];
|
||||||
passed = true;
|
passed = true;
|
||||||
}
|
}
|
||||||
if (algorithms & IDENTITY_ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
if (algorithms & Self::ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
||||||
if !ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg) {
|
if !ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -367,22 +351,19 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_buffer_with_options(&self, include_algorithms: u8, include_private: bool) -> Buffer<MAX_MARSHAL_SIZE> {
|
pub fn to_buffer_with_options(&self, include_algorithms: u8, include_private: bool) -> Buffer<{ Self::MAX_MARSHAL_SIZE }> {
|
||||||
let mut b: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
let mut b: Buffer<{ Self::MAX_MARSHAL_SIZE }> = Buffer::new();
|
||||||
assert!(self.marshal_with_options(&mut b, include_algorithms, include_private).is_ok());
|
assert!(self.marshal_with_options(&mut b, include_algorithms, include_private).is_ok());
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
const P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE) as u16;
|
|
||||||
const P384_PUBLIC_ONLY_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u16;
|
|
||||||
|
|
||||||
pub fn marshal_with_options<const BL: usize>(&self, buf: &mut Buffer<BL>, include_algorithms: u8, include_private: bool) -> std::io::Result<()> {
|
pub fn marshal_with_options<const BL: usize>(&self, buf: &mut Buffer<BL>, include_algorithms: u8, include_private: bool) -> std::io::Result<()> {
|
||||||
let algorithms = self.algorithms() & include_algorithms;
|
let algorithms = self.algorithms() & include_algorithms;
|
||||||
let secret = self.secret.as_ref();
|
let secret = self.secret.as_ref();
|
||||||
|
|
||||||
buf.append_bytes_fixed(&self.address.to_bytes())?;
|
buf.append_bytes_fixed(&self.address.to_bytes())?;
|
||||||
|
|
||||||
if (algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
if (algorithms & Self::ALGORITHM_X25519) != 0 {
|
||||||
buf.append_u8(0x00)?; // 0x00 is used for X25519 for backward compatibility with v0 identities
|
buf.append_u8(0x00)?; // 0x00 is used for X25519 for backward compatibility with v0 identities
|
||||||
buf.append_bytes_fixed(&self.c25519)?;
|
buf.append_bytes_fixed(&self.c25519)?;
|
||||||
buf.append_bytes_fixed(&self.ed25519)?;
|
buf.append_bytes_fixed(&self.ed25519)?;
|
||||||
|
@ -396,53 +377,40 @@ impl Identity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && self.p384.is_some() {
|
/*
|
||||||
|
* For legacy backward compatibility, any key pairs and other material after the x25519
|
||||||
|
* keys are prefixed by 0x03 followed by the number of remaining bytes. This allows old nodes
|
||||||
|
* to parse HELLO normally and ignore the rest of the extended identity. It's ignored by
|
||||||
|
* newer nodes.
|
||||||
|
*/
|
||||||
|
buf.append_u8(0x03)?;
|
||||||
|
let remaining_data_size_field_at = buf.len();
|
||||||
|
buf.append_padding(0, 2)?;
|
||||||
|
|
||||||
|
if (algorithms & Self::ALGORITHM_EC_NIST_P384) != 0 && self.p384.is_some() {
|
||||||
let p384 = self.p384.as_ref().unwrap();
|
let p384 = self.p384.as_ref().unwrap();
|
||||||
|
let p384s = if include_private { secret.clone().map_or(None, |s| s.p384.as_ref()) } else { None };
|
||||||
|
|
||||||
/*
|
buf.append_u8(Self::ALGORITHM_EC_NIST_P384)?;
|
||||||
* For legacy backward compatibility, any key pairs and other material after the x25519
|
buf.append_varint(if p384s.is_some() {
|
||||||
* keys are prefixed by 0x03 followed by the total size of this section. This lets us parsimoniously
|
((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + (P384_SECRET_KEY_SIZE * 2)) as u64
|
||||||
* maintain backward compatibility with old versions' parsing of HELLO.
|
|
||||||
*
|
|
||||||
* In old HELLO the identity was followed by an InetAddress. The InetAddress encoding does support
|
|
||||||
* a variable length encoding for unknown "future use" address types. This consists of 0x03 followed
|
|
||||||
* by a 16-bit size.
|
|
||||||
*
|
|
||||||
* By mimicking this we can create a HELLO containing a new format identity and cleverly skip the
|
|
||||||
* InetAddress after it and old nodes will parse this as an old x25519 only identity followed by
|
|
||||||
* an unrecognized type InetAddress that will be ignored.
|
|
||||||
*
|
|
||||||
* Key agreement can then proceed using only x25519 keys.
|
|
||||||
*/
|
|
||||||
buf.append_u8(0x03)?;
|
|
||||||
let p384_has_private = if include_private && secret.map_or(false, |s| s.p384.is_some()) {
|
|
||||||
buf.append_u16(Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE + 1 + 2)?;
|
|
||||||
true
|
|
||||||
} else {
|
} else {
|
||||||
buf.append_u16(Self::P384_PUBLIC_ONLY_BUNDLE_SIZE + 1 + 2)?;
|
((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u64
|
||||||
false
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
buf.append_u8(IDENTITY_ALGORITHM_EC_NIST_P384)?;
|
|
||||||
if p384_has_private {
|
|
||||||
buf.append_u16(Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE)?;
|
|
||||||
} else {
|
|
||||||
buf.append_u16(Self::P384_PUBLIC_ONLY_BUNDLE_SIZE)?;
|
|
||||||
}
|
|
||||||
buf.append_bytes_fixed(p384.ecdh.as_bytes())?;
|
buf.append_bytes_fixed(p384.ecdh.as_bytes())?;
|
||||||
buf.append_bytes_fixed(p384.ecdsa.as_bytes())?;
|
buf.append_bytes_fixed(p384.ecdsa.as_bytes())?;
|
||||||
buf.append_bytes_fixed(&p384.ecdsa_self_signature)?;
|
buf.append_bytes_fixed(&p384.ecdsa_self_signature)?;
|
||||||
buf.append_bytes_fixed(&p384.ed25519_self_signature)?;
|
buf.append_bytes_fixed(&p384.ed25519_self_signature)?;
|
||||||
if p384_has_private {
|
if let Some(p384s) = p384s {
|
||||||
let p384s = secret.unwrap().p384.as_ref().unwrap();
|
|
||||||
buf.append_bytes_fixed(&p384s.ecdh.secret_key_bytes().0)?;
|
buf.append_bytes_fixed(&p384s.ecdh.secret_key_bytes().0)?;
|
||||||
buf.append_bytes_fixed(&p384s.ecdsa.secret_key_bytes().0)?;
|
buf.append_bytes_fixed(&p384s.ecdsa.secret_key_bytes().0)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A size of zero tells unmarshal() to stop.
|
buf.append_u8(0xff)?;
|
||||||
buf.append_u8(0x03)?;
|
|
||||||
buf.append_u16(0)?;
|
// Fill in the remaining data field earmarked above.
|
||||||
|
*buf.bytes_fixed_mut_at(remaining_data_size_field_at).unwrap() = ((buf.len() - remaining_data_size_field_at) as u16).to_be_bytes();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -452,12 +420,12 @@ impl Identity {
|
||||||
/// The include_algorithms bitmap controls which algorithms will be included, provided we have them.
|
/// The include_algorithms bitmap controls which algorithms will be included, provided we have them.
|
||||||
/// If include_private is true private keys will be included, again if we have them.
|
/// If include_private is true private keys will be included, again if we have them.
|
||||||
pub fn to_string_with_options(&self, include_algorithms: u8, include_private: bool) -> String {
|
pub fn to_string_with_options(&self, include_algorithms: u8, include_private: bool) -> String {
|
||||||
let include_p384 = self.p384.is_some() && ((include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0);
|
let include_p384 = self.p384.is_some() && ((include_algorithms & Self::ALGORITHM_EC_NIST_P384) != 0);
|
||||||
|
|
||||||
let mut s = String::with_capacity(MAX_MARSHAL_SIZE * 2);
|
let mut s = String::with_capacity(Self::MAX_MARSHAL_SIZE * 2);
|
||||||
s.push_str(self.address.to_string().as_str());
|
s.push_str(self.address.to_string().as_str());
|
||||||
|
|
||||||
if (include_algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
if (include_algorithms & Self::ALGORITHM_X25519) != 0 {
|
||||||
s.push_str(":0:"); // 0 used for x25519 for legacy reasons just like in marshal()
|
s.push_str(":0:"); // 0 used for x25519 for legacy reasons just like in marshal()
|
||||||
s.push_str(hex::to_string(&self.c25519).as_str());
|
s.push_str(hex::to_string(&self.c25519).as_str());
|
||||||
s.push_str(hex::to_string(&self.ed25519).as_str());
|
s.push_str(hex::to_string(&self.ed25519).as_str());
|
||||||
|
@ -493,7 +461,7 @@ impl Identity {
|
||||||
|
|
||||||
/// Get this identity in string form with all ciphers and with secrets (if present)
|
/// Get this identity in string form with all ciphers and with secrets (if present)
|
||||||
pub fn to_secret_string(&self) -> String {
|
pub fn to_secret_string(&self) -> String {
|
||||||
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, true)
|
self.to_string_with_options(Self::ALGORITHM_ALL, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +469,7 @@ impl ToString for Identity {
|
||||||
/// Get only the public portion of this identity as a string, including all cipher suites.
|
/// Get only the public portion of this identity as a string, including all cipher suites.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false)
|
self.to_string_with_options(Self::ALGORITHM_ALL, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,7 +535,7 @@ impl FromStr for Identity {
|
||||||
sha.update(&address.to_bytes());
|
sha.update(&address.to_bytes());
|
||||||
sha.update(&keys[0].as_slice()[0..64]);
|
sha.update(&keys[0].as_slice()[0..64]);
|
||||||
if !keys[2].is_empty() {
|
if !keys[2].is_empty() {
|
||||||
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]);
|
sha.update(&[Self::ALGORITHM_EC_NIST_P384]);
|
||||||
sha.update(&keys[2].as_slice()[0..(P384_PUBLIC_KEY_SIZE * 2)]);
|
sha.update(&keys[2].as_slice()[0..(P384_PUBLIC_KEY_SIZE * 2)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,11 +607,24 @@ impl FromStr for Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marshalable for Identity {
|
impl Marshalable for Identity {
|
||||||
const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE;
|
/// Current sanity limit for the size of a marshaled Identity
|
||||||
|
/// This is padded just a little up to 512 and can be increased if new key types are ever added.
|
||||||
|
const MAX_MARSHAL_SIZE: usize = 25
|
||||||
|
+ ADDRESS_SIZE
|
||||||
|
+ C25519_PUBLIC_KEY_SIZE
|
||||||
|
+ ED25519_PUBLIC_KEY_SIZE
|
||||||
|
+ C25519_SECRET_KEY_SIZE
|
||||||
|
+ ED25519_SECRET_KEY_SIZE
|
||||||
|
+ P384_PUBLIC_KEY_SIZE
|
||||||
|
+ P384_PUBLIC_KEY_SIZE
|
||||||
|
+ P384_SECRET_KEY_SIZE
|
||||||
|
+ P384_SECRET_KEY_SIZE
|
||||||
|
+ P384_ECDSA_SIGNATURE_SIZE
|
||||||
|
+ ED25519_SIGNATURE_SIZE;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
self.marshal_with_options(buf, IDENTITY_ALGORITHM_ALL, false)
|
self.marshal_with_options(buf, Self::ALGORITHM_ALL, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Identity> {
|
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Identity> {
|
||||||
|
@ -653,27 +634,26 @@ impl Marshalable for Identity {
|
||||||
}
|
}
|
||||||
let address = address.unwrap();
|
let address = address.unwrap();
|
||||||
|
|
||||||
let mut x25519_public: Option<([u8; C25519_PUBLIC_KEY_SIZE], [u8; ED25519_PUBLIC_KEY_SIZE])> = None;
|
let mut x25519_public: Option<(&[u8; C25519_PUBLIC_KEY_SIZE], &[u8; ED25519_PUBLIC_KEY_SIZE])> = None;
|
||||||
let mut x25519_secret: Option<([u8; C25519_SECRET_KEY_SIZE], [u8; ED25519_SECRET_KEY_SIZE])> = None;
|
let mut x25519_secret: Option<(&[u8; C25519_SECRET_KEY_SIZE], &[u8; ED25519_SECRET_KEY_SIZE])> = None;
|
||||||
let mut p384_ecdh_ecdsa_public: Option<(P384PublicKey, P384PublicKey, [u8; P384_ECDSA_SIGNATURE_SIZE], [u8; ED25519_SIGNATURE_SIZE])> = None;
|
let mut p384_ecdh_ecdsa_public: Option<(P384PublicKey, P384PublicKey, &[u8; P384_ECDSA_SIGNATURE_SIZE], &[u8; ED25519_SIGNATURE_SIZE])> = None;
|
||||||
let mut p384_ecdh_ecdsa_secret: Option<([u8; P384_SECRET_KEY_SIZE], [u8; P384_SECRET_KEY_SIZE])> = None;
|
let mut p384_ecdh_ecdsa_secret: Option<(&[u8; P384_SECRET_KEY_SIZE], &[u8; P384_SECRET_KEY_SIZE])> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let algorithm = buf.read_u8(cursor);
|
let mut algorithm = buf.read_u8(cursor)?;
|
||||||
if algorithm.is_err() {
|
if algorithm == 0 {
|
||||||
break;
|
algorithm = Self::ALGORITHM_X25519;
|
||||||
}
|
}
|
||||||
let algorithm = algorithm.unwrap();
|
|
||||||
match algorithm {
|
match algorithm {
|
||||||
0x00 | IDENTITY_ALGORITHM_X25519 => {
|
Self::ALGORITHM_X25519 => {
|
||||||
let a = buf.read_bytes_fixed::<C25519_PUBLIC_KEY_SIZE>(cursor)?;
|
let a = buf.read_bytes_fixed::<C25519_PUBLIC_KEY_SIZE>(cursor)?;
|
||||||
let b = buf.read_bytes_fixed::<ED25519_PUBLIC_KEY_SIZE>(cursor)?;
|
let b = buf.read_bytes_fixed::<ED25519_PUBLIC_KEY_SIZE>(cursor)?;
|
||||||
x25519_public = Some((a.clone(), b.clone()));
|
x25519_public = Some((a, b));
|
||||||
let sec_size = buf.read_u8(cursor)?;
|
let sec_size = buf.read_u8(cursor)?;
|
||||||
if sec_size == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 {
|
if sec_size == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 {
|
||||||
let a = buf.read_bytes_fixed::<C25519_SECRET_KEY_SIZE>(cursor)?;
|
let a = buf.read_bytes_fixed::<C25519_SECRET_KEY_SIZE>(cursor)?;
|
||||||
let b = buf.read_bytes_fixed::<ED25519_SECRET_KEY_SIZE>(cursor)?;
|
let b = buf.read_bytes_fixed::<ED25519_SECRET_KEY_SIZE>(cursor)?;
|
||||||
x25519_secret = Some((a.clone(), b.clone()));
|
x25519_secret = Some((a, b));
|
||||||
} else if sec_size != 0 {
|
} else if sec_size != 0 {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid x25519 secret"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid x25519 secret"));
|
||||||
}
|
}
|
||||||
|
@ -682,43 +662,38 @@ impl Marshalable for Identity {
|
||||||
// This isn't an algorithm; each algorithm is identified by just one bit. This
|
// This isn't an algorithm; each algorithm is identified by just one bit. This
|
||||||
// indicates the total size of the section after the x25519 keys for backward
|
// indicates the total size of the section after the x25519 keys for backward
|
||||||
// compatibility. See comments in marshal(). New versions can ignore this field.
|
// compatibility. See comments in marshal(). New versions can ignore this field.
|
||||||
let size = buf.read_u16(cursor)?;
|
*cursor += 2;
|
||||||
if size == 0 {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
*cursor += size as usize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IDENTITY_ALGORITHM_EC_NIST_P384 => {
|
Self::ALGORITHM_EC_NIST_P384 => {
|
||||||
let size = buf.read_u16(cursor)?;
|
let field_length = buf.read_varint(cursor)?;
|
||||||
if size < Self::P384_PUBLIC_ONLY_BUNDLE_SIZE {
|
let has_secret = if field_length == ((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u64 {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
false
|
||||||
}
|
} else {
|
||||||
let a = buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?;
|
if field_length == ((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + (P384_SECRET_KEY_SIZE * 2)) as u64 {
|
||||||
let b = buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?;
|
true
|
||||||
|
} else {
|
||||||
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let a = P384PublicKey::from_bytes(buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?);
|
||||||
|
let b = P384PublicKey::from_bytes(buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?);
|
||||||
let c = buf.read_bytes_fixed::<P384_ECDSA_SIGNATURE_SIZE>(cursor)?;
|
let c = buf.read_bytes_fixed::<P384_ECDSA_SIGNATURE_SIZE>(cursor)?;
|
||||||
let d = buf.read_bytes_fixed::<ED25519_SIGNATURE_SIZE>(cursor)?;
|
let d = buf.read_bytes_fixed::<ED25519_SIGNATURE_SIZE>(cursor)?;
|
||||||
let a = P384PublicKey::from_bytes(a);
|
|
||||||
let b = P384PublicKey::from_bytes(b);
|
|
||||||
if a.is_none() || b.is_none() {
|
if a.is_none() || b.is_none() {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
||||||
}
|
}
|
||||||
p384_ecdh_ecdsa_public = Some((a.unwrap(), b.unwrap(), c.clone(), d.clone()));
|
p384_ecdh_ecdsa_public = Some((a.unwrap(), b.unwrap(), c, d));
|
||||||
if size > Self::P384_PUBLIC_ONLY_BUNDLE_SIZE {
|
|
||||||
if size != Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE {
|
if has_secret {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 secret key"));
|
|
||||||
}
|
|
||||||
let a = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
let a = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
||||||
let b = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
let b = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
||||||
p384_ecdh_ecdsa_secret = Some((a.clone(), b.clone()));
|
p384_ecdh_ecdsa_secret = Some((a, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0xff => break,
|
||||||
_ => {
|
_ => {
|
||||||
// Skip any unrecognized cipher suites, all of which will be prefixed by a size.
|
*cursor += buf.read_varint(cursor)? as usize;
|
||||||
*cursor += buf.read_u16(cursor)? as usize;
|
|
||||||
if *cursor > buf.len() {
|
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid field length"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -730,11 +705,11 @@ impl Marshalable for Identity {
|
||||||
|
|
||||||
let mut sha = SHA512::new();
|
let mut sha = SHA512::new();
|
||||||
sha.update(&address.to_bytes());
|
sha.update(&address.to_bytes());
|
||||||
sha.update(&x25519_public.0);
|
sha.update(x25519_public.0);
|
||||||
sha.update(&x25519_public.1);
|
sha.update(x25519_public.1);
|
||||||
if p384_ecdh_ecdsa_public.is_some() {
|
if p384_ecdh_ecdsa_public.is_some() {
|
||||||
let p384 = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
let p384 = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
||||||
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]);
|
sha.update(&[Self::ALGORITHM_EC_NIST_P384]);
|
||||||
sha.update(p384.0.as_bytes());
|
sha.update(p384.0.as_bytes());
|
||||||
sha.update(p384.1.as_bytes());
|
sha.update(p384.1.as_bytes());
|
||||||
}
|
}
|
||||||
|
@ -756,8 +731,8 @@ impl Marshalable for Identity {
|
||||||
},
|
},
|
||||||
secret: if x25519_secret.is_some() {
|
secret: if x25519_secret.is_some() {
|
||||||
let x25519_secret = x25519_secret.unwrap();
|
let x25519_secret = x25519_secret.unwrap();
|
||||||
let c25519_secret = C25519KeyPair::from_bytes(&x25519_public.0, &x25519_secret.0);
|
let c25519_secret = C25519KeyPair::from_bytes(x25519_public.0, x25519_secret.0);
|
||||||
let ed25519_secret = Ed25519KeyPair::from_bytes(&x25519_public.1, &x25519_secret.1);
|
let ed25519_secret = Ed25519KeyPair::from_bytes(x25519_public.1, x25519_secret.1);
|
||||||
if c25519_secret.is_none() || ed25519_secret.is_none() {
|
if c25519_secret.is_none() || ed25519_secret.is_none() {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 public key invalid"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 public key invalid"));
|
||||||
}
|
}
|
||||||
|
@ -767,8 +742,8 @@ impl Marshalable for Identity {
|
||||||
p384: if p384_ecdh_ecdsa_secret.is_some() && p384_ecdh_ecdsa_public.is_some() {
|
p384: if p384_ecdh_ecdsa_secret.is_some() && p384_ecdh_ecdsa_public.is_some() {
|
||||||
let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
||||||
let p384_ecdh_ecdsa_secret = p384_ecdh_ecdsa_secret.as_ref().unwrap();
|
let p384_ecdh_ecdsa_secret = p384_ecdh_ecdsa_secret.as_ref().unwrap();
|
||||||
let p384_ecdh_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.0.as_bytes(), &p384_ecdh_ecdsa_secret.0);
|
let p384_ecdh_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.0.as_bytes(), p384_ecdh_ecdsa_secret.0);
|
||||||
let p384_ecdsa_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.1.as_bytes(), &p384_ecdh_ecdsa_secret.1);
|
let p384_ecdsa_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.1.as_bytes(), p384_ecdh_ecdsa_secret.1);
|
||||||
if p384_ecdh_secret.is_none() || p384_ecdsa_secret.is_none() {
|
if p384_ecdh_secret.is_none() || p384_ecdsa_secret.is_none() {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p384 secret key invalid"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p384 secret key invalid"));
|
||||||
}
|
}
|
||||||
|
@ -823,10 +798,10 @@ impl Serialize for Identity {
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
if serializer.is_human_readable() {
|
if serializer.is_human_readable() {
|
||||||
serializer.serialize_str(self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false).as_str())
|
serializer.serialize_str(self.to_string_with_options(Self::ALGORITHM_ALL, false).as_str())
|
||||||
} else {
|
} else {
|
||||||
let mut tmp: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
let mut tmp: Buffer<{ Self::MAX_MARSHAL_SIZE }> = Buffer::new();
|
||||||
assert!(self.marshal_with_options(&mut tmp, IDENTITY_ALGORITHM_ALL, false).is_ok());
|
assert!(self.marshal_with_options(&mut tmp, Self::ALGORITHM_ALL, false).is_ok());
|
||||||
serializer.serialize_bytes(tmp.as_bytes())
|
serializer.serialize_bytes(tmp.as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,8 +820,8 @@ impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
if v.len() <= MAX_MARSHAL_SIZE {
|
if v.len() <= Identity::MAX_MARSHAL_SIZE {
|
||||||
let mut tmp: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
let mut tmp: Buffer<{ Identity::MAX_MARSHAL_SIZE }> = Buffer::new();
|
||||||
let _ = tmp.append_bytes(v);
|
let _ = tmp.append_bytes(v);
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
Identity::unmarshal(&tmp, &mut cursor).map_err(|e| E::custom(e.to_string()))
|
Identity::unmarshal(&tmp, &mut cursor).map_err(|e| E::custom(e.to_string()))
|
||||||
|
@ -960,7 +935,7 @@ pub(crate) fn purge_verification_memory_pool() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::util::marshalable::Marshalable;
|
use crate::util::marshalable::Marshalable;
|
||||||
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
use crate::vl1::identity::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
@ -996,14 +971,17 @@ mod tests {
|
||||||
let gen = Identity::generate();
|
let gen = Identity::generate();
|
||||||
assert!(gen.agree(&gen).is_some());
|
assert!(gen.agree(&gen).is_some());
|
||||||
assert!(gen.validate_identity());
|
assert!(gen.validate_identity());
|
||||||
let bytes = gen.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true);
|
let bytes = gen.to_buffer_with_options(Identity::ALGORITHM_ALL, true);
|
||||||
let string = gen.to_string_with_options(IDENTITY_ALGORITHM_ALL, true);
|
let string = gen.to_string_with_options(Identity::ALGORITHM_ALL, true);
|
||||||
//println!("{}", string);
|
|
||||||
assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen));
|
assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen));
|
||||||
|
|
||||||
let mut cursor = 0_usize;
|
let mut cursor = 0_usize;
|
||||||
assert!(Identity::unmarshal(&bytes, &mut cursor).unwrap().eq(&gen));
|
let gen_unmarshaled = Identity::unmarshal(&bytes, &mut cursor).unwrap();
|
||||||
cursor = 0;
|
assert!(gen_unmarshaled.secret.is_some());
|
||||||
assert!(Identity::unmarshal(&bytes, &mut cursor).unwrap().secret.is_some());
|
if !gen_unmarshaled.eq(&gen) {
|
||||||
|
println!("{} != {}", hex::to_string(&gen_unmarshaled.fingerprint), hex::to_string(&gen.fingerprint));
|
||||||
|
}
|
||||||
|
|
||||||
assert!(Identity::from_str(string.as_str()).unwrap().secret.is_some());
|
assert!(Identity::from_str(string.as_str()).unwrap().secret.is_some());
|
||||||
|
|
||||||
let gen2 = Identity::generate();
|
let gen2 = Identity::generate();
|
||||||
|
@ -1012,18 +990,18 @@ mod tests {
|
||||||
|
|
||||||
for id_str in GOOD_V0_IDENTITIES {
|
for id_str in GOOD_V0_IDENTITIES {
|
||||||
let mut id = Identity::from_str(id_str).unwrap();
|
let mut id = Identity::from_str(id_str).unwrap();
|
||||||
assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, true).as_str(), id_str);
|
assert_eq!(id.to_string_with_options(Identity::ALGORITHM_ALL, true).as_str(), id_str);
|
||||||
|
|
||||||
assert!(id.validate_identity());
|
assert!(id.validate_identity());
|
||||||
assert!(id.p384.is_none());
|
assert!(id.p384.is_none());
|
||||||
|
|
||||||
let idb = id.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true);
|
let idb = id.to_buffer_with_options(Identity::ALGORITHM_ALL, true);
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap();
|
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap();
|
||||||
assert!(id == id_unmarshal);
|
assert!(id == id_unmarshal);
|
||||||
assert!(id_unmarshal.secret.is_some());
|
assert!(id_unmarshal.secret.is_some());
|
||||||
|
|
||||||
let idb2 = id_unmarshal.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, false);
|
let idb2 = id_unmarshal.to_buffer_with_options(Identity::ALGORITHM_ALL, false);
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap();
|
let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap();
|
||||||
assert!(id_unmarshal2 == id_unmarshal);
|
assert!(id_unmarshal2 == id_unmarshal);
|
||||||
|
@ -1043,18 +1021,18 @@ mod tests {
|
||||||
}
|
}
|
||||||
for id_str in GOOD_V1_IDENTITIES {
|
for id_str in GOOD_V1_IDENTITIES {
|
||||||
let id = Identity::from_str(id_str).unwrap();
|
let id = Identity::from_str(id_str).unwrap();
|
||||||
assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, true).as_str(), id_str);
|
assert_eq!(id.to_string_with_options(Identity::ALGORITHM_ALL, true).as_str(), id_str);
|
||||||
|
|
||||||
assert!(id.validate_identity());
|
assert!(id.validate_identity());
|
||||||
assert!(id.p384.is_some());
|
assert!(id.p384.is_some());
|
||||||
|
|
||||||
let idb = id.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true);
|
let idb = id.to_buffer_with_options(Identity::ALGORITHM_ALL, true);
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap();
|
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap();
|
||||||
assert!(id == id_unmarshal);
|
assert!(id == id_unmarshal);
|
||||||
|
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
let idb2 = id_unmarshal.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, false);
|
let idb2 = id_unmarshal.to_buffer_with_options(Identity::ALGORITHM_ALL, false);
|
||||||
let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap();
|
let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap();
|
||||||
assert!(id_unmarshal2 == id_unmarshal);
|
assert!(id_unmarshal2 == id_unmarshal);
|
||||||
assert!(id_unmarshal2 == id);
|
assert!(id_unmarshal2 == id);
|
||||||
|
|
|
@ -267,7 +267,7 @@ impl Default for InetAddress {
|
||||||
impl std::fmt::Debug for InetAddress {
|
impl std::fmt::Debug for InetAddress {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_str(&self.to_string())
|
f.write_str(self.to_string().as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +471,17 @@ impl InetAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a Rust stdlib SocketAddr structure from this InetAddress.
|
||||||
|
pub fn to_socketaddr(&self) -> Option<SocketAddr> {
|
||||||
|
unsafe {
|
||||||
|
match self.sa.sa_family {
|
||||||
|
AF_INET => Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(self.sin.sin_addr.s_addr.to_ne_bytes()), u16::from_be(self.sin.sin_port as u16)))),
|
||||||
|
AF_INET6 => Some(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(self.sin6.sin6_addr.s6_addr), u16::from_be(self.sin6.sin6_port as u16), 0, 0))),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the IP port for this InetAddress.
|
/// Get the IP port for this InetAddress.
|
||||||
pub fn port(&self) -> u16 {
|
pub fn port(&self) -> u16 {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -497,6 +508,47 @@ impl InetAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether this IP address is within a CIDR range
|
||||||
|
///
|
||||||
|
/// The argument is a CIDR range in which the port is interpreted as the number of bits, e.g. 10.0.0.0/24.
|
||||||
|
pub fn is_within(&self, cidr: &InetAddress) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if self.sa.sa_family == cidr.sa.sa_family {
|
||||||
|
let mut cidr_bits = cidr.port() as u32;
|
||||||
|
match self.sa.sa_family as u8 {
|
||||||
|
AF_INET => {
|
||||||
|
if cidr_bits <= 32 {
|
||||||
|
let discard_bits = 32 - cidr_bits;
|
||||||
|
if u32::from_be(self.sin.sin_addr.s_addr as u32).wrapping_shr(discard_bits) == u32::from_be(cidr.sin.sin_addr.s_addr as u32).wrapping_shr(discard_bits) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AF_INET6 => {
|
||||||
|
if cidr_bits <= 128 {
|
||||||
|
let a = &self.sin6.sin6_addr.s6_addr;
|
||||||
|
let b = &cidr.sin6.sin6_addr.s6_addr;
|
||||||
|
let mut p = 0;
|
||||||
|
while cidr_bits >= 8 {
|
||||||
|
cidr_bits -= 8;
|
||||||
|
if a[p] != b[p] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
let discard_bits = 8 - cidr_bits;
|
||||||
|
if a[p].wrapping_shr(discard_bits) == b[p].wrapping_shr(discard_bits) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get this IP address's scope as per RFC documents and what is advertised via BGP.
|
/// Get this IP address's scope as per RFC documents and what is advertised via BGP.
|
||||||
pub fn scope(&self) -> IpScope {
|
pub fn scope(&self) -> IpScope {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -29,12 +29,12 @@ impl Debug for MAC {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MAC {
|
impl MAC {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_u64(i: u64) -> Option<MAC> {
|
pub fn from_u64(i: u64) -> Option<MAC> {
|
||||||
NonZeroU64::new(i & 0xffffffffffff).map(|i| MAC(i))
|
NonZeroU64::new(i & 0xffffffffffff).map(|i| MAC(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
||||||
if b.len() >= 6 {
|
if b.len() >= 6 {
|
||||||
NonZeroU64::new((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64).map(|i| MAC(i))
|
NonZeroU64::new((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64).map(|i| MAC(i))
|
||||||
|
@ -43,12 +43,12 @@ impl MAC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn from_bytes_fixed(b: &[u8; 6]) -> Option<MAC> {
|
pub fn from_bytes_fixed(b: &[u8; 6]) -> Option<MAC> {
|
||||||
NonZeroU64::new((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64).map(|i| MAC(i))
|
NonZeroU64::new((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64).map(|i| MAC(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn to_bytes(&self) -> [u8; 6] {
|
pub fn to_bytes(&self) -> [u8; 6] {
|
||||||
let i = self.0.get();
|
let i = self.0.get();
|
||||||
[(i >> 40) as u8, (i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
[(i >> 40) as u8, (i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
||||||
|
@ -63,12 +63,12 @@ impl MAC {
|
||||||
impl Marshalable for MAC {
|
impl Marshalable for MAC {
|
||||||
const MAX_MARSHAL_SIZE: usize = 6;
|
const MAX_MARSHAL_SIZE: usize = 6;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
buf.append_bytes(&self.0.get().to_be_bytes()[2..])
|
buf.append_bytes(&self.0.get().to_be_bytes()[2..])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
||||||
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a))
|
Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,31 +6,30 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod endpoint;
|
|
||||||
pub mod identity;
|
|
||||||
pub mod inetaddress;
|
|
||||||
|
|
||||||
mod address;
|
mod address;
|
||||||
mod dictionary;
|
mod dictionary;
|
||||||
|
mod endpoint;
|
||||||
|
mod fragmentedpacket;
|
||||||
|
mod identity;
|
||||||
|
mod inetaddress;
|
||||||
mod mac;
|
mod mac;
|
||||||
mod path;
|
mod path;
|
||||||
mod peer;
|
mod peer;
|
||||||
mod rootset;
|
mod rootset;
|
||||||
|
mod symmetricsecret;
|
||||||
|
mod whoisqueue;
|
||||||
|
|
||||||
pub(crate) mod fragmentedpacket;
|
|
||||||
pub(crate) mod node;
|
pub(crate) mod node;
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) mod protocol;
|
pub(crate) mod protocol;
|
||||||
pub(crate) mod symmetricsecret;
|
|
||||||
pub(crate) mod whoisqueue;
|
|
||||||
|
|
||||||
pub use address::Address;
|
pub use address::Address;
|
||||||
pub use dictionary::Dictionary;
|
pub use dictionary::Dictionary;
|
||||||
pub use endpoint::Endpoint;
|
pub use endpoint::Endpoint;
|
||||||
pub use identity::Identity;
|
pub use identity::*;
|
||||||
pub use inetaddress::InetAddress;
|
pub use inetaddress::{InetAddress, IpScope};
|
||||||
pub use mac::MAC;
|
pub use mac::MAC;
|
||||||
pub use node::{Node, SystemInterface};
|
pub use node::{InnerProtocolInterface, Node, SystemInterface};
|
||||||
pub use path::Path;
|
pub use path::Path;
|
||||||
pub use peer::Peer;
|
pub use peer::Peer;
|
||||||
pub use rootset::{Root, RootSet};
|
pub use rootset::{Root, RootSet};
|
||||||
|
|
|
@ -7,31 +7,34 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::num::NonZeroI64;
|
use std::num::NonZeroI64;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
|
||||||
use crate::error::InvalidParameterError;
|
use crate::error::InvalidParameterError;
|
||||||
use crate::util::buffer::Buffer;
|
|
||||||
use crate::util::gate::IntervalGate;
|
use crate::util::gate::IntervalGate;
|
||||||
use crate::vl1::path::Path;
|
use crate::vl1::path::Path;
|
||||||
use crate::vl1::peer::Peer;
|
use crate::vl1::peer::Peer;
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::vl1::whoisqueue::{QueuedPacket, WhoisQueue};
|
use crate::vl1::whoisqueue::{QueuedPacket, WhoisQueue};
|
||||||
use crate::vl1::{Address, Endpoint, Identity, RootSet};
|
use crate::vl1::{Address, Endpoint, Identity, RootSet};
|
||||||
use crate::{PacketBuffer, PacketBufferFactory, PacketBufferPool};
|
|
||||||
|
|
||||||
/// Trait implemented by external code to handle events and provide an interface to the system or application.
|
/// Trait implemented by external code to handle events and provide an interface to the system or application.
|
||||||
///
|
///
|
||||||
/// These methods are basically callbacks that the core calls to request or transmit things. They are called
|
/// These methods are basically callbacks that the core calls to request or transmit things. They are called
|
||||||
/// during calls to things like wire_recieve() and do_background_tasks().
|
/// during calls to things like wire_recieve() and do_background_tasks().
|
||||||
pub trait SystemInterface: Sync + Send {
|
pub trait SystemInterface: Sync + Send + 'static {
|
||||||
|
/// Type for local system sockets.
|
||||||
|
type LocalSocket: Sync + Send + Sized + Hash + PartialEq + Eq + Clone;
|
||||||
|
|
||||||
|
/// Type for local system interfaces.
|
||||||
|
type LocalInterface: Sync + Send + Sized + Hash + PartialEq + Eq + Clone;
|
||||||
|
|
||||||
/// Node is up and ready for operation.
|
/// Node is up and ready for operation.
|
||||||
fn event_node_is_up(&self);
|
fn event_node_is_up(&self);
|
||||||
|
|
||||||
|
@ -47,28 +50,35 @@ pub trait SystemInterface: Sync + Send {
|
||||||
/// VL1 core generated a security warning.
|
/// VL1 core generated a security warning.
|
||||||
fn event_security_warning(&self, warning: &str);
|
fn event_security_warning(&self, warning: &str);
|
||||||
|
|
||||||
|
/// Check a local socket for validity.
|
||||||
|
///
|
||||||
|
/// This could return false if the socket's interface no longer exists, its port has been
|
||||||
|
/// unbound, etc.
|
||||||
|
fn local_socket_is_valid(&self, socket: &Self::LocalSocket) -> bool;
|
||||||
|
|
||||||
/// Load this node's identity from the data store.
|
/// Load this node's identity from the data store.
|
||||||
fn load_node_identity(&self) -> Option<Vec<u8>>;
|
fn load_node_identity(&self) -> Option<Identity>;
|
||||||
|
|
||||||
/// Save this node's identity.
|
/// Save this node's identity.
|
||||||
fn save_node_identity(&self, id: &Identity);
|
fn save_node_identity(&self, id: &Identity);
|
||||||
|
|
||||||
/// Called to send a packet over the physical network (virtual -> physical).
|
/// Called to send a packet over the physical network (virtual -> physical).
|
||||||
///
|
///
|
||||||
/// This may return false if the send definitely failed, and may return true if the send
|
/// This may return false if the send definitely failed. Otherwise it should return true
|
||||||
/// succeeded or may have succeeded (in the case of UDP and similar).
|
/// which indicates possible success but with no guarantee (UDP semantics).
|
||||||
///
|
///
|
||||||
/// If local socket and/or local interface are None, the sending code should make its
|
/// If a local socket is specified the implementation should send from that socket or not
|
||||||
/// own decision about what local socket or interface to use. It may send on a random
|
/// at all (returning false). If a local interface is specified the implementation should
|
||||||
/// one, the best fit, or all at once.
|
/// send from all sockets on that interface. If neither is specified the packet may be
|
||||||
|
/// sent on all sockets or a random subset.
|
||||||
///
|
///
|
||||||
/// If packet TTL is non-zero it should be used to set the packet TTL for outgoing packets
|
/// For endpoint types that support a packet TTL, the implementation may set the TTL
|
||||||
/// for supported protocols such as UDP, but otherwise it can be ignored. It can also be
|
/// if the 'ttl' parameter is not zero. If the parameter is zero or TTL setting is not
|
||||||
/// ignored if the platform does not support setting the TTL.
|
/// supported, the default TTL should be used.
|
||||||
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>, data: &[&[u8]], packet_ttl: u8) -> bool;
|
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<&Self::LocalSocket>, local_interface: Option<&Self::LocalInterface>, data: &[&[u8]], packet_ttl: u8) -> bool;
|
||||||
|
|
||||||
/// Called to check and see if a physical address should be used for ZeroTier traffic to a node.
|
/// Called to check and see if a physical address should be used for ZeroTier traffic to a node.
|
||||||
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> bool;
|
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<&Self::LocalSocket>, local_interface: Option<&Self::LocalInterface>) -> bool;
|
||||||
|
|
||||||
/// Called to look up any statically defined or memorized paths to known nodes.
|
/// Called to look up any statically defined or memorized paths to known nodes.
|
||||||
fn get_path_hints(&self, id: &Identity) -> Option<Vec<(Endpoint, Option<NonZeroI64>, Option<NonZeroI64>)>>;
|
fn get_path_hints(&self, id: &Identity) -> Option<Vec<(Endpoint, Option<NonZeroI64>, Option<NonZeroI64>)>>;
|
||||||
|
@ -86,19 +96,19 @@ pub trait SystemInterface: Sync + Send {
|
||||||
///
|
///
|
||||||
/// This is implemented by Switch in VL2. It's usually not used outside of VL2 in the core but
|
/// 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.
|
/// it could also be implemented for testing or "off label" use of VL1 to carry different protocols.
|
||||||
pub trait InnerProtocolInterface: Sync + Send {
|
pub trait InnerProtocolInterface: Sync + Send + 'static {
|
||||||
/// Handle a packet, returning true if it was handled by the next layer.
|
/// 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().
|
/// Do not attempt to handle OK or ERROR. Instead implement handle_ok() and handle_error().
|
||||||
/// The return values of these must follow the same semantic of returning true if the message
|
/// The return values of these must follow the same semantic of returning true if the message
|
||||||
/// was handled.
|
/// was handled.
|
||||||
fn handle_packet(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool;
|
fn handle_packet<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &PacketBuffer) -> bool;
|
||||||
|
|
||||||
/// Handle errors, returning true if the error was recognized.
|
/// Handle errors, returning true if the error was recognized.
|
||||||
fn handle_error(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, error_code: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool;
|
fn handle_error<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, error_code: u8, payload: &PacketBuffer, cursor: &mut usize) -> bool;
|
||||||
|
|
||||||
/// Handle an OK, returing true if the OK was recognized.
|
/// Handle an OK, returing true if the OK was recognized.
|
||||||
fn handle_ok(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool;
|
fn handle_ok<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, payload: &PacketBuffer, cursor: &mut usize) -> bool;
|
||||||
|
|
||||||
/// Check if this remote peer has a trust relationship with this node.
|
/// Check if this remote peer has a trust relationship with this node.
|
||||||
///
|
///
|
||||||
|
@ -107,39 +117,26 @@ pub trait InnerProtocolInterface: Sync + Send {
|
||||||
fn has_trust_relationship(&self, id: &Identity) -> bool;
|
fn has_trust_relationship(&self, id: &Identity) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for objects that are serviced in the background loop (the actual loop is external).
|
|
||||||
pub(crate) trait BackgroundServicable {
|
|
||||||
/// How often in milliseconds to call service().
|
|
||||||
const SERVICE_INTERVAL_MS: i64;
|
|
||||||
|
|
||||||
/// Service object and return true if the object should be retained (if applicable).
|
|
||||||
fn service<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// How often to check the root cluster definitions against the root list and update.
|
/// How often to check the root cluster definitions against the root list and update.
|
||||||
const ROOT_SYNC_INTERVAL_MS: i64 = 1000;
|
const ROOT_SYNC_INTERVAL_MS: i64 = 1000;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref BACKGROUND_TASK_INTERVAL: Duration = Duration::from_millis((ROOT_SYNC_INTERVAL_MS.min(WhoisQueue::SERVICE_INTERVAL_MS).min(Path::SERVICE_INTERVAL_MS).min(Peer::SERVICE_INTERVAL_MS) as u64) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct BackgroundTaskIntervals {
|
struct BackgroundTaskIntervals {
|
||||||
whois: IntervalGate<{ WhoisQueue::SERVICE_INTERVAL_MS }>,
|
whois: IntervalGate<{ crate::vl1::whoisqueue::SERVICE_INTERVAL_MS }>,
|
||||||
paths: IntervalGate<{ Path::SERVICE_INTERVAL_MS }>,
|
paths: IntervalGate<{ crate::vl1::path::SERVICE_INTERVAL_MS }>,
|
||||||
peers: IntervalGate<{ Peer::SERVICE_INTERVAL_MS }>,
|
peers: IntervalGate<{ crate::vl1::peer::SERVICE_INTERVAL_MS }>,
|
||||||
root_sync: IntervalGate<ROOT_SYNC_INTERVAL_MS>,
|
root_sync: IntervalGate<ROOT_SYNC_INTERVAL_MS>,
|
||||||
root_hello: IntervalGate<ROOT_HELLO_INTERVAL>,
|
root_hello: IntervalGate<ROOT_HELLO_INTERVAL>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RootInfo {
|
struct RootInfo<SI: SystemInterface> {
|
||||||
roots: HashMap<Arc<Peer>, Vec<Endpoint>>,
|
roots: HashMap<Arc<Peer<SI>>, Vec<Endpoint>>,
|
||||||
sets: HashMap<String, RootSet>,
|
sets: HashMap<String, RootSet>,
|
||||||
sets_modified: bool,
|
sets_modified: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A VL1 global P2P network node.
|
/// A VL1 global P2P network node.
|
||||||
pub struct Node {
|
pub struct Node<SI: SystemInterface> {
|
||||||
/// A random ID generated to identify this particular running instance.
|
/// A random ID generated to identify this particular running instance.
|
||||||
pub instance_id: u64,
|
pub instance_id: u64,
|
||||||
|
|
||||||
|
@ -150,16 +147,16 @@ pub struct Node {
|
||||||
intervals: Mutex<BackgroundTaskIntervals>,
|
intervals: Mutex<BackgroundTaskIntervals>,
|
||||||
|
|
||||||
/// Canonicalized network paths, held as Weak<> to be automatically cleaned when no longer in use.
|
/// Canonicalized network paths, held as Weak<> to be automatically cleaned when no longer in use.
|
||||||
paths: DashMap<(u64, u64), Weak<Path>>,
|
paths: DashMap<Endpoint, parking_lot::RwLock<HashMap<SI::LocalSocket, Arc<Path<SI>>>>>,
|
||||||
|
|
||||||
/// Peers with which we are currently communicating.
|
/// Peers with which we are currently communicating.
|
||||||
peers: DashMap<Address, Arc<Peer>>,
|
peers: DashMap<Address, Arc<Peer<SI>>>,
|
||||||
|
|
||||||
/// This node's trusted roots, sorted in ascending order of quality/preference, and cluster definitions.
|
/// This node's trusted roots, sorted in ascending order of quality/preference, and cluster definitions.
|
||||||
roots: Mutex<RootInfo>,
|
roots: Mutex<RootInfo<SI>>,
|
||||||
|
|
||||||
/// Current best root.
|
/// Current best root.
|
||||||
best_root: RwLock<Option<Arc<Peer>>>,
|
best_root: RwLock<Option<Arc<Peer<SI>>>>,
|
||||||
|
|
||||||
/// Identity lookup queue, also holds packets waiting on a lookup.
|
/// Identity lookup queue, also holds packets waiting on a lookup.
|
||||||
whois: WhoisQueue,
|
whois: WhoisQueue,
|
||||||
|
@ -168,12 +165,12 @@ pub struct Node {
|
||||||
buffer_pool: PacketBufferPool,
|
buffer_pool: PacketBufferPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl<SI: SystemInterface> Node<SI> {
|
||||||
/// Create a new Node.
|
/// Create a new Node.
|
||||||
pub fn new<SI: SystemInterface>(si: &SI, auto_generate_identity: bool) -> Result<Self, InvalidParameterError> {
|
pub fn new(si: &SI, auto_generate_identity: bool) -> Result<Self, InvalidParameterError> {
|
||||||
let mut id = {
|
let mut id = {
|
||||||
let id_str = si.load_node_identity();
|
let id = si.load_node_identity();
|
||||||
if id_str.is_none() {
|
if id.is_none() {
|
||||||
if !auto_generate_identity {
|
if !auto_generate_identity {
|
||||||
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
|
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,13 +179,7 @@ impl Node {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let id_str = String::from_utf8_lossy(id_str.as_ref().unwrap().as_slice());
|
id.unwrap()
|
||||||
let id = Identity::from_str(id_str.as_ref().trim());
|
|
||||||
if id.is_err() {
|
|
||||||
return Err(InvalidParameterError("invalid identity"));
|
|
||||||
} else {
|
|
||||||
id.unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,31 +207,21 @@ impl Node {
|
||||||
|
|
||||||
/// Get a packet buffer that will automatically check itself back into the pool on drop.
|
/// Get a packet buffer that will automatically check itself back into the pool on drop.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
pub fn get_packet_buffer(&self) -> PooledPacketBuffer {
|
||||||
self.buffer_pool.get()
|
self.buffer_pool.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a peer by address.
|
/// Get a peer by address.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> {
|
pub fn peer(&self, a: Address) -> Option<Arc<Peer<SI>>> {
|
||||||
self.peers.get(&a).map(|peer| peer.value().clone())
|
self.peers.get(&a).map(|peer| peer.value().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all peers currently in the peer cache.
|
|
||||||
pub fn peers(&self) -> Vec<Arc<Peer>> {
|
|
||||||
let mut v: Vec<Arc<Peer>> = Vec::new();
|
|
||||||
v.reserve(self.peers.len());
|
|
||||||
for p in self.peers.iter() {
|
|
||||||
v.push(p.value().clone());
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run background tasks and return desired delay until next call in milliseconds.
|
/// Run background tasks and return desired delay until next call in milliseconds.
|
||||||
///
|
///
|
||||||
/// This should only be called periodically from a single thread, but that thread can be
|
/// This should only be called periodically from a single thread, but that thread can be
|
||||||
/// different each time. Calling it concurrently won't crash but won't accomplish anything.
|
/// different each time. Calling it concurrently won't crash but won't accomplish anything.
|
||||||
pub fn do_background_tasks<SI: SystemInterface>(&self, si: &SI) -> Duration {
|
pub fn do_background_tasks(&self, si: &SI) -> Duration {
|
||||||
let mut intervals = self.intervals.lock();
|
let mut intervals = self.intervals.lock();
|
||||||
let tt = si.time_ticks();
|
let tt = si.time_ticks();
|
||||||
|
|
||||||
|
@ -270,7 +251,7 @@ impl Node {
|
||||||
let _ = self
|
let _ = self
|
||||||
.peers
|
.peers
|
||||||
.entry(m.identity.address)
|
.entry(m.identity.address)
|
||||||
.or_try_insert_with(|| Peer::new(&self.identity, m.identity.clone(), tt).map_or(Err(crate::error::UnexpectedError), |new_root| Ok(Arc::new(new_root))))
|
.or_try_insert_with(|| Peer::<SI>::new(&self.identity, m.identity.clone(), si.time_clock()).map_or(Err(crate::error::UnexpectedError), |new_root| Ok(Arc::new(new_root))))
|
||||||
.and_then(|root_peer_entry| {
|
.and_then(|root_peer_entry| {
|
||||||
let rp = root_peer_entry.value();
|
let rp = root_peer_entry.value();
|
||||||
if rp.identity.eq(&m.identity) {
|
if rp.identity.eq(&m.identity) {
|
||||||
|
@ -305,7 +286,7 @@ impl Node {
|
||||||
// The best root is the one that has replied to a HELLO most recently. Since we send HELLOs in unison
|
// The best root is the one that has replied to a HELLO most recently. Since we send HELLOs in unison
|
||||||
// this is a proxy for latency and also causes roots that fail to reply to drop out quickly.
|
// this is a proxy for latency and also causes roots that fail to reply to drop out quickly.
|
||||||
let mut latest_hello_reply = 0;
|
let mut latest_hello_reply = 0;
|
||||||
let mut best: Option<&Arc<Peer>> = None;
|
let mut best: Option<&Arc<Peer<SI>>> = None;
|
||||||
for (r, _) in roots.iter() {
|
for (r, _) in roots.iter() {
|
||||||
let t = r.last_hello_reply_time_ticks.load(Ordering::Relaxed);
|
let t = r.last_hello_reply_time_ticks.load(Ordering::Relaxed);
|
||||||
if t >= latest_hello_reply {
|
if t >= latest_hello_reply {
|
||||||
|
@ -325,25 +306,40 @@ impl Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
if intervals.paths.gate(tt) {
|
if intervals.paths.gate(tt) {
|
||||||
self.paths.retain(|_, path| path.upgrade().map_or(false, |p| p.service(si, self, tt)));
|
// Service all paths, removing expired or invalid ones.
|
||||||
|
self.paths.retain(|_, pbs| {
|
||||||
|
let mut expired_paths = Vec::new();
|
||||||
|
for (ls, path) in pbs.read().iter() {
|
||||||
|
if !si.local_socket_is_valid(ls) || !path.service(si, self, tt) {
|
||||||
|
expired_paths.push(Arc::as_ptr(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expired_paths.is_empty() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let mut pbs_w = pbs.write();
|
||||||
|
pbs_w.retain(|_, path| !expired_paths.contains(&Arc::as_ptr(path)));
|
||||||
|
!pbs_w.is_empty()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if intervals.whois.gate(tt) {
|
if intervals.whois.gate(tt) {
|
||||||
let _ = self.whois.service(si, self, tt);
|
let _ = self.whois.service(si, self, tt);
|
||||||
}
|
}
|
||||||
|
|
||||||
*BACKGROUND_TASK_INTERVAL
|
Duration::from_millis((ROOT_SYNC_INTERVAL_MS.min(crate::vl1::whoisqueue::SERVICE_INTERVAL_MS).min(crate::vl1::path::SERVICE_INTERVAL_MS).min(crate::vl1::peer::SERVICE_INTERVAL_MS) as u64) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when a packet is received on the physical wire.
|
/// Called when a packet is received on the physical wire.
|
||||||
pub fn wire_receive<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, source_endpoint: &Endpoint, source_local_socket: Option<NonZeroI64>, source_local_interface: Option<NonZeroI64>, mut data: PacketBuffer) {
|
pub fn wire_receive<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, source_endpoint: &Endpoint, source_local_socket: &SI::LocalSocket, source_local_interface: &SI::LocalInterface, mut data: PooledPacketBuffer) {
|
||||||
if let Ok(fragment_header) = data.struct_mut_at::<FragmentHeader>(0) {
|
if let Ok(fragment_header) = data.struct_mut_at::<FragmentHeader>(0) {
|
||||||
if let Some(dest) = Address::from_bytes(&fragment_header.dest) {
|
if let Some(dest) = Address::from_bytes_fixed(&fragment_header.dest) {
|
||||||
let time_ticks = si.time_ticks();
|
let time_ticks = si.time_ticks();
|
||||||
if dest == self.identity.address {
|
if dest == self.identity.address {
|
||||||
// Handle packets (seemingly) addressed to this node.
|
// Handle packets addressed to this node.
|
||||||
|
|
||||||
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface);
|
let path = self.canonical_path(source_endpoint, source_local_socket, source_local_interface, time_ticks);
|
||||||
path.log_receive_anything(time_ticks);
|
path.log_receive_anything(time_ticks);
|
||||||
|
|
||||||
if fragment_header.is_fragment() {
|
if fragment_header.is_fragment() {
|
||||||
|
@ -405,12 +401,12 @@ impl Node {
|
||||||
|
|
||||||
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn root(&self) -> Option<Arc<Peer>> {
|
pub fn root(&self) -> Option<Arc<Peer<SI>>> {
|
||||||
self.best_root.read().clone()
|
self.best_root.read().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if a peer is a root.
|
/// Return true if a peer is a root.
|
||||||
pub fn is_peer_root(&self, peer: &Peer) -> bool {
|
pub fn is_peer_root(&self, peer: &Peer<SI>) -> bool {
|
||||||
self.roots.lock().roots.contains_key(peer)
|
self.roots.lock().roots.contains_key(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,37 +422,41 @@ impl Node {
|
||||||
/// This returns true if the new root set was accepted and false otherwise.
|
/// This returns true if the new root set was accepted and false otherwise.
|
||||||
pub fn add_update_root_set(&self, rs: RootSet) -> bool {
|
pub fn add_update_root_set(&self, rs: RootSet) -> bool {
|
||||||
let mut roots = self.roots.lock();
|
let mut roots = self.roots.lock();
|
||||||
let entry = roots.sets.get_mut(&rs.name);
|
if let Some(entry) = roots.sets.get_mut(&rs.name) {
|
||||||
if entry.is_some() {
|
if rs.should_replace(entry) {
|
||||||
let old_rs = entry.unwrap();
|
*entry = rs;
|
||||||
if rs.should_replace(old_rs) {
|
|
||||||
*old_rs = rs;
|
|
||||||
roots.sets_modified = true;
|
roots.sets_modified = true;
|
||||||
true
|
return true;
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
} else if rs.verify() {
|
} else if rs.verify() {
|
||||||
roots.sets.insert(rs.name.clone(), rs);
|
roots.sets.insert(rs.name.clone(), rs);
|
||||||
roots.sets_modified = true;
|
roots.sets_modified = true;
|
||||||
true
|
return true;
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the canonical Path object for a given endpoint and local socket information.
|
/// Get the canonical Path object for a given endpoint and local socket information.
|
||||||
///
|
///
|
||||||
/// This is a canonicalizing function that returns a unique path object for every tuple
|
/// This is a canonicalizing function that returns a unique path object for every tuple
|
||||||
/// of endpoint, local socket, and local interface.
|
/// of endpoint, local socket, and local interface.
|
||||||
pub fn canonical_path(&self, ep: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> Arc<Path> {
|
pub fn canonical_path(&self, ep: &Endpoint, local_socket: &SI::LocalSocket, local_interface: &SI::LocalInterface, time_ticks: i64) -> Arc<Path<SI>> {
|
||||||
let mut path_entry = self.paths.entry(Path::local_lookup_key(ep, local_socket, local_interface)).or_default();
|
// It's faster to do a read only lookup first since most of the time this will succeed. The second
|
||||||
if let Some(path) = path_entry.value().upgrade() {
|
// version below this only gets invoked if it's a new path.
|
||||||
path
|
if let Some(path) = self.paths.get(ep) {
|
||||||
} else {
|
if let Some(path) = path.value().read().get(local_socket) {
|
||||||
let p = Arc::new(Path::new(ep.clone(), local_socket, local_interface));
|
return path.clone();
|
||||||
*path_entry.value_mut() = Arc::downgrade(&p);
|
}
|
||||||
p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
.paths
|
||||||
|
.entry(ep.clone())
|
||||||
|
.or_insert_with(|| parking_lot::RwLock::new(HashMap::with_capacity(2)))
|
||||||
|
.value_mut()
|
||||||
|
.write()
|
||||||
|
.entry(local_socket.clone())
|
||||||
|
.or_insert_with(|| Arc::new(Path::new(ep.clone(), local_socket.clone(), local_interface.clone(), time_ticks)))
|
||||||
|
.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,111 +7,54 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::num::NonZeroI64;
|
|
||||||
use std::sync::atomic::{AtomicI64, Ordering};
|
use std::sync::atomic::{AtomicI64, Ordering};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use metrohash::MetroHash128;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use zerotier_core_crypto::random;
|
|
||||||
|
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
use crate::vl1::endpoint::Endpoint;
|
||||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||||
use crate::vl1::node::*;
|
use crate::vl1::node::*;
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::vl1::{endpoint, Endpoint};
|
|
||||||
use crate::PacketBuffer;
|
|
||||||
|
|
||||||
lazy_static! {
|
pub(crate) const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
|
||||||
static ref METROHASH_SEED: u64 = random::next_u64_secure();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A remote endpoint paired with a local socket and a local interface.
|
/// A remote endpoint paired with a local socket and a local interface.
|
||||||
/// These are maintained in Node and canonicalized so that all unique paths have
|
/// These are maintained in Node and canonicalized so that all unique paths have
|
||||||
/// one and only one unique path object. That enables statistics to be tracked
|
/// one and only one unique path object. That enables statistics to be tracked
|
||||||
/// for them and uniform application of things like keepalives.
|
/// for them and uniform application of things like keepalives.
|
||||||
pub struct Path {
|
pub struct Path<SI: SystemInterface> {
|
||||||
pub endpoint: Endpoint,
|
pub endpoint: Endpoint,
|
||||||
pub local_socket: Option<NonZeroI64>,
|
pub local_socket: SI::LocalSocket,
|
||||||
pub local_interface: Option<NonZeroI64>,
|
pub local_interface: SI::LocalInterface,
|
||||||
last_send_time_ticks: AtomicI64,
|
pub(crate) last_send_time_ticks: AtomicI64,
|
||||||
last_receive_time_ticks: AtomicI64,
|
pub(crate) last_receive_time_ticks: AtomicI64,
|
||||||
|
pub(crate) create_time_ticks: i64,
|
||||||
fragmented_packets: Mutex<HashMap<u64, FragmentedPacket, U64NoOpHasher>>,
|
fragmented_packets: Mutex<HashMap<u64, FragmentedPacket, U64NoOpHasher>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl<SI: SystemInterface> Path<SI> {
|
||||||
/// Get a 128-bit key to look up this endpoint in the local node path map.
|
pub fn new(endpoint: Endpoint, local_socket: SI::LocalSocket, local_interface: SI::LocalInterface, time_ticks: i64) -> Self {
|
||||||
pub(crate) fn local_lookup_key(endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> (u64, u64) {
|
|
||||||
let mut h = MetroHash128::with_seed(*METROHASH_SEED);
|
|
||||||
h.write_u64(local_socket.map_or(0, |s| s.get() as u64));
|
|
||||||
h.write_u64(local_interface.map_or(0, |s| s.get() as u64));
|
|
||||||
match endpoint {
|
|
||||||
Endpoint::Nil => h.write_u8(endpoint::TYPE_NIL),
|
|
||||||
Endpoint::ZeroTier(_, fingerprint) => {
|
|
||||||
h.write_u8(endpoint::TYPE_ZEROTIER);
|
|
||||||
h.write(fingerprint);
|
|
||||||
}
|
|
||||||
Endpoint::Ethernet(m) => {
|
|
||||||
h.write_u8(endpoint::TYPE_ETHERNET);
|
|
||||||
h.write_u64(m.to_u64());
|
|
||||||
}
|
|
||||||
Endpoint::WifiDirect(m) => {
|
|
||||||
h.write_u8(endpoint::TYPE_WIFIDIRECT);
|
|
||||||
h.write_u64(m.to_u64());
|
|
||||||
}
|
|
||||||
Endpoint::Bluetooth(m) => {
|
|
||||||
h.write_u8(endpoint::TYPE_BLUETOOTH);
|
|
||||||
h.write_u64(m.to_u64());
|
|
||||||
}
|
|
||||||
Endpoint::Ip(ip) => {
|
|
||||||
h.write_u8(endpoint::TYPE_IP);
|
|
||||||
h.write(ip.ip_bytes());
|
|
||||||
}
|
|
||||||
Endpoint::IpUdp(ip) => {
|
|
||||||
h.write_u8(endpoint::TYPE_IPUDP);
|
|
||||||
ip.hash(&mut h);
|
|
||||||
}
|
|
||||||
Endpoint::IpTcp(ip) => {
|
|
||||||
h.write_u8(endpoint::TYPE_IPTCP);
|
|
||||||
ip.hash(&mut h);
|
|
||||||
}
|
|
||||||
Endpoint::Http(s) => {
|
|
||||||
h.write_u8(endpoint::TYPE_HTTP);
|
|
||||||
h.write(s.as_bytes());
|
|
||||||
}
|
|
||||||
Endpoint::WebRTC(b) => {
|
|
||||||
h.write_u8(endpoint::TYPE_WEBRTC);
|
|
||||||
h.write(b.as_slice());
|
|
||||||
}
|
|
||||||
Endpoint::ZeroTierEncap(_, fingerprint) => {
|
|
||||||
h.write_u8(endpoint::TYPE_ZEROTIER_ENCAP);
|
|
||||||
h.write(fingerprint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.finish128()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(endpoint: Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
endpoint,
|
endpoint,
|
||||||
local_socket,
|
local_socket,
|
||||||
local_interface,
|
local_interface,
|
||||||
last_send_time_ticks: AtomicI64::new(0),
|
last_send_time_ticks: AtomicI64::new(0),
|
||||||
last_receive_time_ticks: AtomicI64::new(0),
|
last_receive_time_ticks: AtomicI64::new(0),
|
||||||
|
create_time_ticks: time_ticks,
|
||||||
fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(4, U64NoOpHasher::new())),
|
fragmented_packets: Mutex::new(HashMap::with_capacity_and_hasher(4, U64NoOpHasher::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive a fragment and return a FragmentedPacket if the entire packet was assembled.
|
/// Receive a fragment and return a FragmentedPacket if the entire packet was assembled.
|
||||||
/// This returns None if more fragments are needed to assemble the packet.
|
/// This returns None if more fragments are needed to assemble the packet.
|
||||||
pub(crate) fn receive_fragment(&self, packet_id: u64, fragment_no: u8, fragment_expecting_count: u8, packet: PacketBuffer, time_ticks: i64) -> Option<FragmentedPacket> {
|
pub(crate) fn receive_fragment(&self, packet_id: u64, fragment_no: u8, fragment_expecting_count: u8, packet: PooledPacketBuffer, time_ticks: i64) -> Option<FragmentedPacket> {
|
||||||
let mut fp = self.fragmented_packets.lock();
|
let mut fp = self.fragmented_packets.lock();
|
||||||
|
|
||||||
// Discard some old waiting packets if the total incoming fragments for a path exceeds a
|
// Discard some old waiting packets if the total incoming fragments for a path exceeds a
|
||||||
// sanity limit. This is to prevent memory exhaustion DOS attacks.
|
// sanity limit. This is to prevent memory exhaustion DOS attacks.
|
||||||
let fps = fp.len();
|
let fps = fp.len();
|
||||||
if fps > PACKET_FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH {
|
if fps > packet_constants::FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH {
|
||||||
let mut entries: Vec<(i64, u64)> = Vec::new();
|
let mut entries: Vec<(i64, u64)> = Vec::new();
|
||||||
entries.reserve(fps);
|
entries.reserve(fps);
|
||||||
for f in fp.iter() {
|
for f in fp.iter() {
|
||||||
|
@ -130,7 +73,6 @@ impl Path {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when any packet is received.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn log_receive_anything(&self, time_ticks: i64) {
|
pub(crate) fn log_receive_anything(&self, time_ticks: i64) {
|
||||||
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
|
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
|
@ -140,17 +82,19 @@ impl Path {
|
||||||
pub(crate) fn log_send_anything(&self, time_ticks: i64) {
|
pub(crate) fn log_send_anything(&self, time_ticks: i64) {
|
||||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl BackgroundServicable for Path {
|
pub(crate) fn service(&self, si: &SI, _: &Node<SI>, time_ticks: i64) -> bool {
|
||||||
const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
|
self.fragmented_packets.lock().retain(|_, frag| (time_ticks - frag.ts_ticks) < packet_constants::FRAGMENT_EXPIRATION);
|
||||||
|
if (time_ticks - self.last_receive_time_ticks.load(Ordering::Relaxed)) < PATH_EXPIRATION_TIME {
|
||||||
fn service<SI: SystemInterface>(&self, si: &SI, _: &Node, time_ticks: i64) -> bool {
|
if (time_ticks - self.last_send_time_ticks.load(Ordering::Relaxed)) >= PATH_KEEPALIVE_INTERVAL {
|
||||||
self.fragmented_packets.lock().retain(|_, frag| (time_ticks - frag.ts_ticks) < PACKET_FRAGMENT_EXPIRATION);
|
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
if (time_ticks - self.last_send_time_ticks.load(Ordering::Relaxed)) >= PATH_KEEPALIVE_INTERVAL {
|
si.wire_send(&self.endpoint, Some(&self.local_socket), Some(&self.local_interface), &[&ZEROES[..1]], 0);
|
||||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
}
|
||||||
si.wire_send(&self.endpoint, self.local_socket, self.local_interface, &[&ZEROES[..1]], 0);
|
true
|
||||||
|
} else if (time_ticks - self.create_time_ticks) < PATH_EXPIRATION_TIME {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,12 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::num::NonZeroI64;
|
|
||||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
|
||||||
use zerotier_core_crypto::aes_gmac_siv::AesCtr;
|
use zerotier_core_crypto::aes_gmac_siv::AesCtr;
|
||||||
use zerotier_core_crypto::hash::*;
|
use zerotier_core_crypto::hash::*;
|
||||||
|
@ -22,23 +20,25 @@ use zerotier_core_crypto::random::{get_bytes_secure, next_u64_secure};
|
||||||
use zerotier_core_crypto::salsa::Salsa;
|
use zerotier_core_crypto::salsa::Salsa;
|
||||||
use zerotier_core_crypto::secret::Secret;
|
use zerotier_core_crypto::secret::Secret;
|
||||||
|
|
||||||
use crate::util::buffer::Buffer;
|
|
||||||
use crate::util::byte_array_range;
|
use crate::util::byte_array_range;
|
||||||
use crate::util::marshalable::Marshalable;
|
use crate::util::marshalable::Marshalable;
|
||||||
use crate::vl1::identity::{IDENTITY_ALGORITHM_ALL, IDENTITY_ALGORITHM_X25519};
|
|
||||||
use crate::vl1::node::*;
|
use crate::vl1::node::*;
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret};
|
use crate::vl1::symmetricsecret::{EphemeralSymmetricSecret, SymmetricSecret};
|
||||||
use crate::vl1::{Dictionary, Endpoint, Identity, Path};
|
use crate::vl1::{Dictionary, Endpoint, Identity, Path};
|
||||||
use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||||
|
|
||||||
|
pub(crate) const SERVICE_INTERVAL_MS: i64 = security_constants::EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
|
||||||
|
|
||||||
|
struct PeerPath<SI: SystemInterface> {
|
||||||
|
path: Weak<Path<SI>>,
|
||||||
|
last_receive_time_ticks: i64,
|
||||||
|
}
|
||||||
|
|
||||||
/// A remote peer known to this node.
|
/// A remote peer known to this node.
|
||||||
///
|
///
|
||||||
/// NOTE: this implements PartialEq/Eq and Hash in terms of the pointer identity of
|
/// Equality and hashing is implemented in terms of the identity.
|
||||||
/// the structure. This means two peers are equal only if they are the same instance in
|
pub struct Peer<SI: SystemInterface> {
|
||||||
/// memory. This is done because they are only stored in an Arc<> internally and we want
|
|
||||||
/// to use these as efficient hash map keys.
|
|
||||||
pub struct Peer {
|
|
||||||
// This peer's identity.
|
// This peer's identity.
|
||||||
pub(crate) identity: Identity,
|
pub(crate) identity: Identity,
|
||||||
|
|
||||||
|
@ -46,22 +46,16 @@ pub struct Peer {
|
||||||
identity_symmetric_key: SymmetricSecret,
|
identity_symmetric_key: SymmetricSecret,
|
||||||
|
|
||||||
// Latest ephemeral secret or None if not yet negotiated.
|
// Latest ephemeral secret or None if not yet negotiated.
|
||||||
ephemeral_symmetric_key: Mutex<Option<Arc<EphemeralSymmetricSecret>>>,
|
ephemeral_symmetric_key: RwLock<Option<EphemeralSymmetricSecret>>,
|
||||||
|
|
||||||
// Paths sorted in descending order of quality / preference.
|
// Paths sorted in descending order of quality / preference.
|
||||||
paths: Mutex<Vec<Arc<Path>>>,
|
paths: Mutex<Vec<PeerPath<SI>>>,
|
||||||
|
|
||||||
// Statistics and times of events.
|
// Statistics and times of events.
|
||||||
create_time_ticks: i64,
|
|
||||||
pub(crate) last_send_time_ticks: AtomicI64,
|
pub(crate) last_send_time_ticks: AtomicI64,
|
||||||
pub(crate) last_receive_time_ticks: AtomicI64,
|
pub(crate) last_receive_time_ticks: AtomicI64,
|
||||||
pub(crate) last_hello_reply_time_ticks: AtomicI64,
|
pub(crate) last_hello_reply_time_ticks: AtomicI64,
|
||||||
last_forward_time_ticks: AtomicI64,
|
pub(crate) last_forward_time_ticks: AtomicI64,
|
||||||
total_bytes_sent: AtomicU64,
|
|
||||||
total_bytes_sent_indirect: AtomicU64,
|
|
||||||
total_bytes_received: AtomicU64,
|
|
||||||
total_bytes_received_indirect: AtomicU64,
|
|
||||||
total_bytes_forwarded: AtomicU64,
|
|
||||||
|
|
||||||
// Counter for assigning sequential message IDs.
|
// Counter for assigning sequential message IDs.
|
||||||
message_id_counter: AtomicU64,
|
message_id_counter: AtomicU64,
|
||||||
|
@ -71,48 +65,38 @@ pub struct Peer {
|
||||||
remote_protocol_version: AtomicU8,
|
remote_protocol_version: AtomicU8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive per-packet key for Sals20/12 encryption (and Poly1305 authentication).
|
|
||||||
///
|
|
||||||
/// This effectively adds a few additional bits of entropy to the IV from packet
|
|
||||||
/// characteristics such as its size and direction of communication. It also
|
|
||||||
/// effectively incorporates header information as AAD, since if the header info
|
|
||||||
/// is different the key will be wrong and MAC will fail.
|
|
||||||
///
|
|
||||||
/// This is only used for Salsa/Poly modes.
|
|
||||||
fn salsa_derive_per_packet_key(key: &Secret<64>, header: &PacketHeader, packet_size: usize) -> Secret<64> {
|
|
||||||
let hb = header.as_bytes();
|
|
||||||
let mut k = key.clone();
|
|
||||||
for i in 0..18 {
|
|
||||||
k.0[i] ^= hb[i];
|
|
||||||
}
|
|
||||||
k.0[18] ^= hb[HEADER_FLAGS_FIELD_INDEX] & HEADER_FLAGS_FIELD_MASK_HIDE_HOPS;
|
|
||||||
k.0[19] ^= (packet_size >> 8) as u8;
|
|
||||||
k.0[20] ^= packet_size as u8;
|
|
||||||
k
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
|
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
|
||||||
|
/// This is deprecated and is not used with AES-GMAC-SIV.
|
||||||
fn salsa_poly_create(secret: &SymmetricSecret, header: &PacketHeader, packet_size: usize) -> (Salsa<12>, Poly1305) {
|
fn salsa_poly_create(secret: &SymmetricSecret, header: &PacketHeader, packet_size: usize) -> (Salsa<12>, Poly1305) {
|
||||||
let key = salsa_derive_per_packet_key(&secret.key, header, packet_size);
|
// Create a per-packet key from the IV, source, destination, and packet size.
|
||||||
let mut salsa = Salsa::<12>::new(&key.0[0..32], &header.id);
|
let mut key: Secret<32> = secret.key.first_n();
|
||||||
|
let hb = header.as_bytes();
|
||||||
|
for i in 0..18 {
|
||||||
|
key.0[i] ^= hb[i];
|
||||||
|
}
|
||||||
|
key.0[18] ^= hb[packet_constants::FLAGS_FIELD_INDEX] & packet_constants::FLAGS_FIELD_MASK_HIDE_HOPS;
|
||||||
|
key.0[19] ^= (packet_size >> 8) as u8;
|
||||||
|
key.0[20] ^= packet_size as u8;
|
||||||
|
|
||||||
|
let mut salsa = Salsa::<12>::new(&key.0, &header.id);
|
||||||
let mut poly1305_key = [0_u8; 32];
|
let mut poly1305_key = [0_u8; 32];
|
||||||
salsa.crypt_in_place(&mut poly1305_key);
|
salsa.crypt_in_place(&mut poly1305_key);
|
||||||
(salsa, Poly1305::new(&poly1305_key).unwrap())
|
(salsa, Poly1305::new(&poly1305_key).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt AEAD packet encryption and MAC validation. Returns message ID on success.
|
/// Attempt AEAD packet encryption and MAC validation. Returns message ID on success.
|
||||||
fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8], header: &PacketHeader, fragments: &[Option<PacketBuffer>], payload: &mut Buffer<PACKET_SIZE_MAX>) -> Option<u64> {
|
fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8], header: &PacketHeader, fragments: &[Option<PooledPacketBuffer>], payload: &mut PacketBuffer) -> Option<u64> {
|
||||||
packet_frag0_payload_bytes.get(0).map_or(None, |verb| {
|
packet_frag0_payload_bytes.get(0).map_or(None, |verb| {
|
||||||
match header.cipher() {
|
match header.cipher() {
|
||||||
CIPHER_NOCRYPT_POLY1305 => {
|
security_constants::CIPHER_NOCRYPT_POLY1305 => {
|
||||||
if (verb & VERB_MASK) == VERB_VL1_HELLO {
|
if (verb & packet_constants::VERB_MASK) == verbs::VL1_HELLO {
|
||||||
let mut total_packet_len = packet_frag0_payload_bytes.len() + PACKET_HEADER_SIZE;
|
let mut total_packet_len = packet_frag0_payload_bytes.len() + packet_constants::HEADER_SIZE;
|
||||||
for f in fragments.iter() {
|
for f in fragments.iter() {
|
||||||
total_packet_len += f.as_ref().map_or(0, |f| f.len());
|
total_packet_len += f.as_ref().map_or(0, |f| f.len());
|
||||||
}
|
}
|
||||||
let _ = payload.append_bytes(packet_frag0_payload_bytes);
|
let _ = payload.append_bytes(packet_frag0_payload_bytes);
|
||||||
for f in fragments.iter() {
|
for f in fragments.iter() {
|
||||||
let _ = f.as_ref().map(|f| f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| payload.append_bytes(f)));
|
let _ = f.as_ref().map(|f| f.as_bytes_starting_at(packet_constants::HEADER_SIZE).map(|f| payload.append_bytes(f)));
|
||||||
}
|
}
|
||||||
let (_, mut poly) = salsa_poly_create(secret, header, total_packet_len);
|
let (_, mut poly) = salsa_poly_create(secret, header, total_packet_len);
|
||||||
poly.update(payload.as_bytes());
|
poly.update(payload.as_bytes());
|
||||||
|
@ -127,8 +111,8 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CIPHER_SALSA2012_POLY1305 => {
|
security_constants::CIPHER_SALSA2012_POLY1305 => {
|
||||||
let mut total_packet_len = packet_frag0_payload_bytes.len() + PACKET_HEADER_SIZE;
|
let mut total_packet_len = packet_frag0_payload_bytes.len() + packet_constants::HEADER_SIZE;
|
||||||
for f in fragments.iter() {
|
for f in fragments.iter() {
|
||||||
total_packet_len += f.as_ref().map_or(0, |f| f.len());
|
total_packet_len += f.as_ref().map_or(0, |f| f.len());
|
||||||
}
|
}
|
||||||
|
@ -137,7 +121,7 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| salsa.crypt(packet_frag0_payload_bytes, b));
|
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| salsa.crypt(packet_frag0_payload_bytes, b));
|
||||||
for f in fragments.iter() {
|
for f in fragments.iter() {
|
||||||
let _ = f.as_ref().map(|f| {
|
let _ = f.as_ref().map(|f| {
|
||||||
f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
f.as_bytes_starting_at(packet_constants::FRAGMENT_HEADER_SIZE).map(|f| {
|
||||||
poly.update(f);
|
poly.update(f);
|
||||||
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
||||||
})
|
})
|
||||||
|
@ -150,7 +134,7 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CIPHER_AES_GMAC_SIV => {
|
security_constants::CIPHER_AES_GMAC_SIV => {
|
||||||
let mut aes = secret.aes_gmac_siv.get();
|
let mut aes = secret.aes_gmac_siv.get();
|
||||||
aes.decrypt_init(&header.aes_gmac_siv_tag());
|
aes.decrypt_init(&header.aes_gmac_siv_tag());
|
||||||
aes.decrypt_set_aad(&header.aad_bytes());
|
aes.decrypt_set_aad(&header.aad_bytes());
|
||||||
|
@ -159,7 +143,7 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| aes.decrypt(packet_frag0_payload_bytes, b));
|
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| aes.decrypt(packet_frag0_payload_bytes, b));
|
||||||
for f in fragments.iter() {
|
for f in fragments.iter() {
|
||||||
f.as_ref().map(|f| {
|
f.as_ref().map(|f| {
|
||||||
f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
f.as_bytes_starting_at(packet_constants::FRAGMENT_HEADER_SIZE).map(|f| {
|
||||||
let _ = payload.append_bytes_get_mut(f.len()).map(|b| aes.decrypt(f, b));
|
let _ = payload.append_bytes_get_mut(f.len()).map(|b| aes.decrypt(f, b));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -177,29 +161,35 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Peer {
|
impl<SI: SystemInterface> Peer<SI> {
|
||||||
/// Create a new peer.
|
/// Create a new peer.
|
||||||
///
|
///
|
||||||
/// This only returns None if this_node_identity does not have its secrets or if some
|
/// 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.
|
/// fatal error occurs performing key agreement between the two identities.
|
||||||
pub(crate) fn new(this_node_identity: &Identity, id: Identity, time_ticks: i64) -> Option<Peer> {
|
pub(crate) fn new(this_node_identity: &Identity, id: Identity, time_clock: i64) -> Option<Peer<SI>> {
|
||||||
this_node_identity.agree(&id).map(|static_secret| -> Peer {
|
this_node_identity.agree(&id).map(|static_secret| -> Self {
|
||||||
Peer {
|
/*
|
||||||
|
* SECURITY NOTE:
|
||||||
|
*
|
||||||
|
* The message ID counter is initialized from the number of minutes since the Unix epoch (according to
|
||||||
|
* the current clock) in the most significant 26 bits followed by two zero bits followed by 36 random
|
||||||
|
* bits.
|
||||||
|
*
|
||||||
|
* The nature of AES-GMAC-SIV means that message ID duplication is not particularly dangerous, but we
|
||||||
|
* still want to avoid it. If the clock is at least marginally correct this will mean that message IDs
|
||||||
|
* will remain unique for over a hundred years. Message IDs are kept secret as well because they are
|
||||||
|
* encrypted along with a GMAC code to form an opaque 128-bit packet tag.
|
||||||
|
*/
|
||||||
|
Self {
|
||||||
identity: id,
|
identity: id,
|
||||||
identity_symmetric_key: SymmetricSecret::new(static_secret),
|
identity_symmetric_key: SymmetricSecret::new(static_secret),
|
||||||
ephemeral_symmetric_key: Mutex::new(None),
|
ephemeral_symmetric_key: RwLock::new(None),
|
||||||
paths: Mutex::new(Vec::new()),
|
paths: Mutex::new(Vec::with_capacity(4)),
|
||||||
create_time_ticks: time_ticks,
|
|
||||||
last_send_time_ticks: AtomicI64::new(0),
|
last_send_time_ticks: AtomicI64::new(0),
|
||||||
last_receive_time_ticks: AtomicI64::new(0),
|
last_receive_time_ticks: AtomicI64::new(0),
|
||||||
last_hello_reply_time_ticks: AtomicI64::new(0),
|
|
||||||
last_forward_time_ticks: AtomicI64::new(0),
|
last_forward_time_ticks: AtomicI64::new(0),
|
||||||
total_bytes_sent: AtomicU64::new(0),
|
last_hello_reply_time_ticks: AtomicI64::new(0),
|
||||||
total_bytes_sent_indirect: AtomicU64::new(0),
|
message_id_counter: AtomicU64::new(((time_clock as u64) / 60000).wrapping_shl(38) ^ next_u64_secure().wrapping_shr(28)),
|
||||||
total_bytes_received: AtomicU64::new(0),
|
|
||||||
total_bytes_received_indirect: AtomicU64::new(0),
|
|
||||||
total_bytes_forwarded: AtomicU64::new(0),
|
|
||||||
message_id_counter: AtomicU64::new(next_u64_secure()),
|
|
||||||
remote_version: AtomicU64::new(0),
|
remote_version: AtomicU64::new(0),
|
||||||
remote_protocol_version: AtomicU8::new(0),
|
remote_protocol_version: AtomicU8::new(0),
|
||||||
}
|
}
|
||||||
|
@ -209,18 +199,20 @@ impl Peer {
|
||||||
/// Get the next message ID for sending a message to this peer.
|
/// Get the next message ID for sending a message to this peer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn next_message_id(&self) -> u64 {
|
pub(crate) fn next_message_id(&self) -> u64 {
|
||||||
self.message_id_counter.fetch_add(1, Ordering::Relaxed)
|
// SECURITY NOTE: uses the strictest memory ordering to avoid duplicate IDs on loose architectures like ARM64.
|
||||||
|
self.message_id_counter.fetch_add(1, Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive, decrypt, authenticate, and process an incoming packet from this peer.
|
/// Receive, decrypt, authenticate, and process an incoming packet from this peer.
|
||||||
///
|
///
|
||||||
/// If the packet comes in multiple fragments, the fragments slice should contain all
|
/// If the packet comes in multiple fragments, the fragments slice should contain all
|
||||||
/// those fragments after the main packet header and first chunk.
|
/// those fragments after the main packet header and first chunk.
|
||||||
pub(crate) fn receive<SI: SystemInterface, VI: InnerProtocolInterface>(&self, node: &Node, si: &SI, vi: &VI, time_ticks: i64, source_path: &Arc<Path>, header: &PacketHeader, frag0: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option<PacketBuffer>]) {
|
pub(crate) fn receive<VI: InnerProtocolInterface>(&self, node: &Node<SI>, si: &SI, vi: &VI, time_ticks: i64, source_path: &Arc<Path<SI>>, header: &PacketHeader, frag0: &PacketBuffer, fragments: &[Option<PooledPacketBuffer>]) {
|
||||||
let _ = frag0.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| {
|
if let Ok(packet_frag0_payload_bytes) = frag0.as_bytes_starting_at(packet_constants::VERB_INDEX) {
|
||||||
let mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
|
let mut payload = unsafe { PacketBuffer::new_without_memzero() };
|
||||||
|
|
||||||
let (forward_secrecy, mut message_id) = if let Some(ephemeral_secret) = self.ephemeral_symmetric_key.lock().clone() {
|
// First try decrypting and authenticating with an ephemeral secret if one is negotiated.
|
||||||
|
let (forward_secrecy, mut message_id) = if let Some(ephemeral_secret) = self.ephemeral_symmetric_key.read().as_ref() {
|
||||||
if let Some(message_id) = try_aead_decrypt(&ephemeral_secret.secret, packet_frag0_payload_bytes, header, fragments, &mut payload) {
|
if let Some(message_id) = try_aead_decrypt(&ephemeral_secret.secret, packet_frag0_payload_bytes, header, fragments, &mut payload) {
|
||||||
// Decryption successful with ephemeral secret
|
// Decryption successful with ephemeral secret
|
||||||
ephemeral_secret.decrypt_uses.fetch_add(1, Ordering::Relaxed);
|
ephemeral_secret.decrypt_uses.fetch_add(1, Ordering::Relaxed);
|
||||||
|
@ -233,6 +225,8 @@ impl Peer {
|
||||||
// There is no ephemeral secret negotiated (yet?).
|
// There is no ephemeral secret negotiated (yet?).
|
||||||
(false, 0)
|
(false, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Then try the permanent secret.
|
||||||
if !forward_secrecy {
|
if !forward_secrecy {
|
||||||
if let Some(message_id2) = try_aead_decrypt(&self.identity_symmetric_key, packet_frag0_payload_bytes, header, fragments, &mut payload) {
|
if let Some(message_id2) = try_aead_decrypt(&self.identity_symmetric_key, packet_frag0_payload_bytes, header, fragments, &mut payload) {
|
||||||
// Decryption successful with static secret.
|
// Decryption successful with static secret.
|
||||||
|
@ -242,69 +236,72 @@ impl Peer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_assert!(!payload.is_empty());
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
if let Ok(mut verb) = payload.u8_at(0) {
|
||||||
// If we made it here it decrypted and passed authentication.
|
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
// ---------------------------------------------------------------
|
|
||||||
|
|
||||||
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
|
let extended_authentication = (verb & packet_constants::VERB_FLAG_EXTENDED_AUTHENTICATION) != 0;
|
||||||
self.total_bytes_received.fetch_add((payload.len() + PACKET_HEADER_SIZE) as u64, Ordering::Relaxed);
|
if extended_authentication {
|
||||||
|
if payload.len() >= SHA512_HASH_SIZE {
|
||||||
|
let actual_end_of_payload = payload.len() - SHA512_HASH_SIZE;
|
||||||
|
let mut hmac = HMACSHA512::new(self.identity_symmetric_key.packet_hmac_key.as_bytes());
|
||||||
|
hmac.update(&message_id.to_ne_bytes());
|
||||||
|
hmac.update(&payload.as_bytes()[..actual_end_of_payload]);
|
||||||
|
if !hmac.finish().eq(&payload.as_bytes()[actual_end_of_payload..]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload.set_size(actual_end_of_payload);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut verb = payload.as_bytes()[0];
|
// ---------------------------------------------------------------
|
||||||
|
// If we made it here it decrypted and passed authentication.
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
// If this flag is set, the end of the payload is a full HMAC-SHA384 authentication
|
if (verb & packet_constants::VERB_FLAG_COMPRESSED) != 0 {
|
||||||
// tag for much stronger authentication than is offered by the packet MAC.
|
let mut decompressed_payload: [u8; packet_constants::SIZE_MAX] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
let extended_authentication = (verb & VERB_FLAG_EXTENDED_AUTHENTICATION) != 0;
|
decompressed_payload[0] = verb;
|
||||||
if extended_authentication {
|
if let Ok(dlen) = lz4_flex::block::decompress_into(&payload.as_bytes()[1..], &mut decompressed_payload[1..]) {
|
||||||
if payload.len() >= (1 + SHA384_HASH_SIZE) {
|
payload.set_to(&decompressed_payload[..(dlen + 1)]);
|
||||||
let actual_end_of_payload = payload.len() - SHA384_HASH_SIZE;
|
} else {
|
||||||
//let hmac = hmac_sha384(self.static_secret.packet_hmac_key.as_ref(), &[u64_as_bytes(&message_id), payload.as_bytes()]);
|
return;
|
||||||
//if !hmac.eq(&(payload.as_bytes()[actual_end_of_payload..])) {
|
}
|
||||||
// return;
|
}
|
||||||
//}
|
|
||||||
payload.set_size(actual_end_of_payload);
|
let source_path_ptr = Arc::as_ptr(source_path);
|
||||||
} else {
|
for p in self.paths.lock().iter_mut() {
|
||||||
return;
|
if Weak::as_ptr(&p.path) == source_path_ptr {
|
||||||
|
p.last_receive_time_ticks = time_ticks;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For performance reasons we let VL2 handle packets first. It returns false
|
||||||
|
// if it didn't handle the packet, in which case it's handled at VL1. This is
|
||||||
|
// because the most performance critical path is the handling of the ???_FRAME
|
||||||
|
// verbs, which are in VL2.
|
||||||
|
verb &= packet_constants::VERB_MASK; // mask off flags
|
||||||
|
if !vi.handle_packet(self, source_path, forward_secrecy, extended_authentication, verb, &payload) {
|
||||||
|
match verb {
|
||||||
|
//VERB_VL1_NOP => {}
|
||||||
|
verbs::VL1_HELLO => self.receive_hello(si, node, time_ticks, source_path, &payload),
|
||||||
|
verbs::VL1_ERROR => self.receive_error(si, vi, node, time_ticks, source_path, forward_secrecy, extended_authentication, &payload),
|
||||||
|
verbs::VL1_OK => self.receive_ok(si, vi, node, time_ticks, source_path, forward_secrecy, extended_authentication, &payload),
|
||||||
|
verbs::VL1_WHOIS => self.receive_whois(si, node, time_ticks, source_path, &payload),
|
||||||
|
verbs::VL1_RENDEZVOUS => self.receive_rendezvous(si, node, time_ticks, source_path, &payload),
|
||||||
|
verbs::VL1_ECHO => self.receive_echo(si, node, time_ticks, source_path, &payload),
|
||||||
|
verbs::VL1_PUSH_DIRECT_PATHS => self.receive_push_direct_paths(si, node, time_ticks, source_path, &payload),
|
||||||
|
verbs::VL1_USER_MESSAGE => self.receive_user_message(si, node, time_ticks, source_path, &payload),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (verb & VERB_FLAG_COMPRESSED) != 0 {
|
|
||||||
let mut decompressed_payload: [u8; PACKET_SIZE_MAX] = unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
decompressed_payload[0] = verb;
|
|
||||||
let dlen = lz4_flex::block::decompress_into(&payload.as_bytes()[1..], &mut decompressed_payload[1..]);
|
|
||||||
if dlen.is_ok() {
|
|
||||||
payload.set_to(&decompressed_payload[0..(dlen.unwrap() + 1)]);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For performance reasons we let VL2 handle packets first. It returns false
|
|
||||||
// if it didn't handle the packet, in which case it's handled at VL1. This is
|
|
||||||
// because the most performance critical path is the handling of the ???_FRAME
|
|
||||||
// verbs, which are in VL2.
|
|
||||||
verb &= VERB_MASK; // mask off flags
|
|
||||||
if !vi.handle_packet(self, source_path, forward_secrecy, extended_authentication, verb, &payload) {
|
|
||||||
match verb {
|
|
||||||
//VERB_VL1_NOP => {}
|
|
||||||
VERB_VL1_HELLO => self.receive_hello(si, node, time_ticks, source_path, &payload),
|
|
||||||
VERB_VL1_ERROR => self.receive_error(si, vi, node, time_ticks, source_path, forward_secrecy, extended_authentication, &payload),
|
|
||||||
VERB_VL1_OK => self.receive_ok(si, vi, node, time_ticks, source_path, forward_secrecy, extended_authentication, &payload),
|
|
||||||
VERB_VL1_WHOIS => self.receive_whois(si, node, time_ticks, source_path, &payload),
|
|
||||||
VERB_VL1_RENDEZVOUS => self.receive_rendezvous(si, node, time_ticks, source_path, &payload),
|
|
||||||
VERB_VL1_ECHO => self.receive_echo(si, node, time_ticks, source_path, &payload),
|
|
||||||
VERB_VL1_PUSH_DIRECT_PATHS => self.receive_push_direct_paths(si, node, time_ticks, source_path, &payload),
|
|
||||||
VERB_VL1_USER_MESSAGE => self.receive_user_message(si, node, time_ticks, source_path, &payload),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_endpoint<SI: SystemInterface>(&self, si: &SI, endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
fn send_to_endpoint(&self, si: &SI, endpoint: &Endpoint, local_socket: Option<&SI::LocalSocket>, local_interface: Option<&SI::LocalInterface>, packet: &PacketBuffer) -> bool {
|
||||||
debug_assert!(packet.len() <= PACKET_SIZE_MAX);
|
|
||||||
debug_assert!(packet.len() >= PACKET_SIZE_MIN);
|
|
||||||
match endpoint {
|
match endpoint {
|
||||||
Endpoint::Ip(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => {
|
Endpoint::Ip(_) | Endpoint::IpUdp(_) | Endpoint::Ethernet(_) | Endpoint::Bluetooth(_) | Endpoint::WifiDirect(_) => {
|
||||||
let packet_size = packet.len();
|
let packet_size = packet.len();
|
||||||
|
@ -317,18 +314,18 @@ impl Peer {
|
||||||
let mut pos = UDP_DEFAULT_MTU;
|
let mut pos = UDP_DEFAULT_MTU;
|
||||||
|
|
||||||
let overrun_size = (packet_size - UDP_DEFAULT_MTU) as u32;
|
let overrun_size = (packet_size - UDP_DEFAULT_MTU) as u32;
|
||||||
let fragment_count = (overrun_size / (UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE) as u32) + (((overrun_size % (UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE) as u32) != 0) as u32);
|
let fragment_count = (overrun_size / (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) + (((overrun_size % (UDP_DEFAULT_MTU - packet_constants::FRAGMENT_HEADER_SIZE) as u32) != 0) as u32);
|
||||||
debug_assert!(fragment_count <= PACKET_FRAGMENT_COUNT_MAX as u32);
|
debug_assert!(fragment_count <= packet_constants::FRAGMENT_COUNT_MAX as u32);
|
||||||
|
|
||||||
let mut header = FragmentHeader {
|
let mut header = FragmentHeader {
|
||||||
id: unsafe { *packet.as_bytes().as_ptr().cast::<[u8; 8]>() },
|
id: *packet.bytes_fixed_at(0).unwrap(),
|
||||||
dest: bytes[PACKET_DESTINATION_INDEX..PACKET_DESTINATION_INDEX + ADDRESS_SIZE].try_into().unwrap(),
|
dest: *packet.bytes_fixed_at(packet_constants::DESTINATION_INDEX).unwrap(),
|
||||||
fragment_indicator: PACKET_FRAGMENT_INDICATOR,
|
fragment_indicator: packet_constants::FRAGMENT_INDICATOR,
|
||||||
total_and_fragment_no: ((fragment_count + 1) << 4) as u8,
|
total_and_fragment_no: ((fragment_count + 1) << 4) as u8,
|
||||||
reserved_hops: 0,
|
reserved_hops: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE);
|
let mut chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - packet_constants::HEADER_SIZE);
|
||||||
loop {
|
loop {
|
||||||
header.total_and_fragment_no += 1;
|
header.total_and_fragment_no += 1;
|
||||||
let next_pos = pos + chunk_size;
|
let next_pos = pos + chunk_size;
|
||||||
|
@ -337,7 +334,7 @@ impl Peer {
|
||||||
}
|
}
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
if pos < packet_size {
|
if pos < packet_size {
|
||||||
chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - FRAGMENT_HEADER_SIZE);
|
chunk_size = (packet_size - pos).min(UDP_DEFAULT_MTU - packet_constants::HEADER_SIZE);
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -356,16 +353,14 @@ impl Peer {
|
||||||
///
|
///
|
||||||
/// This will go directly if there is an active path, or otherwise indirectly
|
/// This will go directly if there is an active path, or otherwise indirectly
|
||||||
/// via a root or some other route.
|
/// via a root or some other route.
|
||||||
pub(crate) fn send<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
pub(crate) fn send(&self, si: &SI, node: &Node<SI>, time_ticks: i64, packet: &PacketBuffer) -> bool {
|
||||||
self.path(node).map_or(false, |path| {
|
if let Some(path) = self.path(node) {
|
||||||
if self.send_to_endpoint(si, &path.endpoint, path.local_socket, path.local_interface, packet) {
|
if self.send_to_endpoint(si, &path.endpoint, Some(&path.local_socket), Some(&path.local_interface), packet) {
|
||||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
self.total_bytes_sent.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
return true;
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forward a packet to this peer.
|
/// Forward a packet to this peer.
|
||||||
|
@ -375,16 +370,14 @@ impl Peer {
|
||||||
///
|
///
|
||||||
/// This doesn't fragment large packets since fragments are forwarded individually.
|
/// This doesn't fragment large packets since fragments are forwarded individually.
|
||||||
/// Intermediates don't need to adjust fragmentation.
|
/// Intermediates don't need to adjust fragmentation.
|
||||||
pub(crate) fn forward<SI: SystemInterface>(&self, si: &SI, time_ticks: i64, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
pub(crate) fn forward(&self, si: &SI, time_ticks: i64, packet: &PacketBuffer) -> bool {
|
||||||
self.direct_path().map_or(false, |path| {
|
if let Some(path) = self.direct_path() {
|
||||||
if si.wire_send(&path.endpoint, path.local_socket, path.local_interface, &[packet.as_bytes()], 0) {
|
if si.wire_send(&path.endpoint, Some(&path.local_socket), Some(&path.local_interface), &[packet.as_bytes()], 0) {
|
||||||
self.last_forward_time_ticks.store(time_ticks, Ordering::Relaxed);
|
self.last_forward_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
self.total_bytes_forwarded.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
return true;
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a HELLO to this peer.
|
/// Send a HELLO to this peer.
|
||||||
|
@ -394,7 +387,7 @@ impl Peer {
|
||||||
///
|
///
|
||||||
/// Unlike other messages HELLO is sent partially in the clear and always with the long-lived
|
/// Unlike other messages HELLO is sent partially in the clear and always with the long-lived
|
||||||
/// static identity key.
|
/// static identity key.
|
||||||
pub(crate) fn send_hello<SI: SystemInterface>(&self, si: &SI, node: &Node, explicit_endpoint: Option<&Endpoint>) -> bool {
|
pub(crate) fn send_hello(&self, si: &SI, node: &Node<SI>, explicit_endpoint: Option<&Endpoint>) -> bool {
|
||||||
let mut path = None;
|
let mut path = None;
|
||||||
let destination = explicit_endpoint.map_or_else(
|
let destination = explicit_endpoint.map_or_else(
|
||||||
|| {
|
|| {
|
||||||
|
@ -410,7 +403,7 @@ impl Peer {
|
||||||
}
|
}
|
||||||
let destination = destination.unwrap();
|
let destination = destination.unwrap();
|
||||||
|
|
||||||
let mut packet: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
|
let mut packet = PacketBuffer::new();
|
||||||
let time_ticks = si.time_ticks();
|
let time_ticks = si.time_ticks();
|
||||||
let message_id = self.next_message_id();
|
let message_id = self.next_message_id();
|
||||||
|
|
||||||
|
@ -419,35 +412,31 @@ impl Peer {
|
||||||
packet_header.id = message_id.to_ne_bytes(); // packet ID and message ID are the same when Poly1305 MAC is used
|
packet_header.id = message_id.to_ne_bytes(); // packet ID and message ID are the same when Poly1305 MAC is used
|
||||||
packet_header.dest = self.identity.address.to_bytes();
|
packet_header.dest = self.identity.address.to_bytes();
|
||||||
packet_header.src = node.identity.address.to_bytes();
|
packet_header.src = node.identity.address.to_bytes();
|
||||||
packet_header.flags_cipher_hops = CIPHER_NOCRYPT_POLY1305;
|
packet_header.flags_cipher_hops = security_constants::CIPHER_NOCRYPT_POLY1305;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let hello_fixed_headers: &mut message_component_structs::HelloFixedHeaderFields = packet.append_struct_get_mut().unwrap();
|
let hello_fixed_headers: &mut message_component_structs::HelloFixedHeaderFields = packet.append_struct_get_mut().unwrap();
|
||||||
hello_fixed_headers.verb = VERB_VL1_HELLO | VERB_FLAG_EXTENDED_AUTHENTICATION;
|
hello_fixed_headers.verb = verbs::VL1_HELLO | packet_constants::VERB_FLAG_EXTENDED_AUTHENTICATION;
|
||||||
hello_fixed_headers.version_proto = VERSION_PROTO;
|
hello_fixed_headers.version_proto = PROTOCOL_VERSION;
|
||||||
hello_fixed_headers.version_major = VERSION_MAJOR;
|
hello_fixed_headers.version_major = VERSION_MAJOR;
|
||||||
hello_fixed_headers.version_minor = VERSION_MINOR;
|
hello_fixed_headers.version_minor = VERSION_MINOR;
|
||||||
hello_fixed_headers.version_revision = (VERSION_REVISION as u16).to_be_bytes();
|
hello_fixed_headers.version_revision = (VERSION_REVISION as u16).to_be_bytes();
|
||||||
hello_fixed_headers.timestamp = (time_ticks as u64).to_be_bytes();
|
hello_fixed_headers.timestamp = (time_ticks as u64).to_be_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(self.identity.marshal_with_options(&mut packet, IDENTITY_ALGORITHM_ALL, false).is_ok());
|
// Full identity of the node establishing the session.
|
||||||
if self.identity.algorithms() == IDENTITY_ALGORITHM_X25519 {
|
assert!(self.identity.marshal_with_options(&mut packet, Identity::ALGORITHM_ALL, false).is_ok());
|
||||||
// LEGACY: append an extra zero when marshaling identities containing only x25519 keys.
|
|
||||||
// See comments in Identity::marshal(). This can go away eventually.
|
|
||||||
assert!(packet.append_u8(0).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8 reserved bytes, must be zero for legacy compatibility.
|
// 8 reserved bytes, must be zero for compatibility with old nodes.
|
||||||
assert!(packet.append_padding(0, 8).is_ok());
|
assert!(packet.append_padding(0, 8).is_ok());
|
||||||
|
|
||||||
// Generate a 12-byte nonce for the private section of HELLO.
|
// Generate a 12-byte nonce for the private section of HELLO.
|
||||||
let mut nonce = get_bytes_secure::<12>();
|
let mut nonce = get_bytes_secure::<12>();
|
||||||
|
|
||||||
// LEGACY: create a 16-bit encrypted field that specifies zero "moons." This is ignored now
|
// LEGACY: create a 16-bit encrypted field that specifies zero "moons." Current nodes ignore this
|
||||||
// but causes old nodes to be able to parse this packet properly. Newer nodes will treat this
|
// and treat it as part of the random AES-CTR nonce, but old versions need it to parse the packet
|
||||||
// as part of a 12-byte nonce and otherwise ignore it. These bytes will be random.
|
// correctly.
|
||||||
let mut salsa_iv = message_id.to_ne_bytes();
|
let mut salsa_iv = message_id.to_ne_bytes();
|
||||||
salsa_iv[7] &= 0xf8;
|
salsa_iv[7] &= 0xf8;
|
||||||
Salsa::<12>::new(&self.identity_symmetric_key.key.0[0..32], &salsa_iv).crypt(&[0_u8, 0_u8], &mut nonce[8..10]);
|
Salsa::<12>::new(&self.identity_symmetric_key.key.0[0..32], &salsa_iv).crypt(&[0_u8, 0_u8], &mut nonce[8..10]);
|
||||||
|
@ -455,12 +444,13 @@ impl Peer {
|
||||||
// Append 12-byte AES-CTR nonce.
|
// Append 12-byte AES-CTR nonce.
|
||||||
assert!(packet.append_bytes_fixed(&nonce).is_ok());
|
assert!(packet.append_bytes_fixed(&nonce).is_ok());
|
||||||
|
|
||||||
// Add encrypted private field map. Plain AES-CTR is used with no MAC or SIV because
|
// Add session meta-data, which is encrypted using plain AES-CTR. No authentication (AEAD) is needed
|
||||||
// the whole packet is authenticated with HMAC-SHA512.
|
// because the whole packet is authenticated. Data in the session is not technically secret in a
|
||||||
|
// cryptographic sense but we encrypt it for privacy and as a defense in depth.
|
||||||
let mut fields = Dictionary::new();
|
let mut fields = Dictionary::new();
|
||||||
fields.set_u64(SESSION_METADATA_INSTANCE_ID, node.instance_id);
|
fields.set_u64(session_metadata::INSTANCE_ID, node.instance_id);
|
||||||
fields.set_u64(SESSION_METADATA_CLOCK, si.time_clock() as u64);
|
fields.set_u64(session_metadata::CLOCK, si.time_clock() as u64);
|
||||||
fields.set_bytes(SESSION_METADATA_SENT_TO, destination.to_buffer::<{ Endpoint::MAX_MARSHAL_SIZE }>().unwrap().as_bytes().to_vec());
|
fields.set_bytes(session_metadata::SENT_TO, destination.to_buffer::<{ Endpoint::MAX_MARSHAL_SIZE }>().unwrap().as_bytes().to_vec());
|
||||||
let fields = fields.to_bytes();
|
let fields = fields.to_bytes();
|
||||||
assert!(fields.len() <= 0xffff); // sanity check, should be impossible
|
assert!(fields.len() <= 0xffff); // sanity check, should be impossible
|
||||||
assert!(packet.append_u16(fields.len() as u16).is_ok()); // prefix with unencrypted size
|
assert!(packet.append_u16(fields.len() as u16).is_ok()); // prefix with unencrypted size
|
||||||
|
@ -472,24 +462,23 @@ impl Peer {
|
||||||
drop(aes);
|
drop(aes);
|
||||||
drop(fields);
|
drop(fields);
|
||||||
|
|
||||||
// Add extended authentication at end of packet.
|
// Seal packet with HMAC-SHA512 extended authentication.
|
||||||
let mut hmac = HMACSHA512::new(self.identity_symmetric_key.packet_hmac_key.as_bytes());
|
let mut hmac = HMACSHA512::new(self.identity_symmetric_key.packet_hmac_key.as_bytes());
|
||||||
hmac.update(&message_id.to_ne_bytes());
|
hmac.update(&message_id.to_ne_bytes());
|
||||||
hmac.update(&packet.as_bytes()[PACKET_HEADER_SIZE..]);
|
hmac.update(&packet.as_bytes()[packet_constants::HEADER_SIZE..]);
|
||||||
assert!(packet.append_bytes_fixed(&hmac.finish()).is_ok());
|
assert!(packet.append_bytes_fixed(&hmac.finish()).is_ok());
|
||||||
|
|
||||||
// Set legacy poly1305 MAC in packet header. Newer nodes also check HMAC-SHA512 but older ones only use this.
|
// Set poly1305 in header, which is the only authentication for old nodes.
|
||||||
let (_, mut poly) = salsa_poly_create(&self.identity_symmetric_key, packet.struct_at::<PacketHeader>(0).unwrap(), packet.len());
|
let (_, mut poly) = salsa_poly_create(&self.identity_symmetric_key, packet.struct_at::<PacketHeader>(0).unwrap(), packet.len());
|
||||||
poly.update(packet.as_bytes_starting_at(PACKET_HEADER_SIZE).unwrap());
|
poly.update(packet.as_bytes_starting_at(packet_constants::HEADER_SIZE).unwrap());
|
||||||
packet.as_mut()[HEADER_MAC_FIELD_INDEX..HEADER_MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
|
packet.as_mut()[packet_constants::MAC_FIELD_INDEX..packet_constants::MAC_FIELD_INDEX + 8].copy_from_slice(&poly.finish()[0..8]);
|
||||||
|
|
||||||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||||
self.total_bytes_sent.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
|
||||||
|
|
||||||
path.map_or_else(
|
path.map_or_else(
|
||||||
|| self.send_to_endpoint(si, &destination, None, None, &packet),
|
|| self.send_to_endpoint(si, &destination, None, None, &packet),
|
||||||
|p| {
|
|p| {
|
||||||
if self.send_to_endpoint(si, &destination, p.local_socket, p.local_interface, &packet) {
|
if self.send_to_endpoint(si, &destination, Some(&p.local_socket), Some(&p.local_interface), &packet) {
|
||||||
p.log_send_anything(time_ticks);
|
p.log_send_anything(time_ticks);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -499,13 +488,11 @@ impl Peer {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_hello(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_hello<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<PACKET_SIZE_MAX>) {}
|
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_error<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, forward_secrecy: bool, extended_authentication: bool, payload: &PacketBuffer) {
|
||||||
fn receive_error<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<PACKET_SIZE_MAX>) {
|
let mut cursor: usize = 1;
|
||||||
let mut cursor: usize = 0;
|
if let Ok(error_header) = payload.read_struct::<message_component_structs::ErrorHeader>(&mut cursor) {
|
||||||
let _ = payload.read_struct::<message_component_structs::ErrorHeader>(&mut cursor).map(|error_header| {
|
|
||||||
let in_re_message_id = u64::from_ne_bytes(error_header.in_re_message_id);
|
let in_re_message_id = u64::from_ne_bytes(error_header.in_re_message_id);
|
||||||
let current_packet_id_counter = self.message_id_counter.load(Ordering::Relaxed);
|
let current_packet_id_counter = self.message_id_counter.load(Ordering::Relaxed);
|
||||||
if current_packet_id_counter.wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX {
|
if current_packet_id_counter.wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX {
|
||||||
|
@ -515,54 +502,59 @@ impl Peer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_ok<PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, forward_secrecy: bool, extended_authentication: bool, payload: &PacketBuffer) {
|
||||||
fn receive_ok<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<PACKET_SIZE_MAX>) {
|
let mut cursor: usize = 1;
|
||||||
let mut cursor: usize = 0;
|
if let Ok(ok_header) = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor) {
|
||||||
let _ = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor).map(|ok_header| {
|
|
||||||
let in_re_message_id = u64::from_ne_bytes(ok_header.in_re_message_id);
|
let in_re_message_id = u64::from_ne_bytes(ok_header.in_re_message_id);
|
||||||
let current_packet_id_counter = self.message_id_counter.load(Ordering::Relaxed);
|
let current_packet_id_counter = self.message_id_counter.load(Ordering::Relaxed);
|
||||||
if current_packet_id_counter.wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX {
|
if current_packet_id_counter.wrapping_sub(in_re_message_id) <= PACKET_RESPONSE_COUNTER_DELTA_MAX {
|
||||||
match ok_header.in_re_verb {
|
match ok_header.in_re_verb {
|
||||||
VERB_VL1_HELLO => {
|
verbs::VL1_HELLO => {
|
||||||
// TODO
|
// TODO
|
||||||
self.last_hello_reply_time_ticks.store(time_ticks, Ordering::Relaxed);
|
|
||||||
}
|
}
|
||||||
VERB_VL1_WHOIS => {}
|
verbs::VL1_WHOIS => {}
|
||||||
_ => {
|
_ => {
|
||||||
ph.handle_ok(self, source_path, forward_secrecy, extended_authentication, ok_header.in_re_verb, in_re_message_id, payload, &mut cursor);
|
ph.handle_ok(self, source_path, forward_secrecy, extended_authentication, ok_header.in_re_verb, in_re_message_id, payload, &mut cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_whois(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_whois<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_rendezvous(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_rendezvous<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_echo(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_echo<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_push_direct_paths(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_push_direct_paths<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
|
||||||
|
|
||||||
#[inline(always)]
|
fn receive_user_message(&self, si: &SI, node: &Node<SI>, time_ticks: i64, source_path: &Arc<Path<SI>>, payload: &PacketBuffer) {}
|
||||||
fn receive_user_message<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
|
||||||
|
|
||||||
/// Get current best path or None if there are no direct paths to this peer.
|
/// Get current best path or None if there are no direct paths to this peer.
|
||||||
#[inline(always)]
|
pub fn direct_path(&self) -> Option<Arc<Path<SI>>> {
|
||||||
pub fn direct_path(&self) -> Option<Arc<Path>> {
|
for p in self.paths.lock().iter() {
|
||||||
self.paths.lock().first().map(|p| p.clone())
|
let pp = p.path.upgrade();
|
||||||
|
if pp.is_some() {
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get either the current best direct path or an indirect path.
|
/// Get either the current best direct path or an indirect path.
|
||||||
pub fn path(&self, node: &Node) -> Option<Arc<Path>> {
|
pub fn path(&self, node: &Node<SI>) -> Option<Arc<Path<SI>>> {
|
||||||
self.direct_path().map_or_else(|| node.root().map_or(None, |root| root.direct_path().map_or(None, |bp| Some(bp))), |bp| Some(bp))
|
let direct_path = self.direct_path();
|
||||||
|
if direct_path.is_some() {
|
||||||
|
return direct_path;
|
||||||
|
}
|
||||||
|
if let Some(root) = node.root() {
|
||||||
|
return root.direct_path();
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the remote version of this peer: major, minor, revision, and build.
|
/// Get the remote version of this peer: major, minor, revision, and build.
|
||||||
|
@ -585,29 +577,32 @@ impl Peer {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn service(&self, _: &SI, _: &Node<SI>, time_ticks: i64) -> bool {
|
||||||
|
let mut paths = self.paths.lock();
|
||||||
|
if (time_ticks - self.last_receive_time_ticks.load(Ordering::Relaxed)) < PEER_EXPIRATION_TIME {
|
||||||
|
paths.retain(|p| ((time_ticks - p.last_receive_time_ticks) < PEER_EXPIRATION_TIME) && (p.path.strong_count() > 0));
|
||||||
|
paths.sort_unstable_by(|a, b| a.last_receive_time_ticks.cmp(&b.last_receive_time_ticks).reverse());
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
paths.clear();
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Peer {
|
impl<SI: SystemInterface> PartialEq for Peer<SI> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
std::ptr::eq(self as *const Peer, other as *const Peer)
|
self.identity.eq(&other.identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Peer {}
|
impl<SI: SystemInterface> Eq for Peer<SI> {}
|
||||||
|
|
||||||
impl Hash for Peer {
|
impl<SI: SystemInterface> Hash for Peer<SI> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
state.write_usize((self as *const Peer) as usize)
|
self.identity.hash(state);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackgroundServicable for Peer {
|
|
||||||
const SERVICE_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn service<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64) -> bool {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,72 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
use crate::util::buffer::{Buffer, FlatBlob};
|
use crate::util::buffer::Buffer;
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
|
|
||||||
pub const VERB_VL1_NOP: u8 = 0x00;
|
/*
|
||||||
pub const VERB_VL1_HELLO: u8 = 0x01;
|
* Protocol versions
|
||||||
pub const VERB_VL1_ERROR: u8 = 0x02;
|
*
|
||||||
pub const VERB_VL1_OK: u8 = 0x03;
|
* 1 - 0.2.0 ... 0.2.5
|
||||||
pub const VERB_VL1_WHOIS: u8 = 0x04;
|
* 2 - 0.3.0 ... 0.4.5
|
||||||
pub const VERB_VL1_RENDEZVOUS: u8 = 0x05;
|
* + Added signature and originating peer to multicast frame
|
||||||
pub const VERB_VL1_ECHO: u8 = 0x08;
|
* + Double size of multicast frame bloom filter
|
||||||
pub const VERB_VL1_PUSH_DIRECT_PATHS: u8 = 0x10;
|
* 3 - 0.5.0 ... 0.6.0
|
||||||
pub const VERB_VL1_USER_MESSAGE: u8 = 0x14;
|
* + Yet another multicast redesign
|
||||||
|
* + New crypto completely changes key agreement cipher
|
||||||
|
* 4 - 0.6.0 ... 1.0.6
|
||||||
|
* + BREAKING CHANGE: New identity format based on hashcash design
|
||||||
|
* 5 - 1.1.0 ... 1.1.5
|
||||||
|
* + Supports echo
|
||||||
|
* + Supports in-band world (root server definition) updates
|
||||||
|
* + Clustering! (Though this will work with protocol v4 clients.)
|
||||||
|
* + Otherwise backward compatible with protocol v4
|
||||||
|
* 6 - 1.1.5 ... 1.1.10
|
||||||
|
* + Network configuration format revisions including binary values
|
||||||
|
* 7 - 1.1.10 ... 1.1.17
|
||||||
|
* + Introduce trusted paths for local SDN use
|
||||||
|
* 8 - 1.1.17 ... 1.2.0
|
||||||
|
* + Multipart network configurations for large network configs
|
||||||
|
* + Tags and Capabilities
|
||||||
|
* + inline push of CertificateOfMembership deprecated
|
||||||
|
* 9 - 1.2.0 ... 1.2.14
|
||||||
|
* 10 - 1.4.0 ... 1.4.6
|
||||||
|
* + Contained early pre-alpha versions of multipath, which are deprecated
|
||||||
|
* 11 - 1.6.0 ... 2.0.0
|
||||||
|
* + Supports and prefers AES-GMAC-SIV symmetric crypto, backported.
|
||||||
|
*
|
||||||
|
* 20 - 2.0.0 ... CURRENT
|
||||||
|
* + Forward secrecy with cryptographic ratchet! Finally!!!
|
||||||
|
* + New identity format including both x25519 and NIST P-521 keys.
|
||||||
|
* + AES-GMAC-SIV, a FIPS-compliant SIV construction using AES.
|
||||||
|
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
|
||||||
|
* + HELLO and OK(HELLO) use a dictionary for better extensibilit.
|
||||||
|
*/
|
||||||
|
pub const PROTOCOL_VERSION: u8 = 20;
|
||||||
|
|
||||||
|
/// Buffer sized for ZeroTier packets.
|
||||||
|
pub type PacketBuffer = Buffer<{ packet_constants::SIZE_MAX }>;
|
||||||
|
|
||||||
|
/// Factory type to supply to a new PacketBufferPool, used in PooledPacketBuffer and PacketBufferPool types.
|
||||||
|
pub type PacketBufferFactory = crate::util::buffer::PooledBufferFactory<{ crate::vl1::protocol::packet_constants::SIZE_MAX }>;
|
||||||
|
|
||||||
|
/// Packet buffer checked out of pool, automatically returns on drop.
|
||||||
|
pub type PooledPacketBuffer = crate::util::pool::Pooled<PacketBuffer, PacketBufferFactory>;
|
||||||
|
|
||||||
|
/// Source for instances of PacketBuffer
|
||||||
|
pub type PacketBufferPool = crate::util::pool::Pool<PacketBuffer, PacketBufferFactory>;
|
||||||
|
|
||||||
|
pub mod verbs {
|
||||||
|
pub const VL1_NOP: u8 = 0x00;
|
||||||
|
pub const VL1_HELLO: u8 = 0x01;
|
||||||
|
pub const VL1_ERROR: u8 = 0x02;
|
||||||
|
pub const VL1_OK: u8 = 0x03;
|
||||||
|
pub const VL1_WHOIS: u8 = 0x04;
|
||||||
|
pub const VL1_RENDEZVOUS: u8 = 0x05;
|
||||||
|
pub const VL1_ECHO: u8 = 0x08;
|
||||||
|
pub const VL1_PUSH_DIRECT_PATHS: u8 = 0x10;
|
||||||
|
pub const VL1_USER_MESSAGE: u8 = 0x14;
|
||||||
|
}
|
||||||
|
|
||||||
/// Default maximum payload size for UDP transport.
|
/// Default maximum payload size for UDP transport.
|
||||||
///
|
///
|
||||||
|
@ -29,39 +83,6 @@ pub const VERB_VL1_USER_MESSAGE: u8 = 0x14;
|
||||||
/// two fragments.
|
/// two fragments.
|
||||||
pub const UDP_DEFAULT_MTU: usize = 1432;
|
pub const UDP_DEFAULT_MTU: usize = 1432;
|
||||||
|
|
||||||
/// KBKDF usage label indicating a key used to HMAC packets for extended authentication.
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_PACKET_HMAC: u8 = b'M';
|
|
||||||
|
|
||||||
/// KBKDF usage label for the first AES-GMAC-SIV key.
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0: u8 = b'0';
|
|
||||||
|
|
||||||
/// KBKDF usage label for the second AES-GMAC-SIV key.
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
|
|
||||||
|
|
||||||
/// KBKDF usage label for the private section of HELLOs.
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_HELLO_PRIVATE_SECTION: u8 = b'h';
|
|
||||||
|
|
||||||
/// KBKDF usage label for the key used to advance the ratchet.
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY: u8 = b'e';
|
|
||||||
|
|
||||||
/// Try to re-key ephemeral keys after this time.
|
|
||||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
|
||||||
|
|
||||||
/// Maximum number of times to use an ephemeral secret before trying to replace it.
|
|
||||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: usize = 536870912; // 1/4 the NIST/FIPS security bound of 2^31
|
|
||||||
|
|
||||||
/// Ephemeral secret reject after time.
|
|
||||||
pub const EPHEMERAL_SECRET_REJECT_AFTER_TIME: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME * 2;
|
|
||||||
|
|
||||||
/// Ephemeral secret reject after uses.
|
|
||||||
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: usize = 2147483648; // NIST/FIPS security bound
|
|
||||||
|
|
||||||
pub const SESSION_METADATA_INSTANCE_ID: &'static str = "i";
|
|
||||||
pub const SESSION_METADATA_CLOCK: &'static str = "t";
|
|
||||||
pub const SESSION_METADATA_SENT_TO: &'static str = "d";
|
|
||||||
pub const SESSION_METADATA_EPHEMERAL_CURRENT_SYMMETRIC_KEY_ID: &'static str = "e";
|
|
||||||
pub const SESSION_METADATA_EPHEMERAL_PUBLIC_OFFER: &'static str = "E";
|
|
||||||
|
|
||||||
/// Length of an address in bytes.
|
/// Length of an address in bytes.
|
||||||
pub const ADDRESS_SIZE: usize = 5;
|
pub const ADDRESS_SIZE: usize = 5;
|
||||||
|
|
||||||
|
@ -71,96 +92,133 @@ pub const ADDRESS_SIZE_STRING: usize = 10;
|
||||||
/// Prefix indicating reserved addresses (that can't actually be addresses).
|
/// Prefix indicating reserved addresses (that can't actually be addresses).
|
||||||
pub const ADDRESS_RESERVED_PREFIX: u8 = 0xff;
|
pub const ADDRESS_RESERVED_PREFIX: u8 = 0xff;
|
||||||
|
|
||||||
/// Size of packet header that lies outside the encryption envelope.
|
pub mod packet_constants {
|
||||||
pub const PACKET_HEADER_SIZE: usize = 27;
|
/// Size of packet header that lies outside the encryption envelope.
|
||||||
|
pub const HEADER_SIZE: usize = 27;
|
||||||
|
|
||||||
/// Minimum packet, which is the header plus a verb.
|
/// Maximum packet payload size including the verb/flags field.
|
||||||
pub const PACKET_SIZE_MIN: usize = PACKET_HEADER_SIZE + 1;
|
///
|
||||||
|
/// This is large enough to carry "jumbo MTU" packets. The exact
|
||||||
|
/// value is because 10005+27 == 10032 which is divisible by 16. This
|
||||||
|
/// improves memory layout and alignment when buffers are allocated.
|
||||||
|
/// This value could technically be increased but it would require a
|
||||||
|
/// protocol version bump and only new nodes would be able to accept
|
||||||
|
/// the new size.
|
||||||
|
pub const PAYLOAD_SIZE_MAX: usize = 10005;
|
||||||
|
|
||||||
/// Maximum size of an entire packet.
|
/// Minimum packet, which is the header plus a verb.
|
||||||
pub const PACKET_SIZE_MAX: usize = PACKET_HEADER_SIZE + PACKET_PAYLOAD_SIZE_MAX;
|
pub const SIZE_MIN: usize = HEADER_SIZE + 1;
|
||||||
|
|
||||||
/// Maximum packet payload size including the verb/flags field.
|
/// Maximum size of an entire packet.
|
||||||
///
|
pub const SIZE_MAX: usize = HEADER_SIZE + PAYLOAD_SIZE_MAX;
|
||||||
/// This is large enough to carry "jumbo MTU" packets. The exact
|
|
||||||
/// value is because 10005+27 == 10032 which is divisible by 16. This
|
|
||||||
/// improves memory layout and alignment when buffers are allocated.
|
|
||||||
/// This value could technically be increased but it would require a
|
|
||||||
/// protocol version bump and only new nodes would be able to accept
|
|
||||||
/// the new size.
|
|
||||||
pub const PACKET_PAYLOAD_SIZE_MAX: usize = 10005;
|
|
||||||
|
|
||||||
/// Index of packet verb after header.
|
/// Index of packet verb after header.
|
||||||
pub const PACKET_VERB_INDEX: usize = 27;
|
pub const VERB_INDEX: usize = 27;
|
||||||
|
|
||||||
/// Index of destination in both fragment and full packet headers.
|
/// Index of destination in both fragment and full packet headers.
|
||||||
pub const PACKET_DESTINATION_INDEX: usize = 8;
|
pub const DESTINATION_INDEX: usize = 8;
|
||||||
|
|
||||||
/// Index of 8-byte MAC field in packet header.
|
/// Index of 8-byte MAC field in packet header.
|
||||||
pub const HEADER_MAC_FIELD_INDEX: usize = 19;
|
pub const MAC_FIELD_INDEX: usize = 19;
|
||||||
|
|
||||||
/// Mask to select cipher from header flags field.
|
/// Mask to select cipher from header flags field.
|
||||||
pub const HEADER_FLAGS_FIELD_MASK_CIPHER: u8 = 0x30;
|
pub const FLAGS_FIELD_MASK_CIPHER: u8 = 0x30;
|
||||||
|
|
||||||
/// Mask to select packet hops from header flags field.
|
/// Mask to select packet hops from header flags field.
|
||||||
pub const HEADER_FLAGS_FIELD_MASK_HOPS: u8 = 0x07;
|
pub const FLAGS_FIELD_MASK_HOPS: u8 = 0x07;
|
||||||
|
|
||||||
/// Mask to select packet hops from header flags field.
|
/// Mask to select packet hops from header flags field.
|
||||||
pub const HEADER_FLAGS_FIELD_MASK_HIDE_HOPS: u8 = 0xf8;
|
pub const FLAGS_FIELD_MASK_HIDE_HOPS: u8 = 0xf8;
|
||||||
|
|
||||||
/// Index of hops/flags field
|
/// Index of hops/flags field
|
||||||
pub const HEADER_FLAGS_FIELD_INDEX: usize = 18;
|
pub const FLAGS_FIELD_INDEX: usize = 18;
|
||||||
|
|
||||||
/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext.
|
/// Minimum size of a fragment.
|
||||||
/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305.
|
pub const FRAGMENT_SIZE_MIN: usize = 16;
|
||||||
pub const CIPHER_NOCRYPT_POLY1305: u8 = 0x00;
|
|
||||||
|
|
||||||
/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305.
|
/// Size of fragment header after which data begins.
|
||||||
/// Construction is the same as that which is used in the NaCl secret box functions.
|
pub const FRAGMENT_HEADER_SIZE: usize = 16;
|
||||||
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10;
|
|
||||||
|
|
||||||
/// Formerly 'NONE' which is deprecated; reserved for future use.
|
/// Maximum allowed number of fragments.
|
||||||
pub const CIPHER_RESERVED: u8 = 0x20;
|
pub const FRAGMENT_COUNT_MAX: usize = 8;
|
||||||
|
|
||||||
/// Packet is encrypted and authenticated with AES-GMAC-SIV (AES-256).
|
/// Time after which an incomplete fragmented packet expires.
|
||||||
pub const CIPHER_AES_GMAC_SIV: u8 = 0x30;
|
pub const FRAGMENT_EXPIRATION: i64 = 1500;
|
||||||
|
|
||||||
/// Header (outer) flag indicating that this packet has additional fragments.
|
/// Maximum number of inbound fragmented packets to handle at once per path.
|
||||||
pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
/// This is a sanity limit to prevent memory exhaustion due to DOS attacks or broken peers.
|
||||||
|
pub const FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH: usize = 256;
|
||||||
|
|
||||||
/// Minimum size of a fragment.
|
/// Index of packet fragment indicator byte to detect fragments.
|
||||||
pub const PACKET_FRAGMENT_SIZE_MIN: usize = 16;
|
pub const FRAGMENT_INDICATOR_INDEX: usize = 13;
|
||||||
|
|
||||||
/// Size of fragment header after which data begins.
|
/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment.
|
||||||
pub const FRAGMENT_HEADER_SIZE: usize = 16;
|
pub const FRAGMENT_INDICATOR: u8 = 0xff;
|
||||||
|
|
||||||
/// Maximum allowed number of fragments.
|
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
||||||
pub const PACKET_FRAGMENT_COUNT_MAX: usize = 8;
|
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
||||||
|
|
||||||
/// Time after which an incomplete fragmented packet expires.
|
/// Verb (inner) flag indicating that payload is authenticated with HMAC-SHA384.
|
||||||
pub const PACKET_FRAGMENT_EXPIRATION: i64 = 1500;
|
pub const VERB_FLAG_EXTENDED_AUTHENTICATION: u8 = 0x40;
|
||||||
|
|
||||||
/// Maximum number of inbound fragmented packets to handle at once per path.
|
/// Mask to get only the verb from the verb + verb flags byte.
|
||||||
/// This is a sanity limit to prevent memory exhaustion due to DOS attacks or broken peers.
|
pub const VERB_MASK: u8 = 0x1f;
|
||||||
pub const PACKET_FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH: usize = 256;
|
|
||||||
|
|
||||||
/// Index of packet fragment indicator byte to detect fragments.
|
/// Maximum number of verbs that the protocol can support.
|
||||||
pub const PACKET_FRAGMENT_INDICATOR_INDEX: usize = 13;
|
pub const VERB_MAX_COUNT: usize = 32;
|
||||||
|
|
||||||
/// Byte found at FRAGMENT_INDICATOR_INDEX to indicate a fragment.
|
/// Header (outer) flag indicating that this packet has additional fragments.
|
||||||
pub const PACKET_FRAGMENT_INDICATOR: u8 = 0xff;
|
pub const HEADER_FLAG_FRAGMENTED: u8 = 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
/// Verb (inner) flag indicating that the packet's payload (after the verb) is LZ4 compressed.
|
pub mod security_constants {
|
||||||
pub const VERB_FLAG_COMPRESSED: u8 = 0x80;
|
/// Packet is not encrypted but contains a Poly1305 MAC of the plaintext.
|
||||||
|
/// Poly1305 is initialized with Salsa20/12 in the same manner as SALSA2012_POLY1305.
|
||||||
|
pub const CIPHER_NOCRYPT_POLY1305: u8 = 0x00;
|
||||||
|
|
||||||
/// Verb (inner) flag indicating that payload is authenticated with HMAC-SHA384.
|
/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305.
|
||||||
pub const VERB_FLAG_EXTENDED_AUTHENTICATION: u8 = 0x40;
|
/// Construction is the same as that which is used in the NaCl secret box functions.
|
||||||
|
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10;
|
||||||
|
|
||||||
/// Mask to get only the verb from the verb + verb flags byte.
|
/// Formerly 'NONE' which is deprecated; reserved for future use.
|
||||||
pub const VERB_MASK: u8 = 0x1f;
|
pub const CIPHER_RESERVED: u8 = 0x20;
|
||||||
|
|
||||||
/// Maximum number of verbs that the protocol can support.
|
/// Packet is encrypted and authenticated with AES-GMAC-SIV (AES-256).
|
||||||
pub const VERB_MAX_COUNT: usize = 32;
|
pub const CIPHER_AES_GMAC_SIV: u8 = 0x30;
|
||||||
|
|
||||||
|
/// KBKDF usage label indicating a key used to HMAC packets for extended authentication.
|
||||||
|
pub const KBKDF_KEY_USAGE_LABEL_PACKET_HMAC: u8 = b'M';
|
||||||
|
|
||||||
|
/// KBKDF usage label for the first AES-GMAC-SIV key.
|
||||||
|
pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0: u8 = b'0';
|
||||||
|
|
||||||
|
/// KBKDF usage label for the second AES-GMAC-SIV key.
|
||||||
|
pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
|
||||||
|
|
||||||
|
/// KBKDF usage label for the private section of HELLOs.
|
||||||
|
pub const KBKDF_KEY_USAGE_LABEL_HELLO_PRIVATE_SECTION: u8 = b'h';
|
||||||
|
|
||||||
|
/// KBKDF usage label for the key used to advance the ratchet.
|
||||||
|
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY: u8 = b'e';
|
||||||
|
|
||||||
|
/// Try to re-key ephemeral keys after this time.
|
||||||
|
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
||||||
|
|
||||||
|
/// Maximum number of times to use an ephemeral secret before trying to replace it.
|
||||||
|
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: usize = 536870912; // 1/4 the NIST/FIPS security bound of 2^31
|
||||||
|
|
||||||
|
/// Ephemeral secret reject after time.
|
||||||
|
pub const EPHEMERAL_SECRET_REJECT_AFTER_TIME: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME * 2;
|
||||||
|
|
||||||
|
/// Ephemeral secret reject after uses.
|
||||||
|
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: usize = 2147483648; // NIST/FIPS security bound
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod session_metadata {
|
||||||
|
pub const INSTANCE_ID: &'static str = "i";
|
||||||
|
pub const CLOCK: &'static str = "t";
|
||||||
|
pub const SENT_TO: &'static str = "d";
|
||||||
|
}
|
||||||
|
|
||||||
/// Maximum number of packet hops allowed by the protocol.
|
/// Maximum number of packet hops allowed by the protocol.
|
||||||
pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
||||||
|
@ -168,7 +226,7 @@ pub const PROTOCOL_MAX_HOPS: u8 = 7;
|
||||||
/// Maximum number of hops to allow.
|
/// Maximum number of hops to allow.
|
||||||
pub const FORWARD_MAX_HOPS: u8 = 3;
|
pub const FORWARD_MAX_HOPS: u8 = 3;
|
||||||
|
|
||||||
/// Maximum difference between current packet ID counter and OK/ERROR in-re packet ID.
|
/// Maximum difference between current message ID and OK/ERROR in-re message ID.
|
||||||
pub const PACKET_RESPONSE_COUNTER_DELTA_MAX: u64 = 1024;
|
pub const PACKET_RESPONSE_COUNTER_DELTA_MAX: u64 = 1024;
|
||||||
|
|
||||||
/// Frequency for WHOIS retries
|
/// Frequency for WHOIS retries
|
||||||
|
@ -183,9 +241,18 @@ pub const WHOIS_MAX_WAITING_PACKETS: usize = 64;
|
||||||
/// Keepalive interval for paths in milliseconds.
|
/// Keepalive interval for paths in milliseconds.
|
||||||
pub const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
pub const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
||||||
|
|
||||||
|
/// Path object expiration time in milliseconds since last receive.
|
||||||
|
pub const PATH_EXPIRATION_TIME: i64 = (PATH_KEEPALIVE_INTERVAL * 2) + 10000;
|
||||||
|
|
||||||
/// How often to send HELLOs to roots, which is more often than normal peers.
|
/// How often to send HELLOs to roots, which is more often than normal peers.
|
||||||
pub const ROOT_HELLO_INTERVAL: i64 = PATH_KEEPALIVE_INTERVAL * 2;
|
pub const ROOT_HELLO_INTERVAL: i64 = PATH_KEEPALIVE_INTERVAL * 2;
|
||||||
|
|
||||||
|
/// How often to send HELLOs to regular peers.
|
||||||
|
pub const PEER_HELLO_INTERVAL_MAX: i64 = 300000;
|
||||||
|
|
||||||
|
/// Timeout for path association with peers and for peers themselves.
|
||||||
|
pub const PEER_EXPIRATION_TIME: i64 = (PEER_HELLO_INTERVAL_MAX * 2) + 10000;
|
||||||
|
|
||||||
/// Proof of work difficulty (threshold) for identity generation.
|
/// Proof of work difficulty (threshold) for identity generation.
|
||||||
pub const IDENTITY_POW_THRESHOLD: u8 = 17;
|
pub const IDENTITY_POW_THRESHOLD: u8 = 17;
|
||||||
|
|
||||||
|
@ -194,18 +261,18 @@ pub const IDENTITY_POW_THRESHOLD: u8 = 17;
|
||||||
/// If this returns true the destination buffer will contain a compressed packet. If false is
|
/// If this returns true the destination buffer will contain a compressed packet. If false is
|
||||||
/// returned the contents of 'dest' are entirely undefined. This indicates that the data was not
|
/// returned the contents of 'dest' are entirely undefined. This indicates that the data was not
|
||||||
/// compressable or some other error occurred.
|
/// compressable or some other error occurred.
|
||||||
pub fn compress_packet(src: &[u8], dest: &mut Buffer<PACKET_SIZE_MAX>) -> bool {
|
pub fn compress_packet<const S: usize>(src: &[u8], dest: &mut Buffer<S>) -> bool {
|
||||||
if src.len() > (PACKET_VERB_INDEX + 16) {
|
if src.len() > (packet_constants::VERB_INDEX + 16) {
|
||||||
let compressed_data_size = {
|
let compressed_data_size = {
|
||||||
let d = unsafe { dest.entire_buffer_mut() };
|
let d = unsafe { dest.entire_buffer_mut() };
|
||||||
d[0..PACKET_VERB_INDEX].copy_from_slice(&src[0..PACKET_VERB_INDEX]);
|
d[..packet_constants::VERB_INDEX].copy_from_slice(&src[0..packet_constants::VERB_INDEX]);
|
||||||
d[PACKET_VERB_INDEX] = src[PACKET_VERB_INDEX] | VERB_FLAG_COMPRESSED;
|
d[packet_constants::VERB_INDEX] = src[packet_constants::VERB_INDEX] | packet_constants::VERB_FLAG_COMPRESSED;
|
||||||
lz4_flex::block::compress_into(&src[PACKET_VERB_INDEX + 1..], &mut d[PACKET_VERB_INDEX + 1..])
|
lz4_flex::block::compress_into(&src[packet_constants::VERB_INDEX + 1..], &mut d[packet_constants::VERB_INDEX + 1..])
|
||||||
};
|
};
|
||||||
if compressed_data_size.is_ok() {
|
if compressed_data_size.is_ok() {
|
||||||
let compressed_data_size = compressed_data_size.unwrap();
|
let compressed_data_size = compressed_data_size.unwrap();
|
||||||
if compressed_data_size > 0 && compressed_data_size < (src.len() - PACKET_VERB_INDEX) {
|
if compressed_data_size > 0 && compressed_data_size < (src.len() - packet_constants::VERB_INDEX) {
|
||||||
unsafe { dest.set_size_unchecked(PACKET_VERB_INDEX + 1 + compressed_data_size) };
|
unsafe { dest.set_size_unchecked(packet_constants::VERB_INDEX + 1 + compressed_data_size) };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,6 +284,7 @@ pub fn compress_packet(src: &[u8], dest: &mut Buffer<PACKET_SIZE_MAX>) -> bool {
|
||||||
///
|
///
|
||||||
/// This is the header for a complete packet. If the fragmented flag is set, it will
|
/// This is the header for a complete packet. If the fragmented flag is set, it will
|
||||||
/// arrive with one or more fragments that must be assembled to complete it.
|
/// arrive with one or more fragments that must be assembled to complete it.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct PacketHeader {
|
pub struct PacketHeader {
|
||||||
pub id: [u8; 8],
|
pub id: [u8; 8],
|
||||||
|
@ -226,35 +294,33 @@ pub struct PacketHeader {
|
||||||
pub mac: [u8; 8],
|
pub mac: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for PacketHeader {}
|
|
||||||
|
|
||||||
impl PacketHeader {
|
impl PacketHeader {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn cipher(&self) -> u8 {
|
pub fn cipher(&self) -> u8 {
|
||||||
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER
|
self.flags_cipher_hops & packet_constants::FLAGS_FIELD_MASK_CIPHER
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hops(&self) -> u8 {
|
pub fn hops(&self) -> u8 {
|
||||||
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
self.flags_cipher_hops & packet_constants::FLAGS_FIELD_MASK_HOPS
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn increment_hops(&mut self) -> u8 {
|
pub fn increment_hops(&mut self) -> u8 {
|
||||||
let f = self.flags_cipher_hops;
|
let f = self.flags_cipher_hops;
|
||||||
let h = (f + 1) & HEADER_FLAGS_FIELD_MASK_HOPS;
|
let h = (f + 1) & packet_constants::FLAGS_FIELD_MASK_HOPS;
|
||||||
self.flags_cipher_hops = (f & HEADER_FLAGS_FIELD_MASK_HIDE_HOPS) | h;
|
self.flags_cipher_hops = (f & packet_constants::FLAGS_FIELD_MASK_HIDE_HOPS) | h;
|
||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_fragmented(&self) -> bool {
|
pub fn is_fragmented(&self) -> bool {
|
||||||
(self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0
|
(self.flags_cipher_hops & packet_constants::HEADER_FLAG_FRAGMENTED) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; PACKET_HEADER_SIZE] {
|
pub fn as_bytes(&self) -> &[u8; packet_constants::HEADER_SIZE] {
|
||||||
unsafe { &*(self as *const Self).cast::<[u8; PACKET_HEADER_SIZE]>() }
|
unsafe { &*(self as *const Self).cast::<[u8; packet_constants::HEADER_SIZE]>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -262,7 +328,7 @@ impl PacketHeader {
|
||||||
let mut id = unsafe { MaybeUninit::<[u8; 11]>::uninit().assume_init() };
|
let mut id = unsafe { MaybeUninit::<[u8; 11]>::uninit().assume_init() };
|
||||||
id[0..5].copy_from_slice(&self.dest);
|
id[0..5].copy_from_slice(&self.dest);
|
||||||
id[5..10].copy_from_slice(&self.src);
|
id[5..10].copy_from_slice(&self.src);
|
||||||
id[10] = self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HIDE_HOPS;
|
id[10] = self.flags_cipher_hops & packet_constants::FLAGS_FIELD_MASK_HIDE_HOPS;
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +347,7 @@ impl PacketHeader {
|
||||||
/// is normally illegal since addresses can't begin with that. Fragmented packets
|
/// is normally illegal since addresses can't begin with that. Fragmented packets
|
||||||
/// will arrive with the first fragment carrying a normal header with the fragment
|
/// will arrive with the first fragment carrying a normal header with the fragment
|
||||||
/// bit set and remaining fragments being these.
|
/// bit set and remaining fragments being these.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct FragmentHeader {
|
pub struct FragmentHeader {
|
||||||
pub id: [u8; 8], // (outer) packet ID
|
pub id: [u8; 8], // (outer) packet ID
|
||||||
|
@ -290,12 +357,10 @@ pub struct FragmentHeader {
|
||||||
pub reserved_hops: u8, // rrrrrHHH (3 hops bits, rest reserved)
|
pub reserved_hops: u8, // rrrrrHHH (3 hops bits, rest reserved)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for FragmentHeader {}
|
|
||||||
|
|
||||||
impl FragmentHeader {
|
impl FragmentHeader {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_fragment(&self) -> bool {
|
pub fn is_fragment(&self) -> bool {
|
||||||
self.fragment_indicator == PACKET_FRAGMENT_INDICATOR
|
self.fragment_indicator == packet_constants::FRAGMENT_INDICATOR
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -310,34 +375,32 @@ impl FragmentHeader {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hops(&self) -> u8 {
|
pub fn hops(&self) -> u8 {
|
||||||
self.reserved_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
self.reserved_hops & packet_constants::FLAGS_FIELD_MASK_HOPS
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn increment_hops(&mut self) -> u8 {
|
pub fn increment_hops(&mut self) -> u8 {
|
||||||
let f = self.reserved_hops;
|
let f = self.reserved_hops;
|
||||||
let h = (f + 1) & HEADER_FLAGS_FIELD_MASK_HOPS;
|
let h = (f + 1) & packet_constants::FLAGS_FIELD_MASK_HOPS;
|
||||||
self.reserved_hops = (f & HEADER_FLAGS_FIELD_MASK_HIDE_HOPS) | h;
|
self.reserved_hops = (f & packet_constants::FLAGS_FIELD_MASK_HIDE_HOPS) | h;
|
||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] {
|
pub fn as_bytes(&self) -> &[u8; packet_constants::FRAGMENT_HEADER_SIZE] {
|
||||||
unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() }
|
unsafe { &*(self as *const Self).cast::<[u8; packet_constants::FRAGMENT_HEADER_SIZE]>() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod message_component_structs {
|
pub(crate) mod message_component_structs {
|
||||||
use crate::util::buffer::FlatBlob;
|
#[derive(Clone, Copy)]
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct OkHeader {
|
pub struct OkHeader {
|
||||||
pub in_re_verb: u8,
|
pub in_re_verb: u8,
|
||||||
pub in_re_message_id: [u8; 8],
|
pub in_re_message_id: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for OkHeader {}
|
#[derive(Clone, Copy)]
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct ErrorHeader {
|
pub struct ErrorHeader {
|
||||||
pub in_re_verb: u8,
|
pub in_re_verb: u8,
|
||||||
|
@ -345,8 +408,7 @@ pub(crate) mod message_component_structs {
|
||||||
pub error_code: u8,
|
pub error_code: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for ErrorHeader {}
|
#[derive(Clone, Copy)]
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct HelloFixedHeaderFields {
|
pub struct HelloFixedHeaderFields {
|
||||||
pub verb: u8,
|
pub verb: u8,
|
||||||
|
@ -357,8 +419,7 @@ pub(crate) mod message_component_structs {
|
||||||
pub timestamp: [u8; 8], // u64
|
pub timestamp: [u8; 8], // u64
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for HelloFixedHeaderFields {}
|
#[derive(Clone, Copy)]
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct OkHelloFixedHeaderFields {
|
pub struct OkHelloFixedHeaderFields {
|
||||||
pub timestamp_echo: [u8; 8], // u64
|
pub timestamp_echo: [u8; 8], // u64
|
||||||
|
@ -367,8 +428,6 @@ pub(crate) mod message_component_structs {
|
||||||
pub version_minor: u8,
|
pub version_minor: u8,
|
||||||
pub version_revision: [u8; 2], // u16
|
pub version_revision: [u8; 2], // u16
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FlatBlob for OkHelloFixedHeaderFields {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -381,8 +440,8 @@ mod tests {
|
||||||
fn representation() {
|
fn representation() {
|
||||||
assert_eq!(size_of::<message_component_structs::OkHeader>(), 9);
|
assert_eq!(size_of::<message_component_structs::OkHeader>(), 9);
|
||||||
assert_eq!(size_of::<message_component_structs::ErrorHeader>(), 10);
|
assert_eq!(size_of::<message_component_structs::ErrorHeader>(), 10);
|
||||||
assert_eq!(size_of::<PacketHeader>(), PACKET_HEADER_SIZE);
|
assert_eq!(size_of::<PacketHeader>(), packet_constants::HEADER_SIZE);
|
||||||
assert_eq!(size_of::<FragmentHeader>(), FRAGMENT_HEADER_SIZE);
|
assert_eq!(size_of::<FragmentHeader>(), packet_constants::FRAGMENT_HEADER_SIZE);
|
||||||
|
|
||||||
let mut foo = [0_u8; 32];
|
let mut foo = [0_u8; 32];
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -12,7 +12,6 @@ use std::io::Write;
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::util::marshalable::Marshalable;
|
use crate::util::marshalable::Marshalable;
|
||||||
use crate::vl1::identity::*;
|
use crate::vl1::identity::*;
|
||||||
use crate::vl1::protocol::PACKET_SIZE_MAX;
|
|
||||||
use crate::vl1::Endpoint;
|
use crate::vl1::Endpoint;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -48,14 +47,14 @@ pub struct Root {
|
||||||
impl PartialOrd for Root {
|
impl PartialOrd for Root {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
self.identity.address.partial_cmp(&other.identity.address)
|
self.identity.partial_cmp(&other.identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Root {
|
impl Ord for Root {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.identity.address.cmp(&other.identity.address)
|
self.identity.cmp(&other.identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ impl RootSet {
|
||||||
buf.append_varint(self.revision)?;
|
buf.append_varint(self.revision)?;
|
||||||
buf.append_varint(self.members.len() as u64)?;
|
buf.append_varint(self.members.len() as u64)?;
|
||||||
for m in self.members.iter() {
|
for m in self.members.iter() {
|
||||||
m.identity.marshal_with_options(buf, IDENTITY_ALGORITHM_ALL, false)?;
|
m.identity.marshal_with_options(buf, Identity::ALGORITHM_ALL, false)?;
|
||||||
if m.endpoints.is_some() {
|
if m.endpoints.is_some() {
|
||||||
let endpoints = m.endpoints.as_ref().unwrap();
|
let endpoints = m.endpoints.as_ref().unwrap();
|
||||||
buf.append_varint(endpoints.len() as u64)?;
|
buf.append_varint(endpoints.len() as u64)?;
|
||||||
|
@ -135,8 +134,8 @@ impl RootSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal method to marshal without signatures for use during sign and verify.
|
/// Internal method to marshal without signatures for use during sign and verify.
|
||||||
fn marshal_for_signing(&self) -> Buffer<MAX_MARSHAL_SIZE> {
|
fn marshal_for_signing(&self) -> Buffer<{ Self::MAX_MARSHAL_SIZE }> {
|
||||||
let mut tmp = Buffer::<MAX_MARSHAL_SIZE>::new();
|
let mut tmp = Buffer::<{ Self::MAX_MARSHAL_SIZE }>::new();
|
||||||
assert!(self.marshal_internal(&mut tmp, false).is_ok());
|
assert!(self.marshal_internal(&mut tmp, false).is_ok());
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
@ -183,7 +182,7 @@ impl RootSet {
|
||||||
/// All current members must sign whether they are disabled (witnessing) or active. The verify()
|
/// All current members must sign whether they are disabled (witnessing) or active. The verify()
|
||||||
/// method will return true when signing is complete.
|
/// method will return true when signing is complete.
|
||||||
pub fn sign(&mut self, member_identity: &Identity) -> bool {
|
pub fn sign(&mut self, member_identity: &Identity) -> bool {
|
||||||
let signature = member_identity.sign(self.marshal_for_signing().as_bytes(), IDENTITY_ALGORITHM_ALL, false);
|
let signature = member_identity.sign(self.marshal_for_signing().as_bytes(), Identity::ALGORITHM_ALL, false);
|
||||||
let unsigned_entry = self.members.iter().find_map(|m| if m.identity.eq(member_identity) { Some(m.clone()) } else { None });
|
let unsigned_entry = self.members.iter().find_map(|m| if m.identity.eq(member_identity) { Some(m.clone()) } else { None });
|
||||||
if unsigned_entry.is_some() && signature.is_some() {
|
if unsigned_entry.is_some() && signature.is_some() {
|
||||||
let unsigned_entry = unsigned_entry.unwrap();
|
let unsigned_entry = unsigned_entry.unwrap();
|
||||||
|
@ -243,7 +242,7 @@ impl RootSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Marshalable for RootSet {
|
impl Marshalable for RootSet {
|
||||||
const MAX_MARSHAL_SIZE: usize = PACKET_SIZE_MAX;
|
const MAX_MARSHAL_SIZE: usize = crate::vl1::protocol::packet_constants::SIZE_MAX;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
|
|
|
@ -15,21 +15,6 @@ use zerotier_core_crypto::secret::Secret;
|
||||||
use crate::util::pool::{Pool, PoolFactory};
|
use crate::util::pool::{Pool, PoolFactory};
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
|
|
||||||
/// Pool of reusable AES-GMAC-SIV instances.
|
|
||||||
pub(crate) struct AesGmacSivPoolFactory(Secret<32>, Secret<32>);
|
|
||||||
|
|
||||||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
|
||||||
#[inline(always)]
|
|
||||||
fn create(&self) -> AesGmacSiv {
|
|
||||||
AesGmacSiv::new(self.0.as_bytes(), self.1.as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn reset(&self, obj: &mut AesGmacSiv) {
|
|
||||||
obj.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A symmetric secret key negotiated between peers.
|
/// A symmetric secret key negotiated between peers.
|
||||||
///
|
///
|
||||||
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
||||||
|
@ -50,22 +35,14 @@ pub(crate) struct SymmetricSecret {
|
||||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for SymmetricSecret {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.key == other.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for SymmetricSecret {}
|
|
||||||
|
|
||||||
impl SymmetricSecret {
|
impl SymmetricSecret {
|
||||||
/// Create a new symmetric secret, deriving all sub-keys and such.
|
/// Create a new symmetric secret, deriving all sub-keys and such.
|
||||||
pub fn new(key: Secret<64>) -> SymmetricSecret {
|
pub fn new(key: Secret<64>) -> SymmetricSecret {
|
||||||
let hello_private_section_key = zt_kbkdf_hmac_sha384(&key.0, KBKDF_KEY_USAGE_LABEL_HELLO_PRIVATE_SECTION, 0, 0);
|
let hello_private_section_key = zt_kbkdf_hmac_sha384(&key.0, security_constants::KBKDF_KEY_USAGE_LABEL_HELLO_PRIVATE_SECTION);
|
||||||
let packet_hmac_key = zt_kbkdf_hmac_sha512(&key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
let packet_hmac_key = zt_kbkdf_hmac_sha512(&key.0, security_constants::KBKDF_KEY_USAGE_LABEL_PACKET_HMAC);
|
||||||
let ephemeral_ratchet_key = zt_kbkdf_hmac_sha512(&key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY, 0, 0);
|
let ephemeral_ratchet_key = zt_kbkdf_hmac_sha512(&key.0, security_constants::KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY);
|
||||||
let aes_factory = AesGmacSivPoolFactory(zt_kbkdf_hmac_sha384(&key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0).first_n(), zt_kbkdf_hmac_sha384(&key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0).first_n());
|
let aes_factory =
|
||||||
|
AesGmacSivPoolFactory(zt_kbkdf_hmac_sha384(&key.0[..48], security_constants::KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0).first_n(), zt_kbkdf_hmac_sha384(&key.0[..48], security_constants::KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1).first_n());
|
||||||
SymmetricSecret {
|
SymmetricSecret {
|
||||||
key,
|
key,
|
||||||
hello_private_section_key,
|
hello_private_section_key,
|
||||||
|
@ -78,10 +55,9 @@ impl SymmetricSecret {
|
||||||
|
|
||||||
/// An ephemeral symmetric secret with usage timers and counters.
|
/// An ephemeral symmetric secret with usage timers and counters.
|
||||||
pub(crate) struct EphemeralSymmetricSecret {
|
pub(crate) struct EphemeralSymmetricSecret {
|
||||||
pub id: [u8; 16], // first 16 bytes of SHA384 of symmetric secret
|
|
||||||
pub secret: SymmetricSecret,
|
pub secret: SymmetricSecret,
|
||||||
pub rekey_time: i64,
|
pub rekey_time_ticks: i64,
|
||||||
pub expire_time: i64,
|
pub expire_time_ticks: i64,
|
||||||
pub ratchet_count: u64,
|
pub ratchet_count: u64,
|
||||||
pub encrypt_uses: AtomicUsize,
|
pub encrypt_uses: AtomicUsize,
|
||||||
pub decrypt_uses: AtomicUsize,
|
pub decrypt_uses: AtomicUsize,
|
||||||
|
@ -91,11 +67,25 @@ pub(crate) struct EphemeralSymmetricSecret {
|
||||||
impl EphemeralSymmetricSecret {
|
impl EphemeralSymmetricSecret {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn should_rekey(&self, time_ticks: i64) -> bool {
|
pub fn should_rekey(&self, time_ticks: i64) -> bool {
|
||||||
time_ticks >= self.rekey_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REKEY_AFTER_USES
|
time_ticks >= self.rekey_time_ticks || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= security_constants::EPHEMERAL_SECRET_REKEY_AFTER_USES
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn expired(&self, time_ticks: i64) -> bool {
|
pub fn is_expired(&self, time_ticks: i64) -> bool {
|
||||||
time_ticks >= self.expire_time || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= EPHEMERAL_SECRET_REJECT_AFTER_USES
|
time_ticks >= self.expire_time_ticks || self.encrypt_uses.load(Ordering::Relaxed).max(self.decrypt_uses.load(Ordering::Relaxed)) >= security_constants::EPHEMERAL_SECRET_REJECT_AFTER_USES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct AesGmacSivPoolFactory(Secret<32>, Secret<32>);
|
||||||
|
|
||||||
|
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||||
|
#[inline(always)]
|
||||||
|
fn create(&self) -> AesGmacSiv {
|
||||||
|
AesGmacSiv::new(self.0.as_bytes(), self.1.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset(&self, obj: &mut AesGmacSiv) {
|
||||||
|
obj.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,14 @@ use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::util::gate::IntervalGate;
|
use crate::util::gate::IntervalGate;
|
||||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||||
use crate::vl1::node::{BackgroundServicable, Node, SystemInterface};
|
use crate::vl1::node::{Node, SystemInterface};
|
||||||
use crate::vl1::protocol::{WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_INTERVAL, WHOIS_RETRY_MAX};
|
use crate::vl1::protocol::{PooledPacketBuffer, WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_INTERVAL, WHOIS_RETRY_MAX};
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
use crate::PacketBuffer;
|
|
||||||
|
pub(crate) const SERVICE_INTERVAL_MS: i64 = WHOIS_RETRY_INTERVAL;
|
||||||
|
|
||||||
pub(crate) enum QueuedPacket {
|
pub(crate) enum QueuedPacket {
|
||||||
Unfragmented(PacketBuffer),
|
Unfragmented(PooledPacketBuffer),
|
||||||
Fragmented(FragmentedPacket),
|
Fragmented(FragmentedPacket),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ impl WhoisQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch or renew a WHOIS query and enqueue a packet to be processed when (if) it is received.
|
/// Launch or renew a WHOIS query and enqueue a packet to be processed when (if) it is received.
|
||||||
pub fn query<SI: SystemInterface>(&self, node: &Node, si: &SI, target: Address, packet: Option<QueuedPacket>) {
|
pub fn query<SI: SystemInterface>(&self, node: &Node<SI>, si: &SI, target: Address, packet: Option<QueuedPacket>) {
|
||||||
let mut q = self.0.lock();
|
let mut q = self.0.lock();
|
||||||
|
|
||||||
let qi = q.entry(target).or_insert_with(|| WhoisQueueItem {
|
let qi = q.entry(target).or_insert_with(|| WhoisQueueItem {
|
||||||
|
@ -63,15 +64,11 @@ impl WhoisQueue {
|
||||||
let _ = qi.map(|mut qi| qi.packet_queue.iter_mut().for_each(packet_handler));
|
let _ = qi.map(|mut qi| qi.packet_queue.iter_mut().for_each(packet_handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_whois<SI: SystemInterface>(&self, node: &Node, si: &SI, targets: &[Address]) {
|
fn send_whois<SI: SystemInterface>(&self, node: &Node<SI>, si: &SI, targets: &[Address]) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl BackgroundServicable for WhoisQueue {
|
pub(crate) fn service<SI: SystemInterface>(&self, si: &SI, node: &Node<SI>, time_ticks: i64) -> bool {
|
||||||
const SERVICE_INTERVAL_MS: i64 = WHOIS_RETRY_INTERVAL;
|
|
||||||
|
|
||||||
fn service<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64) -> bool {
|
|
||||||
let mut targets: Vec<Address> = Vec::new();
|
let mut targets: Vec<Address> = Vec::new();
|
||||||
self.0.lock().retain(|target, qi| {
|
self.0.lock().retain(|target, qi| {
|
||||||
if qi.retry_count < WHOIS_RETRY_MAX {
|
if qi.retry_count < WHOIS_RETRY_MAX {
|
||||||
|
|
|
@ -6,10 +6,7 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::sync::Arc;
|
use crate::vl1::node::{InnerProtocolInterface, SystemInterface};
|
||||||
|
|
||||||
use crate::util::buffer::Buffer;
|
|
||||||
use crate::vl1::node::InnerProtocolInterface;
|
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::vl1::{Identity, Path, Peer};
|
use crate::vl1::{Identity, Path, Peer};
|
||||||
|
|
||||||
|
@ -18,15 +15,15 @@ pub trait SwitchInterface: Sync + Send {}
|
||||||
pub struct Switch {}
|
pub struct Switch {}
|
||||||
|
|
||||||
impl InnerProtocolInterface for Switch {
|
impl InnerProtocolInterface for Switch {
|
||||||
fn handle_packet(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
fn handle_packet<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &PacketBuffer) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, error_code: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool {
|
fn handle_error<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, error_code: u8, payload: &PacketBuffer, cursor: &mut usize) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ok(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, payload: &Buffer<{ PACKET_SIZE_MAX }>, cursor: &mut usize) -> bool {
|
fn handle_ok<SI: SystemInterface>(&self, peer: &Peer<SI>, source_path: &Path<SI>, forward_secrecy: bool, extended_authentication: bool, in_re_verb: u8, in_re_message_id: u64, payload: &PacketBuffer, cursor: &mut usize) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
zerotier-system-service/Cargo.lock
generated
7
zerotier-system-service/Cargo.lock
generated
|
@ -366,12 +366,6 @@ version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "metrohash"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ba553cb19e2acbc54baa16faef215126243fe45e53357a3b2e9f4ebc7b0506c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -1044,7 +1038,6 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"lz4_flex",
|
"lz4_flex",
|
||||||
"metrohash",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
"winapi",
|
"winapi",
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{exitcode, Flags};
|
||||||
use zerotier_network_hypervisor::util::marshalable::Marshalable;
|
use zerotier_network_hypervisor::util::marshalable::Marshalable;
|
||||||
use zerotier_network_hypervisor::vl1::RootSet;
|
use zerotier_network_hypervisor::vl1::RootSet;
|
||||||
|
|
||||||
pub async fn cmd(flags: Flags, cmd_args: &ArgMatches) -> i32 {
|
pub async fn cmd(_: Flags, cmd_args: &ArgMatches) -> i32 {
|
||||||
match cmd_args.subcommand() {
|
match cmd_args.subcommand() {
|
||||||
Some(("trust", sc_args)) => todo!(),
|
Some(("trust", sc_args)) => todo!(),
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ use crate::utils::{read_limit, DEFAULT_FILE_IO_READ_LIMIT};
|
||||||
use tokio::sync::{Mutex, RwLock, RwLockReadGuard};
|
use tokio::sync::{Mutex, RwLock, RwLockReadGuard};
|
||||||
|
|
||||||
use zerotier_core_crypto::random::next_u32_secure;
|
use zerotier_core_crypto::random::next_u32_secure;
|
||||||
use zerotier_network_hypervisor::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
use zerotier_network_hypervisor::vl1::Identity;
|
||||||
|
|
||||||
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 64;
|
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 48;
|
||||||
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
|
const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
|
||||||
const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
|
const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
|
||||||
|
@ -72,8 +72,8 @@ impl DataDir {
|
||||||
/// Save identity.secret and identity.public to data directory.
|
/// Save identity.secret and identity.public to data directory.
|
||||||
pub async fn save_identity(&self, id: &Identity) -> std::io::Result<()> {
|
pub async fn save_identity(&self, id: &Identity) -> std::io::Result<()> {
|
||||||
assert!(id.secret.is_some());
|
assert!(id.secret.is_some());
|
||||||
let id_secret_str = id.to_string_with_options(IDENTITY_ALGORITHM_ALL, true);
|
let id_secret_str = id.to_string_with_options(Identity::ALGORITHM_ALL, true);
|
||||||
let id_public_str = id.to_string_with_options(IDENTITY_ALGORITHM_ALL, false);
|
let id_public_str = id.to_string_with_options(Identity::ALGORITHM_ALL, false);
|
||||||
let secret_path = self.base_path.join(IDENTITY_SECRET_FILENAME);
|
let secret_path = self.base_path.join(IDENTITY_SECRET_FILENAME);
|
||||||
tokio::fs::write(&secret_path, id_secret_str.as_bytes()).await?;
|
tokio::fs::write(&secret_path, id_secret_str.as_bytes()).await?;
|
||||||
assert!(crate::utils::fs_restrict_permissions(&secret_path));
|
assert!(crate::utils::fs_restrict_permissions(&secret_path));
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn s6_addr_as_ptr<A>(a: &A) -> *const A {
|
||||||
|
|
||||||
/// Call supplied function or closure for each physical IP address in the system.
|
/// Call supplied function or closure for each physical IP address in the system.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub(crate) fn for_each_address<F: FnMut(&InetAddress, &str)>(mut f: F) {
|
pub fn for_each_address<F: FnMut(&InetAddress, &str)>(mut f: F) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut ifa_name = [0_u8; libc::IFNAMSIZ as usize];
|
let mut ifa_name = [0_u8; libc::IFNAMSIZ as usize];
|
||||||
let mut ifap: *mut libc::ifaddrs = null_mut();
|
let mut ifap: *mut libc::ifaddrs = null_mut();
|
||||||
|
@ -90,7 +90,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_getifaddrs() {
|
fn test_getifaddrs() {
|
||||||
println!("starting getifaddrs...");
|
println!("starting getifaddrs...");
|
||||||
crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| println!(" {} {}", dev, a.to_string()));
|
crate::vnic::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| println!(" {} {}", dev, a.to_string()));
|
||||||
println!("done.")
|
println!("done.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,24 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use clap::error::{ContextKind, ContextValue};
|
|
||||||
use clap::{Arg, ArgMatches, Command};
|
|
||||||
|
|
||||||
use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod datadir;
|
pub mod datadir;
|
||||||
pub mod exitcode;
|
pub mod exitcode;
|
||||||
pub mod getifaddrs;
|
pub mod getifaddrs;
|
||||||
pub mod jsonformatter;
|
pub mod jsonformatter;
|
||||||
pub mod localconfig;
|
pub mod localconfig;
|
||||||
|
pub mod service;
|
||||||
|
pub mod udp;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod vnic;
|
pub mod vnic;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use clap::error::{ContextKind, ContextValue};
|
||||||
|
use clap::{Arg, ArgMatches, Command};
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
|
||||||
|
|
||||||
fn make_help() -> String {
|
fn make_help() -> String {
|
||||||
format!(
|
format!(
|
||||||
r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
|
r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
|
||||||
|
@ -124,7 +126,7 @@ pub struct Flags {
|
||||||
pub auth_token_override: Option<String>,
|
pub auth_token_override: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn async_main(flags: Flags, global_args: ArgMatches) -> i32 {
|
async fn async_main(flags: Flags, global_args: Box<ArgMatches>) -> i32 {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
return match global_args.subcommand() {
|
return match global_args.subcommand() {
|
||||||
Some(("help", _)) => {
|
Some(("help", _)) => {
|
||||||
|
@ -141,7 +143,10 @@ async fn async_main(flags: Flags, global_args: ArgMatches) -> i32 {
|
||||||
Some(("network", cmd_args)) => todo!(),
|
Some(("network", cmd_args)) => todo!(),
|
||||||
Some(("join", cmd_args)) => todo!(),
|
Some(("join", cmd_args)) => todo!(),
|
||||||
Some(("leave", cmd_args)) => todo!(),
|
Some(("leave", cmd_args)) => todo!(),
|
||||||
Some(("service", _)) => todo!(),
|
Some(("service", _)) => {
|
||||||
|
drop(global_args); // free unnecessary heap
|
||||||
|
assert!(service::Service::new(flags.base_path.as_str()).await.is_ok());
|
||||||
|
}
|
||||||
Some(("identity", cmd_args)) => todo!(),
|
Some(("identity", cmd_args)) => todo!(),
|
||||||
Some(("rootset", cmd_args)) => cli::rootset::cmd(flags, cmd_args).await,
|
Some(("rootset", cmd_args)) => cli::rootset::cmd(flags, cmd_args).await,
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -152,7 +157,7 @@ async fn async_main(flags: Flags, global_args: ArgMatches) -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let global_args = {
|
let global_args = Box::new({
|
||||||
let help = make_help();
|
let help = make_help();
|
||||||
Command::new("zerotier")
|
Command::new("zerotier")
|
||||||
.arg(Arg::new("json").short('j'))
|
.arg(Arg::new("json").short('j'))
|
||||||
|
@ -239,7 +244,7 @@ fn main() {
|
||||||
std::process::exit(exitcode::ERR_USAGE);
|
std::process::exit(exitcode::ERR_USAGE);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
});
|
||||||
|
|
||||||
let flags = Flags {
|
let flags = Flags {
|
||||||
json_output: global_args.is_present("json"),
|
json_output: global_args.is_present("json"),
|
||||||
|
|
124
zerotier-system-service/src/service.rs
Normal file
124
zerotier-system-service/src/service.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* (c)2021 ZeroTier, Inc.
|
||||||
|
* https://www.zerotier.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::num::NonZeroI64;
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
use tokio::net::UdpSocket;
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::*;
|
||||||
|
use zerotier_network_hypervisor::vl2::*;
|
||||||
|
use zerotier_network_hypervisor::*;
|
||||||
|
|
||||||
|
use crate::datadir::DataDir;
|
||||||
|
use crate::udp::BoundUdpSocket;
|
||||||
|
use crate::utils::{ms_monotonic, ms_since_epoch};
|
||||||
|
|
||||||
|
pub type DynamicError = Box<dyn Error>;
|
||||||
|
|
||||||
|
pub struct Service {
|
||||||
|
pub rt: tokio::runtime::Handle,
|
||||||
|
pub data: DataDir,
|
||||||
|
pub local_socket_unique_id_counter: AtomicUsize,
|
||||||
|
pub udp_sockets: parking_lot::RwLock<HashMap<u16, Vec<Arc<BoundUdpSocket>>>>,
|
||||||
|
pub core: Option<NetworkHypervisor<Self>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service {
|
||||||
|
pub async fn new(base_path: &str) -> Result<Self, DynamicError> {
|
||||||
|
let mut svc = Self {
|
||||||
|
rt: tokio::runtime::Handle::current(),
|
||||||
|
data: DataDir::open(base_path).await.map_err(|e| Box::new(e))?,
|
||||||
|
local_socket_unique_id_counter: AtomicUsize::new(1),
|
||||||
|
udp_sockets: parking_lot::RwLock::new(HashMap::with_capacity(4)),
|
||||||
|
core: None,
|
||||||
|
};
|
||||||
|
let _ = svc.core.insert(NetworkHypervisor::new(&svc, true).map_err(|e| Box::new(e))?);
|
||||||
|
|
||||||
|
let config = svc.data.config().await;
|
||||||
|
|
||||||
|
Ok(svc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Local socket wrapper implementing equality and hash in terms of an arbitrary unique ID.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LocalSocket(Weak<BoundUdpSocket>, usize);
|
||||||
|
|
||||||
|
impl PartialEq for LocalSocket {
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.1 == other.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for LocalSocket {}
|
||||||
|
|
||||||
|
impl Hash for LocalSocket {
|
||||||
|
#[inline(always)]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.1.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemInterface for Service {
|
||||||
|
type LocalSocket = crate::service::LocalSocket;
|
||||||
|
|
||||||
|
type LocalInterface = String;
|
||||||
|
|
||||||
|
fn event_node_is_up(&self) {}
|
||||||
|
|
||||||
|
fn event_node_is_down(&self) {}
|
||||||
|
|
||||||
|
fn event_online_status_change(&self, online: bool) {}
|
||||||
|
|
||||||
|
fn event_user_message(&self, source: &Identity, message_type: u64, message: &[u8]) {}
|
||||||
|
|
||||||
|
fn event_security_warning(&self, warning: &str) {}
|
||||||
|
|
||||||
|
fn local_socket_is_valid(&self, socket: &Self::LocalSocket) -> bool {
|
||||||
|
socket.0.strong_count() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_node_identity(&self) -> Option<Identity> {
|
||||||
|
self.rt.block_on(async { self.data.load_identity().await.map_or(None, |i| Some(i)) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_node_identity(&self, id: &Identity) {
|
||||||
|
self.rt.block_on(async { assert!(self.data.save_identity(id).await.is_ok()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<&Self::LocalSocket>, local_interface: Option<&Self::LocalInterface>, data: &[&[u8]], packet_ttl: u8) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<&Self::LocalSocket>, local_interface: Option<&Self::LocalInterface>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path_hints(&self, id: &Identity) -> Option<Vec<(Endpoint, Option<Self::LocalSocket>, Option<Self::LocalInterface>)>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn time_ticks(&self) -> i64 {
|
||||||
|
ms_monotonic()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn time_clock(&self) -> i64 {
|
||||||
|
ms_since_epoch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchInterface for Service {}
|
||||||
|
|
||||||
|
impl Interface for Service {}
|
172
zerotier-system-service/src/udp.rs
Normal file
172
zerotier-system-service/src/udp.rs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* (c)2021 ZeroTier, Inc.
|
||||||
|
* https://www.zerotier.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::num::NonZeroI64;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::io::{FromRawFd, RawFd};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use num_traits::AsPrimitive;
|
||||||
|
|
||||||
|
use crate::getifaddrs;
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::inetaddress::{InetAddress, IpScope};
|
||||||
|
|
||||||
|
/// A locally bound UDP socket.
|
||||||
|
pub struct BoundUdpSocket {
|
||||||
|
/// Locally bound address.
|
||||||
|
pub address: InetAddress,
|
||||||
|
/// Locally bound (to device) socket.
|
||||||
|
pub socket: tokio::net::UdpSocket,
|
||||||
|
/// Local interface device name or other unique identifier (OS-specific).
|
||||||
|
pub interface: String,
|
||||||
|
/// Raw socket FD, which only remains valid as long as 'socket' exists.
|
||||||
|
pub fd: RawFd,
|
||||||
|
/// Monotonic time of last activity.
|
||||||
|
pub last_activity_time_ticks: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundUdpSocket {
|
||||||
|
/// Update 'sockets' by adding any missing local bindings and removing any that are no longer valid.
|
||||||
|
///
|
||||||
|
/// Any device or local IP within any of the supplied blacklists is ignored. Multicast or loopback addresses are
|
||||||
|
/// also ignored. All errors encountered are returned.
|
||||||
|
///
|
||||||
|
/// This should always be called on the same port for the same socket collection. Calling on the same 'sockets'
|
||||||
|
/// with different ports will lead to redundant or missed bindings.
|
||||||
|
///
|
||||||
|
/// We must bind directly to each device/address pair for each port so default route override can work.
|
||||||
|
fn update_bindings_for_port(sockets: &mut Vec<Arc<BoundUdpSocket>>, port: u16, device_prefix_blacklist: &Vec<String>, cidr_blacklist: &Vec<InetAddress>) -> Vec<std::io::Error> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let mut existing_bind_points: HashMap<String, Vec<InetAddress>> = HashMap::with_capacity(id_assignment_state.devices.len() + 1);
|
||||||
|
let now = crate::utils::ms_monotonic();
|
||||||
|
getifaddrs::for_each_address(|address, device| {
|
||||||
|
if address.is_ip()
|
||||||
|
&& matches!(address.scope(), IpScope::Global | IpScope::PseudoPrivate | IpScope::Private | IpScope::Shared)
|
||||||
|
&& !device_prefix_blacklist.iter().any(|pfx| device.starts_with(pfx.as_str()))
|
||||||
|
&& !cidr_blacklist.iter().any(|r| address.is_within(r))
|
||||||
|
{
|
||||||
|
existing_bind_points.entry(device.to_string()).or_default().push(address.clone());
|
||||||
|
if !sockets.iter().any(|_, s| s.address == address || s.local_device_id == did) {
|
||||||
|
let s = unsafe { bind_udp_to_device(device, address) };
|
||||||
|
if s.is_ok() {
|
||||||
|
let fd = s.unwrap();
|
||||||
|
let s = tokio::net::UdpSocket::from_std(unsafe { std::net::UdpSocket::from_raw_fd(fd) });
|
||||||
|
if s.is_ok() {
|
||||||
|
id_assignment_state.socket_id_counter += 1;
|
||||||
|
let lsid = NonZeroI64::new(id_assignment_state.socket_id_counter).unwrap();
|
||||||
|
sockets.push(Arc::new(BoundUdpSocket {
|
||||||
|
address: address.clone(),
|
||||||
|
socket: s.unwrap(),
|
||||||
|
interface: device.to_string(),
|
||||||
|
fd,
|
||||||
|
last_activity_time_ticks: now,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
errors.push(s.err().unwrap());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.push(std::io::Error::new(std::io::ErrorKind::AddrInUse, s.err().unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sockets.retain(|s| existing_bind_points.get(&s.local_interface).map_or(false, |addr_list| addr_list.contains(&s.address)));
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe fn bind_udp_to_device(device_name: &str, address: &InetAddress) -> Result<RawFd, &'static str> {
|
||||||
|
let (af, sa_len) = match address.family() {
|
||||||
|
InetAddressFamily::IPv4 => (libc::AF_INET, std::mem::size_of::<libc::sockaddr_in>().as_()),
|
||||||
|
InetAddressFamily::IPv6 => (libc::AF_INET6, std::mem::size_of::<libc::sockaddr_in6>().as_()),
|
||||||
|
_ => {
|
||||||
|
return Err("unrecognized address family");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = libc::socket(af.as_(), libc::SOCK_DGRAM, 0);
|
||||||
|
if s <= 0 {
|
||||||
|
return Err("unable to create socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut setsockopt_results: c_int = 0;
|
||||||
|
|
||||||
|
let mut fl: c_int = 0;
|
||||||
|
setsockopt_results |= libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_LINGER.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
|
||||||
|
fl = 1;
|
||||||
|
setsockopt_results |= libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_BROADCAST.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
if af == libc::AF_INET6 {
|
||||||
|
fl = 1;
|
||||||
|
setsockopt_results |= libc::setsockopt(s, libc::IPPROTO_IPV6.as_(), libc::IPV6_V6ONLY.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
if !device_name.is_empty() {
|
||||||
|
let _ = std::ffi::CString::new(device_name).map(|dn| {
|
||||||
|
let dnb = dn.as_bytes_with_nul();
|
||||||
|
let _ = libc::setsockopt(s.as_(), libc::SOL_SOCKET.as_(), libc::SO_BINDTODEVICE.as_(), dnb.as_ptr().cast(), (dnb.len() - 1).as_());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if setsockopt_results != 0 {
|
||||||
|
libc::close(s);
|
||||||
|
return Err("setsockopt() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if af == libc::AF_INET {
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
fl = 0;
|
||||||
|
libc::setsockopt(s, libc::IPPROTO_IP.as_(), libc::IP_DF.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
fl = libc::IP_PMTUDISC_DONT as c_int;
|
||||||
|
libc::setsockopt(s, libc::IPPROTO_IP.as_(), libc::IP_MTU_DISCOVER.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if af == libc::AF_INET6 {
|
||||||
|
fl = 0;
|
||||||
|
libc::setsockopt(s, libc::IPPROTO_IPV6.as_(), libc::IPV6_DONTFRAG.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
|
||||||
|
}
|
||||||
|
|
||||||
|
fl = 1048576;
|
||||||
|
while fl >= 131072 {
|
||||||
|
if libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_RCVBUF.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_()) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fl -= 65536;
|
||||||
|
}
|
||||||
|
fl = 1048576;
|
||||||
|
while fl >= 131072 {
|
||||||
|
if libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_SNDBUF.as_(), (&mut fl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_()) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fl -= 65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
if libc::bind(s, (address as *const InetAddress).cast(), sa_len) != 0 {
|
||||||
|
libc::close(s);
|
||||||
|
return Err("bind to address failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(s as RawFd)
|
||||||
|
}
|
|
@ -96,85 +96,6 @@ lazy_static! {
|
||||||
static ref MAC_FETH_BPF_DEVICES_USED: Mutex<BTreeSet<u32>> = Mutex::new(BTreeSet::new());
|
static ref MAC_FETH_BPF_DEVICES_USED: Mutex<BTreeSet<u32>> = Mutex::new(BTreeSet::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
struct nd_ifinfo {
|
|
||||||
u_int32_t linkmtu; /* LinkMTU */
|
|
||||||
u_int32_t maxmtu; /* Upper bound of LinkMTU */
|
|
||||||
u_int32_t basereachable; /* BaseReachableTime */
|
|
||||||
u_int32_t reachable; /* Reachable Time */
|
|
||||||
u_int32_t retrans; /* Retrans Timer */
|
|
||||||
u_int32_t flags; /* Flags */
|
|
||||||
int recalctm; /* BaseReacable re-calculation timer */
|
|
||||||
u_int8_t chlim; /* CurHopLimit */
|
|
||||||
u_int8_t receivedra;
|
|
||||||
};
|
|
||||||
struct in6_ndireq {
|
|
||||||
char ifname[IFNAMSIZ];
|
|
||||||
struct nd_ifinfo ndi;
|
|
||||||
};
|
|
||||||
struct in6_addrlifetime {
|
|
||||||
time_t ia6t_expire; /* valid lifetime expiration time */
|
|
||||||
time_t ia6t_preferred; /* preferred lifetime expiration time */
|
|
||||||
u_int32_t ia6t_vltime; /* valid lifetime */
|
|
||||||
u_int32_t ia6t_pltime; /* prefix lifetime */
|
|
||||||
};
|
|
||||||
struct in6_ifstat {
|
|
||||||
ifs6_in_receive; /* # of total input datagram */
|
|
||||||
ifs6_in_hdrerr; /* # of datagrams with invalid hdr */
|
|
||||||
ifs6_in_toobig; /* # of datagrams exceeded MTU */
|
|
||||||
ifs6_in_noroute; /* # of datagrams with no route */
|
|
||||||
ifs6_in_addrerr; /* # of datagrams with invalid dst */
|
|
||||||
ifs6_in_protounknown; /* # of datagrams with unknown proto */
|
|
||||||
/* NOTE: increment on final dst if */
|
|
||||||
ifs6_in_truncated; /* # of truncated datagrams */
|
|
||||||
ifs6_in_discard; /* # of discarded datagrams */
|
|
||||||
/* NOTE: fragment timeout is not here */
|
|
||||||
ifs6_in_deliver; /* # of datagrams delivered to ULP */
|
|
||||||
/* NOTE: increment on final dst if */
|
|
||||||
ifs6_out_forward; /* # of datagrams forwarded */
|
|
||||||
/* NOTE: increment on outgoing if */
|
|
||||||
ifs6_out_request; /* # of outgoing datagrams from ULP */
|
|
||||||
/* NOTE: does not include forwrads */
|
|
||||||
ifs6_out_discard; /* # of discarded datagrams */
|
|
||||||
ifs6_out_fragok; /* # of datagrams fragmented */
|
|
||||||
ifs6_out_fragfail; /* # of datagrams failed on fragment */
|
|
||||||
ifs6_out_fragcreat; /* # of fragment datagrams */
|
|
||||||
/* NOTE: this is # after fragment */
|
|
||||||
ifs6_reass_reqd; /* # of incoming fragmented packets */
|
|
||||||
/* NOTE: increment on final dst if */
|
|
||||||
ifs6_reass_ok; /* # of reassembled packets */
|
|
||||||
/* NOTE: this is # after reass */
|
|
||||||
/* NOTE: increment on final dst if */
|
|
||||||
ifs6_atmfrag_rcvd; /* # of atomic fragments received */
|
|
||||||
ifs6_reass_fail; /* # of reass failures */
|
|
||||||
/* NOTE: may not be packet count */
|
|
||||||
/* NOTE: increment on final dst if */
|
|
||||||
ifs6_in_mcast; /* # of inbound multicast datagrams */
|
|
||||||
ifs6_out_mcast; /* # of outbound multicast datagrams */
|
|
||||||
|
|
||||||
ifs6_cantfoward_icmp6; /* # of ICMPv6 packets received for unreachable dest */
|
|
||||||
ifs6_addr_expiry_cnt; /* # of address expiry events (excluding privacy addresses) */
|
|
||||||
ifs6_pfx_expiry_cnt; /* # of prefix expiry events */
|
|
||||||
ifs6_defrtr_expiry_cnt; /* # of default router expiry events */
|
|
||||||
};
|
|
||||||
struct in6_ifreq {
|
|
||||||
char ifr_name[IFNAMSIZ];
|
|
||||||
union {
|
|
||||||
struct sockaddr_in6 ifru_addr;
|
|
||||||
struct sockaddr_in6 ifru_dstaddr;
|
|
||||||
int ifru_flags;
|
|
||||||
int ifru_flags6;
|
|
||||||
int ifru_metric;
|
|
||||||
int ifru_intval;
|
|
||||||
caddr_t ifru_data;
|
|
||||||
struct in6_addrlifetime ifru_lifetime;
|
|
||||||
struct in6_ifstat ifru_stat;
|
|
||||||
struct icmp6_ifstat ifru_icmp6stat;
|
|
||||||
u_int32_t ifru_scope_id[SCOPE6_ID_MAX];
|
|
||||||
} ifr_ifru;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -347,81 +268,6 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
|
||||||
ok
|
ok
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
struct ifkpi {
|
|
||||||
unsigned int ifk_module_id;
|
|
||||||
unsigned int ifk_type;
|
|
||||||
union {
|
|
||||||
void *ifk_ptr;
|
|
||||||
int ifk_value;
|
|
||||||
} ifk_data;
|
|
||||||
};
|
|
||||||
struct ifdevmtu {
|
|
||||||
int ifdm_current;
|
|
||||||
int ifdm_min;
|
|
||||||
int ifdm_max;
|
|
||||||
};
|
|
||||||
struct ifreq {
|
|
||||||
#ifndef IFNAMSIZ
|
|
||||||
#define IFNAMSIZ IF_NAMESIZE
|
|
||||||
#endif
|
|
||||||
char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
|
||||||
union {
|
|
||||||
struct sockaddr ifru_addr;
|
|
||||||
struct sockaddr ifru_dstaddr;
|
|
||||||
struct sockaddr ifru_broadaddr;
|
|
||||||
short ifru_flags;
|
|
||||||
int ifru_metric;
|
|
||||||
int ifru_mtu;
|
|
||||||
int ifru_phys;
|
|
||||||
int ifru_media;
|
|
||||||
int ifru_intval;
|
|
||||||
caddr_t ifru_data;
|
|
||||||
struct ifdevmtu ifru_devmtu;
|
|
||||||
struct ifkpi ifru_kpi;
|
|
||||||
u_int32_t ifru_wake_flags;
|
|
||||||
u_int32_t ifru_route_refcnt;
|
|
||||||
int ifru_cap[2];
|
|
||||||
u_int32_t ifru_functional_type;
|
|
||||||
#define IFRTYPE_FUNCTIONAL_UNKNOWN 0
|
|
||||||
#define IFRTYPE_FUNCTIONAL_LOOPBACK 1
|
|
||||||
#define IFRTYPE_FUNCTIONAL_WIRED 2
|
|
||||||
#define IFRTYPE_FUNCTIONAL_WIFI_INFRA 3
|
|
||||||
#define IFRTYPE_FUNCTIONAL_WIFI_AWDL 4
|
|
||||||
#define IFRTYPE_FUNCTIONAL_CELLULAR 5
|
|
||||||
#define IFRTYPE_FUNCTIONAL_INTCOPROC 6
|
|
||||||
#define IFRTYPE_FUNCTIONAL_COMPANIONLINK 7
|
|
||||||
#define IFRTYPE_FUNCTIONAL_LAST 7
|
|
||||||
} ifr_ifru;
|
|
||||||
#define ifr_addr ifr_ifru.ifru_addr /* address */
|
|
||||||
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
|
|
||||||
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#define ifr_flags ifr_ifru.ifru_flags /* flags */
|
|
||||||
#else
|
|
||||||
#define ifr_flags ifr_ifru.ifru_flags[0] /* flags */
|
|
||||||
#define ifr_prevflags ifr_ifru.ifru_flags[1] /* flags */
|
|
||||||
#endif /* __APPLE__ */
|
|
||||||
#define ifr_metric ifr_ifru.ifru_metric /* metric */
|
|
||||||
#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
|
|
||||||
#define ifr_phys ifr_ifru.ifru_phys /* physical wire */
|
|
||||||
#define ifr_media ifr_ifru.ifru_media /* physical media */
|
|
||||||
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
|
|
||||||
#define ifr_devmtu ifr_ifru.ifru_devmtu
|
|
||||||
#define ifr_intval ifr_ifru.ifru_intval /* integer value */
|
|
||||||
#define ifr_kpi ifr_ifru.ifru_kpi
|
|
||||||
#define ifr_wake_flags ifr_ifru.ifru_wake_flags /* wake capabilities */
|
|
||||||
#define ifr_route_refcnt ifr_ifru.ifru_route_refcnt /* route references count */
|
|
||||||
#define ifr_reqcap ifr_ifru.ifru_cap[0] /* requested capabilities */
|
|
||||||
#define ifr_curcap ifr_ifru.ifru_cap[1] /* current capabilities */
|
|
||||||
};
|
|
||||||
struct sockaddr_ndrv {
|
|
||||||
unsigned char snd_len;
|
|
||||||
unsigned char snd_family;
|
|
||||||
unsigned char snd_name[IFNAMSIZ]; /* from if.h */
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -814,7 +660,8 @@ impl VNIC for MacFethTap {
|
||||||
fn put(&self, source_mac: &zerotier_network_hypervisor::vl1::MAC, dest_mac: &zerotier_network_hypervisor::vl1::MAC, ethertype: u16, _vlan_id: u16, data: *const u8, len: usize) -> bool {
|
fn put(&self, source_mac: &zerotier_network_hypervisor::vl1::MAC, dest_mac: &zerotier_network_hypervisor::vl1::MAC, ethertype: u16, _vlan_id: u16, data: *const u8, len: usize) -> bool {
|
||||||
let dm = dest_mac.0;
|
let dm = dest_mac.0;
|
||||||
let sm = source_mac.0;
|
let sm = source_mac.0;
|
||||||
let mut hdr: [u8; 14] = [(dm >> 40) as u8, (dm >> 32) as u8, (dm >> 24) as u8, (dm >> 16) as u8, (dm >> 8) as u8, dm as u8, (sm >> 40) as u8, (sm >> 32) as u8, (sm >> 24) as u8, (sm >> 16) as u8, (sm >> 8) as u8, sm as u8, (ethertype >> 8) as u8, ethertype as u8];
|
let mut hdr: [u8; 14] =
|
||||||
|
[(dm >> 40) as u8, (dm >> 32) as u8, (dm >> 24) as u8, (dm >> 16) as u8, (dm >> 8) as u8, dm as u8, (sm >> 40) as u8, (sm >> 32) as u8, (sm >> 24) as u8, (sm >> 16) as u8, (sm >> 8) as u8, sm as u8, (ethertype >> 8) as u8, ethertype as u8];
|
||||||
unsafe {
|
unsafe {
|
||||||
let iov: [libc::iovec; 2] = [
|
let iov: [libc::iovec; 2] = [
|
||||||
libc::iovec { iov_base: hdr.as_mut_ptr().cast(), iov_len: 14 },
|
libc::iovec { iov_base: hdr.as_mut_ptr().cast(), iov_len: 14 },
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
pub mod vnic;
|
||||||
|
|
||||||
//mod common;
|
//mod common;
|
||||||
mod vnic;
|
|
||||||
|
|
||||||
//#[cfg(target_os = "macos")]
|
//#[cfg(target_os = "macos")]
|
||||||
//mod mac_feth_tap;
|
//mod mac_feth_tap;
|
||||||
|
|
Loading…
Add table
Reference in a new issue