mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-29 10:23:48 +02:00
Merge branch 'master' of github.com:zerotier/tetanus
This commit is contained in:
commit
dcfdd036cf
62 changed files with 1494 additions and 908 deletions
11
Makefile
11
Makefile
|
@ -1,6 +1,15 @@
|
||||||
all:
|
all:
|
||||||
|
|
||||||
clean: FORCE
|
clean: FORCE
|
||||||
rm -rf zerotier-core-crypto/target zerotier-network-hypervisor/target zerotier-system-service/target syncwhole/target aes-gmac-siv/target iblt/target
|
for i in */Cargo.toml; do cd $$(dirname $$i); cargo clean || exit 1; cd ..; done
|
||||||
|
|
||||||
|
test:
|
||||||
|
for i in */Cargo.toml; do cd $$(dirname $$i); cargo test || exit 1; cd ..; done
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
set extglob; for i in */Cargo.toml; do cd $$(dirname $$i); rustfmt **/*.rs || exit 1; cd ..; done
|
||||||
|
|
||||||
|
|
||||||
FORCE:
|
FORCE:
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
|
|
@ -122,7 +122,8 @@ impl AesGmacSiv {
|
||||||
pub fn encrypt_first_pass_finish(&mut self) {
|
pub fn encrypt_first_pass_finish(&mut self) {
|
||||||
let _ = self.gmac.flush();
|
let _ = self.gmac.flush();
|
||||||
let _ = self.gmac.get_mac(&mut self.tmp);
|
let _ = self.gmac.get_mac(&mut self.tmp);
|
||||||
unsafe { // tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
unsafe {
|
||||||
|
// tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) = *tmp ^ *tmp.offset(1);
|
*self.tag.as_mut_ptr().cast::<u64>().offset(1) = *tmp ^ *tmp.offset(1);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +159,8 @@ impl AesGmacSiv {
|
||||||
self.tmp[12] &= 0x7f;
|
self.tmp[12] &= 0x7f;
|
||||||
let _ = self.ctr.set_ctr(&self.tmp);
|
let _ = self.ctr.set_ctr(&self.tmp);
|
||||||
let _ = self.ecb.decrypt_inplace(&mut self.tag);
|
let _ = self.ecb.decrypt_inplace(&mut self.tag);
|
||||||
unsafe { // tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
unsafe {
|
||||||
|
// tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*tmp = *self.tag.as_mut_ptr().cast::<u64>();
|
*tmp = *self.tag.as_mut_ptr().cast::<u64>();
|
||||||
*tmp.offset(1) = 0;
|
*tmp.offset(1) = 0;
|
||||||
|
@ -203,7 +205,8 @@ impl AesGmacSiv {
|
||||||
pub fn decrypt_finish(&mut self) -> bool {
|
pub fn decrypt_finish(&mut self) -> bool {
|
||||||
let _ = self.gmac.flush();
|
let _ = self.gmac.flush();
|
||||||
let _ = self.gmac.get_mac(&mut self.tmp);
|
let _ = self.gmac.get_mac(&mut self.tmp);
|
||||||
unsafe { // tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
unsafe {
|
||||||
|
// tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1)
|
*self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
// AES-GMAC-SIV implemented using MacOS/iOS CommonCrypto (MacOS 10.13 or newer required).
|
// AES-GMAC-SIV implemented using MacOS/iOS CommonCrypto (MacOS 10.13 or newer required).
|
||||||
|
|
||||||
use std::os::raw::{c_void, c_int};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::ptr::{null_mut, null};
|
use std::ptr::{null, null_mut};
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
const kCCModeECB: i32 = 1;
|
const kCCModeECB: i32 = 1;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
// AES-GMAC-SIV implemented using OpenSSL.
|
// AES-GMAC-SIV implemented using OpenSSL.
|
||||||
|
|
||||||
use openssl::symm::{Crypter, Cipher, Mode};
|
use openssl::symm::{Cipher, Crypter, Mode};
|
||||||
|
|
||||||
fn aes_ctr_by_key_size(ks: usize) -> Cipher {
|
fn aes_ctr_by_key_size(ks: usize) -> Cipher {
|
||||||
match ks {
|
match ks {
|
||||||
|
@ -145,7 +145,8 @@ impl AesGmacSiv {
|
||||||
let gmac = self.gmac.as_mut().unwrap();
|
let gmac = self.gmac.as_mut().unwrap();
|
||||||
let _ = gmac.finalize(&mut self.tmp);
|
let _ = gmac.finalize(&mut self.tmp);
|
||||||
let _ = gmac.get_tag(&mut self.tmp);
|
let _ = gmac.get_tag(&mut self.tmp);
|
||||||
unsafe { // tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
unsafe {
|
||||||
|
// tag[8..16] = tmp[0..8] ^ tmp[8..16]
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) = *tmp ^ *tmp.offset(1);
|
*self.tag.as_mut_ptr().cast::<u64>().offset(1) = *tmp ^ *tmp.offset(1);
|
||||||
}
|
}
|
||||||
|
@ -185,7 +186,8 @@ impl AesGmacSiv {
|
||||||
let mut tag_tmp = [0_u8; 32];
|
let mut tag_tmp = [0_u8; 32];
|
||||||
let _ = Crypter::new(aes_ecb_by_key_size(self.k1.len()), Mode::Decrypt, self.k1.as_slice(), None).unwrap().update(&self.tag, &mut tag_tmp);
|
let _ = Crypter::new(aes_ecb_by_key_size(self.k1.len()), Mode::Decrypt, self.k1.as_slice(), None).unwrap().update(&self.tag, &mut tag_tmp);
|
||||||
self.tag.copy_from_slice(&tag_tmp[0..16]);
|
self.tag.copy_from_slice(&tag_tmp[0..16]);
|
||||||
unsafe { // tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
unsafe {
|
||||||
|
// tmp[0..8] = tag[0..8], tmp[8..16] = 0
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*tmp = *self.tag.as_mut_ptr().cast::<u64>();
|
*tmp = *self.tag.as_mut_ptr().cast::<u64>();
|
||||||
*tmp.offset(1) = 0;
|
*tmp.offset(1) = 0;
|
||||||
|
@ -227,13 +229,18 @@ impl AesGmacSiv {
|
||||||
/// Finish decryption and return true if authentication appears valid.
|
/// Finish decryption and return true if authentication appears valid.
|
||||||
/// If this returns false the message should be dropped.
|
/// If this returns false the message should be dropped.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn decrypt_finish(&mut self) -> bool {
|
pub fn decrypt_finish(&mut self) -> Option<&[u8; 16]> {
|
||||||
let gmac = self.gmac.as_mut().unwrap();
|
let gmac = self.gmac.as_mut().unwrap();
|
||||||
let _ = gmac.finalize(&mut self.tmp);
|
let _ = gmac.finalize(&mut self.tmp);
|
||||||
let _ = gmac.get_tag(&mut self.tmp);
|
let _ = gmac.get_tag(&mut self.tmp);
|
||||||
unsafe { // tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
unsafe {
|
||||||
|
// tag[8..16] == tmp[0..8] ^ tmp[8..16]
|
||||||
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
let tmp = self.tmp.as_mut_ptr().cast::<u64>();
|
||||||
*self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1)
|
if *self.tag.as_mut_ptr().cast::<u64>().offset(1) == *tmp ^ *tmp.offset(1) {
|
||||||
|
Some(&self.tag)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,26 @@ panic = 'abort'
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crc32fast = "^1"
|
crc32fast = "^1"
|
||||||
|
zerocopy = { version = "0.6.1", features = ["alloc"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = ">=0"
|
||||||
|
criterion = ">=0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "to_from_bytes"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "clone"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "list"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "merge"
|
||||||
|
harness = false
|
||||||
|
|
19
iblt/benches/clone.rs
Normal file
19
iblt/benches/clone.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use iblt::IBLT;
|
||||||
|
|
||||||
|
const CAPACITY: usize = 4096;
|
||||||
|
type OurIBLT = IBLT<[u8; 32], CAPACITY, 3>;
|
||||||
|
|
||||||
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut iblt = OurIBLT::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 32];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
iblt.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("clone", |b| b.iter(|| iblt.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
39
iblt/benches/list.rs
Normal file
39
iblt/benches/list.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use iblt::IBLT;
|
||||||
|
|
||||||
|
const CAPACITY: usize = 4096;
|
||||||
|
type IBLT32 = IBLT<[u8; 32], CAPACITY, 3>;
|
||||||
|
type IBLT16 = IBLT<[u8; 16], CAPACITY, 3>;
|
||||||
|
type IBLT8 = IBLT<[u8; 8], CAPACITY, 3>;
|
||||||
|
|
||||||
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut iblt = IBLT32::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 32];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
iblt.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("list 32", |b| b.iter(|| iblt.list(|_, _| {})));
|
||||||
|
|
||||||
|
let mut iblt = IBLT16::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 16];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
iblt.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("list 16", |b| b.iter(|| iblt.list(|_, _| {})));
|
||||||
|
|
||||||
|
let mut iblt = IBLT8::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 8];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
iblt.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("list 8", |b| b.iter(|| iblt.list(|_, _| {})));
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
78
iblt/benches/merge.rs
Normal file
78
iblt/benches/merge.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use iblt::IBLT;
|
||||||
|
|
||||||
|
const CAPACITY: usize = 4096;
|
||||||
|
type IBLT32 = IBLT<[u8; 32], CAPACITY, 3>;
|
||||||
|
type IBLT16 = IBLT<[u8; 16], CAPACITY, 3>;
|
||||||
|
type IBLT8 = IBLT<[u8; 8], CAPACITY, 3>;
|
||||||
|
|
||||||
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut orig = IBLT32::new();
|
||||||
|
let mut new = IBLT32::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 32];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
orig.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 32];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
new.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("merge 32", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut new2 = new.clone();
|
||||||
|
orig.subtract(&new);
|
||||||
|
new2.subtract(&orig);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut orig = IBLT16::new();
|
||||||
|
let mut new = IBLT16::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 16];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
orig.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 16];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
new.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("merge 16", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut new2 = new.clone();
|
||||||
|
orig.subtract(&new);
|
||||||
|
new2.subtract(&orig);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut orig = IBLT8::new();
|
||||||
|
let mut new = IBLT8::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 8];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
orig.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 8];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
new.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("merge 8", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut new2 = new.clone();
|
||||||
|
orig.subtract(&new);
|
||||||
|
new2.subtract(&orig);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
19
iblt/benches/to_from_bytes.rs
Normal file
19
iblt/benches/to_from_bytes.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use iblt::IBLT;
|
||||||
|
|
||||||
|
const CAPACITY: usize = 4096;
|
||||||
|
type OurIBLT = IBLT<[u8; 32], CAPACITY, 3>;
|
||||||
|
|
||||||
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut iblt = OurIBLT::new();
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
let mut v = [0u8; 32];
|
||||||
|
v.fill_with(rand::random);
|
||||||
|
iblt.insert(&v);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.bench_function("to_from_bytes", |b| b.iter(|| OurIBLT::from_bytes(iblt.as_bytes())));
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
294
iblt/src/lib.rs
294
iblt/src/lib.rs
|
@ -6,30 +6,38 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use zerocopy::{AsBytes, FromBytes};
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc64")))]
|
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc64")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn xor_with<const L: usize>(x: &mut [u8; L], y: &[u8; L]) {
|
fn xor_with<T>(x: &mut T, y: &T)
|
||||||
x.iter_mut().zip(y.iter()).for_each(|(a, b)| *a ^= *b);
|
where
|
||||||
|
T: FromBytes + AsBytes + Sized,
|
||||||
|
{
|
||||||
|
x.as_bytes_mut().iter_mut().zip(y.as_bytes().iter()).for_each(|(a, b)| *a ^= *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc64"))]
|
#[cfg(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn xor_with<const L: usize>(x: &mut [u8; L], y: &[u8; L]) {
|
fn xor_with<T>(x: &mut T, y: &T)
|
||||||
if L >= 16 {
|
where
|
||||||
for i in 0..(L / 16) {
|
T: FromBytes + AsBytes + Sized,
|
||||||
unsafe { *x.as_mut_ptr().cast::<u128>().add(i) ^= *y.as_ptr().cast::<u128>().add(i) };
|
{
|
||||||
|
let size = std::mem::size_of::<T>();
|
||||||
|
|
||||||
|
if size >= 16 {
|
||||||
|
for i in 0..(size / 16) {
|
||||||
|
unsafe { *x.as_bytes_mut().as_mut_ptr().cast::<u128>().add(i) ^= *y.as_bytes().as_ptr().cast::<u128>().add(i) };
|
||||||
}
|
}
|
||||||
for i in (L - (L % 16))..L {
|
for i in (size - (size % 16))..size {
|
||||||
unsafe { *x.as_mut_ptr().add(i) ^= *y.as_ptr().add(i) };
|
unsafe { *x.as_bytes_mut().as_mut_ptr().add(i) ^= *y.as_bytes().as_ptr().add(i) };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i in 0..(L / 8) {
|
for i in 0..(size / 8) {
|
||||||
unsafe { *x.as_mut_ptr().cast::<u64>().add(i) ^= *y.as_ptr().cast::<u64>().add(i) };
|
unsafe { *x.as_bytes_mut().as_mut_ptr().cast::<u64>().add(i) ^= *y.as_bytes().as_ptr().cast::<u64>().add(i) };
|
||||||
}
|
}
|
||||||
for i in (L - (L % 8))..L {
|
for i in (size - (size % 8))..size {
|
||||||
unsafe { *x.as_mut_ptr().add(i) ^= *y.as_ptr().add(i) };
|
unsafe { *x.as_bytes_mut().as_mut_ptr().add(i) ^= *y.as_bytes().as_ptr().add(i) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,29 +69,38 @@ fn murmurhash32_mix32(mut x: u32) -> u32 {
|
||||||
///
|
///
|
||||||
/// The best value for HASHES seems to be 3 for an optimal fill of 75%.
|
/// The best value for HASHES seems to be 3 for an optimal fill of 75%.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct IBLT<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> {
|
pub struct IBLT<T, const BUCKETS: usize, const HASHES: usize>
|
||||||
check_hash: [u32; BUCKETS],
|
where
|
||||||
count: [i8; BUCKETS],
|
T: FromBytes + AsBytes + Default + Sized + Clone,
|
||||||
key: [[u8; ITEM_BYTES]; BUCKETS],
|
{
|
||||||
|
check_hash: Box<Vec<u32>>,
|
||||||
|
count: Box<Vec<i8>>,
|
||||||
|
key: Box<Vec<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> Clone for IBLT<BUCKETS, ITEM_BYTES, HASHES> {
|
impl<T, const BUCKETS: usize, const HASHES: usize> Clone for IBLT<T, BUCKETS, HASHES>
|
||||||
#[inline(always)]
|
where
|
||||||
|
T: FromBytes + AsBytes + Default + Sized + Clone,
|
||||||
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut tmp: Self = std::mem::MaybeUninit::uninit().assume_init();
|
let mut tmp = Self::new();
|
||||||
std::ptr::copy_nonoverlapping((self as *const Self).cast::<u8>(), (&mut tmp as *mut Self).cast::<u8>(), Self::SIZE_BYTES);
|
std::ptr::copy_nonoverlapping(self.check_hash.as_ptr(), tmp.check_hash.as_mut_ptr(), BUCKETS);
|
||||||
|
std::ptr::copy_nonoverlapping(self.count.as_ptr(), tmp.count.as_mut_ptr(), BUCKETS);
|
||||||
|
std::ptr::copy_nonoverlapping(self.key.as_ptr(), tmp.key.as_mut_ptr(), BUCKETS);
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BUCKETS, ITEM_BYTES, HASHES> {
|
impl<T, const BUCKETS: usize, const HASHES: usize> IBLT<T, BUCKETS, HASHES>
|
||||||
|
where
|
||||||
|
T: FromBytes + AsBytes + Default + Sized + Clone,
|
||||||
|
{
|
||||||
/// Number of bytes each bucket consumes (not congituously, but doesn't matter).
|
/// Number of bytes each bucket consumes (not congituously, but doesn't matter).
|
||||||
const BUCKET_SIZE_BYTES: usize = ITEM_BYTES + 4 + 1;
|
const BUCKET_SIZE_BYTES: usize = std::mem::size_of::<T>() + 4 + 1;
|
||||||
|
|
||||||
/// Number of buckets in this IBLT.
|
/// Number of buckets in this IBLT.
|
||||||
#[allow(unused)]
|
|
||||||
pub const BUCKETS: usize = BUCKETS;
|
pub const BUCKETS: usize = BUCKETS;
|
||||||
|
|
||||||
/// Size of this IBLT in bytes.
|
/// Size of this IBLT in bytes.
|
||||||
|
@ -92,16 +109,34 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
/// Create a new zeroed IBLT.
|
/// Create a new zeroed IBLT.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
assert!(Self::SIZE_BYTES <= std::mem::size_of::<Self>());
|
|
||||||
assert!(BUCKETS < (i32::MAX as usize));
|
assert!(BUCKETS < (i32::MAX as usize));
|
||||||
unsafe { std::mem::zeroed() }
|
|
||||||
|
let mut s = Self {
|
||||||
|
check_hash: Box::new(Vec::with_capacity(BUCKETS)),
|
||||||
|
count: Box::new(Vec::with_capacity(BUCKETS)),
|
||||||
|
key: Box::new(Vec::with_capacity(BUCKETS)),
|
||||||
|
};
|
||||||
|
|
||||||
|
s.reset();
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this IBLT as a byte slice (free cast operation).
|
|
||||||
/// The returned slice is always SIZE_BYTES in length.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> Box<Vec<u8>> {
|
||||||
unsafe { &*std::ptr::slice_from_raw_parts((self as *const Self).cast::<u8>(), Self::SIZE_BYTES) }
|
let check_hash_len = BUCKETS * 4;
|
||||||
|
let t_len = BUCKETS * std::mem::size_of::<T>();
|
||||||
|
let len = check_hash_len + BUCKETS + t_len;
|
||||||
|
|
||||||
|
let mut buf = Box::new(Vec::with_capacity(len));
|
||||||
|
buf.resize(len, 0);
|
||||||
|
|
||||||
|
let byt = buf.as_bytes_mut();
|
||||||
|
|
||||||
|
byt[0..check_hash_len].copy_from_slice(self.check_hash.as_bytes());
|
||||||
|
byt[check_hash_len..BUCKETS + check_hash_len].copy_from_slice(self.count.as_bytes());
|
||||||
|
byt[len - t_len..len].copy_from_slice(self.key.as_bytes());
|
||||||
|
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain an IBLT from bytes in memory.
|
/// Obtain an IBLT from bytes in memory.
|
||||||
|
@ -110,20 +145,33 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
/// Cow to 'b' that is just a cast. If re-alignment is necessary it returns an owned Cow containing a properly
|
/// Cow to 'b' that is just a cast. If re-alignment is necessary it returns an owned Cow containing a properly
|
||||||
/// aligned copy. This makes conversion a nearly free cast when alignment adjustment isn't needed.
|
/// aligned copy. This makes conversion a nearly free cast when alignment adjustment isn't needed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bytes<'a>(b: &'a [u8]) -> Option<Cow<'a, Self>> {
|
pub fn from_bytes(b: Box<Vec<u8>>) -> Option<Self> {
|
||||||
if b.len() == Self::SIZE_BYTES {
|
if b.len() == Self::SIZE_BYTES {
|
||||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "powerpc64", target_arch = "aarch64")))]
|
// FIXME I commented this out because I do not have access to the architectures needed.
|
||||||
{
|
// #[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "powerpc64", target_arch = "aarch64")))]
|
||||||
if b.as_ptr().align_offset(8) == 0 {
|
// {
|
||||||
Some(Cow::Borrowed(unsafe { &*b.as_ptr().cast() }))
|
// if b.as_ptr().align_offset(8) == 0 {
|
||||||
} else {
|
// Some(Cow::Borrowed(unsafe { &*b.as_ptr().cast() }))
|
||||||
// NOTE: clone() is implemented above using a raw copy so that alignment doesn't matter.
|
// } else {
|
||||||
Some(Cow::Owned(unsafe { &*b.as_ptr().cast::<Self>() }.clone()))
|
// // NOTE: clone() is implemented above using a raw copy so that alignment doesn't matter.
|
||||||
}
|
// Some(Cow::Owned(unsafe { &*b.as_ptr().cast::<Self>() }.clone()))
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "powerpc64", target_arch = "aarch64"))]
|
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "powerpc64", target_arch = "aarch64"))]
|
||||||
{
|
{
|
||||||
Some(Cow::Borrowed(unsafe { &*b.as_ptr().cast() }))
|
let mut tmp = Self::new();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
tmp.check_hash.as_bytes_mut().copy_from_slice(&b[0..BUCKETS * 4]);
|
||||||
|
i += BUCKETS * 4;
|
||||||
|
|
||||||
|
tmp.count.as_bytes_mut().copy_from_slice(&b[i..i + BUCKETS]);
|
||||||
|
i += BUCKETS;
|
||||||
|
|
||||||
|
tmp.key.as_bytes_mut().copy_from_slice(&b[i..i + std::mem::size_of::<T>() * BUCKETS]);
|
||||||
|
|
||||||
|
Some(tmp)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -133,42 +181,45 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
/// Zero this IBLT.
|
/// Zero this IBLT.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
unsafe { std::ptr::write_bytes((self as *mut Self).cast::<u8>(), 0, std::mem::size_of::<Self>()) };
|
self.check_hash.clear();
|
||||||
|
self.count.clear();
|
||||||
|
self.key.clear();
|
||||||
|
self.check_hash.resize(BUCKETS, 0);
|
||||||
|
self.count.resize(BUCKETS, 0);
|
||||||
|
self.key.resize(BUCKETS, Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ins_rem(&mut self, key: &[u8; ITEM_BYTES], delta: i8) {
|
pub(crate) fn ins_rem(&mut self, key: &T, delta: i8) {
|
||||||
let check_hash = crc32fast::hash(key);
|
let check_hash = crc32fast::hash(key.as_bytes());
|
||||||
let mut iteration_index = u32::from_le(check_hash).wrapping_add(1);
|
let mut iteration_index = u32::from_le(check_hash).wrapping_add(1);
|
||||||
for _ in 0..(HASHES as u64) {
|
for _ in 0..(HASHES as u64) {
|
||||||
iteration_index = murmurhash32_mix32(iteration_index);
|
iteration_index = murmurhash32_mix32(iteration_index);
|
||||||
let i = (iteration_index as usize) % BUCKETS;
|
let i = (iteration_index as usize) % BUCKETS;
|
||||||
self.check_hash[i] ^= check_hash;
|
self.check_hash[i] ^= check_hash;
|
||||||
self.count[i] = self.count[i].wrapping_add(delta);
|
self.count[i] = self.count[i].wrapping_add(delta);
|
||||||
xor_with(&mut self.key[i], key);
|
xor_with(&mut self.key[i], &key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a set item into this set.
|
/// Insert a set item into this set.
|
||||||
/// This will panic if the slice is smaller than ITEM_BYTES.
|
/// This will panic if the slice is smaller than ITEM_BYTES.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn insert(&mut self, key: &[u8]) {
|
pub fn insert(&mut self, key: &T) {
|
||||||
assert!(key.len() >= ITEM_BYTES);
|
self.ins_rem(key, 1);
|
||||||
self.ins_rem(unsafe { &*key.as_ptr().cast() }, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a set item into this set.
|
/// Insert a set item into this set.
|
||||||
/// This will panic if the slice is smaller than ITEM_BYTES.
|
/// This will panic if the slice is smaller than ITEM_BYTES.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn remove(&mut self, key: &[u8]) {
|
pub fn remove(&mut self, key: &T) {
|
||||||
assert!(key.len() >= ITEM_BYTES);
|
self.ins_rem(key, -1);
|
||||||
self.ins_rem(unsafe { &*key.as_ptr().cast() }, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtract another IBLT from this one to get a set difference.
|
/// Subtract another IBLT from this one to get a set difference.
|
||||||
pub fn subtract(&mut self, other: &Self) {
|
pub fn subtract(&mut self, other: &Self) {
|
||||||
self.check_hash.iter_mut().zip(other.check_hash.iter()).for_each(|(a, b)| *a ^= *b);
|
self.check_hash.iter_mut().zip(other.check_hash.iter()).for_each(|(a, b)| *a ^= *b);
|
||||||
self.count.iter_mut().zip(other.count.iter()).for_each(|(a, b)| *a = a.wrapping_sub(*b));
|
self.count.iter_mut().zip(other.count.iter()).for_each(|(a, b)| *a = a.wrapping_sub(*b));
|
||||||
self.key.iter_mut().zip(other.key.iter()).for_each(|(a, b)| xor_with(a, b));
|
self.key.iter_mut().zip(other.key.iter()).for_each(|(a, b)| xor_with(a, &b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List as many entries in this IBLT as can be extracted.
|
/// List as many entries in this IBLT as can be extracted.
|
||||||
|
@ -185,12 +236,12 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
/// Due to the small check hash sizes used in this IBLT there is a very small chance this will list
|
/// Due to the small check hash sizes used in this IBLT there is a very small chance this will list
|
||||||
/// bogus items that were never added. This is not an issue with this protocol as it would just result
|
/// bogus items that were never added. This is not an issue with this protocol as it would just result
|
||||||
/// in an unsatisfied record request.
|
/// in an unsatisfied record request.
|
||||||
pub fn list<F: FnMut([u8; ITEM_BYTES], bool)>(mut self, mut f: F) -> bool {
|
pub fn list<F: FnMut(T, bool)>(&mut self, mut f: F) -> bool {
|
||||||
let mut queue: Vec<u32> = Vec::with_capacity(BUCKETS);
|
let mut queue: Box<Vec<u32>> = Box::new(Vec::with_capacity(BUCKETS));
|
||||||
|
|
||||||
for i in 0..BUCKETS {
|
for i in 0..BUCKETS {
|
||||||
let count = self.count[i];
|
let count = self.count[i];
|
||||||
if (count == 1 || count == -1) && crc32fast::hash(&self.key[i]) == self.check_hash[i] {
|
if (count == 1 || count == -1) && crc32fast::hash(&self.key[i].as_bytes()) == self.check_hash[i] {
|
||||||
queue.push(i as u32);
|
queue.push(i as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +257,7 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
let check_hash = self.check_hash[i];
|
let check_hash = self.check_hash[i];
|
||||||
let count = self.count[i];
|
let count = self.count[i];
|
||||||
let key = &self.key[i];
|
let key = &self.key[i];
|
||||||
if (count == 1 || count == -1) && check_hash == crc32fast::hash(key) {
|
if (count == 1 || count == -1) && check_hash == crc32fast::hash(key.as_bytes()) {
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
|
|
||||||
let mut iteration_index = u32::from_le(check_hash).wrapping_add(1);
|
let mut iteration_index = u32::from_le(check_hash).wrapping_add(1);
|
||||||
|
@ -219,7 +270,7 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
self.check_hash[i2] = check_hash2;
|
self.check_hash[i2] = check_hash2;
|
||||||
self.count[i2] = count2;
|
self.count[i2] = count2;
|
||||||
xor_with(key2, &key);
|
xor_with(key2, &key);
|
||||||
if (count2 == 1 || count2 == -1) && check_hash2 == crc32fast::hash(key2) {
|
if (count2 == 1 || count2 == -1) && check_hash2 == crc32fast::hash(key2.as_bytes()) {
|
||||||
if queue.len() > BUCKETS {
|
if queue.len() > BUCKETS {
|
||||||
// sanity check, should be impossible
|
// sanity check, should be impossible
|
||||||
break 'list_main;
|
break 'list_main;
|
||||||
|
@ -236,14 +287,17 @@ impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> IBLT<BU
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> PartialEq for IBLT<BUCKETS, ITEM_BYTES, HASHES> {
|
impl<T, const BUCKETS: usize, const HASHES: usize> PartialEq for IBLT<T, BUCKETS, HASHES>
|
||||||
|
where
|
||||||
|
T: AsBytes + FromBytes + Default + Clone,
|
||||||
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.as_bytes().eq(other.as_bytes())
|
self.as_bytes().eq(&other.as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const BUCKETS: usize, const ITEM_BYTES: usize, const HASHES: usize> Eq for IBLT<BUCKETS, ITEM_BYTES, HASHES> {}
|
impl<T, const BUCKETS: usize, const HASHES: usize> Eq for IBLT<T, BUCKETS, HASHES> where T: AsBytes + FromBytes + Default + Clone {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -272,6 +326,15 @@ mod tests {
|
||||||
assert!(actual.eq(&expected));
|
assert!(actual.eq(&expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn typical_iblt() -> IBLT<[u8; 32], 16, 3> {
|
||||||
|
// Typical case
|
||||||
|
let mut tmp = IBLT::<[u8; 32], 16, 3>::new();
|
||||||
|
tmp.check_hash.fill(0x01010101);
|
||||||
|
tmp.count.fill(1);
|
||||||
|
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_xor_with() {
|
fn check_xor_with() {
|
||||||
check_xor_with2::<128>();
|
check_xor_with2::<128>();
|
||||||
|
@ -297,29 +360,25 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn struct_packing() {
|
fn struct_packing() {
|
||||||
// Typical case
|
let tmp = typical_iblt();
|
||||||
let mut tmp = IBLT::<64, 16, 3>::new();
|
|
||||||
tmp.check_hash.fill(0x01010101);
|
|
||||||
tmp.count.fill(1);
|
|
||||||
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
|
||||||
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
||||||
|
|
||||||
// Pathological alignment case #1
|
// Pathological alignment case #1
|
||||||
let mut tmp = IBLT::<17, 13, 3>::new();
|
let mut tmp = IBLT::<[u8; 17], 13, 3>::new();
|
||||||
tmp.check_hash.fill(0x01010101);
|
tmp.check_hash.fill(0x01010101);
|
||||||
tmp.count.fill(1);
|
tmp.count.fill(1);
|
||||||
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
||||||
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
||||||
|
|
||||||
// Pathological alignment case #2
|
// Pathological alignment case #2
|
||||||
let mut tmp = IBLT::<17, 8, 3>::new();
|
let mut tmp = IBLT::<[u8; 17], 8, 3>::new();
|
||||||
tmp.check_hash.fill(0x01010101);
|
tmp.check_hash.fill(0x01010101);
|
||||||
tmp.count.fill(1);
|
tmp.count.fill(1);
|
||||||
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
||||||
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
assert!(tmp.as_bytes().iter().all(|x| *x == 1));
|
||||||
|
|
||||||
// Pathological alignment case #3
|
// Pathological alignment case #3
|
||||||
let mut tmp = IBLT::<16, 7, 3>::new();
|
let mut tmp = IBLT::<[u8; 16], 7, 3>::new();
|
||||||
tmp.check_hash.fill(0x01010101);
|
tmp.check_hash.fill(0x01010101);
|
||||||
tmp.count.fill(1);
|
tmp.count.fill(1);
|
||||||
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
tmp.key.iter_mut().for_each(|x| x.fill(1));
|
||||||
|
@ -328,12 +387,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fill_list_performance() {
|
fn fill_list_performance() {
|
||||||
|
const LENGTH: usize = 16;
|
||||||
const CAPACITY: usize = 4096;
|
const CAPACITY: usize = 4096;
|
||||||
let mut rn: u128 = 0xd3b07384d113edec49eaa6238ad5ff00;
|
let mut rn: u128 = 0xd3b07384d113edec49eaa6238ad5ff00;
|
||||||
let mut expected: HashSet<u128> = HashSet::with_capacity(4096);
|
let mut expected: HashSet<u128> = HashSet::with_capacity(CAPACITY);
|
||||||
let mut count = 64;
|
let mut count = LENGTH;
|
||||||
while count <= CAPACITY {
|
while count <= CAPACITY {
|
||||||
let mut test = IBLT::<CAPACITY, 16, HASHES>::new();
|
let mut test = IBLT::<[u8; LENGTH], CAPACITY, HASHES>::new();
|
||||||
expected.clear();
|
expected.clear();
|
||||||
|
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
|
@ -350,15 +410,16 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("inserted: {}\tlisted: {}\tcapacity: {}\tscore: {:.4}\tfill: {:.4}", count, list_count, CAPACITY, (list_count as f64) / (count as f64), (count as f64) / (CAPACITY as f64));
|
println!("inserted: {}\tlisted: {}\tcapacity: {}\tscore: {:.4}\tfill: {:.4}", count, list_count, CAPACITY, (list_count as f64) / (count as f64), (count as f64) / (CAPACITY as f64));
|
||||||
count += 64;
|
count += LENGTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn merge_sets() {
|
fn merge_sets() {
|
||||||
const CAPACITY: usize = 16384;
|
const CAPACITY: usize = 4096; // previously 16384;
|
||||||
const REMOTE_SIZE: usize = 1024 * 1024 * 2;
|
const REMOTE_SIZE: usize = 1024 * 1024 * 2;
|
||||||
const STEP: usize = 1024;
|
const STEP: usize = 1024;
|
||||||
|
const LENGTH: usize = 16;
|
||||||
let mut rn: u128 = 0xd3b07384d113edec49eaa6238ad5ff00;
|
let mut rn: u128 = 0xd3b07384d113edec49eaa6238ad5ff00;
|
||||||
let mut missing_count = 1024;
|
let mut missing_count = 1024;
|
||||||
let mut missing: HashSet<u128> = HashSet::with_capacity(CAPACITY * 2);
|
let mut missing: HashSet<u128> = HashSet::with_capacity(CAPACITY * 2);
|
||||||
|
@ -366,8 +427,8 @@ mod tests {
|
||||||
while missing_count <= CAPACITY {
|
while missing_count <= CAPACITY {
|
||||||
missing.clear();
|
missing.clear();
|
||||||
all.clear();
|
all.clear();
|
||||||
let mut local = IBLT::<CAPACITY, 16, HASHES>::new();
|
let mut local = IBLT::<[u8; LENGTH], CAPACITY, HASHES>::new();
|
||||||
let mut remote = IBLT::<CAPACITY, 16, HASHES>::new();
|
let mut remote = IBLT::<[u8; LENGTH], CAPACITY, HASHES>::new();
|
||||||
|
|
||||||
let mut k = 0;
|
let mut k = 0;
|
||||||
while k < REMOTE_SIZE {
|
while k < REMOTE_SIZE {
|
||||||
|
@ -402,4 +463,87 @@ mod tests {
|
||||||
missing_count += STEP;
|
missing_count += STEP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Clone, AsBytes, FromBytes, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct TestType {
|
||||||
|
thing: [u8; 256],
|
||||||
|
other_thing: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestType {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::zeroed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestType {
|
||||||
|
pub fn zeroed() -> Self {
|
||||||
|
unsafe { std::mem::zeroed() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut newtype = Self::zeroed();
|
||||||
|
newtype.thing.fill_with(|| rand::random());
|
||||||
|
newtype.other_thing.fill_with(|| rand::random());
|
||||||
|
newtype
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_polymorphism() {
|
||||||
|
const CAPACITY: usize = 4096;
|
||||||
|
let mut full = Box::new(IBLT::<TestType, CAPACITY, HASHES>::new());
|
||||||
|
let mut zero = Box::new(IBLT::<TestType, CAPACITY, HASHES>::new());
|
||||||
|
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
zero.insert(&TestType::zeroed());
|
||||||
|
full.insert(&TestType::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
zero.subtract(&full);
|
||||||
|
|
||||||
|
zero.list(|item, new| {
|
||||||
|
if new {
|
||||||
|
assert_eq!(item, TestType::zeroed());
|
||||||
|
} else {
|
||||||
|
assert_ne!(item, TestType::zeroed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
zero.reset();
|
||||||
|
full.reset();
|
||||||
|
|
||||||
|
for _ in 0..CAPACITY {
|
||||||
|
zero.insert(&TestType::zeroed());
|
||||||
|
full.insert(&TestType::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
full.subtract(&zero);
|
||||||
|
full.list(|item, new| {
|
||||||
|
if new {
|
||||||
|
assert_ne!(item, TestType::zeroed());
|
||||||
|
} else {
|
||||||
|
assert_eq!(item, TestType::zeroed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_from_bytes() {
|
||||||
|
let tmp = typical_iblt();
|
||||||
|
let mut tmp2 = IBLT::<[u8; 32], 16, 3>::from_bytes(tmp.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
tmp2.subtract(&tmp);
|
||||||
|
tmp2.list(|_, new| assert!(!new));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clone() {
|
||||||
|
let tmp = typical_iblt();
|
||||||
|
let mut tmp2 = tmp.clone();
|
||||||
|
|
||||||
|
tmp2.subtract(&tmp);
|
||||||
|
tmp2.list(|_, new| assert!(!new));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,5 @@ max_width = 300
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
control_brace_style = "AlwaysSameLine"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
imports_granularity = "Crate"
|
|
||||||
group_imports = "StdExternalCrate"
|
|
||||||
fn_single_line = true
|
|
||||||
struct_lit_width = 60
|
struct_lit_width = 60
|
||||||
|
|
|
@ -45,10 +45,14 @@ impl C25519KeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn public_bytes(&self) -> [u8; C25519_PUBLIC_KEY_SIZE] { self.1.to_bytes() }
|
pub fn public_bytes(&self) -> [u8; C25519_PUBLIC_KEY_SIZE] {
|
||||||
|
self.1.to_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> { Secret(self.0.to_bytes()) }
|
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> {
|
||||||
|
Secret(self.0.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute ECDH agreement and return a raw (un-hashed) shared secret key.
|
/// Execute ECDH agreement and return a raw (un-hashed) shared secret key.
|
||||||
pub fn agree(&self, their_public: &[u8]) -> Secret<{ C25519_SHARED_SECRET_SIZE }> {
|
pub fn agree(&self, their_public: &[u8]) -> Secret<{ C25519_SHARED_SECRET_SIZE }> {
|
||||||
|
@ -60,7 +64,9 @@ impl C25519KeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for C25519KeyPair {
|
impl Clone for C25519KeyPair {
|
||||||
fn clone(&self) -> Self { Self(x25519_dalek::StaticSecret::from(self.0.to_bytes()), x25519_dalek::PublicKey::from(self.1.to_bytes())) }
|
fn clone(&self) -> Self {
|
||||||
|
Self(x25519_dalek::StaticSecret::from(self.0.to_bytes()), x25519_dalek::PublicKey::from(self.1.to_bytes()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ed25519 key pair for EDDSA signatures.
|
/// Ed25519 key pair for EDDSA signatures.
|
||||||
|
@ -78,10 +84,7 @@ impl Ed25519KeyPair {
|
||||||
let pk = ed25519_dalek::PublicKey::from_bytes(public_bytes);
|
let pk = ed25519_dalek::PublicKey::from_bytes(public_bytes);
|
||||||
let sk = ed25519_dalek::SecretKey::from_bytes(secret_bytes);
|
let sk = ed25519_dalek::SecretKey::from_bytes(secret_bytes);
|
||||||
if pk.is_ok() && sk.is_ok() {
|
if pk.is_ok() && sk.is_ok() {
|
||||||
Some(Ed25519KeyPair(ed25519_dalek::Keypair {
|
Some(Ed25519KeyPair(ed25519_dalek::Keypair { public: pk.unwrap(), secret: sk.unwrap() }))
|
||||||
public: pk.unwrap(),
|
|
||||||
secret: sk.unwrap(),
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -91,10 +94,14 @@ impl Ed25519KeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] { self.0.public.to_bytes() }
|
pub fn public_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_SIZE] {
|
||||||
|
self.0.public.to_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> { Secret(self.0.secret.to_bytes()) }
|
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> {
|
||||||
|
Secret(self.0.secret.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sign(&self, msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
|
pub fn sign(&self, msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
|
||||||
let mut h = ed25519_dalek::Sha512::new();
|
let mut h = ed25519_dalek::Sha512::new();
|
||||||
|
@ -118,7 +125,9 @@ impl Ed25519KeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Ed25519KeyPair {
|
impl Clone for Ed25519KeyPair {
|
||||||
fn clone(&self) -> Self { Self(ed25519_dalek::Keypair::from_bytes(&self.0.to_bytes()).unwrap()) }
|
fn clone(&self) -> Self {
|
||||||
|
Self(ed25519_dalek::Keypair::from_bytes(&self.0.to_bytes()).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ed25519_verify(public_key: &[u8], signature: &[u8], msg: &[u8]) -> bool {
|
pub fn ed25519_verify(public_key: &[u8], signature: &[u8], msg: &[u8]) -> bool {
|
||||||
|
|
|
@ -52,7 +52,9 @@ impl Write for SHA512 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SHA384(Option<openssl::sha::Sha384>);
|
pub struct SHA384(Option<openssl::sha::Sha384>);
|
||||||
|
@ -92,7 +94,9 @@ impl Write for SHA384 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#[link(name="crypto")]
|
//#[link(name="crypto")]
|
||||||
|
@ -109,22 +113,18 @@ extern "C" {
|
||||||
|
|
||||||
pub struct HMACSHA512 {
|
pub struct HMACSHA512 {
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
evp_md: *const c_void
|
evp_md: *const c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HMACSHA512 {
|
impl HMACSHA512 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(key: &[u8]) -> Self {
|
pub fn new(key: &[u8]) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let hm = Self {
|
let hm = Self { ctx: HMAC_CTX_new(), evp_md: EVP_sha512() };
|
||||||
ctx: HMAC_CTX_new(),
|
|
||||||
evp_md: EVP_sha512()
|
|
||||||
};
|
|
||||||
assert!(!hm.ctx.is_null());
|
assert!(!hm.ctx.is_null());
|
||||||
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
||||||
hm
|
hm
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -172,22 +172,18 @@ unsafe impl Send for HMACSHA512 {}
|
||||||
|
|
||||||
pub struct HMACSHA384 {
|
pub struct HMACSHA384 {
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
evp_md: *const c_void
|
evp_md: *const c_void,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HMACSHA384 {
|
impl HMACSHA384 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(key: &[u8]) -> Self {
|
pub fn new(key: &[u8]) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let hm = Self {
|
let hm = Self { ctx: HMAC_CTX_new(), evp_md: EVP_sha384() };
|
||||||
ctx: HMAC_CTX_new(),
|
|
||||||
evp_md: EVP_sha384()
|
|
||||||
};
|
|
||||||
assert!(!hm.ctx.is_null());
|
assert!(!hm.ctx.is_null());
|
||||||
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
||||||
hm
|
hm
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub const HEX_CHARS: [u8; 16] = [ b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f'];
|
pub const HEX_CHARS: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f'];
|
||||||
|
|
||||||
/// Encode a byte slice to a hexadecimal string.
|
/// Encode a byte slice to a hexadecimal string.
|
||||||
pub fn to_string(b: &[u8]) -> String {
|
pub fn to_string(b: &[u8]) -> String {
|
||||||
|
|
|
@ -12,29 +12,43 @@ use crate::secret::Secret;
|
||||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||||
|
|
||||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<48> {
|
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<48> {
|
||||||
Secret(crate::hash::hmac_sha384(key, &[
|
Secret(crate::hash::hmac_sha384(
|
||||||
(iter >> 24) as u8,
|
key,
|
||||||
(iter >> 16) as u8,
|
&[
|
||||||
(iter >> 8) as u8,
|
(iter >> 24) as u8,
|
||||||
iter as u8,
|
(iter >> 16) as u8,
|
||||||
b'Z', b'T', // can also be considered part of "label"
|
(iter >> 8) as u8,
|
||||||
label,
|
iter as u8,
|
||||||
0,
|
b'Z',
|
||||||
context,
|
b'T', // can also be considered part of "label"
|
||||||
0, 0, 0x01, 0x80 // 384 bits
|
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> {
|
pub fn zt_kbkdf_hmac_sha512(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<64> {
|
||||||
Secret(crate::hash::hmac_sha512(key, &[
|
Secret(crate::hash::hmac_sha512(
|
||||||
(iter >> 24) as u8,
|
key,
|
||||||
(iter >> 16) as u8,
|
&[
|
||||||
(iter >> 8) as u8,
|
(iter >> 24) as u8,
|
||||||
iter as u8,
|
(iter >> 16) as u8,
|
||||||
b'Z', b'T', // can also be considered part of "label"
|
(iter >> 8) as u8,
|
||||||
label,
|
iter as u8,
|
||||||
0,
|
b'Z',
|
||||||
context,
|
b'T', // can also be considered part of "label"
|
||||||
0, 0, 0x01, 0x80 // 384 bits
|
label,
|
||||||
]))
|
0,
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0x01,
|
||||||
|
0x80, // 384 bits
|
||||||
|
],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
pub mod c25519;
|
pub mod c25519;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod p384;
|
|
||||||
pub mod salsa;
|
|
||||||
pub mod poly1305;
|
|
||||||
pub mod kbkdf;
|
|
||||||
pub mod random;
|
|
||||||
pub mod secret;
|
|
||||||
pub mod hex;
|
pub mod hex;
|
||||||
|
pub mod kbkdf;
|
||||||
|
pub mod p384;
|
||||||
|
pub mod poly1305;
|
||||||
|
pub mod random;
|
||||||
|
pub mod salsa;
|
||||||
|
pub mod secret;
|
||||||
pub mod varint;
|
pub mod varint;
|
||||||
|
|
||||||
pub use aes_gmac_siv;
|
pub use aes_gmac_siv;
|
||||||
|
|
|
@ -10,13 +10,13 @@ use std::convert::TryInto;
|
||||||
use std::os::raw::{c_int, c_ulong, c_void};
|
use std::os::raw::{c_int, c_ulong, c_void};
|
||||||
use std::ptr::{null, write_volatile};
|
use std::ptr::{null, write_volatile};
|
||||||
|
|
||||||
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use openssl::bn::{BigNum, BigNumContext};
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
use openssl::ec::{EcKey, EcPoint, EcPointRef, PointConversionForm};
|
use openssl::ec::{EcKey, EcPoint, EcPointRef, PointConversionForm};
|
||||||
use openssl::ecdsa::EcdsaSig;
|
use openssl::ecdsa::EcdsaSig;
|
||||||
use openssl::nid::Nid;
|
use openssl::nid::Nid;
|
||||||
use openssl::pkey::{Private, Public};
|
use openssl::pkey::{Private, Public};
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
|
||||||
|
|
||||||
use crate::hash::SHA384;
|
use crate::hash::SHA384;
|
||||||
use crate::secret::Secret;
|
use crate::secret::Secret;
|
||||||
|
@ -39,7 +39,7 @@ lazy_static! {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct P384PublicKey {
|
pub struct P384PublicKey {
|
||||||
key: EcKey<Public>,
|
key: EcKey<Public>,
|
||||||
bytes: [u8; 49]
|
bytes: [u8; 49],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl P384PublicKey {
|
impl P384PublicKey {
|
||||||
|
@ -50,7 +50,7 @@ impl P384PublicKey {
|
||||||
bytes[(49 - kb.len())..].copy_from_slice(kb.as_slice());
|
bytes[(49 - kb.len())..].copy_from_slice(kb.as_slice());
|
||||||
Self {
|
Self {
|
||||||
key: EcKey::from_public_key(GROUP_P384.as_ref(), key).unwrap(),
|
key: EcKey::from_public_key(GROUP_P384.as_ref(), key).unwrap(),
|
||||||
bytes
|
bytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,10 +63,7 @@ impl P384PublicKey {
|
||||||
if key.is_on_curve(GROUP_P384.as_ref(), &mut bnc).unwrap_or(false) {
|
if key.is_on_curve(GROUP_P384.as_ref(), &mut bnc).unwrap_or(false) {
|
||||||
let key = EcKey::from_public_key(GROUP_P384.as_ref(), key.as_ref());
|
let key = EcKey::from_public_key(GROUP_P384.as_ref(), key.as_ref());
|
||||||
if key.is_ok() {
|
if key.is_ok() {
|
||||||
return Some(Self {
|
return Some(Self { key: key.unwrap(), bytes: b.try_into().unwrap() });
|
||||||
key: key.unwrap(),
|
|
||||||
bytes: b.try_into().unwrap()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,12 +86,16 @@ impl P384PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; 49] { &self.bytes }
|
pub fn as_bytes(&self) -> &[u8; 49] {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for P384PublicKey {
|
impl PartialEq for P384PublicKey {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool { self.bytes == other.bytes }
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.bytes == other.bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for P384PublicKey {}
|
unsafe impl Send for P384PublicKey {}
|
||||||
|
@ -113,10 +114,7 @@ impl P384KeyPair {
|
||||||
let pair = EcKey::generate(GROUP_P384.as_ref()).unwrap(); // failure implies a serious problem
|
let pair = EcKey::generate(GROUP_P384.as_ref()).unwrap(); // failure implies a serious problem
|
||||||
assert!(pair.check_key().is_ok()); // also would imply a serious problem
|
assert!(pair.check_key().is_ok()); // also would imply a serious problem
|
||||||
let public = P384PublicKey::new_from_point(pair.public_key());
|
let public = P384PublicKey::new_from_point(pair.public_key());
|
||||||
Self {
|
Self { pair, public }
|
||||||
pair,
|
|
||||||
public,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<P384KeyPair> {
|
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<P384KeyPair> {
|
||||||
|
@ -127,10 +125,7 @@ impl P384KeyPair {
|
||||||
if pair.is_ok() {
|
if pair.is_ok() {
|
||||||
let pair = pair.unwrap();
|
let pair = pair.unwrap();
|
||||||
if pair.check_key().is_ok() {
|
if pair.check_key().is_ok() {
|
||||||
Some(Self {
|
Some(Self { pair, public })
|
||||||
pair,
|
|
||||||
public
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -145,10 +140,14 @@ impl P384KeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn public_key(&self) -> &P384PublicKey { &self.public }
|
pub fn public_key(&self) -> &P384PublicKey {
|
||||||
|
&self.public
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn public_key_bytes(&self) -> &[u8; P384_PUBLIC_KEY_SIZE] { &self.public.bytes }
|
pub fn public_key_bytes(&self) -> &[u8; P384_PUBLIC_KEY_SIZE] {
|
||||||
|
&self.public.bytes
|
||||||
|
}
|
||||||
|
|
||||||
pub fn secret_key_bytes(&self) -> Secret<P384_SECRET_KEY_SIZE> {
|
pub fn secret_key_bytes(&self) -> Secret<P384_SECRET_KEY_SIZE> {
|
||||||
let mut tmp: Secret<P384_SECRET_KEY_SIZE> = Secret::default();
|
let mut tmp: Secret<P384_SECRET_KEY_SIZE> = Secret::default();
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use openssl::rand::rand_bytes;
|
use openssl::rand::rand_bytes;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
pub struct SecureRandom;
|
pub struct SecureRandom;
|
||||||
|
|
||||||
|
@ -43,18 +43,26 @@ pub fn get_bytes_secure<const COUNT: usize>() -> [u8; COUNT] {
|
||||||
|
|
||||||
impl SecureRandom {
|
impl SecureRandom {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get() -> Self { Self }
|
pub fn get() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl rand_core::RngCore for SecureRandom {
|
impl rand_core::RngCore for SecureRandom {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next_u32(&mut self) -> u32 { next_u32_secure() }
|
fn next_u32(&mut self) -> u32 {
|
||||||
|
next_u32_secure()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next_u64(&mut self) -> u64 { next_u64_secure() }
|
fn next_u64(&mut self) -> u64 {
|
||||||
|
next_u64_secure()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fill_bytes(&mut self, dest: &mut [u8]) { fill_bytes_secure(dest); }
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
|
fill_bytes_secure(dest);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||||
|
|
||||||
const CONSTANTS: [u32; 4] = [ u32::from_le_bytes(*b"expa"), u32::from_le_bytes(*b"nd 3"), u32::from_le_bytes(*b"2-by"), u32::from_le_bytes(*b"te k") ];
|
const CONSTANTS: [u32; 4] = [u32::from_le_bytes(*b"expa"), u32::from_le_bytes(*b"nd 3"), u32::from_le_bytes(*b"2-by"), u32::from_le_bytes(*b"te k")];
|
||||||
|
|
||||||
/// Salsa stream cipher implementation supporting 8, 12, or 20 rounds.
|
/// Salsa stream cipher implementation supporting 8, 12, or 20 rounds.
|
||||||
///
|
///
|
||||||
|
@ -19,7 +19,7 @@ const CONSTANTS: [u32; 4] = [ u32::from_le_bytes(*b"expa"), u32::from_le_bytes(*
|
||||||
/// transport encryption in ZeroTier anyway, but is still used to derive addresses from
|
/// transport encryption in ZeroTier anyway, but is still used to derive addresses from
|
||||||
/// identity public keys.
|
/// identity public keys.
|
||||||
pub struct Salsa<const ROUNDS: usize> {
|
pub struct Salsa<const ROUNDS: usize> {
|
||||||
state: [u32; 16]
|
state: [u32; 16],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
|
@ -45,13 +45,14 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
u32::from_le_bytes((&key[20..24]).try_into().unwrap()),
|
u32::from_le_bytes((&key[20..24]).try_into().unwrap()),
|
||||||
u32::from_le_bytes((&key[24..28]).try_into().unwrap()),
|
u32::from_le_bytes((&key[24..28]).try_into().unwrap()),
|
||||||
u32::from_le_bytes((&key[28..32]).try_into().unwrap()),
|
u32::from_le_bytes((&key[28..32]).try_into().unwrap()),
|
||||||
CONSTANTS[3]
|
CONSTANTS[3],
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crypt(&mut self, mut plaintext: &[u8], mut ciphertext: &mut [u8]) {
|
pub fn crypt(&mut self, mut plaintext: &[u8], mut ciphertext: &mut [u8]) {
|
||||||
let (j0, j1, j2, j3, j4, j5, j6, j7, mut j8, mut j9, j10, j11, j12, j13, j14, j15) = (self.state[0], self.state[1], self.state[2], self.state[3], self.state[4], self.state[5], self.state[6], self.state[7], self.state[8], self.state[9], self.state[10], self.state[11], self.state[12], self.state[13], self.state[14], self.state[15]);
|
let (j0, j1, j2, j3, j4, j5, j6, j7, mut j8, mut j9, j10, j11, j12, j13, j14, j15) =
|
||||||
|
(self.state[0], self.state[1], self.state[2], self.state[3], self.state[4], self.state[5], self.state[6], self.state[7], self.state[8], self.state[9], self.state[10], self.state[11], self.state[12], self.state[13], self.state[14], self.state[15]);
|
||||||
loop {
|
loop {
|
||||||
let (mut x0, mut x1, mut x2, mut x3, mut x4, mut x5, mut x6, mut x7, mut x8, mut x9, mut x10, mut x11, mut x12, mut x13, mut x14, mut x15) = (j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15);
|
let (mut x0, mut x1, mut x2, mut x3, mut x4, mut x5, mut x6, mut x7, mut x8, mut x9, mut x10, mut x11, mut x12, mut x13, mut x14, mut x15) = (j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15);
|
||||||
|
|
||||||
|
@ -108,7 +109,8 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
x15 = x15.wrapping_add(j15);
|
x15 = x15.wrapping_add(j15);
|
||||||
|
|
||||||
if plaintext.len() >= 64 {
|
if plaintext.len() >= 64 {
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] {
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||||
|
{
|
||||||
// Slightly faster keystream XOR for little-endian platforms with unaligned load/store.
|
// Slightly faster keystream XOR for little-endian platforms with unaligned load/store.
|
||||||
unsafe {
|
unsafe {
|
||||||
*ciphertext.as_mut_ptr().cast::<u32>() = *plaintext.as_ptr().cast::<u32>() ^ x0;
|
*ciphertext.as_mut_ptr().cast::<u32>() = *plaintext.as_ptr().cast::<u32>() ^ x0;
|
||||||
|
@ -129,7 +131,8 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
*ciphertext.as_mut_ptr().cast::<u32>().add(15) = *plaintext.as_ptr().cast::<u32>().add(15) ^ x15;
|
*ciphertext.as_mut_ptr().cast::<u32>().add(15) = *plaintext.as_ptr().cast::<u32>().add(15) ^ x15;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] {
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
{
|
||||||
// Portable keystream XOR with alignment-safe access and native to little-endian conversion.
|
// Portable keystream XOR with alignment-safe access and native to little-endian conversion.
|
||||||
ciphertext[0..4].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().cast::<[u8; 4]>() }) ^ x0.to_le()).to_ne_bytes());
|
ciphertext[0..4].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().cast::<[u8; 4]>() }) ^ x0.to_le()).to_ne_bytes());
|
||||||
ciphertext[4..8].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(4).cast::<[u8; 4]>() }) ^ x1.to_le()).to_ne_bytes());
|
ciphertext[4..8].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(4).cast::<[u8; 4]>() }) ^ x1.to_le()).to_ne_bytes());
|
||||||
|
@ -171,8 +174,6 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn crypt_in_place(&mut self, data: &mut [u8]) {
|
pub fn crypt_in_place(&mut self, data: &mut [u8]) {
|
||||||
unsafe {
|
unsafe { self.crypt(&*slice_from_raw_parts(data.as_ptr(), data.len()), &mut *slice_from_raw_parts_mut(data.as_mut_ptr(), data.len())) }
|
||||||
self.crypt(&*slice_from_raw_parts(data.as_ptr(), data.len()), &mut *slice_from_raw_parts_mut(data.as_mut_ptr(), data.len()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,20 @@ pub struct Secret<const L: usize>(pub [u8; L]);
|
||||||
|
|
||||||
impl<const L: usize> Secret<L> {
|
impl<const L: usize> Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self([0_u8; L]) }
|
pub fn new() -> Self {
|
||||||
|
Self([0_u8; L])
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy bytes into secret, will panic if size does not match.
|
/// Copy bytes into secret, will panic if size does not match.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bytes(b: &[u8]) -> Self { Self(b.try_into().unwrap()) }
|
pub fn from_bytes(b: &[u8]) -> Self {
|
||||||
|
Self(b.try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; L] { return &self.0 }
|
pub fn as_bytes(&self) -> &[u8; L] {
|
||||||
|
return &self.0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a clone of the first N bytes of this secret.
|
/// Get a clone of the first N bytes of this secret.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -54,25 +60,35 @@ impl<const L: usize> Drop for Secret<L> {
|
||||||
|
|
||||||
impl<const L: usize> Default for Secret<L> {
|
impl<const L: usize> Default for Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self { Self([0_u8; L]) }
|
fn default() -> Self {
|
||||||
|
Self([0_u8; L])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsRef<[u8]> for Secret<L> {
|
impl<const L: usize> AsRef<[u8]> for Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &[u8] { &self.0 }
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsRef<[u8; L]> for Secret<L> {
|
impl<const L: usize> AsRef<[u8; L]> for Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &[u8; L] { &self.0 }
|
fn as_ref(&self) -> &[u8; L] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsMut<[u8]> for Secret<L> {
|
impl<const L: usize> AsMut<[u8]> for Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsMut<[u8; L]> for Secret<L> {
|
impl<const L: usize> AsMut<[u8; L]> for Secret<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mut(&mut self) -> &mut [u8; L] { &mut self.0 }
|
fn as_mut(&mut self) -> &mut [u8; L] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,9 @@ impl Encoded {
|
||||||
|
|
||||||
impl AsRef<[u8]> for Encoded {
|
impl AsRef<[u8]> for Encoded {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &[u8] { &self.0[0..(self.1 as usize)] }
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0[0..(self.1 as usize)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -19,6 +19,8 @@ dashmap = "^4"
|
||||||
parking_lot = "^0"
|
parking_lot = "^0"
|
||||||
lazy_static = "^1"
|
lazy_static = "^1"
|
||||||
highway = "^0"
|
highway = "^0"
|
||||||
|
serde = "^1"
|
||||||
|
serde_json = "^1"
|
||||||
|
|
||||||
[target."cfg(not(windows))".dependencies]
|
[target."cfg(not(windows))".dependencies]
|
||||||
libc = "^0"
|
libc = "^0"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Debug};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
pub struct InvalidFormatError;
|
pub struct InvalidFormatError;
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ pub const VERSION_MAJOR: u8 = 1;
|
||||||
pub const VERSION_MINOR: u8 = 99;
|
pub const VERSION_MINOR: u8 = 99;
|
||||||
pub const VERSION_REVISION: u8 = 1;
|
pub const VERSION_REVISION: u8 = 1;
|
||||||
|
|
||||||
pub mod util;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod util;
|
||||||
pub mod vl1;
|
pub mod vl1;
|
||||||
pub mod vl2;
|
pub mod vl2;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::num::NonZeroI64;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::error::InvalidParameterError;
|
use crate::error::InvalidParameterError;
|
||||||
use crate::vl1::{Address, Identity, Endpoint, SystemInterface, Node};
|
use crate::vl1::{Address, Endpoint, Identity, Node, SystemInterface};
|
||||||
use crate::vl2::{Switch, SwitchInterface};
|
use crate::vl2::{Switch, SwitchInterface};
|
||||||
use crate::PacketBuffer;
|
use crate::PacketBuffer;
|
||||||
|
|
||||||
|
@ -33,13 +33,19 @@ impl NetworkHypervisor {
|
||||||
///
|
///
|
||||||
/// The returned object is a Pooled<Buffer<>> instance. The buffer is returned to the pool when the container is destroyed.
|
/// The returned object is a Pooled<Buffer<>> instance. The buffer is returned to the pool when the container is destroyed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_packet_buffer(&self) -> PacketBuffer { self.vl1.get_packet_buffer() }
|
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
||||||
|
self.vl1.get_packet_buffer()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn address(&self) -> Address { self.vl1.identity.address }
|
pub fn address(&self) -> Address {
|
||||||
|
self.vl1.identity.address
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn identity(&self) -> &Identity { &self.vl1.identity }
|
pub fn identity(&self) -> &Identity {
|
||||||
|
&self.vl1.identity
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_background_tasks<I: Interface>(&self, ii: &I) -> Duration {
|
pub fn do_background_tasks<I: Interface>(&self, ii: &I) -> Duration {
|
||||||
self.vl1.do_background_tasks(ii)
|
self.vl1.do_background_tasks(ii)
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
use std::mem::{MaybeUninit, size_of};
|
|
||||||
use crate::util::{array_range, array_range_mut};
|
use crate::util::{array_range, array_range_mut};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::mem::{size_of, MaybeUninit};
|
||||||
|
|
||||||
use crate::util::pool::PoolFactory;
|
use crate::util::pool::PoolFactory;
|
||||||
|
|
||||||
|
@ -27,7 +27,9 @@ unsafe impl<const L: usize> RawObject 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 { Self(0, [0_u8; L]) }
|
fn default() -> Self {
|
||||||
|
Self(0, [0_u8; L])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const OVERFLOW_ERR_MSG: &'static str = "overflow";
|
const OVERFLOW_ERR_MSG: &'static str = "overflow";
|
||||||
|
@ -35,14 +37,20 @@ const OVERFLOW_ERR_MSG: &'static str = "overflow";
|
||||||
impl<const L: usize> Buffer<L> {
|
impl<const L: usize> Buffer<L> {
|
||||||
pub const CAPACITY: usize = L;
|
pub const CAPACITY: usize = L;
|
||||||
|
|
||||||
pub const fn capacity(&self) -> usize { L }
|
pub const fn capacity(&self) -> usize {
|
||||||
|
L
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self(0, [0_u8; L]) }
|
pub fn new() -> Self {
|
||||||
|
Self(0, [0_u8; L])
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an empty buffer without zeroing its memory (saving a bit of CPU).
|
/// Create an empty buffer without zeroing its memory (saving a bit of CPU).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn new_without_memzero() -> Self { Self(0, MaybeUninit::uninit().assume_init()) }
|
pub unsafe fn new_without_memzero() -> Self {
|
||||||
|
Self(0, MaybeUninit::uninit().assume_init())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
|
pub fn from_bytes(b: &[u8]) -> std::io::Result<Self> {
|
||||||
|
@ -58,22 +66,34 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8] { &self.1[0..self.0] }
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.1[0..self.0]
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.1[0..self.0] }
|
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||||
|
&mut self.1[0..self.0]
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_range_fixed<const START: usize, const LEN: usize>(&self) -> &[u8; LEN] { array_range::<u8, L, START, LEN>(&self.1) }
|
pub fn as_range_fixed<const START: usize, const LEN: usize>(&self) -> &[u8; LEN] {
|
||||||
|
array_range::<u8, L, START, LEN>(&self.1)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_ptr(&self) -> *const u8 { self.1.as_ptr() }
|
pub fn as_ptr(&self) -> *const u8 {
|
||||||
|
self.1.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 { self.1.as_mut_ptr() }
|
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||||
|
self.1.as_mut_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_mut_range_fixed<const START: usize, const LEN: usize>(&mut self) -> &mut [u8; LEN] { array_range_mut::<u8, L, START, LEN>(&mut self.1) }
|
pub fn as_mut_range_fixed<const START: usize, const LEN: usize>(&mut self) -> &mut [u8; LEN] {
|
||||||
|
array_range_mut::<u8, L, START, LEN>(&mut self.1)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all bytes after a given position.
|
/// Get all bytes after a given position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -106,10 +126,14 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize { self.0 }
|
pub fn len(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool { self.0 == 0 }
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the size of this buffer's data.
|
/// Set the size of this buffer's data.
|
||||||
///
|
///
|
||||||
|
@ -127,10 +151,14 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn set_size_unchecked(&mut self, s: usize) { self.0 = s; }
|
pub unsafe fn set_size_unchecked(&mut self, s: usize) {
|
||||||
|
self.0 = s;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn get_unchecked(&self, i: usize) -> u8 { *self.1.get_unchecked(i) }
|
pub unsafe fn get_unchecked(&self, i: usize) -> u8 {
|
||||||
|
*self.1.get_unchecked(i)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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)]
|
||||||
|
@ -314,9 +342,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn struct_at<T: RawObject>(&self, ptr: usize) -> std::io::Result<&T> {
|
pub fn struct_at<T: RawObject>(&self, ptr: usize) -> std::io::Result<&T> {
|
||||||
if (ptr + size_of::<T>()) <= self.0 {
|
if (ptr + size_of::<T>()) <= self.0 {
|
||||||
unsafe {
|
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
||||||
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||||
}
|
}
|
||||||
|
@ -326,9 +352,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn struct_mut_at<T: RawObject>(&mut self, ptr: usize) -> std::io::Result<&mut T> {
|
pub fn struct_mut_at<T: RawObject>(&mut self, ptr: usize) -> std::io::Result<&mut T> {
|
||||||
if (ptr + size_of::<T>()) <= self.0 {
|
if (ptr + size_of::<T>()) <= self.0 {
|
||||||
unsafe {
|
unsafe { Ok(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
||||||
Ok(&mut *self.1.as_mut_ptr().cast::<u8>().offset(ptr as isize).cast::<T>())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||||
}
|
}
|
||||||
|
@ -351,9 +375,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
debug_assert!(end <= L);
|
debug_assert!(end <= L);
|
||||||
if end <= self.0 {
|
if end <= self.0 {
|
||||||
*cursor = end;
|
*cursor = end;
|
||||||
unsafe {
|
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>()) }
|
||||||
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<T>())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||||
}
|
}
|
||||||
|
@ -366,9 +388,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
debug_assert!(end <= L);
|
debug_assert!(end <= L);
|
||||||
if end <= self.0 {
|
if end <= self.0 {
|
||||||
*cursor = end;
|
*cursor = end;
|
||||||
unsafe {
|
unsafe { Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; S]>()) }
|
||||||
Ok(&*self.1.as_ptr().cast::<u8>().offset(ptr as isize).cast::<[u8; S]>())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, OVERFLOW_ERR_MSG))
|
||||||
}
|
}
|
||||||
|
@ -501,7 +521,9 @@ impl<const L: usize> Buffer<L> {
|
||||||
|
|
||||||
impl<const L: usize> PartialEq for Buffer<L> {
|
impl<const L: usize> PartialEq for Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool { self.1[0..self.0].eq(&other.1[0..other.0]) }
|
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> Eq for Buffer<L> {}
|
||||||
|
@ -521,30 +543,42 @@ impl<const L: usize> Write for Buffer<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsRef<[u8]> for Buffer<L> {
|
impl<const L: usize> AsRef<[u8]> for Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.as_bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> AsMut<[u8]> for Buffer<L> {
|
impl<const L: usize> AsMut<[u8]> for Buffer<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mut(&mut self) -> &mut [u8] { self.as_bytes_mut() }
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.as_bytes_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PooledBufferFactory<const L: usize>;
|
pub struct PooledBufferFactory<const L: usize>;
|
||||||
|
|
||||||
impl<const L: usize> PooledBufferFactory<L> {
|
impl<const L: usize> PooledBufferFactory<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self{} }
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> PoolFactory<Buffer<L>> for PooledBufferFactory<L> {
|
impl<const L: usize> PoolFactory<Buffer<L>> for PooledBufferFactory<L> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn create(&self) -> Buffer<L> { Buffer::new() }
|
fn create(&self) -> Buffer<L> {
|
||||||
|
Buffer::new()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn reset(&self, obj: &mut Buffer<L>) { obj.clear(); }
|
fn reset(&self, obj: &mut Buffer<L>) {
|
||||||
|
obj.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,15 +14,21 @@ pub struct IntervalGate<const FREQ: i64>(i64);
|
||||||
|
|
||||||
impl<const FREQ: i64> Default for IntervalGate<FREQ> {
|
impl<const FREQ: i64> Default for IntervalGate<FREQ> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self { Self(0) }
|
fn default() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const FREQ: i64> IntervalGate<FREQ> {
|
impl<const FREQ: i64> IntervalGate<FREQ> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(initial_ts: i64) -> Self { Self(initial_ts) }
|
pub fn new(initial_ts: i64) -> Self {
|
||||||
|
Self(initial_ts)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reset(&mut self) { self.0 = 0; }
|
pub fn reset(&mut self) {
|
||||||
|
self.0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn gate(&mut self, time: i64) -> bool {
|
pub fn gate(&mut self, time: i64) -> bool {
|
||||||
|
@ -41,15 +47,21 @@ pub struct AtomicIntervalGate<const FREQ: i64>(AtomicI64);
|
||||||
|
|
||||||
impl<const FREQ: i64> Default for AtomicIntervalGate<FREQ> {
|
impl<const FREQ: i64> Default for AtomicIntervalGate<FREQ> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self { Self(AtomicI64::new(0)) }
|
fn default() -> Self {
|
||||||
|
Self(AtomicI64::new(0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const FREQ: i64> AtomicIntervalGate<FREQ> {
|
impl<const FREQ: i64> AtomicIntervalGate<FREQ> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(initial_ts: i64) -> Self { Self(AtomicI64::new(initial_ts)) }
|
pub fn new(initial_ts: i64) -> Self {
|
||||||
|
Self(AtomicI64::new(initial_ts))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reset(&self) { self.0.store(0, Ordering::Relaxed); }
|
pub fn reset(&self) {
|
||||||
|
self.0.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn gate(&self, mut time: i64) -> bool {
|
pub fn gate(&self, mut time: i64) -> bool {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) mod pool;
|
|
||||||
pub(crate) mod gate;
|
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
pub(crate) mod gate;
|
||||||
|
pub(crate) mod pool;
|
||||||
|
|
||||||
pub use zerotier_core_crypto::hex;
|
pub use zerotier_core_crypto::hex;
|
||||||
pub use zerotier_core_crypto::varint;
|
pub use zerotier_core_crypto::varint;
|
||||||
|
@ -31,7 +31,9 @@ pub(crate) fn array_range_mut<T, const A: usize, const START: usize, const LEN:
|
||||||
|
|
||||||
/// Cast a u64 to a byte array.
|
/// Cast a u64 to a byte array.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn u64_as_bytes(i: &u64) -> &[u8; 8] { unsafe { &*(i as *const u64).cast() } }
|
pub(crate) fn u64_as_bytes(i: &u64) -> &[u8; 8] {
|
||||||
|
unsafe { &*(i as *const u64).cast() }
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref HIGHWAYHASHER_KEY: [u64; 4] = [zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure()];
|
static ref HIGHWAYHASHER_KEY: [u64; 4] = [zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure(), zerotier_core_crypto::random::next_u64_secure()];
|
||||||
|
@ -40,7 +42,9 @@ lazy_static! {
|
||||||
/// Get an instance of HighwayHasher initialized with a secret per-process random salt.
|
/// Get an instance of HighwayHasher initialized with a secret per-process random salt.
|
||||||
/// The random salt is generated at process start and so will differ for each invocation of whatever process this is inside.
|
/// The random salt is generated at process start and so will differ for each invocation of whatever process this is inside.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn highwayhasher() -> highway::HighwayHasher { highway::HighwayHasher::new(highway::Key(HIGHWAYHASHER_KEY.clone())) }
|
pub(crate) fn highwayhasher() -> highway::HighwayHasher {
|
||||||
|
highway::HighwayHasher::new(highway::Key(HIGHWAYHASHER_KEY.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
|
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -59,12 +63,16 @@ pub(crate) struct U64NoOpHasher(u64);
|
||||||
|
|
||||||
impl U64NoOpHasher {
|
impl U64NoOpHasher {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self(0) }
|
pub fn new() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hasher for U64NoOpHasher {
|
impl std::hash::Hasher for U64NoOpHasher {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish(&self) -> u64 { self.0 }
|
fn finish(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write(&mut self, _: &[u8]) {
|
fn write(&mut self, _: &[u8]) {
|
||||||
|
@ -72,15 +80,21 @@ impl std::hash::Hasher for U64NoOpHasher {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_u64(&mut self, i: u64) { self.0 += i; }
|
fn write_u64(&mut self, i: u64) {
|
||||||
|
self.0 += i;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_i64(&mut self, i: i64) { self.0 += i as u64; }
|
fn write_i64(&mut self, i: i64) {
|
||||||
|
self.0 += i as u64;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::BuildHasher for U64NoOpHasher {
|
impl std::hash::BuildHasher for U64NoOpHasher {
|
||||||
type Hasher = Self;
|
type Hasher = Self;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn build_hasher(&self) -> Self::Hasher { Self(0) }
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,13 +129,11 @@ impl<O, F: PoolFactory<O>> Pool<O, F> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get(&self) -> Pooled<O, F> {
|
pub fn get(&self) -> Pooled<O, F> {
|
||||||
//let _ = self.0.outstanding_count.fetch_add(1, Ordering::Acquire);
|
//let _ = self.0.outstanding_count.fetch_add(1, Ordering::Acquire);
|
||||||
Pooled::<O, F>(self.0.pool.lock().pop().unwrap_or_else(|| {
|
Pooled::<O, F>(self.0.pool.lock().pop().unwrap_or_else(|| unsafe {
|
||||||
unsafe {
|
NonNull::new_unchecked(Box::into_raw(Box::new(PoolEntry::<O, F> {
|
||||||
NonNull::new_unchecked(Box::into_raw(Box::new(PoolEntry::<O, F> {
|
obj: self.0.factory.create(),
|
||||||
obj: self.0.factory.create(),
|
return_pool: Arc::downgrade(&self.0),
|
||||||
return_pool: Arc::downgrade(&self.0),
|
})))
|
||||||
})))
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +167,8 @@ unsafe impl<O, F: PoolFactory<O>> Send for Pool<O, F> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::util::pool::*;
|
use crate::util::pool::*;
|
||||||
|
@ -189,7 +187,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn threaded_pool_use() {
|
fn threaded_pool_use() {
|
||||||
let p: Arc<Pool<String, TestPoolFactory>> = Arc::new(Pool::new(2, TestPoolFactory{}));
|
let p: Arc<Pool<String, TestPoolFactory>> = Arc::new(Pool::new(2, TestPoolFactory {}));
|
||||||
let ctr = Arc::new(AtomicUsize::new(0));
|
let ctr = Arc::new(AtomicUsize::new(0));
|
||||||
for _ in 0..64 {
|
for _ in 0..64 {
|
||||||
let p2 = p.clone();
|
let p2 = p.clone();
|
||||||
|
|
|
@ -10,12 +10,14 @@ use std::hash::{Hash, Hasher};
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::InvalidFormatError;
|
use crate::error::InvalidFormatError;
|
||||||
use crate::util::hex::HEX_CHARS;
|
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
|
use crate::util::hex::HEX_CHARS;
|
||||||
use crate::vl1::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
|
use crate::vl1::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Address(NonZeroU64);
|
pub struct Address(NonZeroU64);
|
||||||
|
|
||||||
|
@ -24,13 +26,7 @@ impl Address {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
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| {
|
NonZeroU64::new(i).and_then(|ii| if (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 { Some(Address(ii)) } else { None })
|
||||||
if (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 {
|
|
||||||
Some(Address(ii))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -54,7 +50,9 @@ impl Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u64(&self) -> u64 { self.0.get() }
|
pub fn to_u64(&self) -> u64 {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
|
|
|
@ -25,12 +25,24 @@ fn write_escaped<W: Write>(b: &[u8], w: &mut W) -> std::io::Result<()> {
|
||||||
while i < l {
|
while i < l {
|
||||||
let ii = i + 1;
|
let ii = i + 1;
|
||||||
match b[i] {
|
match b[i] {
|
||||||
0 => { w.write_all(&[b'\\', b'0'])?; }
|
0 => {
|
||||||
b'\n' => { w.write_all(&[b'\\', b'n'])?; }
|
w.write_all(&[b'\\', b'0'])?;
|
||||||
b'\r' => { w.write_all(&[b'\\', b'r'])?; }
|
}
|
||||||
b'=' => { w.write_all(&[b'\\', b'e'])?; }
|
b'\n' => {
|
||||||
b'\\' => { w.write_all(&[b'\\', b'\\'])?; }
|
w.write_all(&[b'\\', b'n'])?;
|
||||||
_ => { w.write_all(&b[i..ii])?; }
|
}
|
||||||
|
b'\r' => {
|
||||||
|
w.write_all(&[b'\\', b'r'])?;
|
||||||
|
}
|
||||||
|
b'=' => {
|
||||||
|
w.write_all(&[b'\\', b'e'])?;
|
||||||
|
}
|
||||||
|
b'\\' => {
|
||||||
|
w.write_all(&[b'\\', b'\\'])?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
w.write_all(&b[i..ii])?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i = ii;
|
i = ii;
|
||||||
}
|
}
|
||||||
|
@ -52,15 +64,23 @@ fn append_printable(s: &mut String, b: &[u8]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dictionary {
|
impl Dictionary {
|
||||||
pub fn new() -> Self { Self(BTreeMap::new()) }
|
pub fn new() -> Self {
|
||||||
|
Self(BTreeMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) { self.0.clear() }
|
pub fn clear(&mut self) {
|
||||||
|
self.0.clear()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize { self.0.len() }
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_str(&self, k: &str) -> Option<&str> {
|
pub fn get_str(&self, k: &str) -> Option<&str> {
|
||||||
self.0.get(k).map_or(None, |v| std::str::from_utf8(v.as_slice()).map_or(None, |s| Some(s)))
|
self.0.get(k).map_or(None, |v| std::str::from_utf8(v.as_slice()).map_or(None, |s| Some(s)))
|
||||||
|
@ -131,7 +151,7 @@ impl Dictionary {
|
||||||
b'0' => 0,
|
b'0' => 0,
|
||||||
b'n' => b'\n',
|
b'n' => b'\n',
|
||||||
b'r' => b'\r',
|
b'r' => b'\r',
|
||||||
_ => c // =, \, and escapes before other characters are unnecessary but not errors
|
_ => c, // =, \, and escapes before other characters are unnecessary but not errors
|
||||||
});
|
});
|
||||||
} else if c == b'\\' {
|
} else if c == b'\\' {
|
||||||
escape = true;
|
escape = true;
|
||||||
|
@ -187,7 +207,7 @@ mod tests {
|
||||||
let mut d = Dictionary::new();
|
let mut d = Dictionary::new();
|
||||||
d.set_str("foo", "bar");
|
d.set_str("foo", "bar");
|
||||||
d.set_u64("bar", 0xfeedcafebabebeef);
|
d.set_u64("bar", 0xfeedcafebabebeef);
|
||||||
d.set_bytes("baz", vec![1,2,3,4,5,6,7,8,9]);
|
d.set_bytes("baz", vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
d.set_bool("lala", true);
|
d.set_bool("lala", true);
|
||||||
d.set_bool("haha", false);
|
d.set_bool("haha", false);
|
||||||
let bytes = d.to_bytes();
|
let bytes = d.to_bytes();
|
||||||
|
|
|
@ -11,9 +11,9 @@ use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
||||||
|
|
||||||
use crate::vl1::{Address, MAC};
|
|
||||||
use crate::vl1::inetaddress::InetAddress;
|
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
|
use crate::vl1::inetaddress::InetAddress;
|
||||||
|
use crate::vl1::{Address, MAC};
|
||||||
|
|
||||||
pub const TYPE_NIL: u8 = 0;
|
pub const TYPE_NIL: u8 = 0;
|
||||||
pub const TYPE_ZEROTIER: u8 = 1;
|
pub const TYPE_ZEROTIER: u8 = 1;
|
||||||
|
@ -70,7 +70,9 @@ pub enum Endpoint {
|
||||||
|
|
||||||
impl Default for Endpoint {
|
impl Default for Endpoint {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Endpoint { Endpoint::Nil }
|
fn default() -> Endpoint {
|
||||||
|
Endpoint::Nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
|
@ -81,7 +83,7 @@ impl Endpoint {
|
||||||
Endpoint::Ip(ip) => Some((&ip, TYPE_IP)),
|
Endpoint::Ip(ip) => Some((&ip, TYPE_IP)),
|
||||||
Endpoint::IpUdp(ip) => Some((&ip, TYPE_IPUDP)),
|
Endpoint::IpUdp(ip) => Some((&ip, TYPE_IPUDP)),
|
||||||
Endpoint::IpTcp(ip) => Some((&ip, TYPE_IPTCP)),
|
Endpoint::IpTcp(ip) => Some((&ip, TYPE_IPTCP)),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +104,9 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_nil(&self) -> bool { matches!(self, Endpoint::Nil) }
|
pub fn is_nil(&self) -> bool {
|
||||||
|
matches!(self, Endpoint::Nil)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
@ -113,9 +117,7 @@ impl Endpoint {
|
||||||
|
|
||||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Endpoint::Nil => {
|
Endpoint::Nil => buf.append_u8(TYPE_NIL),
|
||||||
buf.append_u8(TYPE_NIL)
|
|
||||||
}
|
|
||||||
Endpoint::ZeroTier(a, h) => {
|
Endpoint::ZeroTier(a, h) => {
|
||||||
buf.append_u8(16 + TYPE_ZEROTIER)?;
|
buf.append_u8(16 + TYPE_ZEROTIER)?;
|
||||||
buf.append_bytes_fixed(&a.to_bytes())?;
|
buf.append_bytes_fixed(&a.to_bytes())?;
|
||||||
|
@ -201,7 +203,7 @@ impl Endpoint {
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address"))
|
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address"))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
TYPE_ETHERNET => Ok(Endpoint::Ethernet(read_mac(buf, cursor)?)),
|
TYPE_ETHERNET => Ok(Endpoint::Ethernet(read_mac(buf, cursor)?)),
|
||||||
TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(read_mac(buf, cursor)?)),
|
TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(read_mac(buf, cursor)?)),
|
||||||
TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(read_mac(buf, cursor)?)),
|
TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(read_mac(buf, cursor)?)),
|
||||||
|
@ -218,8 +220,8 @@ impl Endpoint {
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address"))
|
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address"))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream"))
|
_ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,14 +292,16 @@ impl Ord for Endpoint {
|
||||||
(Endpoint::Http(a), Endpoint::Http(b)) => a.cmp(b),
|
(Endpoint::Http(a), Endpoint::Http(b)) => a.cmp(b),
|
||||||
(Endpoint::WebRTC(a), Endpoint::WebRTC(b)) => a.cmp(b),
|
(Endpoint::WebRTC(a), Endpoint::WebRTC(b)) => a.cmp(b),
|
||||||
(Endpoint::ZeroTierEncap(a, ah), Endpoint::ZeroTierEncap(b, bh)) => a.cmp(b).then_with(|| ah.cmp(bh)),
|
(Endpoint::ZeroTierEncap(a, ah), Endpoint::ZeroTierEncap(b, bh)) => a.cmp(b).then_with(|| ah.cmp(bh)),
|
||||||
_ => self.type_id().cmp(&other.type_id())
|
_ => self.type_id().cmp(&other.type_id()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Endpoint {
|
impl PartialOrd for Endpoint {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Endpoint {
|
impl ToString for Endpoint {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::PacketBuffer;
|
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
|
use crate::PacketBuffer;
|
||||||
|
|
||||||
/// Packet fragment re-assembler and container.
|
/// Packet fragment re-assembler and container.
|
||||||
///
|
///
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use zerotier_core_crypto::c25519::{C25519_PUBLIC_KEY_SIZE, C25519KeyPair};
|
use zerotier_core_crypto::c25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE};
|
||||||
use zerotier_core_crypto::hash::{hmac_sha512, SHA512};
|
use zerotier_core_crypto::hash::{hmac_sha512, SHA512};
|
||||||
use zerotier_core_crypto::p384::{P384_PUBLIC_KEY_SIZE, P384KeyPair, P384PublicKey};
|
use zerotier_core_crypto::p384::{P384KeyPair, P384PublicKey, P384_PUBLIC_KEY_SIZE};
|
||||||
use zerotier_core_crypto::secret::Secret;
|
use zerotier_core_crypto::secret::Secret;
|
||||||
|
|
||||||
pub const ALGORITHM_C25519: u8 = 0x01;
|
pub const ALGORITHM_C25519: u8 = 0x01;
|
||||||
|
@ -22,14 +22,14 @@ pub const ALGORITHM_ECC_NIST_P384: u8 = 0x02;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HybridKeyPair {
|
pub struct HybridKeyPair {
|
||||||
pub c25519: C25519KeyPair,
|
pub c25519: C25519KeyPair,
|
||||||
pub p384: P384KeyPair
|
pub p384: P384KeyPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HybridKeyPair {
|
impl HybridKeyPair {
|
||||||
pub fn generate() -> HybridKeyPair {
|
pub fn generate() -> HybridKeyPair {
|
||||||
Self {
|
Self {
|
||||||
c25519: C25519KeyPair::generate(),
|
c25519: C25519KeyPair::generate(),
|
||||||
p384: P384KeyPair::generate()
|
p384: P384KeyPair::generate(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ unsafe impl Send for HybridKeyPair {}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HybridPublicKey {
|
pub struct HybridPublicKey {
|
||||||
pub c25519: Option<[u8; C25519_PUBLIC_KEY_SIZE]>,
|
pub c25519: Option<[u8; C25519_PUBLIC_KEY_SIZE]>,
|
||||||
pub p384: Option<P384PublicKey>
|
pub p384: Option<P384PublicKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HybridPublicKey {
|
impl HybridPublicKey {
|
||||||
|
|
|
@ -17,6 +17,8 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use zerotier_core_crypto::c25519::*;
|
use zerotier_core_crypto::c25519::*;
|
||||||
use zerotier_core_crypto::hash::{hmac_sha512, SHA384, SHA384_HASH_SIZE, SHA512, SHA512_HASH_SIZE};
|
use zerotier_core_crypto::hash::{hmac_sha512, SHA384, SHA384_HASH_SIZE, SHA512, SHA512_HASH_SIZE};
|
||||||
use zerotier_core_crypto::hex;
|
use zerotier_core_crypto::hex;
|
||||||
|
@ -26,9 +28,9 @@ use zerotier_core_crypto::secret::Secret;
|
||||||
|
|
||||||
use crate::error::{InvalidFormatError, InvalidParameterError};
|
use crate::error::{InvalidFormatError, InvalidParameterError};
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::util::pool::{Pool, Pooled, PoolFactory};
|
use crate::util::pool::{Pool, PoolFactory, Pooled};
|
||||||
use crate::vl1::Address;
|
|
||||||
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;
|
||||||
|
|
||||||
/// Curve25519 and Ed25519
|
/// Curve25519 and Ed25519
|
||||||
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
||||||
|
@ -40,7 +42,8 @@ pub const IDENTITY_ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
||||||
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
||||||
|
|
||||||
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
||||||
pub const MAX_MARSHAL_SIZE: usize = 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 + 16;
|
pub const MAX_MARSHAL_SIZE: usize =
|
||||||
|
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 + 16;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IdentityP384Secret {
|
pub struct IdentityP384Secret {
|
||||||
|
@ -70,9 +73,12 @@ pub struct Identity {
|
||||||
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||||
pub p384: Option<IdentityP384Public>,
|
pub p384: Option<IdentityP384Public>,
|
||||||
pub secret: Option<IdentitySecret>,
|
pub secret: Option<IdentitySecret>,
|
||||||
pub fingerprint: [u8; SHA512_HASH_SIZE]
|
pub fingerprint: [u8; SHA512_HASH_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd, Deserialize, Serialize)]
|
||||||
|
pub struct NetworkId(pub u64);
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A], b: &[u8; B]) -> [u8; S] {
|
fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A], b: &[u8; B]) -> [u8; S] {
|
||||||
assert_eq!(A + B, S);
|
assert_eq!(A + B, S);
|
||||||
|
@ -129,12 +135,8 @@ impl Identity {
|
||||||
c25519: c25519_pub,
|
c25519: c25519_pub,
|
||||||
ed25519: ed25519_pub,
|
ed25519: ed25519_pub,
|
||||||
p384: None,
|
p384: None,
|
||||||
secret: Some(IdentitySecret {
|
secret: Some(IdentitySecret { c25519, ed25519, p384: None }),
|
||||||
c25519,
|
fingerprint: [0_u8; 64], // replaced in upgrade()
|
||||||
ed25519,
|
|
||||||
p384: None,
|
|
||||||
}),
|
|
||||||
fingerprint: [0_u8; 64] // replaced in upgrade()
|
|
||||||
};
|
};
|
||||||
assert!(id.upgrade().is_ok());
|
assert!(id.upgrade().is_ok());
|
||||||
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
|
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
|
||||||
|
@ -176,10 +178,7 @@ impl Identity {
|
||||||
ecdsa_self_signature,
|
ecdsa_self_signature,
|
||||||
ed25519_self_signature,
|
ed25519_self_signature,
|
||||||
});
|
});
|
||||||
let _ = self.secret.as_mut().unwrap().p384.insert(IdentityP384Secret {
|
let _ = self.secret.as_mut().unwrap().p384.insert(IdentityP384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa });
|
||||||
ecdh: p384_ecdh,
|
|
||||||
ecdsa: p384_ecdsa,
|
|
||||||
});
|
|
||||||
self.fingerprint = SHA512::hash(self_sign_buf.as_slice());
|
self.fingerprint = SHA512::hash(self_sign_buf.as_slice());
|
||||||
|
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
@ -251,9 +250,7 @@ impl Identity {
|
||||||
// for the final result to be technically FIPS compliant. Non-FIPS algorithm secrets are considered
|
// for the final result to be technically FIPS compliant. Non-FIPS algorithm secrets are considered
|
||||||
// a salt in the HMAC(salt, key) HKDF construction.
|
// a salt in the HMAC(salt, key) HKDF construction.
|
||||||
if secret.p384.is_some() && other.p384.is_some() {
|
if secret.p384.is_some() && other.p384.is_some() {
|
||||||
secret.p384.as_ref().unwrap().ecdh.agree(&other.p384.as_ref().unwrap().ecdh).map(|p384_secret| {
|
secret.p384.as_ref().unwrap().ecdh.agree(&other.p384.as_ref().unwrap().ecdh).map(|p384_secret| Secret(hmac_sha512(&c25519_secret.0, &p384_secret.0)))
|
||||||
Secret(hmac_sha512(&c25519_secret.0, &p384_secret.0))
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Some(c25519_secret)
|
Some(c25519_secret)
|
||||||
}
|
}
|
||||||
|
@ -291,7 +288,8 @@ impl Identity {
|
||||||
|
|
||||||
/// Verify a signature against this identity.
|
/// Verify a signature against this identity.
|
||||||
pub fn verify(&self, msg: &[u8], mut signature: &[u8]) -> bool {
|
pub fn verify(&self, msg: &[u8], mut signature: &[u8]) -> bool {
|
||||||
if signature.len() == 96 { // legacy ed25519-only signature with hash included
|
if signature.len() == 96 {
|
||||||
|
// legacy ed25519-only signature with hash included
|
||||||
ed25519_verify(&self.ed25519, signature, msg)
|
ed25519_verify(&self.ed25519, signature, msg)
|
||||||
} else if signature.len() > 1 {
|
} else if signature.len() > 1 {
|
||||||
let algorithms = signature[0];
|
let algorithms = signature[0];
|
||||||
|
@ -519,7 +517,7 @@ impl Identity {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
fingerprint: sha.finish()
|
fingerprint: sha.finish(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,46 +530,42 @@ impl Identity {
|
||||||
let p384 = self.p384.as_ref().unwrap();
|
let p384 = self.p384.as_ref().unwrap();
|
||||||
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
||||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||||
format!("{}:0:{}{}:{}{}:2:{}:{}",
|
format!(
|
||||||
|
"{}:0:{}{}:{}{}:2:{}:{}",
|
||||||
self.address.to_string(),
|
self.address.to_string(),
|
||||||
hex::to_string(&self.c25519),
|
hex::to_string(&self.c25519),
|
||||||
hex::to_string(&self.ed25519),
|
hex::to_string(&self.ed25519),
|
||||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||||
hex::to_string(&secret.ed25519.secret_bytes().0),
|
hex::to_string(&secret.ed25519.secret_bytes().0),
|
||||||
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD),
|
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD),
|
||||||
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD))
|
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("{}:0:{}{}:{}{}",
|
format!("{}:0:{}{}:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), hex::to_string(&secret.c25519.secret_bytes().0), hex::to_string(&secret.ed25519.secret_bytes().0))
|
||||||
self.address.to_string(),
|
|
||||||
hex::to_string(&self.c25519),
|
|
||||||
hex::to_string(&self.ed25519),
|
|
||||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
|
||||||
hex::to_string(&secret.ed25519.secret_bytes().0))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.p384.as_ref().map_or_else(|| {
|
self.p384.as_ref().map_or_else(
|
||||||
format!("{}:0:{}{}",
|
|| format!("{}:0:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519)),
|
||||||
self.address.to_string(),
|
|p384| {
|
||||||
hex::to_string(&self.c25519),hex::to_string(&self.ed25519))
|
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||||
}, |p384| {
|
format!("{}:0:{}{}::2:{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
||||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
},
|
||||||
format!("{}:0:{}{}::2:{}",
|
)
|
||||||
self.address.to_string(),
|
|
||||||
hex::to_string(&self.c25519),
|
|
||||||
hex::to_string(&self.ed25519),
|
|
||||||
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 { self.to_string_with_options(IDENTITY_ALGORITHM_ALL, true) }
|
pub fn to_secret_string(&self) -> String {
|
||||||
|
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Identity {
|
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 { self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false) }
|
fn to_string(&self) -> String {
|
||||||
|
self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Identity {
|
impl FromStr for Identity {
|
||||||
|
@ -616,7 +610,8 @@ impl FromStr for Identity {
|
||||||
ptr += 1;
|
ptr += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let keys = [hex::from_string(keys[0].unwrap_or("")), hex::from_string(keys[1].unwrap_or("")), base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()), base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new())];
|
let keys =
|
||||||
|
[hex::from_string(keys[0].unwrap_or("")), hex::from_string(keys[1].unwrap_or("")), base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()), base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new())];
|
||||||
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
|
@ -697,30 +692,38 @@ impl FromStr for Identity {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fingerprint: sha.finish()
|
fingerprint: sha.finish(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Identity {
|
impl PartialEq for Identity {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool { self.fingerprint == other.fingerprint }
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.fingerprint == other.fingerprint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Identity {}
|
impl Eq for Identity {}
|
||||||
|
|
||||||
impl Ord for Identity {
|
impl Ord for Identity {
|
||||||
fn cmp(&self, other: &Self) -> Ordering { self.address.cmp(&other.address).then_with(|| self.fingerprint.cmp(&other.fingerprint)) }
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.address.cmp(&other.address).then_with(|| self.fingerprint.cmp(&other.fingerprint))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Identity {
|
impl PartialOrd for Identity {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Identity {
|
impl Hash for Identity {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) { state.write_u64(self.address.to_u64()) }
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
state.write_u64(self.address.to_u64())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ADDRESS_DERIVATION_HASH_MEMORY_SIZE: usize = 2097152;
|
const ADDRESS_DERIVATION_HASH_MEMORY_SIZE: usize = 2097152;
|
||||||
|
@ -767,19 +770,25 @@ struct AddressDerivationMemory(*mut u8);
|
||||||
|
|
||||||
impl AddressDerivationMemory {
|
impl AddressDerivationMemory {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_memory(&mut self) -> *mut u8 { self.0 }
|
fn get_memory(&mut self) -> *mut u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AddressDerivationMemory {
|
impl Drop for AddressDerivationMemory {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn drop(&mut self) { unsafe { dealloc(self.0, Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) }; }
|
fn drop(&mut self) {
|
||||||
|
unsafe { dealloc(self.0, Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AddressDerivationMemoryFactory;
|
struct AddressDerivationMemoryFactory;
|
||||||
|
|
||||||
impl PoolFactory<AddressDerivationMemory> for AddressDerivationMemoryFactory {
|
impl PoolFactory<AddressDerivationMemory> for AddressDerivationMemoryFactory {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn create(&self) -> AddressDerivationMemory { AddressDerivationMemory(unsafe { alloc(Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) }) }
|
fn create(&self) -> AddressDerivationMemory {
|
||||||
|
AddressDerivationMemory(unsafe { alloc(Layout::from_size_align(ADDRESS_DERIVATION_HASH_MEMORY_SIZE, 8).unwrap()) })
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn reset(&self, _: &mut AddressDerivationMemory) {}
|
fn reset(&self, _: &mut AddressDerivationMemory) {}
|
||||||
|
@ -799,10 +808,10 @@ pub(crate) fn purge_verification_memory_pool() {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use zerotier_core_crypto::hex;
|
use zerotier_core_crypto::hex;
|
||||||
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
|
||||||
|
|
||||||
const GOOD_V0_IDENTITIES: [&'static str; 10] = [
|
const GOOD_V0_IDENTITIES: [&'static str; 10] = [
|
||||||
"51ef313c3a:0:79fee239cf79833be3a9068565661dc33e04759fa0f7e2218d10f1a51d441f1bf71332eba26dfc3755ce60e14650fe68dede66cf145e429972a7f51e026374de:6d12b1c5e0eae3983a5ee5872fa9061963d9e2f8cdd85adab54bdec4bd67f538cafc91b8b5b93fca658a630aab030ec10d66235f2443ccf362c55c41ae01b46e",
|
"51ef313c3a:0:79fee239cf79833be3a9068565661dc33e04759fa0f7e2218d10f1a51d441f1bf71332eba26dfc3755ce60e14650fe68dede66cf145e429972a7f51e026374de:6d12b1c5e0eae3983a5ee5872fa9061963d9e2f8cdd85adab54bdec4bd67f538cafc91b8b5b93fca658a630aab030ec10d66235f2443ccf362c55c41ae01b46e",
|
||||||
|
@ -814,7 +823,7 @@ mod tests {
|
||||||
"c5bdb4a6a6:0:e0a8575bc0277ecf59aaa4a2724acc55554151fff510c8211b0b863398a04224ed918c16405552336ad4c4da3b98eb6224574f1cacaa69e19cdfde184fd9292d:0d45f17d73337cc1898f7be6aae54a050b39ed0259b608b80619c3f898caf8a3a48ae56e51c3d7d8426ef295c0628d81b1a99616a3ed28da49bf8f81e1bec863",
|
"c5bdb4a6a6:0:e0a8575bc0277ecf59aaa4a2724acc55554151fff510c8211b0b863398a04224ed918c16405552336ad4c4da3b98eb6224574f1cacaa69e19cdfde184fd9292d:0d45f17d73337cc1898f7be6aae54a050b39ed0259b608b80619c3f898caf8a3a48ae56e51c3d7d8426ef295c0628d81b1a99616a3ed28da49bf8f81e1bec863",
|
||||||
"c622dedbe4:0:0cfec354be26b4b2fa9ea29166b4acaf9476d169d51fd741d7e4cd9de93f321c6b80628c50da566d0a6b07d58d651eba8af63e0edc36202c05c3f97c828788ad:31a75d2b46c1b0f33228d3869bc807b42b371bbcef4c96f7232a27c62f56397568558f115d9cff3d6f7b8efb726a1ea49a591662d9aacd1049e295cbb0cf3197",
|
"c622dedbe4:0:0cfec354be26b4b2fa9ea29166b4acaf9476d169d51fd741d7e4cd9de93f321c6b80628c50da566d0a6b07d58d651eba8af63e0edc36202c05c3f97c828788ad:31a75d2b46c1b0f33228d3869bc807b42b371bbcef4c96f7232a27c62f56397568558f115d9cff3d6f7b8efb726a1ea49a591662d9aacd1049e295cbb0cf3197",
|
||||||
"e28829ab3c:0:8e36c4f6cb524cae6bbea5f26dadb601a76f2a3793961779317365effb17ac6cde4ff4149a1b3480fbdbdbabfe62e1f264e764f95540b63158d1ea8b1eb0df5b:957508a7546df18784cd285da2e6216e4265906c6c7fba9a895f29a724d63a2e0268128c0c9c2cc304c8c3304863cdfe437a7b93b12dc778c0372a116088e9cd",
|
"e28829ab3c:0:8e36c4f6cb524cae6bbea5f26dadb601a76f2a3793961779317365effb17ac6cde4ff4149a1b3480fbdbdbabfe62e1f264e764f95540b63158d1ea8b1eb0df5b:957508a7546df18784cd285da2e6216e4265906c6c7fba9a895f29a724d63a2e0268128c0c9c2cc304c8c3304863cdfe437a7b93b12dc778c0372a116088e9cd",
|
||||||
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626"
|
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626",
|
||||||
];
|
];
|
||||||
const GOOD_V1_IDENTITIES: [&'static str; 10] = [
|
const GOOD_V1_IDENTITIES: [&'static str; 10] = [
|
||||||
"a8f6e0566e:0:a13a6394de205384eb75eb62179ef11423295c5ecdccfeed7f2eff6c7a74f8059c99eed164c5dfaf2a4cf395ec7b72b68ee1c3c31916de4bc57c07abfe77f9c2::2:A1Cj2O0hKLlhDQ6guCCv5H1UgzbegZwse0iqTaaZov9LpKifyKH0e1VzmHrPmoKcvgJyzI-BAqRQzBiUjScXIjojneNKOywc0Gvq-zeDCYPcXN393xi3q25mB3ud9iEN-GN6wiXPWFjHy-CBD9tGDJzr-G3ZJZvrdiLGT5rZ5W2cZtx8ORYnp9L9HJJOeb8qgdfVr67B5pT9jPsxSsw8P4qlzFFOlX2WN9Hvvu0TO6S_N4yq173deyr-f-ehcBFiBXsSG96p44oU4uRRBEDZWhzHDuD22Vw8PhsB8mko9IRqVXCGbvlaKJ0vyAZ_PyVRM9n_Z-HAEvLveAT-f61mh4YP",
|
"a8f6e0566e:0:a13a6394de205384eb75eb62179ef11423295c5ecdccfeed7f2eff6c7a74f8059c99eed164c5dfaf2a4cf395ec7b72b68ee1c3c31916de4bc57c07abfe77f9c2::2:A1Cj2O0hKLlhDQ6guCCv5H1UgzbegZwse0iqTaaZov9LpKifyKH0e1VzmHrPmoKcvgJyzI-BAqRQzBiUjScXIjojneNKOywc0Gvq-zeDCYPcXN393xi3q25mB3ud9iEN-GN6wiXPWFjHy-CBD9tGDJzr-G3ZJZvrdiLGT5rZ5W2cZtx8ORYnp9L9HJJOeb8qgdfVr67B5pT9jPsxSsw8P4qlzFFOlX2WN9Hvvu0TO6S_N4yq173deyr-f-ehcBFiBXsSG96p44oU4uRRBEDZWhzHDuD22Vw8PhsB8mko9IRqVXCGbvlaKJ0vyAZ_PyVRM9n_Z-HAEvLveAT-f61mh4YP",
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem::{MaybeUninit, size_of, transmute_copy, zeroed};
|
use std::mem::{size_of, transmute_copy, zeroed, MaybeUninit};
|
||||||
use std::net::{IpAddr, Ipv6Addr};
|
use std::net::{IpAddr, Ipv6Addr};
|
||||||
use std::ptr::{copy_nonoverlapping, null, slice_from_raw_parts, write_bytes};
|
use std::ptr::{copy_nonoverlapping, null, slice_from_raw_parts, write_bytes};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use winapi::um::winsock2 as winsock2;
|
use winapi::um::winsock2;
|
||||||
|
|
||||||
use crate::error::InvalidFormatError;
|
use crate::error::InvalidFormatError;
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
|
@ -74,18 +74,24 @@ pub union InetAddress {
|
||||||
|
|
||||||
impl Clone for InetAddress {
|
impl Clone for InetAddress {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn clone(&self) -> Self { unsafe { transmute_copy(self) } }
|
fn clone(&self) -> Self {
|
||||||
|
unsafe { transmute_copy(self) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InetAddress {
|
impl Default for InetAddress {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> InetAddress { unsafe { zeroed() } }
|
fn default() -> InetAddress {
|
||||||
|
unsafe { zeroed() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InetAddress {
|
impl InetAddress {
|
||||||
/// Get a new zero/nil InetAddress.
|
/// Get a new zero/nil InetAddress.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> InetAddress { unsafe { zeroed() } }
|
pub fn new() -> InetAddress {
|
||||||
|
unsafe { zeroed() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct from IP and port.
|
/// Construct from IP and port.
|
||||||
/// If the IP is not either 4 or 16 bytes in length, a nil/0 InetAddress is returned.
|
/// If the IP is not either 4 or 16 bytes in length, a nil/0 InetAddress is returned.
|
||||||
|
@ -100,7 +106,9 @@ impl InetAddress {
|
||||||
|
|
||||||
/// Zero the contents of this InetAddress.
|
/// Zero the contents of this InetAddress.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn zero(&mut self) { unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) }; }
|
pub fn zero(&mut self) {
|
||||||
|
unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) };
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an instance of 127.0.0.1/port
|
/// Get an instance of 127.0.0.1/port
|
||||||
pub fn ipv4_loopback(port: u16) -> InetAddress {
|
pub fn ipv4_loopback(port: u16) -> InetAddress {
|
||||||
|
@ -138,19 +146,35 @@ impl InetAddress {
|
||||||
|
|
||||||
/// Returns true if this InetAddress is the nil value (zero).
|
/// Returns true if this InetAddress is the nil value (zero).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_nil(&self) -> bool { unsafe { self.sa.sa_family == 0 } }
|
pub fn is_nil(&self) -> bool {
|
||||||
|
unsafe { self.sa.sa_family == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if this is an IPv4 address.
|
/// Check if this is an IPv4 address.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_ipv4(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET } }
|
pub fn is_ipv4(&self) -> bool {
|
||||||
|
unsafe { self.sa.sa_family as u8 == AF_INET }
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if this is an IPv6 address.
|
/// Check if this is an IPv6 address.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_ipv6(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET6 } }
|
pub fn is_ipv6(&self) -> bool {
|
||||||
|
unsafe { self.sa.sa_family as u8 == AF_INET6 }
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn family(&self) -> u8 { unsafe { self.sa.sa_family } }
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn family(&self) -> u8 {
|
||||||
|
unsafe { self.sa.sa_family }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn family(&self) -> u16 {
|
||||||
|
unsafe { self.sa.sa_family }
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a pointer to the C "sockaddr" structure and the size of the returned structure in bytes.
|
/// Get a pointer to the C "sockaddr" structure and the size of the returned structure in bytes.
|
||||||
/// This is useful for interacting with C-level socket APIs. This returns a null pointer if
|
/// This is useful for interacting with C-level socket APIs. This returns a null pointer if
|
||||||
|
@ -161,7 +185,7 @@ impl InetAddress {
|
||||||
match self.sa.sa_family as u8 {
|
match self.sa.sa_family as u8 {
|
||||||
AF_INET => ((&self.sin as *const sockaddr_in).cast(), size_of::<sockaddr_in>()),
|
AF_INET => ((&self.sin as *const sockaddr_in).cast(), size_of::<sockaddr_in>()),
|
||||||
AF_INET6 => ((&self.sin6 as *const sockaddr_in6).cast(), size_of::<sockaddr_in6>()),
|
AF_INET6 => ((&self.sin6 as *const sockaddr_in6).cast(), size_of::<sockaddr_in6>()),
|
||||||
_ => (null(), 0)
|
_ => (null(), 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +247,7 @@ impl InetAddress {
|
||||||
u16::from_be(match self.sa.sa_family as u8 {
|
u16::from_be(match self.sa.sa_family as u8 {
|
||||||
AF_INET => self.sin.sin_port as u16,
|
AF_INET => self.sin.sin_port as u16,
|
||||||
AF_INET6 => self.sin6.sin6_port as u16,
|
AF_INET6 => self.sin6.sin6_port as u16,
|
||||||
_ => 0
|
_ => 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,42 +280,48 @@ impl InetAddress {
|
||||||
0x0a => IpScope::Private, // 10.0.0.0/8
|
0x0a => IpScope::Private, // 10.0.0.0/8
|
||||||
0x7f => IpScope::Loopback, // 127.0.0.0/8
|
0x7f => IpScope::Loopback, // 127.0.0.0/8
|
||||||
0x64 => {
|
0x64 => {
|
||||||
if (ip & 0xffc00000) == 0x64400000 { // 100.64.0.0/10
|
if (ip & 0xffc00000) == 0x64400000 {
|
||||||
|
// 100.64.0.0/10
|
||||||
IpScope::Private
|
IpScope::Private
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xa9 => {
|
0xa9 => {
|
||||||
if (ip & 0xffff0000) == 0xa9fe0000 { // 169.254.0.0/16
|
if (ip & 0xffff0000) == 0xa9fe0000 {
|
||||||
|
// 169.254.0.0/16
|
||||||
IpScope::LinkLocal
|
IpScope::LinkLocal
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xac => {
|
0xac => {
|
||||||
if (ip & 0xfff00000) == 0xac100000 { // 172.16.0.0/12
|
if (ip & 0xfff00000) == 0xac100000 {
|
||||||
|
// 172.16.0.0/12
|
||||||
IpScope::Private
|
IpScope::Private
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xc0 => {
|
0xc0 => {
|
||||||
if (ip & 0xffff0000) == 0xc0a80000 || (ip & 0xffffff00) == 0xc0000200 { // 192.168.0.0/16 and 192.0.2.0/24
|
if (ip & 0xffff0000) == 0xc0a80000 || (ip & 0xffffff00) == 0xc0000200 {
|
||||||
|
// 192.168.0.0/16 and 192.0.2.0/24
|
||||||
IpScope::Private
|
IpScope::Private
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xc6 => {
|
0xc6 => {
|
||||||
if (ip & 0xfffe0000) == 0xc6120000 || (ip & 0xffffff00) == 0xc6336400 { // 198.18.0.0/15 and 198.51.100.0/24
|
if (ip & 0xfffe0000) == 0xc6120000 || (ip & 0xffffff00) == 0xc6336400 {
|
||||||
|
// 198.18.0.0/15 and 198.51.100.0/24
|
||||||
IpScope::Private
|
IpScope::Private
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xcb => {
|
0xcb => {
|
||||||
if (ip & 0xffffff00) == 0xcb007100 { // 203.0.113.0/24
|
if (ip & 0xffffff00) == 0xcb007100 {
|
||||||
|
// 203.0.113.0/24
|
||||||
IpScope::Private
|
IpScope::Private
|
||||||
} else {
|
} else {
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
|
@ -310,13 +340,15 @@ impl InetAddress {
|
||||||
0x33_u8, // 51.0.0.0/8 (UK Department of Social Security)
|
0x33_u8, // 51.0.0.0/8 (UK Department of Social Security)
|
||||||
0x37_u8, // 55.0.0.0/8 (US DoD)
|
0x37_u8, // 55.0.0.0/8 (US DoD)
|
||||||
0x38_u8, // 56.0.0.0/8 (US Postal Service)
|
0x38_u8, // 56.0.0.0/8 (US Postal Service)
|
||||||
].contains(&class_a) {
|
]
|
||||||
|
.contains(&class_a)
|
||||||
|
{
|
||||||
IpScope::PseudoPrivate
|
IpScope::PseudoPrivate
|
||||||
} else {
|
} else {
|
||||||
match ip >> 28 {
|
match ip >> 28 {
|
||||||
0xe => IpScope::Multicast, // 224.0.0.0/4
|
0xe => IpScope::Multicast, // 224.0.0.0/4
|
||||||
0xf => IpScope::Private, // 240.0.0.0/4 ("reserved," usually unusable)
|
0xf => IpScope::Private, // 240.0.0.0/4 ("reserved," usually unusable)
|
||||||
_ => IpScope::Global
|
_ => IpScope::Global,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,7 +388,7 @@ impl InetAddress {
|
||||||
}
|
}
|
||||||
IpScope::Global
|
IpScope::Global
|
||||||
}
|
}
|
||||||
_ => IpScope::None
|
_ => IpScope::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,7 +402,7 @@ impl InetAddress {
|
||||||
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
||||||
}
|
}
|
||||||
AF_INET6 => Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string(),
|
AF_INET6 => Ipv6Addr::from(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).to_string(),
|
||||||
_ => String::from("(null)")
|
_ => String::from("(null)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,7 +426,7 @@ impl InetAddress {
|
||||||
b[18] = *(&self.sin6.sin6_port as *const u16).cast::<u8>().offset(1);
|
b[18] = *(&self.sin6.sin6_port as *const u16).cast::<u8>().offset(1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => buf.append_u8(0)
|
_ => buf.append_u8(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,33 +470,37 @@ impl FromStr for InetAddress {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let mut addr = InetAddress::new();
|
let mut addr = InetAddress::new();
|
||||||
let (ip_str, port) = s.find('/').map_or_else(|| {
|
let (ip_str, port) = s.find('/').map_or_else(
|
||||||
(s, 0)
|
|| (s, 0),
|
||||||
}, |pos| {
|
|pos| {
|
||||||
let ss = s.split_at(pos);
|
let ss = s.split_at(pos);
|
||||||
let mut port_str = ss.1;
|
let mut port_str = ss.1;
|
||||||
if port_str.starts_with('/') {
|
if port_str.starts_with('/') {
|
||||||
port_str = &port_str[1..];
|
port_str = &port_str[1..];
|
||||||
}
|
}
|
||||||
(ss.0, u16::from_str_radix(port_str, 10).unwrap_or(0).to_be())
|
(ss.0, u16::from_str_radix(port_str, 10).unwrap_or(0).to_be())
|
||||||
});
|
},
|
||||||
IpAddr::from_str(ip_str).map_or_else(|_| Err(InvalidFormatError), |ip| {
|
);
|
||||||
unsafe {
|
IpAddr::from_str(ip_str).map_or_else(
|
||||||
match ip {
|
|_| Err(InvalidFormatError),
|
||||||
IpAddr::V4(v4) => {
|
|ip| {
|
||||||
addr.sin.sin_family = AF_INET.into();
|
unsafe {
|
||||||
addr.sin.sin_port = port.into();
|
match ip {
|
||||||
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
|
IpAddr::V4(v4) => {
|
||||||
}
|
addr.sin.sin_family = AF_INET.into();
|
||||||
IpAddr::V6(v6) => {
|
addr.sin.sin_port = port.into();
|
||||||
addr.sin6.sin6_family = AF_INET6.into();
|
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
|
||||||
addr.sin6.sin6_port = port.into();
|
}
|
||||||
copy_nonoverlapping(v6.octets().as_ptr(), (&mut (addr.sin6.sin6_addr) as *mut in6_addr).cast(), 16);
|
IpAddr::V6(v6) => {
|
||||||
|
addr.sin6.sin6_family = AF_INET6.into();
|
||||||
|
addr.sin6.sin6_port = port.into();
|
||||||
|
copy_nonoverlapping(v6.octets().as_ptr(), (&mut (addr.sin6.sin6_addr) as *mut in6_addr).cast(), 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(addr)
|
||||||
Ok(addr)
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +509,7 @@ impl PartialEq for InetAddress {
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.sa.sa_family == other.sa.sa_family {
|
if self.sa.sa_family == other.sa.sa_family {
|
||||||
match self.sa.sa_family as u8 {
|
match self.sa.sa_family as u8 {
|
||||||
AF_INET => { self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr }
|
AF_INET => self.sin.sin_port == other.sin.sin_port && self.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr,
|
||||||
AF_INET6 => {
|
AF_INET6 => {
|
||||||
if self.sin6.sin6_port == other.sin6.sin6_port {
|
if self.sin6.sin6_port == other.sin6.sin6_port {
|
||||||
(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).eq(&*(&(other.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>())
|
(*(&(self.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>()).eq(&*(&(other.sin6.sin6_addr) as *const in6_addr).cast::<[u8; 16]>())
|
||||||
|
@ -481,7 +517,7 @@ impl PartialEq for InetAddress {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => true
|
_ => true,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -494,7 +530,9 @@ impl Eq for InetAddress {}
|
||||||
|
|
||||||
impl PartialOrd for InetAddress {
|
impl PartialOrd for InetAddress {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually implement Ord to ensure consistent sort order across platforms, since we don't know exactly
|
// Manually implement Ord to ensure consistent sort order across platforms, since we don't know exactly
|
||||||
|
@ -504,9 +542,7 @@ impl Ord for InetAddress {
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.sa.sa_family == other.sa.sa_family {
|
if self.sa.sa_family == other.sa.sa_family {
|
||||||
match self.sa.sa_family as u8 {
|
match self.sa.sa_family as u8 {
|
||||||
0 => {
|
0 => Ordering::Equal,
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
AF_INET => {
|
AF_INET => {
|
||||||
let ip_ordering = u32::from_be(self.sin.sin_addr.s_addr as u32).cmp(&u32::from_be(other.sin.sin_addr.s_addr as u32));
|
let ip_ordering = u32::from_be(self.sin.sin_addr.s_addr as u32).cmp(&u32::from_be(other.sin.sin_addr.s_addr as u32));
|
||||||
if ip_ordering == Ordering::Equal {
|
if ip_ordering == Ordering::Equal {
|
||||||
|
@ -532,9 +568,7 @@ impl Ord for InetAddress {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.sa.sa_family as u8 {
|
match self.sa.sa_family as u8 {
|
||||||
0 => {
|
0 => Ordering::Less,
|
||||||
Ordering::Less
|
|
||||||
}
|
|
||||||
AF_INET => {
|
AF_INET => {
|
||||||
if other.sa.sa_family as u8 == AF_INET6 {
|
if other.sa.sa_family as u8 == AF_INET6 {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
use crate::error::InvalidFormatError;
|
use crate::error::InvalidFormatError;
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
|
@ -19,7 +19,9 @@ pub struct MAC(NonZeroU64);
|
||||||
|
|
||||||
impl MAC {
|
impl MAC {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_u64(i: u64) -> Option<MAC> { NonZeroU64::new(i & 0xffffffffffff).map(|i| MAC(i)) }
|
pub fn from_u64(i: u64) -> Option<MAC> {
|
||||||
|
NonZeroU64::new(i & 0xffffffffffff).map(|i| MAC(i))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
||||||
|
@ -42,7 +44,9 @@ impl MAC {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u64(&self) -> u64 { self.0.get() }
|
pub fn to_u64(&self) -> u64 {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||||
|
|
|
@ -6,31 +6,33 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod inetaddress;
|
|
||||||
pub mod endpoint;
|
pub mod endpoint;
|
||||||
pub mod identity;
|
pub mod identity;
|
||||||
|
pub mod inetaddress;
|
||||||
|
|
||||||
#[allow(unused)]
|
pub(crate) mod address;
|
||||||
pub(crate) mod protocol;
|
pub(crate) mod dictionary;
|
||||||
|
pub(crate) mod fragmentedpacket;
|
||||||
|
pub(crate) mod hybridkey;
|
||||||
|
pub(crate) mod mac;
|
||||||
pub(crate) mod node;
|
pub(crate) mod node;
|
||||||
pub(crate) mod path;
|
pub(crate) mod path;
|
||||||
pub(crate) mod peer;
|
pub(crate) mod peer;
|
||||||
pub(crate) mod dictionary;
|
#[allow(unused)]
|
||||||
pub(crate) mod address;
|
pub(crate) mod protocol;
|
||||||
pub(crate) mod mac;
|
|
||||||
pub(crate) mod fragmentedpacket;
|
|
||||||
pub(crate) mod whoisqueue;
|
|
||||||
pub(crate) mod symmetricsecret;
|
pub(crate) mod symmetricsecret;
|
||||||
pub(crate) mod hybridkey;
|
pub(crate) mod system_interface;
|
||||||
|
pub(crate) mod whoisqueue;
|
||||||
|
|
||||||
pub use address::Address;
|
pub use address::Address;
|
||||||
pub use mac::MAC;
|
|
||||||
pub use identity::Identity;
|
|
||||||
pub use endpoint::Endpoint;
|
|
||||||
pub use dictionary::Dictionary;
|
pub use dictionary::Dictionary;
|
||||||
|
pub use endpoint::Endpoint;
|
||||||
|
pub use identity::Identity;
|
||||||
pub use inetaddress::InetAddress;
|
pub use inetaddress::InetAddress;
|
||||||
pub use peer::Peer;
|
pub use mac::MAC;
|
||||||
pub use path::Path;
|
|
||||||
pub use node::{Node, SystemInterface};
|
pub use node::{Node, SystemInterface};
|
||||||
|
pub use path::Path;
|
||||||
|
pub use peer::Peer;
|
||||||
|
pub use system_interface::VL1SystemInterface;
|
||||||
|
|
||||||
pub use protocol::{PACKET_SIZE_MAX, PACKET_FRAGMENT_COUNT_MAX};
|
pub use protocol::{PACKET_FRAGMENT_COUNT_MAX, PACKET_SIZE_MAX};
|
||||||
|
|
|
@ -14,15 +14,15 @@ use std::time::Duration;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::{PacketBuffer, PacketBufferFactory, PacketBufferPool};
|
|
||||||
use crate::error::InvalidParameterError;
|
use crate::error::InvalidParameterError;
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::util::gate::IntervalGate;
|
use crate::util::gate::IntervalGate;
|
||||||
use crate::vl1::{Address, Endpoint, Identity};
|
|
||||||
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};
|
||||||
|
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.
|
||||||
///
|
///
|
||||||
|
@ -180,10 +180,14 @@ 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 { self.buffer_pool.get() }
|
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
||||||
|
self.buffer_pool.get()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a peer by address.
|
/// Get a peer by address.
|
||||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> { self.peers.get(&a).map(|peer| peer.value().clone()) }
|
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> {
|
||||||
|
self.peers.get(&a).map(|peer| peer.value().clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all peers currently in the peer cache.
|
/// Get all peers currently in the peer cache.
|
||||||
pub fn peers(&self) -> Vec<Arc<Peer>> {
|
pub fn peers(&self) -> Vec<Arc<Peer>> {
|
||||||
|
@ -239,7 +243,6 @@ impl Node {
|
||||||
path.log_receive_anything(time_ticks);
|
path.log_receive_anything(time_ticks);
|
||||||
|
|
||||||
if fragment_header.is_fragment() {
|
if fragment_header.is_fragment() {
|
||||||
|
|
||||||
if let Some(assembled_packet) = path.receive_fragment(u64::from_ne_bytes(fragment_header.id), fragment_header.fragment_no(), fragment_header.total_fragments(), data, time_ticks) {
|
if let Some(assembled_packet) = path.receive_fragment(u64::from_ne_bytes(fragment_header.id), fragment_header.fragment_no(), fragment_header.total_fragments(), data, time_ticks) {
|
||||||
if let Some(frag0) = assembled_packet.frags[0].as_ref() {
|
if let Some(frag0) = assembled_packet.frags[0].as_ref() {
|
||||||
let packet_header = frag0.struct_at::<PacketHeader>(0);
|
let packet_header = frag0.struct_at::<PacketHeader>(0);
|
||||||
|
@ -255,9 +258,7 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if let Ok(packet_header) = data.struct_at::<PacketHeader>(0) {
|
if let Ok(packet_header) = data.struct_at::<PacketHeader>(0) {
|
||||||
if let Some(source) = Address::from_bytes(&packet_header.src) {
|
if let Some(source) = Address::from_bytes(&packet_header.src) {
|
||||||
if let Some(peer) = self.peer(source) {
|
if let Some(peer) = self.peer(source) {
|
||||||
|
@ -267,9 +268,7 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Forward packets not destined for this node.
|
// Forward packets not destined for this node.
|
||||||
// TODO: need to add check for whether this node should forward. Regular nodes should only forward if a trust relationship exists.
|
// TODO: need to add check for whether this node should forward. Regular nodes should only forward if a trust relationship exists.
|
||||||
|
@ -296,10 +295,14 @@ 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.
|
||||||
pub fn root(&self) -> Option<Arc<Peer>> { self.roots.lock().first().cloned() }
|
pub fn root(&self) -> Option<Arc<Peer>> {
|
||||||
|
self.roots.lock().first().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 { self.roots.lock().iter().any(|p| Arc::as_ptr(p) == (peer as *const Peer)) }
|
pub fn is_peer_root(&self, peer: &Peer) -> bool {
|
||||||
|
self.roots.lock().iter().any(|p| Arc::as_ptr(p) == (peer as *const Peer))
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -10,19 +10,19 @@ use std::collections::HashMap;
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::num::NonZeroI64;
|
use std::num::NonZeroI64;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicI64, Ordering};
|
use std::sync::atomic::{AtomicI64, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use highway::HighwayHash;
|
use highway::HighwayHash;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
||||||
|
|
||||||
use crate::PacketBuffer;
|
|
||||||
use crate::util::{array_range, highwayhasher, U64NoOpHasher};
|
use crate::util::{array_range, highwayhasher, U64NoOpHasher};
|
||||||
use crate::vl1::Endpoint;
|
|
||||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||||
use crate::vl1::node::SystemInterface;
|
use crate::vl1::node::SystemInterface;
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
|
use crate::vl1::Endpoint;
|
||||||
|
use crate::PacketBuffer;
|
||||||
|
|
||||||
/// Keepalive interval for paths in milliseconds.
|
/// Keepalive interval for paths in milliseconds.
|
||||||
pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
||||||
|
@ -59,7 +59,7 @@ impl Path {
|
||||||
Endpoint::Ethernet(m) => (m.to_u64() | 0x0100000000000000) as u128 ^ lsi,
|
Endpoint::Ethernet(m) => (m.to_u64() | 0x0100000000000000) as u128 ^ lsi,
|
||||||
Endpoint::WifiDirect(m) => (m.to_u64() | 0x0200000000000000) as u128 ^ lsi,
|
Endpoint::WifiDirect(m) => (m.to_u64() | 0x0200000000000000) as u128 ^ lsi,
|
||||||
Endpoint::Bluetooth(m) => (m.to_u64() | 0x0400000000000000) as u128 ^ lsi,
|
Endpoint::Bluetooth(m) => (m.to_u64() | 0x0400000000000000) as u128 ^ lsi,
|
||||||
Endpoint::Ip(ip) => ip.ip_as_native_u128().wrapping_sub(lsi), // naked IP has no port
|
Endpoint::Ip(ip) => ip.ip_as_native_u128().wrapping_sub(lsi), // naked IP has no port
|
||||||
Endpoint::IpUdp(ip) => ip.ip_as_native_u128().wrapping_add(lsi), // UDP maintains one path per IP but merely learns the most recent port
|
Endpoint::IpUdp(ip) => ip.ip_as_native_u128().wrapping_add(lsi), // UDP maintains one path per IP but merely learns the most recent port
|
||||||
Endpoint::IpTcp(ip) => ip.ip_as_native_u128().wrapping_sub(crate::util::hash64_noncrypt((ip.port() as u64).wrapping_add(*RANDOM_64BIT_SALT_2)) as u128).wrapping_sub(lsi),
|
Endpoint::IpTcp(ip) => ip.ip_as_native_u128().wrapping_sub(crate::util::hash64_noncrypt((ip.port() as u64).wrapping_add(*RANDOM_64BIT_SALT_2)) as u128).wrapping_sub(lsi),
|
||||||
Endpoint::Http(s) => {
|
Endpoint::Http(s) => {
|
||||||
|
@ -93,19 +93,29 @@ impl Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn endpoint(&self) -> Arc<Endpoint> { self.endpoint.lock().clone() }
|
pub fn endpoint(&self) -> Arc<Endpoint> {
|
||||||
|
self.endpoint.lock().clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn local_socket(&self) -> Option<NonZeroI64> { self.local_socket }
|
pub fn local_socket(&self) -> Option<NonZeroI64> {
|
||||||
|
self.local_socket
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn local_interface(&self) -> Option<NonZeroI64> { self.local_interface }
|
pub fn local_interface(&self) -> Option<NonZeroI64> {
|
||||||
|
self.local_interface
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn last_send_time_ticks(&self) -> i64 { self.last_send_time_ticks.load(Ordering::Relaxed) }
|
pub fn last_send_time_ticks(&self) -> i64 {
|
||||||
|
self.last_send_time_ticks.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn last_receive_time_ticks(&self) -> i64 { self.last_receive_time_ticks.load(Ordering::Relaxed) }
|
pub fn last_receive_time_ticks(&self) -> i64 {
|
||||||
|
self.last_receive_time_ticks.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -152,10 +162,10 @@ impl Path {
|
||||||
if ip_orig.port() != ip.port() {
|
if ip_orig.port() != ip.port() {
|
||||||
replace = true;
|
replace = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
if replace {
|
if replace {
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::num::NonZeroI64;
|
use std::num::NonZeroI64;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
@ -22,15 +22,15 @@ use zerotier_core_crypto::random::{fill_bytes_secure, get_bytes_secure, next_u64
|
||||||
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::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
|
||||||
use crate::util::{array_range, u64_as_bytes};
|
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path};
|
use crate::util::{array_range, u64_as_bytes};
|
||||||
use crate::vl1::hybridkey::{HybridKeyPair, HybridPublicKey};
|
use crate::vl1::hybridkey::{HybridKeyPair, HybridPublicKey};
|
||||||
use crate::vl1::identity::{IDENTITY_ALGORITHM_ALL, IDENTITY_ALGORITHM_X25519};
|
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, InetAddress, Path};
|
||||||
|
use crate::{PacketBuffer, VERSION_MAJOR, VERSION_MINOR, VERSION_PROTO, VERSION_REVISION};
|
||||||
|
|
||||||
/// A remote peer known to this node.
|
/// A remote peer known to this node.
|
||||||
/// Sending-related and receiving-related fields are locked separately since concurrent
|
/// Sending-related and receiving-related fields are locked separately since concurrent
|
||||||
|
@ -140,10 +140,12 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
|
||||||
poly.update(packet_frag0_payload_bytes);
|
poly.update(packet_frag0_payload_bytes);
|
||||||
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| f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
let _ = f.as_ref().map(|f| {
|
||||||
poly.update(f);
|
f.as_bytes_starting_at(FRAGMENT_HEADER_SIZE).map(|f| {
|
||||||
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
poly.update(f);
|
||||||
}));
|
let _ = payload.append_bytes_get_mut(f.len()).map(|b| salsa.crypt(f, b));
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if poly.finish()[0..8].eq(&header.mac) {
|
if poly.finish()[0..8].eq(&header.mac) {
|
||||||
Some(u64::from_ne_bytes(header.id))
|
Some(u64::from_ne_bytes(header.id))
|
||||||
|
@ -211,24 +213,15 @@ 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 { self.message_id_counter.fetch_add(1, Ordering::Relaxed) }
|
pub(crate) fn next_message_id(&self) -> u64 {
|
||||||
|
self.message_id_counter.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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: VL1VirtualInterface>(
|
pub(crate) fn receive<SI: SystemInterface, VI: VL1VirtualInterface>(&self, node: &Node, si: &SI, vi: &VI, time_ticks: i64, source_endpoint: &Endpoint, source_path: &Arc<Path>, header: &PacketHeader, frag0: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option<PacketBuffer>]) {
|
||||||
&self,
|
|
||||||
node: &Node,
|
|
||||||
si: &SI,
|
|
||||||
vi: &VI,
|
|
||||||
time_ticks: i64,
|
|
||||||
source_endpoint: &Endpoint,
|
|
||||||
source_path: &Arc<Path>,
|
|
||||||
header: &PacketHeader,
|
|
||||||
frag0: &Buffer<{ PACKET_SIZE_MAX }>,
|
|
||||||
fragments: &[Option<PacketBuffer>])
|
|
||||||
{
|
|
||||||
let _ = frag0.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| {
|
let _ = frag0.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| {
|
||||||
let mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
|
let mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
|
||||||
|
|
||||||
|
@ -313,10 +306,11 @@ impl Peer {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In debug build check to make sure the next layer (VL2) is complying with the API contract.
|
// In debug build check to make sure the next layer (VL2) is complying with the API contract.
|
||||||
#[cfg(debug)] {
|
#[cfg(debug)]
|
||||||
|
{
|
||||||
if match verb {
|
if match verb {
|
||||||
VERB_VL1_NOP | VERB_VL1_HELLO | VERB_VL1_ERROR | VERB_VL1_OK | VERB_VL1_WHOIS | VERB_VL1_RENDEZVOUS | VERB_VL1_ECHO | VERB_VL1_PUSH_DIRECT_PATHS | VERB_VL1_USER_MESSAGE => true,
|
VERB_VL1_NOP | VERB_VL1_HELLO | VERB_VL1_ERROR | VERB_VL1_OK | VERB_VL1_WHOIS | VERB_VL1_RENDEZVOUS | VERB_VL1_ECHO | VERB_VL1_PUSH_DIRECT_PATHS | VERB_VL1_USER_MESSAGE => true,
|
||||||
_ => false
|
_ => false,
|
||||||
} {
|
} {
|
||||||
panic!("The next layer handled a VL1 packet! It should not do this.");
|
panic!("The next layer handled a VL1 packet! It should not do this.");
|
||||||
}
|
}
|
||||||
|
@ -419,14 +413,15 @@ impl Peer {
|
||||||
/// 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<SI: SystemInterface>(&self, si: &SI, node: &Node, 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(
|
||||||
self.path(node).map_or(None, |p| {
|
|| {
|
||||||
path = Some(p.clone());
|
self.path(node).map_or(None, |p| {
|
||||||
Some(p.endpoint().as_ref().clone())
|
path = Some(p.clone());
|
||||||
})
|
Some(p.endpoint().as_ref().clone())
|
||||||
}, |endpoint| {
|
})
|
||||||
Some(endpoint.clone())
|
},
|
||||||
});
|
|endpoint| Some(endpoint.clone()),
|
||||||
|
);
|
||||||
if destination.is_none() {
|
if destination.is_none() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -519,16 +514,17 @@ impl Peer {
|
||||||
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);
|
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, p.local_socket(), p.local_interface(), &packet) {
|
||||||
p.log_send_anything(time_ticks);
|
p.log_send_anything(time_ticks);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
|
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10;
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use crate::util::buffer::{Buffer, RawObject};
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
use crate::util::buffer::{RawObject, Buffer};
|
|
||||||
|
|
||||||
pub const VERB_VL1_NOP: u8 = 0x00;
|
pub const VERB_VL1_NOP: u8 = 0x00;
|
||||||
pub const VERB_VL1_HELLO: u8 = 0x01;
|
pub const VERB_VL1_HELLO: u8 = 0x01;
|
||||||
|
@ -224,10 +224,14 @@ unsafe impl RawObject for PacketHeader {}
|
||||||
|
|
||||||
impl PacketHeader {
|
impl PacketHeader {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn cipher(&self) -> u8 { self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER }
|
pub fn cipher(&self) -> u8 {
|
||||||
|
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hops(&self) -> u8 { self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS }
|
pub fn hops(&self) -> u8 {
|
||||||
|
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn increment_hops(&mut self) -> u8 {
|
pub fn increment_hops(&mut self) -> u8 {
|
||||||
|
@ -238,10 +242,14 @@ impl PacketHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_fragmented(&self) -> bool { (self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0 }
|
pub fn is_fragmented(&self) -> bool {
|
||||||
|
(self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; PACKET_HEADER_SIZE] { unsafe { &*(self as *const Self).cast::<[u8; PACKET_HEADER_SIZE]>() } }
|
pub fn as_bytes(&self) -> &[u8; PACKET_HEADER_SIZE] {
|
||||||
|
unsafe { &*(self as *const Self).cast::<[u8; PACKET_HEADER_SIZE]>() }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn aad_bytes(&self) -> [u8; 11] {
|
pub fn aad_bytes(&self) -> [u8; 11] {
|
||||||
|
@ -280,16 +288,24 @@ unsafe impl RawObject for FragmentHeader {}
|
||||||
|
|
||||||
impl FragmentHeader {
|
impl FragmentHeader {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_fragment(&self) -> bool { self.fragment_indicator == PACKET_FRAGMENT_INDICATOR }
|
pub fn is_fragment(&self) -> bool {
|
||||||
|
self.fragment_indicator == PACKET_FRAGMENT_INDICATOR
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn total_fragments(&self) -> u8 { self.total_and_fragment_no >> 4 }
|
pub fn total_fragments(&self) -> u8 {
|
||||||
|
self.total_and_fragment_no >> 4
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn fragment_no(&self) -> u8 { self.total_and_fragment_no & 0x0f }
|
pub fn fragment_no(&self) -> u8 {
|
||||||
|
self.total_and_fragment_no & 0x0f
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hops(&self) -> u8 { self.reserved_hops & HEADER_FLAGS_FIELD_MASK_HOPS }
|
pub fn hops(&self) -> u8 {
|
||||||
|
self.reserved_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn increment_hops(&mut self) -> u8 {
|
pub fn increment_hops(&mut self) -> u8 {
|
||||||
|
@ -300,7 +316,9 @@ impl FragmentHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] { unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() } }
|
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] {
|
||||||
|
unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod message_component_structs {
|
pub(crate) mod message_component_structs {
|
||||||
|
@ -330,7 +348,7 @@ pub(crate) mod message_component_structs {
|
||||||
pub version_major: u8,
|
pub version_major: u8,
|
||||||
pub version_minor: u8,
|
pub version_minor: u8,
|
||||||
pub version_revision: [u8; 2], // u16
|
pub version_revision: [u8; 2], // u16
|
||||||
pub timestamp: [u8; 8], // u64
|
pub timestamp: [u8; 8], // u64
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl RawObject for HelloFixedHeaderFields {}
|
unsafe impl RawObject for HelloFixedHeaderFields {}
|
||||||
|
|
|
@ -19,10 +19,14 @@ pub(crate) struct AesGmacSivPoolFactory(Secret<32>, Secret<32>);
|
||||||
|
|
||||||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn create(&self) -> AesGmacSiv { AesGmacSiv::new(&self.0.0, &self.1.0) }
|
fn create(&self) -> AesGmacSiv {
|
||||||
|
AesGmacSiv::new(&self.0 .0, &self.1 .0)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn reset(&self, obj: &mut AesGmacSiv) { obj.reset(); }
|
fn reset(&self, obj: &mut AesGmacSiv) {
|
||||||
|
obj.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A symmetric secret key negotiated between peers.
|
/// A symmetric secret key negotiated between peers.
|
||||||
|
@ -37,7 +41,9 @@ pub(crate) struct SymmetricSecret {
|
||||||
|
|
||||||
impl PartialEq for SymmetricSecret {
|
impl PartialEq for SymmetricSecret {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool { self.key.0.eq(&other.key.0) }
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.key.0.eq(&other.key.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for SymmetricSecret {}
|
impl Eq for SymmetricSecret {}
|
||||||
|
@ -47,9 +53,7 @@ impl SymmetricSecret {
|
||||||
pub fn new(key: Secret<64>) -> SymmetricSecret {
|
pub fn new(key: Secret<64>) -> SymmetricSecret {
|
||||||
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, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
||||||
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, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_KEY, 0, 0);
|
||||||
let aes_factory = AesGmacSivPoolFactory(
|
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());
|
||||||
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());
|
|
||||||
SymmetricSecret {
|
SymmetricSecret {
|
||||||
key,
|
key,
|
||||||
packet_hmac_key,
|
packet_hmac_key,
|
||||||
|
|
17
zerotier-network-hypervisor/src/vl1/system_interface.rs
Normal file
17
zerotier-network-hypervisor/src/vl1/system_interface.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use super::{Endpoint, Identity};
|
||||||
|
use std::num::NonZeroI64;
|
||||||
|
|
||||||
|
pub trait VL1SystemInterface {
|
||||||
|
fn event_node_is_up(&self);
|
||||||
|
fn event_node_is_down(&self);
|
||||||
|
fn event_identity_collision(&self);
|
||||||
|
fn event_online_status_change(&self, online: bool);
|
||||||
|
fn event_user_message(&self, source: &Identity, message_type: u64, message: &[u8]);
|
||||||
|
fn load_node_identity(&self) -> Option<Vec<u8>>;
|
||||||
|
fn save_node_identity(&self, _: &Identity, public: &[u8], secret: &[u8]);
|
||||||
|
fn wire_send(&self, endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>, data: &[&[u8]], packet_ttl: u8) -> bool;
|
||||||
|
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> bool;
|
||||||
|
fn get_path_hints(&self, id: &Identity) -> Option<&[(&Endpoint, Option<NonZeroI64>, Option<NonZeroI64>)]>;
|
||||||
|
fn time_ticks(&self) -> i64;
|
||||||
|
fn time_clock(&self) -> i64;
|
||||||
|
}
|
|
@ -11,15 +11,15 @@ use std::collections::{HashMap, LinkedList};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::util::gate::IntervalGate;
|
use crate::util::gate::IntervalGate;
|
||||||
use crate::vl1::Address;
|
|
||||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||||
use crate::vl1::node::{Node, SystemInterface};
|
use crate::vl1::node::{Node, SystemInterface};
|
||||||
use crate::vl1::protocol::{WHOIS_RETRY_INTERVAL, WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_MAX};
|
use crate::vl1::protocol::{WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_INTERVAL, WHOIS_RETRY_MAX};
|
||||||
|
use crate::vl1::Address;
|
||||||
use crate::PacketBuffer;
|
use crate::PacketBuffer;
|
||||||
|
|
||||||
pub(crate) enum QueuedPacket {
|
pub(crate) enum QueuedPacket {
|
||||||
Unfragmented(PacketBuffer),
|
Unfragmented(PacketBuffer),
|
||||||
Fragmented(FragmentedPacket)
|
Fragmented(FragmentedPacket),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WhoisQueueItem {
|
struct WhoisQueueItem {
|
||||||
|
@ -33,7 +33,9 @@ pub(crate) struct WhoisQueue(Mutex<HashMap<Address, WhoisQueueItem>>);
|
||||||
impl WhoisQueue {
|
impl WhoisQueue {
|
||||||
pub(crate) const INTERVAL: i64 = WHOIS_RETRY_INTERVAL;
|
pub(crate) const INTERVAL: i64 = WHOIS_RETRY_INTERVAL;
|
||||||
|
|
||||||
pub fn new() -> Self { Self(Mutex::new(HashMap::new())) }
|
pub fn new() -> Self {
|
||||||
|
Self(Mutex::new(HashMap::new()))
|
||||||
|
}
|
||||||
|
|
||||||
/// 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, target: Address, packet: Option<QueuedPacket>) {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod switch;
|
|
||||||
mod multicastgroup;
|
mod multicastgroup;
|
||||||
|
mod switch;
|
||||||
|
|
||||||
pub use multicastgroup::MulticastGroup;
|
pub use multicastgroup::MulticastGroup;
|
||||||
pub use switch::{SwitchInterface, Switch};
|
pub use switch::{Switch, SwitchInterface};
|
||||||
|
|
|
@ -19,20 +19,14 @@ pub struct MulticastGroup {
|
||||||
impl From<&MAC> for MulticastGroup {
|
impl From<&MAC> for MulticastGroup {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(mac: &MAC) -> Self {
|
fn from(mac: &MAC) -> Self {
|
||||||
Self {
|
Self { mac: mac.clone(), adi: 0 }
|
||||||
mac: mac.clone(),
|
|
||||||
adi: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MAC> for MulticastGroup {
|
impl From<MAC> for MulticastGroup {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(mac: MAC) -> Self {
|
fn from(mac: MAC) -> Self {
|
||||||
Self {
|
Self { mac, adi: 0 }
|
||||||
mac,
|
|
||||||
adi: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,14 +36,16 @@ impl Ord for MulticastGroup {
|
||||||
let o = self.mac.cmp(&other.mac);
|
let o = self.mac.cmp(&other.mac);
|
||||||
match o {
|
match o {
|
||||||
Ordering::Equal => self.adi.cmp(&other.adi),
|
Ordering::Equal => self.adi.cmp(&other.adi),
|
||||||
_ => o
|
_ => o,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for MulticastGroup {
|
impl PartialOrd for MulticastGroup {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for MulticastGroup {
|
impl Hash for MulticastGroup {
|
||||||
|
|
|
@ -10,14 +10,12 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::vl1::node::VL1VirtualInterface;
|
use crate::vl1::node::VL1VirtualInterface;
|
||||||
use crate::vl1::{Peer, Path, Identity};
|
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
|
use crate::vl1::{Identity, Path, Peer};
|
||||||
|
|
||||||
pub trait SwitchInterface: Sync + Send {
|
pub trait SwitchInterface: Sync + Send {}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Switch {
|
pub struct Switch {}
|
||||||
}
|
|
||||||
|
|
||||||
impl VL1VirtualInterface for Switch {
|
impl VL1VirtualInterface 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(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
||||||
|
@ -39,6 +37,6 @@ impl VL1VirtualInterface for Switch {
|
||||||
|
|
||||||
impl Switch {
|
impl Switch {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self{}
|
Self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
zerotier-system-service/Cargo.lock
generated
4
zerotier-system-service/Cargo.lock
generated
|
@ -2179,6 +2179,8 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"lz4_flex",
|
"lz4_flex",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"winapi",
|
"winapi",
|
||||||
"zerotier-core-crypto",
|
"zerotier-core-crypto",
|
||||||
]
|
]
|
||||||
|
@ -2191,11 +2193,13 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"digest_auth",
|
"digest_auth",
|
||||||
|
"hex",
|
||||||
"libc",
|
"libc",
|
||||||
"mach",
|
"mach",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"rand 0.7.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smol",
|
"smol",
|
||||||
|
|
|
@ -25,6 +25,8 @@ smol = "^1"
|
||||||
tide = { version = "^0", features = ["h1-server"], default-features = false }
|
tide = { version = "^0", features = ["h1-server"], default-features = false }
|
||||||
digest_auth = "^0"
|
digest_auth = "^0"
|
||||||
chrono = "^0"
|
chrono = "^0"
|
||||||
|
hex = "^0"
|
||||||
|
rand = "^0"
|
||||||
|
|
||||||
[target."cfg(windows)".dependencies]
|
[target."cfg(windows)".dependencies]
|
||||||
winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
|
winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
|
||||||
|
|
|
@ -12,18 +12,18 @@
|
||||||
* for each thread using options like SO_REUSEPORT and concurrent packet listening.
|
* for each thread using options like SO_REUSEPORT and concurrent packet listening.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::mem::{MaybeUninit, transmute, zeroed};
|
use std::mem::{transmute, zeroed, MaybeUninit};
|
||||||
use std::num::NonZeroI64;
|
use std::num::NonZeroI64;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use num_traits::cast::AsPrimitive;
|
use num_traits::cast::AsPrimitive;
|
||||||
|
|
||||||
use zerotier_network_hypervisor::vl1::InetAddress;
|
//use crate::debug;
|
||||||
|
use zerotier_network_hypervisor::vl1::{InetAddress, InetAddressFamily};
|
||||||
use zerotier_network_hypervisor::{PacketBuffer, PacketBufferPool};
|
use zerotier_network_hypervisor::{PacketBuffer, PacketBufferPool};
|
||||||
use crate::debug;
|
|
||||||
|
|
||||||
const FAST_UDP_SOCKET_MAX_THREADS: usize = 4;
|
const FAST_UDP_SOCKET_MAX_THREADS: usize = 4;
|
||||||
|
|
||||||
|
@ -65,12 +65,14 @@ fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPR
|
||||||
setsockopt_results |= libc::setsockopt(s, libc::IPPROTO_IPV6.as_(), libc::IPV6_V6ONLY.as_(), (&mut fl as *mut c_int).cast(), fl_size);
|
setsockopt_results |= libc::setsockopt(s, libc::IPPROTO_IPV6.as_(), libc::IPV6_V6ONLY.as_(), (&mut fl as *mut c_int).cast(), fl_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))] {
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
{
|
||||||
fl = 1;
|
fl = 1;
|
||||||
setsockopt_results |= libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_NOSIGPIPE.as_(), (&mut fl as *mut c_int).cast(), fl_size)
|
setsockopt_results |= libc::setsockopt(s, libc::SOL_SOCKET.as_(), libc::SO_NOSIGPIPE.as_(), (&mut fl as *mut c_int).cast(), fl_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")] {
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
if !_device_name.is_empty() {
|
if !_device_name.is_empty() {
|
||||||
let _ = std::ffi::CString::new(_device_name).map(|dn| {
|
let _ = std::ffi::CString::new(_device_name).map(|dn| {
|
||||||
let dnb = dn.as_bytes_with_nul();
|
let dnb = dn.as_bytes_with_nul();
|
||||||
|
@ -85,11 +87,13 @@ fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPR
|
||||||
}
|
}
|
||||||
|
|
||||||
if af == libc::AF_INET {
|
if af == libc::AF_INET {
|
||||||
#[cfg(not(target_os = "linux"))] {
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
fl = 0;
|
fl = 0;
|
||||||
libc::setsockopt(s, libc::IPPROTO_IP.as_(), libc::IP_DF.as_(), (&mut fl as *mut c_int).cast(), fl_size);
|
libc::setsockopt(s, libc::IPPROTO_IP.as_(), libc::IP_DF.as_(), (&mut fl as *mut c_int).cast(), fl_size);
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "linux")] {
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
fl = libc::IP_PMTUDISC_DONT as c_int;
|
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(), fl_size);
|
libc::setsockopt(s, libc::IPPROTO_IP.as_(), libc::IP_MTU_DISCOVER.as_(), (&mut fl as *mut c_int).cast(), fl_size);
|
||||||
}
|
}
|
||||||
|
@ -135,7 +139,9 @@ pub(crate) struct FastUDPSocket {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fast_udp_socket_close(socket: &FastUDPRawOsSocket) {
|
fn fast_udp_socket_close(socket: &FastUDPRawOsSocket) {
|
||||||
unsafe { libc::close(socket.get().as_()); }
|
unsafe {
|
||||||
|
libc::close(socket.get().as_());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send to a raw UDP socket with optional packet TTL.
|
/// Send to a raw UDP socket with optional packet TTL.
|
||||||
|
@ -165,7 +171,7 @@ pub(crate) fn fast_udp_socket_sendto(socket: &FastUDPRawOsSocket, to_address: &I
|
||||||
msg_iovlen: data_len.as_(),
|
msg_iovlen: data_len.as_(),
|
||||||
msg_control: null_mut(),
|
msg_control: null_mut(),
|
||||||
msg_controllen: 0,
|
msg_controllen: 0,
|
||||||
msg_flags: 0
|
msg_flags: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if packet_ttl == 0 || to_address.is_ipv6() {
|
if packet_ttl == 0 || to_address.is_ipv6() {
|
||||||
|
@ -216,18 +222,23 @@ impl FastUDPSocket {
|
||||||
let thread_run = s.thread_run.clone();
|
let thread_run = s.thread_run.clone();
|
||||||
let handler_copy = handler.clone();
|
let handler_copy = handler.clone();
|
||||||
let packet_buffer_pool_copy = packet_buffer_pool.clone();
|
let packet_buffer_pool_copy = packet_buffer_pool.clone();
|
||||||
s.threads.push(std::thread::Builder::new().stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).spawn(move || {
|
s.threads.push(
|
||||||
let mut from_address = InetAddress::new();
|
std::thread::Builder::new()
|
||||||
while thread_run.load(Ordering::Relaxed) {
|
//.stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE)
|
||||||
let mut buf = packet_buffer_pool_copy.get_packet_buffer();
|
.spawn(move || {
|
||||||
let s = fast_udp_socket_recvfrom(&thread_socket, &mut buf, &mut from_address);
|
let mut from_address = InetAddress::new();
|
||||||
if s > 0 {
|
while thread_run.load(Ordering::Relaxed) {
|
||||||
handler_copy(&thread_socket, &from_address, buf);
|
let mut buf = packet_buffer_pool_copy.get_packet_buffer();
|
||||||
} else if s < 0 {
|
let s = fast_udp_socket_recvfrom(&thread_socket, &mut buf, &mut from_address);
|
||||||
break;
|
if s > 0 {
|
||||||
}
|
handler_copy(&thread_socket, &from_address, buf);
|
||||||
}
|
} else if s < 0 {
|
||||||
}).unwrap());
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
bind_failed_reason = thread_socket.err().unwrap();
|
bind_failed_reason = thread_socket.err().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -244,7 +255,7 @@ impl FastUDPSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn send(&self, to_address: &InetAddress, data: &[u8], packet_ttl: i32) {
|
pub fn send(&self, to_address: &InetAddress, data: &[&[u8]], packet_ttl: u8) {
|
||||||
debug_assert!(!self.sockets.is_empty());
|
debug_assert!(!self.sockets.is_empty());
|
||||||
fast_udp_socket_sendto(unsafe { self.sockets.get_unchecked(0) }, to_address, data, packet_ttl);
|
fast_udp_socket_sendto(unsafe { self.sockets.get_unchecked(0) }, to_address, data, packet_ttl);
|
||||||
}
|
}
|
||||||
|
@ -268,7 +279,7 @@ impl Drop for FastUDPSocket {
|
||||||
self.thread_run.store(false, Ordering::Relaxed);
|
self.thread_run.store(false, Ordering::Relaxed);
|
||||||
for s in self.sockets.iter() {
|
for s in self.sockets.iter() {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::sendto(s.get().as_(), tmp.as_ptr().cast(), 0, 0, (&self.bind_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>() as osdep::socklen_t);
|
libc::sendto(s.get().as_(), tmp.as_ptr().cast(), 0, 0, (&self.bind_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>() as libc::socklen_t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in self.sockets.iter() {
|
for s in self.sockets.iter() {
|
||||||
|
@ -292,19 +303,19 @@ mod tests {
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
use crate::fastudpsocket::*;
|
use crate::fastudpsocket::*;
|
||||||
use zerotier_network_hypervisor::{PacketBufferPool, PacketBufferFactory, PacketBuffer};
|
use zerotier_network_hypervisor::{PacketBuffer, PacketBufferFactory, PacketBufferPool};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_udp_bind_and_transfer() {
|
fn test_udp_bind_and_transfer() {
|
||||||
{
|
{
|
||||||
let pool = Arc::new(PacketBufferPool::new(64, PacketBufferFactory));
|
let pool = Arc::new(PacketBufferPool::new(64, PacketBufferFactory::new()));
|
||||||
|
|
||||||
let ba0 = InetAddress::new_from_string("127.0.0.1/23333");
|
let ba0 = InetAddress::new_from_string("127.0.0.1/23333");
|
||||||
assert!(ba0.is_some());
|
assert!(ba0.is_some());
|
||||||
let ba0 = ba0.unwrap();
|
let ba0 = ba0.unwrap();
|
||||||
let cnt0 = Arc::new(AtomicU32::new(0));
|
let cnt0 = Arc::new(AtomicU32::new(0));
|
||||||
let cnt0c = cnt0.clone();
|
let cnt0c = cnt0.clone();
|
||||||
let s0 = FastUDPSocket::new("", &ba0, &pool, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: PacketBuffer| {
|
let s0 = FastUDPSocket::new("", &ba0, &pool, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: PacketBuffer| {
|
||||||
cnt0c.fetch_add(1, Ordering::Relaxed);
|
cnt0c.fetch_add(1, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
assert!(s0.is_ok());
|
assert!(s0.is_ok());
|
||||||
|
@ -323,8 +334,8 @@ mod tests {
|
||||||
|
|
||||||
let data_bytes = [0_u8; 1024];
|
let data_bytes = [0_u8; 1024];
|
||||||
loop {
|
loop {
|
||||||
s0.send(&ba1, &data_bytes, 0);
|
s0.send(&ba1, &[&data_bytes], 0);
|
||||||
s1.send(&ba0, &data_bytes, 0);
|
s1.send(&ba0, &[&data_bytes], 0);
|
||||||
if cnt0.load(Ordering::Relaxed) > 10000 && cnt1.load(Ordering::Relaxed) > 10000 {
|
if cnt0.load(Ordering::Relaxed) > 10000 && cnt1.load(Ordering::Relaxed) > 10000 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@ use zerotier_network_hypervisor::vl1::InetAddress;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn s6_addr_as_ptr<A>(a: &A) -> *const A { a as *const A }
|
fn s6_addr_as_ptr<A>(a: &A) -> *const A {
|
||||||
|
a as *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)]
|
||||||
|
@ -88,9 +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| {
|
crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| println!(" {} {}", dev, a.to_string()));
|
||||||
println!(" {} {}", dev, a.to_string())
|
|
||||||
});
|
|
||||||
println!("done.")
|
println!("done.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,65 +10,29 @@ use std::collections::BTreeMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::identity::NetworkId;
|
||||||
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
use zerotier_network_hypervisor::vl1::{Address, InetAddress};
|
||||||
|
|
||||||
pub const UNASSIGNED_PRIVILEGED_PORTS: [u16; 299] = [
|
pub const UNASSIGNED_PRIVILEGED_PORTS: [u16; 299] = [
|
||||||
4,
|
4, 6, 8, 10, 12, 14, 15, 16, 26, 28, 30, 32, 34, 36, 40, 60, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 285, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 334, 335, 336, 337, 338,
|
||||||
6,
|
339, 340, 341, 342, 343, 703, 708, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 732, 733, 734, 735, 736, 737, 738, 739, 740, 743, 745, 746, 755, 756, 766, 768, 778, 779, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798,
|
||||||
8,
|
799, 802, 803, 804, 805, 806, 807, 808, 809, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 862, 863, 864, 865, 866, 867, 868, 869, 870,
|
||||||
10,
|
871, 872, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 904, 905, 906, 907, 908, 909, 910, 911, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939,
|
||||||
12,
|
940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008,
|
||||||
14,
|
1009, 1023,
|
||||||
15,
|
|
||||||
16,
|
|
||||||
26,
|
|
||||||
28,
|
|
||||||
30,
|
|
||||||
32,
|
|
||||||
34,
|
|
||||||
36,
|
|
||||||
40,
|
|
||||||
60,
|
|
||||||
269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
|
|
||||||
285,
|
|
||||||
288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
|
|
||||||
323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
|
|
||||||
334, 335, 336, 337, 338, 339, 340, 341, 342, 343,
|
|
||||||
703,
|
|
||||||
708,
|
|
||||||
713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728,
|
|
||||||
732, 733, 734, 735, 736, 737, 738, 739, 740,
|
|
||||||
743,
|
|
||||||
745, 746,
|
|
||||||
755, 756,
|
|
||||||
766,
|
|
||||||
768,
|
|
||||||
778, 779,
|
|
||||||
781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799,
|
|
||||||
802, 803, 804, 805, 806, 807, 808, 809,
|
|
||||||
811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827,
|
|
||||||
834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846,
|
|
||||||
849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859,
|
|
||||||
862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872,
|
|
||||||
874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885,
|
|
||||||
889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899,
|
|
||||||
904, 905, 906, 907, 908, 909, 910, 911,
|
|
||||||
914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988,
|
|
||||||
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009,
|
|
||||||
1023,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub const DEFAULT_PORT: u16 = 9993;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigPhysicalPathConfig {
|
pub struct LocalConfigPhysicalPathConfig {
|
||||||
pub blacklist: bool
|
pub blacklist: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LocalConfigPhysicalPathConfig {
|
impl Default for LocalConfigPhysicalPathConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
LocalConfigPhysicalPathConfig {
|
LocalConfigPhysicalPathConfig { blacklist: false }
|
||||||
blacklist: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,14 +40,12 @@ impl Default for LocalConfigPhysicalPathConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigVirtualConfig {
|
pub struct LocalConfigVirtualConfig {
|
||||||
#[serde(rename = "try")]
|
#[serde(rename = "try")]
|
||||||
pub try_: Vec<InetAddress>
|
pub try_: Vec<InetAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LocalConfigVirtualConfig {
|
impl Default for LocalConfigVirtualConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
LocalConfigVirtualConfig {
|
LocalConfigVirtualConfig { try_: Vec::new() }
|
||||||
try_: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +71,7 @@ impl Default for LocalConfigNetworkSettings {
|
||||||
allow_global_ips: false,
|
allow_global_ips: false,
|
||||||
allow_managed_routes: true,
|
allow_managed_routes: true,
|
||||||
allow_global_routes: false,
|
allow_global_routes: false,
|
||||||
allow_default_route_override: false
|
allow_default_route_override: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,11 +132,11 @@ impl Default for LocalConfigSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalConfigSettings {
|
LocalConfigSettings {
|
||||||
primary_port: zerotier_core::DEFAULT_PORT,
|
primary_port: DEFAULT_PORT,
|
||||||
port_mapping: true,
|
port_mapping: true,
|
||||||
log: LocalConfigLogSettings::default(),
|
log: LocalConfigLogSettings::default(),
|
||||||
interface_prefix_blacklist: bl,
|
interface_prefix_blacklist: bl,
|
||||||
explicit_addresses: Vec::new()
|
explicit_addresses: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +177,7 @@ impl Default for LocalConfig {
|
||||||
physical: BTreeMap::new(),
|
physical: BTreeMap::new(),
|
||||||
virtual_: BTreeMap::new(),
|
virtual_: BTreeMap::new(),
|
||||||
network: BTreeMap::new(),
|
network: BTreeMap::new(),
|
||||||
settings: LocalConfigSettings::default()
|
settings: LocalConfigSettings::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Seek, SeekFrom, stderr, Write};
|
use std::io::{stderr, Seek, SeekFrom, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -39,7 +39,7 @@ impl Log {
|
||||||
if !p.is_empty() {
|
if !p.is_empty() {
|
||||||
p.push(' ');
|
p.push(' ');
|
||||||
}
|
}
|
||||||
Arc::new(Mutex::new(Log{
|
Arc::new(Mutex::new(Log {
|
||||||
prefix: p,
|
prefix: p,
|
||||||
path: String::from(path),
|
path: String::from(path),
|
||||||
file: None,
|
file: None,
|
||||||
|
@ -64,7 +64,7 @@ impl Log {
|
||||||
|
|
||||||
fn log_internal(&mut self, pfx: &str, s: &str) {
|
fn log_internal(&mut self, pfx: &str, s: &str) {
|
||||||
if !s.is_empty() {
|
if !s.is_empty() {
|
||||||
let log_line = format!("{}[{}] {}{}\n", l.prefix.as_str(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), pfx, s);
|
let log_line = format!("{}[{}] {}{}\n", self.prefix.as_str(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), pfx, s);
|
||||||
if !self.path.is_empty() {
|
if !self.path.is_empty() {
|
||||||
if self.file.is_none() {
|
if self.file.is_none() {
|
||||||
let f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str());
|
let f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str());
|
||||||
|
|
|
@ -16,19 +16,20 @@ use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION
|
||||||
use crate::store::platform_default_home_path;
|
use crate::store::platform_default_home_path;
|
||||||
|
|
||||||
mod fastudpsocket;
|
mod fastudpsocket;
|
||||||
mod localconfig;
|
|
||||||
mod getifaddrs;
|
mod getifaddrs;
|
||||||
|
mod localconfig;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod log;
|
mod log;
|
||||||
mod store;
|
|
||||||
mod vnic;
|
|
||||||
mod service;
|
mod service;
|
||||||
|
mod store;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod vnic;
|
||||||
|
|
||||||
pub const HTTP_API_OBJECT_SIZE_LIMIT: usize = 131072;
|
pub const HTTP_API_OBJECT_SIZE_LIMIT: usize = 131072;
|
||||||
|
|
||||||
fn make_help(long_help: bool) -> String {
|
fn make_help(long_help: bool) -> String {
|
||||||
format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
|
format!(
|
||||||
|
r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
|
||||||
(c)2013-2021 ZeroTier, Inc.
|
(c)2013-2021 ZeroTier, Inc.
|
||||||
Licensed under the Mozilla Public License (MPL) 2.0 (see LICENSE.txt)
|
Licensed under the Mozilla Public License (MPL) 2.0 (see LICENSE.txt)
|
||||||
|
|
||||||
|
@ -77,8 +78,10 @@ Common Operations:
|
||||||
· join <network> Join a virtual network
|
· join <network> Join a virtual network
|
||||||
· leave <network> Leave a virtual network
|
· leave <network> Leave a virtual network
|
||||||
{}"###,
|
{}"###,
|
||||||
VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION,
|
VERSION_MAJOR,
|
||||||
if long_help {
|
VERSION_MINOR,
|
||||||
|
VERSION_REVISION,
|
||||||
|
if long_help {
|
||||||
r###"
|
r###"
|
||||||
Advanced Operations:
|
Advanced Operations:
|
||||||
|
|
||||||
|
@ -105,7 +108,10 @@ Advanced Operations:
|
||||||
@ Argument is the path to a file containing the object.
|
@ Argument is the path to a file containing the object.
|
||||||
? Argument can be either the object or a path to it (auto-detected).
|
? Argument can be either the object or a path to it (auto-detected).
|
||||||
"###
|
"###
|
||||||
} else { "" })
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_help(long_help: bool) {
|
pub fn print_help(long_help: bool) {
|
||||||
|
@ -117,7 +123,7 @@ pub struct GlobalCommandLineFlags {
|
||||||
pub json_output: bool,
|
pub json_output: bool,
|
||||||
pub base_path: String,
|
pub base_path: String,
|
||||||
pub auth_token_path_override: Option<String>,
|
pub auth_token_path_override: Option<String>,
|
||||||
pub auth_token_override: Option<String>
|
pub auth_token_override: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -131,69 +137,45 @@ fn main() {
|
||||||
.subcommand(App::new("help"))
|
.subcommand(App::new("help"))
|
||||||
.subcommand(App::new("version"))
|
.subcommand(App::new("version"))
|
||||||
.subcommand(App::new("status"))
|
.subcommand(App::new("status"))
|
||||||
.subcommand(App::new("set")
|
.subcommand(
|
||||||
.subcommand(App::new("port")
|
App::new("set")
|
||||||
.arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port)))
|
.subcommand(App::new("port").arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port)))
|
||||||
.subcommand(App::new("secondaryport")
|
.subcommand(App::new("secondaryport").arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port)))
|
||||||
.arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port)))
|
.subcommand(
|
||||||
.subcommand(App::new("blacklist")
|
App::new("blacklist")
|
||||||
.subcommand(App::new("cidr")
|
.subcommand(App::new("cidr").arg(Arg::with_name("ip_bits").index(1)).arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool)))
|
||||||
.arg(Arg::with_name("ip_bits").index(1))
|
.subcommand(App::new("if").arg(Arg::with_name("prefix").index(1)).arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool))),
|
||||||
.arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool)))
|
)
|
||||||
.subcommand(App::new("if")
|
.subcommand(App::new("portmap").arg(Arg::with_name("boolean").index(1).validator(utils::is_valid_bool))),
|
||||||
.arg(Arg::with_name("prefix").index(1))
|
)
|
||||||
.arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool))))
|
.subcommand(App::new("peer").subcommand(App::new("show").arg(Arg::with_name("address").index(1).required(true))).subcommand(App::new("list")).subcommand(App::new("listroots")).subcommand(App::new("try")))
|
||||||
.subcommand(App::new("portmap")
|
.subcommand(
|
||||||
.arg(Arg::with_name("boolean").index(1).validator(utils::is_valid_bool))))
|
App::new("network")
|
||||||
.subcommand(App::new("peer")
|
.subcommand(App::new("show").arg(Arg::with_name("nwid").index(1).required(true)))
|
||||||
.subcommand(App::new("show")
|
.subcommand(App::new("list"))
|
||||||
.arg(Arg::with_name("address").index(1).required(true)))
|
.subcommand(App::new("set").arg(Arg::with_name("nwid").index(1).required(true)).arg(Arg::with_name("setting").index(2).required(false)).arg(Arg::with_name("value").index(3).required(false))),
|
||||||
.subcommand(App::new("list"))
|
)
|
||||||
.subcommand(App::new("listroots"))
|
.subcommand(App::new("join").arg(Arg::with_name("nwid").index(1).required(true)))
|
||||||
.subcommand(App::new("try")))
|
.subcommand(App::new("leave").arg(Arg::with_name("nwid").index(1).required(true)))
|
||||||
.subcommand(App::new("network")
|
|
||||||
.subcommand(App::new("show")
|
|
||||||
.arg(Arg::with_name("nwid").index(1).required(true)))
|
|
||||||
.subcommand(App::new("list"))
|
|
||||||
.subcommand(App::new("set")
|
|
||||||
.arg(Arg::with_name("nwid").index(1).required(true))
|
|
||||||
.arg(Arg::with_name("setting").index(2).required(false))
|
|
||||||
.arg(Arg::with_name("value").index(3).required(false))))
|
|
||||||
.subcommand(App::new("join")
|
|
||||||
.arg(Arg::with_name("nwid").index(1).required(true)))
|
|
||||||
.subcommand(App::new("leave")
|
|
||||||
.arg(Arg::with_name("nwid").index(1).required(true)))
|
|
||||||
.subcommand(App::new("service"))
|
.subcommand(App::new("service"))
|
||||||
.subcommand(App::new("controller")
|
.subcommand(
|
||||||
.subcommand(App::new("list"))
|
App::new("controller")
|
||||||
.subcommand(App::new("new"))
|
.subcommand(App::new("list"))
|
||||||
.subcommand(App::new("set")
|
.subcommand(App::new("new"))
|
||||||
.arg(Arg::with_name("id").index(1).required(true))
|
.subcommand(App::new("set").arg(Arg::with_name("id").index(1).required(true)).arg(Arg::with_name("setting").index(2)).arg(Arg::with_name("value").index(3)))
|
||||||
.arg(Arg::with_name("setting").index(2))
|
.subcommand(App::new("show").arg(Arg::with_name("id").index(1).required(true)).arg(Arg::with_name("member").index(2)))
|
||||||
.arg(Arg::with_name("value").index(3)))
|
.subcommand(App::new("auth").arg(Arg::with_name("member").index(1).required(true)))
|
||||||
.subcommand(App::new("show")
|
.subcommand(App::new("deauth").arg(Arg::with_name("member").index(1).required(true))),
|
||||||
.arg(Arg::with_name("id").index(1).required(true))
|
)
|
||||||
.arg(Arg::with_name("member").index(2)))
|
.subcommand(
|
||||||
.subcommand(App::new("auth")
|
App::new("identity")
|
||||||
.arg(Arg::with_name("member").index(1).required(true)))
|
.subcommand(App::new("new").arg(Arg::with_name("type").possible_value("p384").possible_value("c25519").default_value("c25519").index(1)))
|
||||||
.subcommand(App::new("deauth")
|
.subcommand(App::new("getpublic").arg(Arg::with_name("identity").index(1).required(true)))
|
||||||
.arg(Arg::with_name("member").index(1).required(true))))
|
.subcommand(App::new("fingerprint").arg(Arg::with_name("identity").index(1).required(true)))
|
||||||
.subcommand(App::new("identity")
|
.subcommand(App::new("validate").arg(Arg::with_name("identity").index(1).required(true)))
|
||||||
.subcommand(App::new("new")
|
.subcommand(App::new("sign").arg(Arg::with_name("identity").index(1).required(true)).arg(Arg::with_name("path").index(2).required(true)))
|
||||||
.arg(Arg::with_name("type").possible_value("p384").possible_value("c25519").default_value("c25519").index(1)))
|
.subcommand(App::new("verify").arg(Arg::with_name("identity").index(1).required(true)).arg(Arg::with_name("path").index(2).required(true)).arg(Arg::with_name("signature").index(3).required(true))),
|
||||||
.subcommand(App::new("getpublic")
|
)
|
||||||
.arg(Arg::with_name("identity").index(1).required(true)))
|
|
||||||
.subcommand(App::new("fingerprint")
|
|
||||||
.arg(Arg::with_name("identity").index(1).required(true)))
|
|
||||||
.subcommand(App::new("validate")
|
|
||||||
.arg(Arg::with_name("identity").index(1).required(true)))
|
|
||||||
.subcommand(App::new("sign")
|
|
||||||
.arg(Arg::with_name("identity").index(1).required(true))
|
|
||||||
.arg(Arg::with_name("path").index(2).required(true)))
|
|
||||||
.subcommand(App::new("verify")
|
|
||||||
.arg(Arg::with_name("identity").index(1).required(true))
|
|
||||||
.arg(Arg::with_name("path").index(2).required(true))
|
|
||||||
.arg(Arg::with_name("signature").index(3).required(true))))
|
|
||||||
.help(help.as_str())
|
.help(help.as_str())
|
||||||
.get_matches_from_safe(std::env::args());
|
.get_matches_from_safe(std::env::args());
|
||||||
if args.is_err() {
|
if args.is_err() {
|
||||||
|
@ -215,7 +197,7 @@ fn main() {
|
||||||
json_output: cli_args.is_present("json"),
|
json_output: cli_args.is_present("json"),
|
||||||
base_path: cli_args.value_of("path").map_or_else(|| platform_default_home_path(), |p| p.into_string()),
|
base_path: cli_args.value_of("path").map_or_else(|| platform_default_home_path(), |p| p.into_string()),
|
||||||
auth_token_path_override: cli_args.value_of("token_path").map(|p| p.into_string()),
|
auth_token_path_override: cli_args.value_of("token_path").map(|p| p.into_string()),
|
||||||
auth_token_override: cli_args.value_of("token").map(|t| t.into_string())
|
auth_token_override: cli_args.value_of("token").map(|t| t.into_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
std::process::exit({
|
std::process::exit({
|
||||||
|
|
|
@ -7,21 +7,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::num::NonZeroI64;
|
use std::num::NonZeroI64;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use zerotier_network_hypervisor::{Interface, NetworkHypervisor};
|
use crate::fastudpsocket::{fast_udp_socket_sendto, FastUDPRawOsSocket};
|
||||||
use zerotier_network_hypervisor::vl1::{Endpoint, Identity, IdentityType, Node, VL1SystemInterface};
|
use zerotier_network_hypervisor::vl1::{Endpoint, Identity, IdentityType, Node, VL1SystemInterface};
|
||||||
use zerotier_network_hypervisor::vl2::SwitchInterface;
|
use zerotier_network_hypervisor::vl2::SwitchInterface;
|
||||||
use crate::fastudpsocket::{fast_udp_socket_sendto, FastUDPRawOsSocket};
|
use zerotier_network_hypervisor::{Interface, NetworkHypervisor};
|
||||||
|
|
||||||
use crate::GlobalCommandLineFlags;
|
|
||||||
use crate::log::Log;
|
|
||||||
use crate::utils::{ms_monotonic, ms_since_epoch};
|
|
||||||
use crate::localconfig::LocalConfig;
|
use crate::localconfig::LocalConfig;
|
||||||
use crate::store::{platform_default_home_path, StateObjectType, Store};
|
use crate::log::Log;
|
||||||
|
use crate::store::{StateObjectType, Store};
|
||||||
|
use crate::utils::{ms_monotonic, ms_since_epoch};
|
||||||
|
use crate::GlobalCommandLineFlags;
|
||||||
|
|
||||||
struct ServiceInterface {
|
struct ServiceInterface {
|
||||||
pub store: Store,
|
pub store: Store,
|
||||||
|
@ -59,23 +59,26 @@ impl VL1SystemInterface for ServiceInterface {
|
||||||
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<NonZeroI64>, local_interface: Option<NonZeroI64>, data: &[&[u8]], packet_ttl: u8) -> bool {
|
||||||
match endpoint {
|
match endpoint {
|
||||||
Endpoint::IpUdp(ip) => {
|
Endpoint::IpUdp(ip) => {
|
||||||
local_socket.map_or_else(|| {
|
local_socket.map_or_else(
|
||||||
let ptr = self.all_sockets_spin_ptr.fetch_add(1, Ordering::Relaxed);
|
|| {
|
||||||
let all_sockets = self.all_sockets.lock();
|
let ptr = self.all_sockets_spin_ptr.fetch_add(1, Ordering::Relaxed);
|
||||||
if !all_sockets.is_empty() {
|
let all_sockets = self.all_sockets.lock();
|
||||||
let s = unsafe { all_sockets.get_unchecked(ptr % all_sockets.len()) }.clone();
|
if !all_sockets.is_empty() {
|
||||||
drop(all_sockets); // release mutex
|
let s = unsafe { all_sockets.get_unchecked(ptr % all_sockets.len()) }.clone();
|
||||||
fast_udp_socket_sendto(&s, ip, data, packet_ttl);
|
drop(all_sockets); // release mutex
|
||||||
|
fast_udp_socket_sendto(&s, ip, data, packet_ttl);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|local_socket| {
|
||||||
|
fast_udp_socket_sendto(&local_socket, ip, data, packet_ttl);
|
||||||
true
|
true
|
||||||
} else {
|
},
|
||||||
false
|
)
|
||||||
}
|
|
||||||
}, |local_socket| {
|
|
||||||
fast_udp_socket_sendto(&local_socket, ip, data, packet_ttl);
|
|
||||||
true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,20 +93,23 @@ impl VL1SystemInterface for ServiceInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn time_ticks(&self) -> i64 { ms_monotonic() }
|
fn time_ticks(&self) -> i64 {
|
||||||
|
ms_monotonic()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn time_clock(&self) -> i64 { ms_since_epoch() }
|
fn time_clock(&self) -> i64 {
|
||||||
|
ms_since_epoch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SwitchInterface for ServiceInterface {}
|
impl SwitchInterface for ServiceInterface {}
|
||||||
|
|
||||||
impl Interface for ServiceInterface {}
|
//impl Interface for ServiceInterface {}
|
||||||
|
|
||||||
pub fn run(global_cli_flags: &GlobalCommandLineFlags) -> i32 {
|
pub fn run(global_cli_flags: &GlobalCommandLineFlags) -> i32 {
|
||||||
let store = Store::new(global_cli_flags.base_path.as_str(), &global_cli_flags.auth_token_path_override, &global_cli_flags.auth_token_override);
|
let store = Store::new(global_cli_flags.base_path.as_str(), &global_cli_flags.auth_token_path_override, &global_cli_flags.auth_token_override);
|
||||||
if store.is_err() {
|
if store.is_err() {}
|
||||||
}
|
|
||||||
|
|
||||||
let si = ServiceInterface {
|
let si = ServiceInterface {
|
||||||
store: store.unwrap(),
|
store: store.unwrap(),
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::ffi::CString;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use zerotier_core::{StateObjectType, NetworkId};
|
|
||||||
|
|
||||||
use crate::localconfig::LocalConfig;
|
use crate::localconfig::LocalConfig;
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::identity::NetworkId;
|
||||||
|
|
||||||
const ZEROTIER_PID: &'static str = "zerotier.pid";
|
const ZEROTIER_PID: &'static str = "zerotier.pid";
|
||||||
const ZEROTIER_URI: &'static str = "zerotier.uri";
|
const ZEROTIER_URI: &'static str = "zerotier.uri";
|
||||||
const LOCAL_CONF: &'static str = "local.conf";
|
const LOCAL_CONF: &'static str = "local.conf";
|
||||||
|
@ -27,7 +27,7 @@ pub enum StateObjectType {
|
||||||
IdentityPublic,
|
IdentityPublic,
|
||||||
IdentitySecret,
|
IdentitySecret,
|
||||||
NetworkConfig,
|
NetworkConfig,
|
||||||
Peer
|
Peer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
@ -70,16 +70,8 @@ impl Store {
|
||||||
peers_path: bp.join("peers.d").into_boxed_path(),
|
peers_path: bp.join("peers.d").into_boxed_path(),
|
||||||
controller_path: bp.join("controller.d").into_boxed_path(),
|
controller_path: bp.join("controller.d").into_boxed_path(),
|
||||||
networks_path: bp.join("networks.d").into_boxed_path(),
|
networks_path: bp.join("networks.d").into_boxed_path(),
|
||||||
auth_token_path: Mutex::new(auth_token_path_override.map_or_else(|| {
|
auth_token_path: Mutex::new(auth_token_path_override.map_or_else(|| bp.join(AUTHTOKEN_SECRET).into_boxed_path(), |auth_token_path_override| PathBuf::from(auth_token_path_override).into_boxed_path())),
|
||||||
bp.join(AUTHTOKEN_SECRET).into_boxed_path()
|
auth_token: Mutex::new(auth_token_override.map_or_else(|| String::new(), |auth_token_override| auth_token_override)),
|
||||||
}, |auth_token_path_override| {
|
|
||||||
PathBuf::from(auth_token_path_override).into_boxed_path()
|
|
||||||
})),
|
|
||||||
auth_token: Mutex::new(auth_token_override.map_or_else(|| {
|
|
||||||
String::new()
|
|
||||||
}, |auth_token_override| {
|
|
||||||
auth_token_override
|
|
||||||
})),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = std::fs::create_dir_all(&s.peers_path);
|
let _ = std::fs::create_dir_all(&s.peers_path);
|
||||||
|
@ -99,7 +91,7 @@ impl Store {
|
||||||
} else {
|
} else {
|
||||||
Some(self.networks_path.join(format!("{:0>16x}.conf", obj_id[0])))
|
Some(self.networks_path.join(format!("{:0>16x}.conf", obj_id[0])))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
StateObjectType::Peer => {
|
StateObjectType::Peer => {
|
||||||
if obj_id.len() < 1 {
|
if obj_id.len() < 1 {
|
||||||
None
|
None
|
||||||
|
@ -132,11 +124,11 @@ impl Store {
|
||||||
let p = self.auth_token_path.lock().unwrap();
|
let p = self.auth_token_path.lock().unwrap();
|
||||||
let ps = p.to_str().unwrap();
|
let ps = p.to_str().unwrap();
|
||||||
|
|
||||||
let token2 = self.read_file(ps).map_or(String::new(), |sb| { String::from_utf8(sb).unwrap_or(String::new()).trim().to_string() });
|
let token2 = self.read_file(ps).map_or(String::new(), |sb| String::from_utf8(sb).unwrap_or(String::new()).trim().to_string());
|
||||||
if token2.is_empty() {
|
if token2.is_empty() {
|
||||||
if generate_if_missing {
|
if generate_if_missing {
|
||||||
let mut rb = [0_u8; 32];
|
let mut rb = [0_u8; 32];
|
||||||
unsafe { crate::osdep::getSecureRandom(rb.as_mut_ptr().cast(), 64) };
|
unsafe { rb.fill_with(rand::random) };
|
||||||
token.reserve(rb.len());
|
token.reserve(rb.len());
|
||||||
for b in rb.iter() {
|
for b in rb.iter() {
|
||||||
if *b > 127_u8 {
|
if *b > 127_u8 {
|
||||||
|
@ -173,7 +165,8 @@ impl Store {
|
||||||
if de.is_ok() {
|
if de.is_ok() {
|
||||||
let nn = de.unwrap().file_name();
|
let nn = de.unwrap().file_name();
|
||||||
let n = nn.to_str().unwrap_or("");
|
let n = nn.to_str().unwrap_or("");
|
||||||
if n.len() == 21 && n.ends_with(".conf") { // ################.conf
|
if n.len() == 21 && n.ends_with(".conf") {
|
||||||
|
// ################.conf
|
||||||
let nwid = u64::from_str_radix(&n[0..16], 16);
|
let nwid = u64::from_str_radix(&n[0..16], 16);
|
||||||
if nwid.is_ok() {
|
if nwid.is_ok() {
|
||||||
list.push(NetworkId(nwid.unwrap()));
|
list.push(NetworkId(nwid.unwrap()));
|
||||||
|
@ -241,7 +234,7 @@ impl Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_pid(&self) -> std::io::Result<()> {
|
pub fn write_pid(&self) -> std::io::Result<()> {
|
||||||
let pid = unsafe { crate::osdep::getpid() }.to_string();
|
let pid = unsafe { libc::getpid() }.to_string();
|
||||||
self.write_file(ZEROTIER_PID, pid.as_bytes())
|
self.write_file(ZEROTIER_PID, pid.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,18 @@ use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use zerotier_network_hypervisor::vl1::Identity;
|
|
||||||
use zerotier_core_crypto::hex;
|
use zerotier_core_crypto::hex;
|
||||||
|
use zerotier_network_hypervisor::vl1::Identity;
|
||||||
|
|
||||||
use crate::osdep;
|
//use crate::osdep;
|
||||||
|
|
||||||
pub fn ms_since_epoch() -> i64 {
|
pub fn ms_since_epoch() -> i64 {
|
||||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64
|
std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
@ -30,7 +31,8 @@ pub fn ms_monotonic() -> i64 {
|
||||||
let mut tb: mach::mach_time::mach_timebase_info_data_t = std::mem::zeroed();
|
let mut tb: mach::mach_time::mach_timebase_info_data_t = std::mem::zeroed();
|
||||||
if mach::mach_time::mach_timebase_info(&mut tb) == 0 {
|
if mach::mach_time::mach_timebase_info(&mut tb) == 0 {
|
||||||
let mt = mach::mach_time::mach_continuous_approximate_time(); // ZT doesn't need it to be *that* exact, and this is faster
|
let mt = mach::mach_time::mach_continuous_approximate_time(); // ZT doesn't need it to be *that* exact, and this is faster
|
||||||
(((mt as u128) * tb.numer as u128 * 1000000_u128) / (tb.denom as u128)) as i64 // milliseconds since X
|
(((mt as u128) * tb.numer as u128 * 1000000_u128) / (tb.denom as u128)) as i64
|
||||||
|
// milliseconds since X
|
||||||
} else {
|
} else {
|
||||||
panic!("FATAL: mach_timebase_info() failed");
|
panic!("FATAL: mach_timebase_info() failed");
|
||||||
}
|
}
|
||||||
|
@ -39,20 +41,27 @@ pub fn ms_monotonic() -> i64 {
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||||
pub fn ms_monotonic() -> i64 {
|
pub fn ms_monotonic() -> i64 {
|
||||||
|
std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_bool(v: &str) -> Result<bool, String> {
|
pub fn parse_bool(v: &str) -> Result<bool, String> {
|
||||||
if !v.is_empty() {
|
if !v.is_empty() {
|
||||||
match v.chars().next().unwrap() {
|
match v.chars().next().unwrap() {
|
||||||
'y' | 'Y' | '1' | 't' | 'T' => { return Ok(true); }
|
'y' | 'Y' | '1' | 't' | 'T' => {
|
||||||
'n' | 'N' | '0' | 'f' | 'F' => { return Ok(false); }
|
return Ok(true);
|
||||||
|
}
|
||||||
|
'n' | 'N' | '0' | 'f' | 'F' => {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(format!("invalid boolean value: '{}'", v))
|
Err(format!("invalid boolean value: '{}'", v))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_bool(v: String) -> Result<(), String> { parse_bool(v.as_str()).map(|_| ()) }
|
pub fn is_valid_bool(v: String) -> Result<(), String> {
|
||||||
|
parse_bool(v.as_str()).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_valid_port(v: String) -> Result<(), String> {
|
pub fn is_valid_port(v: String) -> Result<(), String> {
|
||||||
let i = u16::from_str(v.as_str()).unwrap_or(0);
|
let i = u16::from_str(v.as_str()).unwrap_or(0);
|
||||||
|
@ -73,26 +82,19 @@ pub fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<
|
||||||
/// Read an identity as either a literal or from a file.
|
/// Read an identity as either a literal or from a file.
|
||||||
pub fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
pub fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||||
let parse_func = |s: &str| {
|
let parse_func = |s: &str| {
|
||||||
Identity::new_from_string(s).map_or_else(|e| {
|
Identity::new_from_string(s).map_or_else(
|
||||||
Err(format!("invalid identity: {}", e.to_str()))
|
|e| Err(format!("invalid identity: {}", e.to_str())),
|
||||||
}, |id| {
|
|id| {
|
||||||
if !validate || id.validate() {
|
if !validate || id.validate() {
|
||||||
Ok(id)
|
Ok(id)
|
||||||
} else {
|
} else {
|
||||||
Err(String::from("invalid identity: local validation failed"))
|
Err(String::from("invalid identity: local validation failed"))
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if Path::new(input).exists() {
|
if Path::new(input).exists() {
|
||||||
read_limit(input, 16384).map_or_else(|e| {
|
read_limit(input, 16384).map_or_else(|e| Err(e.to_string()), |v| String::from_utf8(v).map_or_else(|e| Err(e.to_string()), |s| parse_func(s.as_str())))
|
||||||
Err(e.to_string())
|
|
||||||
}, |v| {
|
|
||||||
String::from_utf8(v).map_or_else(|e| {
|
|
||||||
Err(e.to_string())
|
|
||||||
}, |s| {
|
|
||||||
parse_func(s.as_str())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
parse_func(input)
|
parse_func(input)
|
||||||
}
|
}
|
||||||
|
@ -105,20 +107,20 @@ pub fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, Strin
|
||||||
pub fn create_http_auth_nonce(timestamp: i64) -> String {
|
pub fn create_http_auth_nonce(timestamp: i64) -> String {
|
||||||
let mut nonce_plaintext: [u64; 2] = [timestamp as u64, timestamp as u64];
|
let mut nonce_plaintext: [u64; 2] = [timestamp as u64, timestamp as u64];
|
||||||
unsafe {
|
unsafe {
|
||||||
osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast());
|
//osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast());
|
||||||
hex::encode(*nonce_plaintext.as_ptr().cast::<[u8; 16]>())
|
hex::to_string(&nonce_plaintext.as_ptr().cast::<[u8]>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt HTTP auth nonce encrypted by this process and return the timestamp.
|
/// Decrypt HTTP auth nonce encrypted by this process and return the timestamp.
|
||||||
/// This returns zero if the input was not valid.
|
/// This returns zero if the input was not valid.
|
||||||
pub fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
|
pub fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
|
||||||
let nonce = hex::decode(nonce.trim());
|
let nonce = hex::from_string(nonce.trim());
|
||||||
if !nonce.is_err() {
|
if !nonce.is_err() {
|
||||||
let mut nonce = nonce.unwrap();
|
let mut nonce = nonce.unwrap();
|
||||||
if nonce.len() == 16 {
|
if nonce.len() == 16 {
|
||||||
unsafe {
|
unsafe {
|
||||||
osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
|
//osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast());
|
||||||
let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
|
let nonce = *nonce.as_ptr().cast::<[u64; 2]>();
|
||||||
if nonce[0] == nonce[1] {
|
if nonce[0] == nonce[1] {
|
||||||
return nonce[0] as i64;
|
return nonce[0] as i64;
|
||||||
|
@ -172,24 +174,33 @@ pub fn json_patch(target: &mut serde_json::value::Value, source: &serde_json::va
|
||||||
/// If there are no changes, None is returned. The depth limit is passed through to json_patch and
|
/// If there are no changes, None is returned. The depth limit is passed through to json_patch and
|
||||||
/// should be set to a sanity check value to prevent overflows.
|
/// should be set to a sanity check value to prevent overflows.
|
||||||
pub fn json_patch_object<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
|
pub fn json_patch_object<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
|
||||||
serde_json::from_str::<serde_json::value::Value>(patch).map_or_else(|e| Err(e), |patch| {
|
serde_json::from_str::<serde_json::value::Value>(patch).map_or_else(
|
||||||
serde_json::value::to_value(obj.borrow()).map_or_else(|e| Err(e), |mut obj_value| {
|
|e| Err(e),
|
||||||
json_patch(&mut obj_value, &patch, depth_limit);
|
|patch| {
|
||||||
serde_json::value::from_value::<O>(obj_value).map_or_else(|e| Err(e), |obj_merged| {
|
serde_json::value::to_value(obj.borrow()).map_or_else(
|
||||||
if obj == obj_merged {
|
|e| Err(e),
|
||||||
Ok(None)
|
|mut obj_value| {
|
||||||
} else {
|
json_patch(&mut obj_value, &patch, depth_limit);
|
||||||
Ok(Some(obj_merged))
|
serde_json::value::from_value::<O>(obj_value).map_or_else(
|
||||||
}
|
|e| Err(e),
|
||||||
})
|
|obj_merged| {
|
||||||
})
|
if obj == obj_merged {
|
||||||
})
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(obj_merged))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
|
||||||
use crate::utils::ms_monotonic;
|
use crate::utils::ms_monotonic;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn monotonic_clock_sanity_check() {
|
fn monotonic_clock_sanity_check() {
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MAC> {
|
||||||
let la: &libc::sockaddr_dl = &*((*i).ifma_addr.cast());
|
let la: &libc::sockaddr_dl = &*((*i).ifma_addr.cast());
|
||||||
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && crate::libc::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
|
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && crate::libc::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
|
||||||
let mi = la.sdl_nlen as usize;
|
let mi = la.sdl_nlen as usize;
|
||||||
MAC::from_u64((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi+1] as u64) << 32 | (la.sdl_data[mi+2] as u64) << 24 | (la.sdl_data[mi+3] as u64) << 16 | (la.sdl_data[mi+4] as u64) << 8 | la.sdl_data[mi+5] as u64).map(|mac| groups.insert(mac));
|
MAC::from_u64((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi + 1] as u64) << 32 | (la.sdl_data[mi + 2] as u64) << 24 | (la.sdl_data[mi + 3] as u64) << 16 | (la.sdl_data[mi + 4] as u64) << 8 | la.sdl_data[mi + 5] as u64).map(|mac| groups.insert(mac));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = (*i).ifma_next;
|
i = (*i).ifma_next;
|
||||||
|
@ -59,6 +59,6 @@ pub fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MAC> {
|
||||||
/// Linux stores this stuff in /proc and it needs to be fetched from there.
|
/// Linux stores this stuff in /proc and it needs to be fetched from there.
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MAC> {
|
pub fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MAC> {
|
||||||
let mut groups: HashSet<MulticastGroup> = HashSet::new();
|
let mut groups: HashSet<MAC> = HashSet::new();
|
||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ const SYSCTL: &'static str = "/usr/sbin/sysctl";
|
||||||
// Holds names of feth devices and destroys them on Drop.
|
// Holds names of feth devices and destroys them on Drop.
|
||||||
struct MacFethDevice {
|
struct MacFethDevice {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub peer_name: String
|
pub peer_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for MacFethDevice {
|
impl Drop for MacFethDevice {
|
||||||
|
@ -100,80 +100,80 @@ lazy_static! {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct nd_ifinfo {
|
struct nd_ifinfo {
|
||||||
u_int32_t linkmtu; /* LinkMTU */
|
u_int32_t linkmtu; /* LinkMTU */
|
||||||
u_int32_t maxmtu; /* Upper bound of LinkMTU */
|
u_int32_t maxmtu; /* Upper bound of LinkMTU */
|
||||||
u_int32_t basereachable; /* BaseReachableTime */
|
u_int32_t basereachable; /* BaseReachableTime */
|
||||||
u_int32_t reachable; /* Reachable Time */
|
u_int32_t reachable; /* Reachable Time */
|
||||||
u_int32_t retrans; /* Retrans Timer */
|
u_int32_t retrans; /* Retrans Timer */
|
||||||
u_int32_t flags; /* Flags */
|
u_int32_t flags; /* Flags */
|
||||||
int recalctm; /* BaseReacable re-calculation timer */
|
int recalctm; /* BaseReacable re-calculation timer */
|
||||||
u_int8_t chlim; /* CurHopLimit */
|
u_int8_t chlim; /* CurHopLimit */
|
||||||
u_int8_t receivedra;
|
u_int8_t receivedra;
|
||||||
};
|
};
|
||||||
struct in6_ndireq {
|
struct in6_ndireq {
|
||||||
char ifname[IFNAMSIZ];
|
char ifname[IFNAMSIZ];
|
||||||
struct nd_ifinfo ndi;
|
struct nd_ifinfo ndi;
|
||||||
};
|
};
|
||||||
struct in6_addrlifetime {
|
struct in6_addrlifetime {
|
||||||
time_t ia6t_expire; /* valid lifetime expiration time */
|
time_t ia6t_expire; /* valid lifetime expiration time */
|
||||||
time_t ia6t_preferred; /* preferred lifetime expiration time */
|
time_t ia6t_preferred; /* preferred lifetime expiration time */
|
||||||
u_int32_t ia6t_vltime; /* valid lifetime */
|
u_int32_t ia6t_vltime; /* valid lifetime */
|
||||||
u_int32_t ia6t_pltime; /* prefix lifetime */
|
u_int32_t ia6t_pltime; /* prefix lifetime */
|
||||||
};
|
};
|
||||||
struct in6_ifstat {
|
struct in6_ifstat {
|
||||||
ifs6_in_receive; /* # of total input datagram */
|
ifs6_in_receive; /* # of total input datagram */
|
||||||
ifs6_in_hdrerr; /* # of datagrams with invalid hdr */
|
ifs6_in_hdrerr; /* # of datagrams with invalid hdr */
|
||||||
ifs6_in_toobig; /* # of datagrams exceeded MTU */
|
ifs6_in_toobig; /* # of datagrams exceeded MTU */
|
||||||
ifs6_in_noroute; /* # of datagrams with no route */
|
ifs6_in_noroute; /* # of datagrams with no route */
|
||||||
ifs6_in_addrerr; /* # of datagrams with invalid dst */
|
ifs6_in_addrerr; /* # of datagrams with invalid dst */
|
||||||
ifs6_in_protounknown; /* # of datagrams with unknown proto */
|
ifs6_in_protounknown; /* # of datagrams with unknown proto */
|
||||||
/* NOTE: increment on final dst if */
|
/* NOTE: increment on final dst if */
|
||||||
ifs6_in_truncated; /* # of truncated datagrams */
|
ifs6_in_truncated; /* # of truncated datagrams */
|
||||||
ifs6_in_discard; /* # of discarded datagrams */
|
ifs6_in_discard; /* # of discarded datagrams */
|
||||||
/* NOTE: fragment timeout is not here */
|
/* NOTE: fragment timeout is not here */
|
||||||
ifs6_in_deliver; /* # of datagrams delivered to ULP */
|
ifs6_in_deliver; /* # of datagrams delivered to ULP */
|
||||||
/* NOTE: increment on final dst if */
|
/* NOTE: increment on final dst if */
|
||||||
ifs6_out_forward; /* # of datagrams forwarded */
|
ifs6_out_forward; /* # of datagrams forwarded */
|
||||||
/* NOTE: increment on outgoing if */
|
/* NOTE: increment on outgoing if */
|
||||||
ifs6_out_request; /* # of outgoing datagrams from ULP */
|
ifs6_out_request; /* # of outgoing datagrams from ULP */
|
||||||
/* NOTE: does not include forwrads */
|
/* NOTE: does not include forwrads */
|
||||||
ifs6_out_discard; /* # of discarded datagrams */
|
ifs6_out_discard; /* # of discarded datagrams */
|
||||||
ifs6_out_fragok; /* # of datagrams fragmented */
|
ifs6_out_fragok; /* # of datagrams fragmented */
|
||||||
ifs6_out_fragfail; /* # of datagrams failed on fragment */
|
ifs6_out_fragfail; /* # of datagrams failed on fragment */
|
||||||
ifs6_out_fragcreat; /* # of fragment datagrams */
|
ifs6_out_fragcreat; /* # of fragment datagrams */
|
||||||
/* NOTE: this is # after fragment */
|
/* NOTE: this is # after fragment */
|
||||||
ifs6_reass_reqd; /* # of incoming fragmented packets */
|
ifs6_reass_reqd; /* # of incoming fragmented packets */
|
||||||
/* NOTE: increment on final dst if */
|
/* NOTE: increment on final dst if */
|
||||||
ifs6_reass_ok; /* # of reassembled packets */
|
ifs6_reass_ok; /* # of reassembled packets */
|
||||||
/* NOTE: this is # after reass */
|
/* NOTE: this is # after reass */
|
||||||
/* NOTE: increment on final dst if */
|
/* NOTE: increment on final dst if */
|
||||||
ifs6_atmfrag_rcvd; /* # of atomic fragments received */
|
ifs6_atmfrag_rcvd; /* # of atomic fragments received */
|
||||||
ifs6_reass_fail; /* # of reass failures */
|
ifs6_reass_fail; /* # of reass failures */
|
||||||
/* NOTE: may not be packet count */
|
/* NOTE: may not be packet count */
|
||||||
/* NOTE: increment on final dst if */
|
/* NOTE: increment on final dst if */
|
||||||
ifs6_in_mcast; /* # of inbound multicast datagrams */
|
ifs6_in_mcast; /* # of inbound multicast datagrams */
|
||||||
ifs6_out_mcast; /* # of outbound multicast datagrams */
|
ifs6_out_mcast; /* # of outbound multicast datagrams */
|
||||||
|
|
||||||
ifs6_cantfoward_icmp6; /* # of ICMPv6 packets received for unreachable dest */
|
ifs6_cantfoward_icmp6; /* # of ICMPv6 packets received for unreachable dest */
|
||||||
ifs6_addr_expiry_cnt; /* # of address expiry events (excluding privacy addresses) */
|
ifs6_addr_expiry_cnt; /* # of address expiry events (excluding privacy addresses) */
|
||||||
ifs6_pfx_expiry_cnt; /* # of prefix expiry events */
|
ifs6_pfx_expiry_cnt; /* # of prefix expiry events */
|
||||||
ifs6_defrtr_expiry_cnt; /* # of default router expiry events */
|
ifs6_defrtr_expiry_cnt; /* # of default router expiry events */
|
||||||
};
|
};
|
||||||
struct in6_ifreq {
|
struct in6_ifreq {
|
||||||
char ifr_name[IFNAMSIZ];
|
char ifr_name[IFNAMSIZ];
|
||||||
union {
|
union {
|
||||||
struct sockaddr_in6 ifru_addr;
|
struct sockaddr_in6 ifru_addr;
|
||||||
struct sockaddr_in6 ifru_dstaddr;
|
struct sockaddr_in6 ifru_dstaddr;
|
||||||
int ifru_flags;
|
int ifru_flags;
|
||||||
int ifru_flags6;
|
int ifru_flags6;
|
||||||
int ifru_metric;
|
int ifru_metric;
|
||||||
int ifru_intval;
|
int ifru_intval;
|
||||||
caddr_t ifru_data;
|
caddr_t ifru_data;
|
||||||
struct in6_addrlifetime ifru_lifetime;
|
struct in6_addrlifetime ifru_lifetime;
|
||||||
struct in6_ifstat ifru_stat;
|
struct in6_ifstat ifru_stat;
|
||||||
struct icmp6_ifstat ifru_icmp6stat;
|
struct icmp6_ifstat ifru_icmp6stat;
|
||||||
u_int32_t ifru_scope_id[SCOPE6_ID_MAX];
|
u_int32_t ifru_scope_id[SCOPE6_ID_MAX];
|
||||||
} ifr_ifru;
|
} ifr_ifru;
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ union u_ifr_ifru {
|
||||||
ifru_lifetime: in6_addrlifetime,
|
ifru_lifetime: in6_addrlifetime,
|
||||||
ifru_stat: in6_ifstat,
|
ifru_stat: in6_ifstat,
|
||||||
ifru_icmp6stat: icmp6_ifstat,
|
ifru_icmp6stat: icmp6_ifstat,
|
||||||
ifru_scope_id: [u32; 16 /* SCOPE6_ID_MAX */],
|
ifru_scope_id: [u32; 16],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -328,7 +328,16 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
|
||||||
|
|
||||||
let mut ifr: in6_ifreq = zeroed();
|
let mut ifr: in6_ifreq = zeroed();
|
||||||
copy_nonoverlapping(dev.as_ptr(), ifr.ifr_name.as_mut_ptr().cast::<u8>(), if dev.len() > (ifr.ifr_name.len() - 1) { ifr.ifr_name.len() - 1 } else { dev.len() });
|
copy_nonoverlapping(dev.as_ptr(), ifr.ifr_name.as_mut_ptr().cast::<u8>(), if dev.len() > (ifr.ifr_name.len() - 1) { ifr.ifr_name.len() - 1 } else { dev.len() });
|
||||||
if libc::ioctl(s, if accept_ra { 132 /* SIOCAUTOCONF_START */ } else { 133 /* SIOCAUTOCONF_STOP */ }, (&mut ifr as *mut in6_ifreq).cast::<c_void>()) != 0 {
|
if libc::ioctl(
|
||||||
|
s,
|
||||||
|
if accept_ra {
|
||||||
|
132 /* SIOCAUTOCONF_START */
|
||||||
|
} else {
|
||||||
|
133 /* SIOCAUTOCONF_STOP */
|
||||||
|
},
|
||||||
|
(&mut ifr as *mut in6_ifreq).cast::<c_void>(),
|
||||||
|
) != 0
|
||||||
|
{
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,40 +348,40 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct ifkpi {
|
struct ifkpi {
|
||||||
unsigned int ifk_module_id;
|
unsigned int ifk_module_id;
|
||||||
unsigned int ifk_type;
|
unsigned int ifk_type;
|
||||||
union {
|
union {
|
||||||
void *ifk_ptr;
|
void *ifk_ptr;
|
||||||
int ifk_value;
|
int ifk_value;
|
||||||
} ifk_data;
|
} ifk_data;
|
||||||
};
|
};
|
||||||
struct ifdevmtu {
|
struct ifdevmtu {
|
||||||
int ifdm_current;
|
int ifdm_current;
|
||||||
int ifdm_min;
|
int ifdm_min;
|
||||||
int ifdm_max;
|
int ifdm_max;
|
||||||
};
|
};
|
||||||
struct ifreq {
|
struct ifreq {
|
||||||
#ifndef IFNAMSIZ
|
#ifndef IFNAMSIZ
|
||||||
#define IFNAMSIZ IF_NAMESIZE
|
#define IFNAMSIZ IF_NAMESIZE
|
||||||
#endif
|
#endif
|
||||||
char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
|
||||||
union {
|
union {
|
||||||
struct sockaddr ifru_addr;
|
struct sockaddr ifru_addr;
|
||||||
struct sockaddr ifru_dstaddr;
|
struct sockaddr ifru_dstaddr;
|
||||||
struct sockaddr ifru_broadaddr;
|
struct sockaddr ifru_broadaddr;
|
||||||
short ifru_flags;
|
short ifru_flags;
|
||||||
int ifru_metric;
|
int ifru_metric;
|
||||||
int ifru_mtu;
|
int ifru_mtu;
|
||||||
int ifru_phys;
|
int ifru_phys;
|
||||||
int ifru_media;
|
int ifru_media;
|
||||||
int ifru_intval;
|
int ifru_intval;
|
||||||
caddr_t ifru_data;
|
caddr_t ifru_data;
|
||||||
struct ifdevmtu ifru_devmtu;
|
struct ifdevmtu ifru_devmtu;
|
||||||
struct ifkpi ifru_kpi;
|
struct ifkpi ifru_kpi;
|
||||||
u_int32_t ifru_wake_flags;
|
u_int32_t ifru_wake_flags;
|
||||||
u_int32_t ifru_route_refcnt;
|
u_int32_t ifru_route_refcnt;
|
||||||
int ifru_cap[2];
|
int ifru_cap[2];
|
||||||
u_int32_t ifru_functional_type;
|
u_int32_t ifru_functional_type;
|
||||||
#define IFRTYPE_FUNCTIONAL_UNKNOWN 0
|
#define IFRTYPE_FUNCTIONAL_UNKNOWN 0
|
||||||
#define IFRTYPE_FUNCTIONAL_LOOPBACK 1
|
#define IFRTYPE_FUNCTIONAL_LOOPBACK 1
|
||||||
#define IFRTYPE_FUNCTIONAL_WIRED 2
|
#define IFRTYPE_FUNCTIONAL_WIRED 2
|
||||||
|
@ -382,7 +391,7 @@ struct ifreq {
|
||||||
#define IFRTYPE_FUNCTIONAL_INTCOPROC 6
|
#define IFRTYPE_FUNCTIONAL_INTCOPROC 6
|
||||||
#define IFRTYPE_FUNCTIONAL_COMPANIONLINK 7
|
#define IFRTYPE_FUNCTIONAL_COMPANIONLINK 7
|
||||||
#define IFRTYPE_FUNCTIONAL_LAST 7
|
#define IFRTYPE_FUNCTIONAL_LAST 7
|
||||||
} ifr_ifru;
|
} ifr_ifru;
|
||||||
#define ifr_addr ifr_ifru.ifru_addr /* address */
|
#define ifr_addr ifr_ifru.ifru_addr /* address */
|
||||||
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
|
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
|
||||||
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
|
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
|
||||||
|
@ -406,9 +415,9 @@ struct ifreq {
|
||||||
#define ifr_curcap ifr_ifru.ifru_cap[1] /* current capabilities */
|
#define ifr_curcap ifr_ifru.ifru_cap[1] /* current capabilities */
|
||||||
};
|
};
|
||||||
struct sockaddr_ndrv {
|
struct sockaddr_ndrv {
|
||||||
unsigned char snd_len;
|
unsigned char snd_len;
|
||||||
unsigned char snd_family;
|
unsigned char snd_family;
|
||||||
unsigned char snd_name[IFNAMSIZ]; /* from if.h */
|
unsigned char snd_name[IFNAMSIZ]; /* from if.h */
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -510,7 +519,9 @@ impl MacFethTap {
|
||||||
// Set sysctl for max if_fake MTU. This is allowed to fail since this sysctl doesn't
|
// Set sysctl for max if_fake MTU. This is allowed to fail since this sysctl doesn't
|
||||||
// exist on older versions of MacOS (and isn't required there). 16000 is larger than
|
// exist on older versions of MacOS (and isn't required there). 16000 is larger than
|
||||||
// anything ZeroTier supports. OS max is 16384 - some overhead.
|
// anything ZeroTier supports. OS max is 16384 - some overhead.
|
||||||
let _ = Command::new(SYSCTL).arg("net.link.fake.max_mtu").arg("10000").spawn().map(|mut c| { let _ = c.wait(); });
|
let _ = Command::new(SYSCTL).arg("net.link.fake.max_mtu").arg("10000").spawn().map(|mut c| {
|
||||||
|
let _ = c.wait();
|
||||||
|
});
|
||||||
|
|
||||||
// Create pair of feth interfaces and create MacFethDevice struct.
|
// Create pair of feth interfaces and create MacFethDevice struct.
|
||||||
let cmd = Command::new(IFCONFIG).arg(&device_name).arg("create").spawn();
|
let cmd = Command::new(IFCONFIG).arg(&device_name).arg("create").spawn();
|
||||||
|
@ -523,10 +534,7 @@ impl MacFethTap {
|
||||||
return Err(format!("unable to create device '{}': {}", peer_device_name.as_str(), cmd.err().unwrap().to_string()));
|
return Err(format!("unable to create device '{}': {}", peer_device_name.as_str(), cmd.err().unwrap().to_string()));
|
||||||
}
|
}
|
||||||
let _ = cmd.unwrap().wait();
|
let _ = cmd.unwrap().wait();
|
||||||
let device = MacFethDevice {
|
let device = MacFethDevice { name: device_name, peer_name: peer_device_name };
|
||||||
name: device_name,
|
|
||||||
peer_name: peer_device_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set link-layer (MAC) address of primary interface.
|
// Set link-layer (MAC) address of primary interface.
|
||||||
let cmd = Command::new(IFCONFIG).arg(&device.name).arg("lladdr").arg(mac.to_string()).spawn();
|
let cmd = Command::new(IFCONFIG).arg(&device.name).arg("lladdr").arg(mac.to_string()).spawn();
|
||||||
|
@ -584,46 +592,78 @@ impl MacFethTap {
|
||||||
// Set/get buffer length to use with reads from BPF device, trying to
|
// Set/get buffer length to use with reads from BPF device, trying to
|
||||||
// use up to BPF_BUFFER_SIZE bytes.
|
// use up to BPF_BUFFER_SIZE bytes.
|
||||||
let mut fl: c_int = BPF_BUFFER_SIZE as c_int;
|
let mut fl: c_int = BPF_BUFFER_SIZE as c_int;
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 102 /* BIOCSBLEN */, (&mut fl as *mut c_int).cast::<c_void>()) } != 0 {
|
if unsafe {
|
||||||
unsafe { libc::close(bpf_fd); }
|
libc::ioctl(bpf_fd as c_int, 102 /* BIOCSBLEN */, (&mut fl as *mut c_int).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
let bpf_read_size = fl as libc::size_t;
|
let bpf_read_size = fl as libc::size_t;
|
||||||
|
|
||||||
// Set immediate mode for "live" capture.
|
// Set immediate mode for "live" capture.
|
||||||
fl = 1;
|
fl = 1;
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 112 /* BIOCIMMEDIATE */, (&mut fl as *mut c_int).cast::<c_void>()) } != 0 {
|
if unsafe {
|
||||||
unsafe { libc::close(bpf_fd); }
|
libc::ioctl(bpf_fd as c_int, 112 /* BIOCIMMEDIATE */, (&mut fl as *mut c_int).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not send us back packets we inject or send.
|
// Do not send us back packets we inject or send.
|
||||||
fl = 0;
|
fl = 0;
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 119 /* BIOCSSEESENT */, (&mut fl as *mut c_int).cast::<c_void>()) } != 0 {
|
if unsafe {
|
||||||
unsafe { libc::close(bpf_fd); }
|
libc::ioctl(bpf_fd as c_int, 119 /* BIOCSSEESENT */, (&mut fl as *mut c_int).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind BPF to secondary feth device.
|
// Bind BPF to secondary feth device.
|
||||||
let mut bpf_ifr: ifreq = unsafe { std::mem::zeroed() };
|
let mut bpf_ifr: ifreq = unsafe { std::mem::zeroed() };
|
||||||
let peer_dev_name_bytes = device.peer_name.as_bytes();
|
let peer_dev_name_bytes = device.peer_name.as_bytes();
|
||||||
unsafe { copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), bpf_ifr.ifr_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() }); }
|
unsafe {
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 108 /* BIOCSETIF */, (&mut bpf_ifr as *mut ifreq).cast::<c_void>()) } != 0 {
|
copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), bpf_ifr.ifr_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() });
|
||||||
unsafe { libc::close(bpf_fd); }
|
}
|
||||||
|
if unsafe {
|
||||||
|
libc::ioctl(bpf_fd as c_int, 108 /* BIOCSETIF */, (&mut bpf_ifr as *mut ifreq).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include Ethernet header in BPF captures.
|
// Include Ethernet header in BPF captures.
|
||||||
fl = 1;
|
fl = 1;
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 117 /* BIOCSHDRCMPLT */, (&mut fl as *mut c_int).cast::<c_void>()) } != 0 {
|
if unsafe {
|
||||||
unsafe { libc::close(bpf_fd); }
|
libc::ioctl(bpf_fd as c_int, 117 /* BIOCSHDRCMPLT */, (&mut fl as *mut c_int).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set promiscuous mode so bridging can work.
|
// Set promiscuous mode so bridging can work.
|
||||||
fl = 1;
|
fl = 1;
|
||||||
if unsafe { libc::ioctl(bpf_fd as c_int, 105 /* BIOCPROMISC */, (&mut fl as *mut c_int).cast::<c_void>()) } != 0 {
|
if unsafe {
|
||||||
unsafe { libc::close(bpf_fd); }
|
libc::ioctl(bpf_fd as c_int, 105 /* BIOCPROMISC */, (&mut fl as *mut c_int).cast::<c_void>())
|
||||||
|
} != 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to configure BPF device"));
|
return Err(String::from("unable to configure BPF device"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,30 +693,46 @@ impl MacFethTap {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if t.is_err() {
|
if t.is_err() {
|
||||||
unsafe { libc::close(bpf_fd); }
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to start thread"));
|
return Err(String::from("unable to start thread"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create AF_NDRV socket used to inject packets. We could inject with BPF but that has
|
// Create AF_NDRV socket used to inject packets. We could inject with BPF but that has
|
||||||
// a hard MTU limit of 2048 so we have to use AF_NDRV instead. Performance is probably
|
// a hard MTU limit of 2048 so we have to use AF_NDRV instead. Performance is probably
|
||||||
// the same, but it means another socket.
|
// the same, but it means another socket.
|
||||||
let ndrv_fd = unsafe { libc::socket(27 /* AF_NDRV */, libc::SOCK_RAW, 0) };
|
let ndrv_fd = unsafe {
|
||||||
|
libc::socket(27 /* AF_NDRV */, libc::SOCK_RAW, 0)
|
||||||
|
};
|
||||||
if ndrv_fd < 0 {
|
if ndrv_fd < 0 {
|
||||||
unsafe { libc::close(bpf_fd); }
|
unsafe {
|
||||||
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to create AF_NDRV socket"));
|
return Err(String::from("unable to create AF_NDRV socket"));
|
||||||
}
|
}
|
||||||
let mut ndrv_sa: sockaddr_ndrv = unsafe { std::mem::zeroed() };
|
let mut ndrv_sa: sockaddr_ndrv = unsafe { std::mem::zeroed() };
|
||||||
ndrv_sa.snd_len = std::mem::size_of::<sockaddr_ndrv>() as c_uchar;
|
ndrv_sa.snd_len = std::mem::size_of::<sockaddr_ndrv>() as c_uchar;
|
||||||
ndrv_sa.snd_family = 27 /* AF_NDRV */;
|
ndrv_sa.snd_family = 27 /* AF_NDRV */;
|
||||||
unsafe { copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), ndrv_sa.snd_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() }); }
|
unsafe {
|
||||||
|
copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), ndrv_sa.snd_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() });
|
||||||
|
}
|
||||||
if unsafe { libc::bind(ndrv_fd, (&ndrv_sa as *const sockaddr_ndrv).cast(), std::mem::size_of::<sockaddr_ndrv>() as libc::socklen_t) } != 0 {
|
if unsafe { libc::bind(ndrv_fd, (&ndrv_sa as *const sockaddr_ndrv).cast(), std::mem::size_of::<sockaddr_ndrv>() as libc::socklen_t) } != 0 {
|
||||||
unsafe { libc::close(bpf_fd); }
|
unsafe {
|
||||||
unsafe { libc::close(ndrv_fd); }
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
libc::close(ndrv_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to bind AF_NDRV socket"));
|
return Err(String::from("unable to bind AF_NDRV socket"));
|
||||||
}
|
}
|
||||||
if unsafe { libc::connect(ndrv_fd, (&ndrv_sa as *const sockaddr_ndrv).cast(), std::mem::size_of::<sockaddr_ndrv>() as libc::socklen_t) } != 0 {
|
if unsafe { libc::connect(ndrv_fd, (&ndrv_sa as *const sockaddr_ndrv).cast(), std::mem::size_of::<sockaddr_ndrv>() as libc::socklen_t) } != 0 {
|
||||||
unsafe { libc::close(bpf_fd); }
|
unsafe {
|
||||||
unsafe { libc::close(ndrv_fd); }
|
libc::close(bpf_fd);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
libc::close(ndrv_fd);
|
||||||
|
}
|
||||||
return Err(String::from("unable to connect AF_NDRV socket"));
|
return Err(String::from("unable to connect AF_NDRV socket"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,7 +744,7 @@ impl MacFethTap {
|
||||||
ndrv_fd,
|
ndrv_fd,
|
||||||
bpf_fd,
|
bpf_fd,
|
||||||
bpf_no,
|
bpf_no,
|
||||||
bpf_read_thread: Cell::new(Some(t.unwrap()))
|
bpf_read_thread: Cell::new(Some(t.unwrap())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,10 +814,7 @@ impl VNIC for MacFethTap {
|
||||||
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 {
|
libc::iovec { iov_base: hdr.as_mut_ptr().cast(), iov_len: 14 },
|
||||||
iov_base: hdr.as_mut_ptr().cast(),
|
|
||||||
iov_len: 14,
|
|
||||||
},
|
|
||||||
libc::iovec {
|
libc::iovec {
|
||||||
iov_base: transmute(data), // have to "cast away const" even though data is not modified by writev()
|
iov_base: transmute(data), // have to "cast away const" even though data is not modified by writev()
|
||||||
iov_len: len as libc::size_t,
|
iov_len: len as libc::size_t,
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod vnic;
|
|
||||||
mod common;
|
mod common;
|
||||||
|
mod vnic;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod mac_feth_tap;
|
mod mac_feth_tap;
|
||||||
|
|
|
@ -6,19 +6,20 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use zerotier_network_hypervisor::vl1::{InetAddress, MAC};
|
||||||
use zerotier_network_hypervisor::vl2::MulticastGroup;
|
use zerotier_network_hypervisor::vl2::MulticastGroup;
|
||||||
|
|
||||||
/// Virtual network interface
|
/// Virtual network interface
|
||||||
pub trait VNIC {
|
pub trait VNIC {
|
||||||
/// Add a new IPv4 or IPv6 address to this interface, returning true on success.
|
/// Add a new IPv4 or IPv6 address to this interface, returning true on success.
|
||||||
fn add_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
|
fn add_ip(&self, ip: &InetAddress) -> bool;
|
||||||
|
|
||||||
/// Remove an IPv4 or IPv6 address, returning true on success.
|
/// Remove an IPv4 or IPv6 address, returning true on success.
|
||||||
/// Nothing happens if the address is not found.
|
/// Nothing happens if the address is not found.
|
||||||
fn remove_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
|
fn remove_ip(&self, ip: &InetAddress) -> bool;
|
||||||
|
|
||||||
/// Enumerate all IPs on this interface including ones assigned outside ZeroTier.
|
/// Enumerate all IPs on this interface including ones assigned outside ZeroTier.
|
||||||
fn ips(&self) -> Vec<zerotier_core::InetAddress>;
|
fn ips(&self) -> Vec<InetAddress>;
|
||||||
|
|
||||||
/// Get the OS-specific device name for this interface, e.g. zt## or tap##.
|
/// Get the OS-specific device name for this interface, e.g. zt## or tap##.
|
||||||
fn device_name(&self) -> String;
|
fn device_name(&self) -> String;
|
||||||
|
@ -30,5 +31,5 @@ pub trait VNIC {
|
||||||
fn get_multicast_groups(&self) -> std::collections::BTreeSet<MulticastGroup>;
|
fn get_multicast_groups(&self) -> std::collections::BTreeSet<MulticastGroup>;
|
||||||
|
|
||||||
/// Inject an Ethernet frame into this port.
|
/// Inject an Ethernet frame into this port.
|
||||||
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool;
|
fn put(&self, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue