Merge branch 'master' of github.com:zerotier/tetanus

This commit is contained in:
Adam Ierymenko 2022-06-27 13:20:31 -04:00
commit f571ce7aa3
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
9 changed files with 473 additions and 37 deletions

View file

@ -28,9 +28,14 @@ serde = { version = "^1", features = ["derive"], default-features = false }
rand = "*"
serde_json = "*"
serde_cbor = "*"
criterion = "0.3"
[target."cfg(not(windows))".dependencies]
libc = "^0"
[target."cfg(windows)".dependencies]
winapi = { version = "^0", features = ["ws2tcpip"] }
[[bench]]
name = "benchmark_identity"
harness = false

View file

@ -0,0 +1,13 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::time::Duration;
use zerotier_network_hypervisor::vl1::Identity;
pub fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("basic");
group.measurement_time(Duration::new(30, 0));
group.bench_function("identity generation", |b| b.iter(|| Identity::generate()));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View file

@ -356,22 +356,22 @@ impl<const L: usize> Buffer<L> {
}
#[inline(always)]
pub fn u16_at(&self, ptr: usize) -> std::io::Result<u64> {
pub fn u16_at(&self, ptr: usize) -> std::io::Result<u16> {
let end = ptr + 2;
debug_assert!(end <= L);
if end <= self.0 {
Ok(u64::from_be(unsafe { self.read_obj_internal(ptr) }))
Ok(u16::from_be(unsafe { self.read_obj_internal(ptr) }))
} else {
Err(overflow_err())
}
}
#[inline(always)]
pub fn u32_at(&self, ptr: usize) -> std::io::Result<u64> {
pub fn u32_at(&self, ptr: usize) -> std::io::Result<u32> {
let end = ptr + 4;
debug_assert!(end <= L);
if end <= self.0 {
Ok(u64::from_be(unsafe { self.read_obj_internal(ptr) }))
Ok(u32::from_be(unsafe { self.read_obj_internal(ptr) }))
} else {
Err(overflow_err())
}
@ -562,3 +562,168 @@ impl<const L: usize> PoolFactory<Buffer<L>> for PooledBufferFactory<L> {
obj.clear();
}
}
#[cfg(test)]
mod tests {
use super::Buffer;
#[test]
fn buffer_basic_u64() {
let mut b = Buffer::<8>::new();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
assert!(b.append_u64(1234).is_ok());
assert_eq!(b.len(), 8);
assert!(!b.is_empty());
assert_eq!(b.read_u64(&mut 0).unwrap(), 1234);
b.clear();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
}
#[test]
fn buffer_basic_u32() {
let mut b = Buffer::<4>::new();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
assert!(b.append_u32(1234).is_ok());
assert_eq!(b.len(), 4);
assert!(!b.is_empty());
assert_eq!(b.read_u32(&mut 0).unwrap(), 1234);
b.clear();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
}
#[test]
fn buffer_basic_u16() {
let mut b = Buffer::<2>::new();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
assert!(b.append_u16(1234).is_ok());
assert_eq!(b.len(), 2);
assert!(!b.is_empty());
assert_eq!(b.read_u16(&mut 0).unwrap(), 1234);
b.clear();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
}
#[test]
fn buffer_basic_u8() {
let mut b = Buffer::<1>::new();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
assert!(b.append_u8(128).is_ok());
assert_eq!(b.len(), 1);
assert!(!b.is_empty());
assert_eq!(b.read_u8(&mut 0).unwrap(), 128);
b.clear();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
}
#[test]
fn buffer_bytes() {
const SIZE: usize = 100;
for _ in 0..1000 {
let mut v: Vec<u8> = Vec::with_capacity(SIZE);
v.fill_with(|| rand::random());
let mut b = Buffer::<SIZE>::new();
assert!(b.append_bytes(&v).is_ok());
assert_eq!(b.read_bytes(v.len(), &mut 0).unwrap(), &v);
let mut v: [u8; SIZE] = [0u8; SIZE];
v.fill_with(|| rand::random());
let mut b = Buffer::<SIZE>::new();
assert!(b.append_bytes_fixed(&v).is_ok());
assert_eq!(b.read_bytes_fixed(&mut 0).unwrap(), &v);
// FIXME: append calls for _get_mut style do not accept anything to append, so we can't
// test them.
//
// let mut b = Buffer::<SIZE>::new();
// let res = b.append_bytes_fixed_get_mut(&v);
// assert!(res.is_ok());
// let byt = res.unwrap();
// assert_eq!(byt, &v);
}
}
#[test]
fn buffer_at() {
const SIZE: usize = 100;
for _ in 0..1000 {
let mut v = [0u8; SIZE];
let mut idx: usize = rand::random::<usize>() % SIZE;
v[idx] = 1;
let mut b = Buffer::<SIZE>::new();
assert!(b.append_bytes(&v).is_ok());
let res = b.bytes_fixed_at::<1>(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap()[0], 1);
let res = b.bytes_fixed_mut_at::<1>(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap()[0], 1);
// the uX integer tests require a little more massage. we're going to rewind the index
// by 8, correcting to 0 if necessary, and then write 1's in. our numbers will be
// consistent this way.
v[idx] = 0;
if idx < 8 {
idx = 0;
} else if (idx + 7) >= SIZE {
idx -= 7;
}
for i in idx..(idx + 8) {
v[i] = 1;
}
let mut b = Buffer::<SIZE>::new();
assert!(b.append_bytes(&v).is_ok());
let res = b.u8_at(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 1);
let res = b.u16_at(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 257);
let res = b.u32_at(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 16843009);
let res = b.u64_at(idx);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 72340172838076673);
}
}
#[test]
fn buffer_sizing() {
const SIZE: usize = 100;
for _ in 0..1000 {
let v = [0u8; SIZE];
let mut b = Buffer::<SIZE>::new();
assert!(b.append_bytes(&v).is_ok());
assert_eq!(b.len(), SIZE);
b.set_size(10);
assert_eq!(b.len(), 10);
unsafe {
b.set_size_unchecked(8675309);
}
assert_eq!(b.len(), 8675309);
}
}
}

View file

@ -14,6 +14,9 @@ pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];
/// A value for ticks that indicates that something never happened, and is thus very long before zero ticks.
pub(crate) const NEVER_HAPPENED_TICKS: i64 = -2147483648;
#[cfg(test)]
pub mod testutil;
#[cfg(feature = "debug_events")]
#[allow(unused_macros)]
macro_rules! debug_event {

View file

@ -0,0 +1,4 @@
// from zeronsd
pub fn randstring(len: u8) -> String {
(0..len).map(|_| (rand::random::<u8>() % 26) + 'a' as u8).map(|c| if rand::random::<bool>() { (c as char).to_ascii_uppercase() } else { c as char }).map(|c| c.to_string()).collect::<Vec<String>>().join("")
}

View file

@ -61,10 +61,12 @@ impl CareOf {
}
}
#[allow(unused)]
pub fn verify(&self, signer: &Identity) -> bool {
signer.verify(self.to_bytes_internal(false).as_slice(), self.signature.as_slice())
}
#[allow(unused)]
pub fn contains(&self, id: &Identity) -> bool {
self.fingerprints.binary_search(&id.fingerprint).is_ok()
}

View file

@ -212,14 +212,9 @@ mod tests {
type TypeMap = HashMap<String, Type>;
use std::collections::HashMap;
use crate::util::testutil::randstring;
use crate::vl1::dictionary::{Dictionary, BOOL_TRUTH};
// from zeronsd
pub fn randstring(len: u8) -> String {
(0..len).map(|_| (rand::random::<u8>() % 26) + 'a' as u8).map(|c| if rand::random::<bool>() { (c as char).to_ascii_uppercase() } else { c as char }).map(|c| c.to_string()).collect::<Vec<String>>().join("")
}
use std::collections::HashMap;
fn make_dictionary() -> (Dictionary, TypeMap) {
let mut d = Dictionary::new();

View file

@ -30,7 +30,7 @@ pub(crate) const MAX_MARSHAL_SIZE: usize = 1024;
/// A communication endpoint on the network where a ZeroTier node can be reached.
///
/// Currently only a few of these are supported. The rest are reserved for future use.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Endpoint {
/// A null endpoint.
Nil,
@ -131,7 +131,7 @@ impl Marshalable for Endpoint {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
match self {
Endpoint::Nil => buf.append_u8(TYPE_NIL),
Endpoint::Nil => buf.append_u8(16 + TYPE_NIL),
Endpoint::ZeroTier(a, h) => {
buf.append_u8(16 + TYPE_ZEROTIER)?;
buf.append_bytes_fixed(&a.to_bytes())?;
@ -310,7 +310,7 @@ impl ToString for Endpoint {
Endpoint::Ip(ip) => format!("ip:{}", ip.to_ip_string()),
Endpoint::IpUdp(ip) => format!("udp:{}", ip.to_string()),
Endpoint::IpTcp(ip) => format!("tcp:{}", ip.to_string()),
Endpoint::Http(url) => url.clone(), // http or https
Endpoint::Http(url) => format!("url:{}", url.clone()), // http or https
Endpoint::WebRTC(offer) => format!("webrtc:{}", base64::encode_config(offer.as_slice(), base64::URL_SAFE_NO_PAD)),
Endpoint::ZeroTierEncap(a, ah) => format!("zte:{}-{}", a.to_string(), base64::encode_config(ah, base64::URL_SAFE_NO_PAD)),
}
@ -354,7 +354,7 @@ impl FromStr for Endpoint {
"ip" => return Ok(Endpoint::Ip(InetAddress::from_str(endpoint_data)?)),
"udp" => return Ok(Endpoint::IpUdp(InetAddress::from_str(endpoint_data)?)),
"tcp" => return Ok(Endpoint::IpTcp(InetAddress::from_str(endpoint_data)?)),
"http" | "https" => return Ok(Endpoint::Http(endpoint_data.into())),
"url" => return Ok(Endpoint::Http(endpoint_data.into())),
"webrtc" => {
let offer = base64::decode_config(endpoint_data, base64::URL_SAFE_NO_PAD);
if offer.is_ok() {
@ -425,3 +425,274 @@ impl<'de> Deserialize<'de> for Endpoint {
}
}
}
#[cfg(test)]
mod tests {
use super::{Endpoint, MAX_MARSHAL_SIZE};
use crate::{
util::marshalable::Marshalable,
vl1::{
protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE, IDENTITY_FINGERPRINT_SIZE},
Address,
},
};
#[test]
fn endpoint_default() {
let e: Endpoint = Default::default();
assert!(matches!(e, Endpoint::Nil))
}
#[test]
fn endpoint_from_bytes() {
let v = [0u8; MAX_MARSHAL_SIZE];
assert!(Endpoint::from_bytes(&v).is_none());
}
#[test]
fn endpoint_marshal_nil() {
use crate::util::buffer::Buffer;
let n = Endpoint::Nil;
let mut buf = Buffer::<1>::new();
let res = n.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let n2 = res.unwrap();
assert_eq!(n, n2);
}
#[test]
fn endpoint_marshal_zerotier() {
use crate::util::buffer::Buffer;
for _ in 0..1000 {
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
let zte = Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash);
const TMP: usize = IDENTITY_FINGERPRINT_SIZE + 8;
let mut buf = Buffer::<TMP>::new();
let res = zte.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let zte2 = res.unwrap();
assert_eq!(zte, zte2);
}
}
#[test]
fn endpoint_marshal_zerotier_encap() {
use crate::util::buffer::Buffer;
for _ in 0..1000 {
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
let zte = Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash);
const TMP: usize = IDENTITY_FINGERPRINT_SIZE + 8;
let mut buf = Buffer::<TMP>::new();
let res = zte.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let zte2 = res.unwrap();
assert_eq!(zte, zte2);
}
}
#[test]
fn endpoint_marshal_mac() {
use crate::util::buffer::Buffer;
for _ in 0..1000 {
let mac = crate::vl1::MAC::from_u64(rand::random()).unwrap();
for e in [Endpoint::Ethernet(mac.clone()), Endpoint::WifiDirect(mac.clone()), Endpoint::Bluetooth(mac.clone())] {
let mut buf = Buffer::<7>::new();
let res = e.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let e2 = res.unwrap();
assert_eq!(e, e2);
}
}
}
#[test]
fn endpoint_marshal_inetaddress() {
use crate::util::buffer::Buffer;
for _ in 0..1000 {
let mut v = [0u8; 16];
v.fill_with(|| rand::random());
let inet = crate::vl1::InetAddress::from_ip_port(&v, 1234);
for e in [Endpoint::Ip(inet.clone()), Endpoint::IpTcp(inet.clone()), Endpoint::IpUdp(inet.clone())] {
let mut buf = Buffer::<20>::new();
let res = e.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let e2 = res.unwrap();
assert_eq!(e, e2);
}
}
}
#[test]
fn endpoint_marshal_http() {
use crate::util::buffer::Buffer;
use crate::util::testutil::randstring;
for _ in 0..1000 {
let http = Endpoint::Http(randstring(30));
let mut buf = Buffer::<33>::new();
assert!(http.marshal(&mut buf).is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let http2 = res.unwrap();
assert_eq!(http, http2);
}
}
#[test]
fn endpoint_marshal_webrtc() {
use crate::util::buffer::Buffer;
for _ in 0..1000 {
let mut v = Vec::with_capacity(100);
v.fill_with(|| rand::random());
let rtc = Endpoint::WebRTC(v);
let mut buf = Buffer::<102>::new();
assert!(rtc.marshal(&mut buf).is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let rtc2 = res.unwrap();
assert_eq!(rtc, rtc2);
}
}
#[test]
fn endpoint_to_from_string() {
use crate::util::testutil::randstring;
use std::str::FromStr;
for _ in 0..1000 {
let mut v = Vec::with_capacity(100);
v.fill_with(|| rand::random());
let rtc = Endpoint::WebRTC(v);
assert_ne!(rtc.to_string().len(), 0);
assert!(rtc.to_string().starts_with("webrtc"));
let rtc2 = Endpoint::from_str(&rtc.to_string()).unwrap();
assert_eq!(rtc, rtc2);
let http = Endpoint::Http(randstring(30));
assert_ne!(http.to_string().len(), 0);
assert!(http.to_string().starts_with("url"));
let http2 = Endpoint::from_str(&http.to_string()).unwrap();
assert_eq!(http, http2);
let mut v = [0u8; 16];
v.fill_with(|| rand::random());
let inet = crate::vl1::InetAddress::from_ip_port(&v, 0);
let ip = Endpoint::Ip(inet.clone());
assert_ne!(ip.to_string().len(), 0);
assert!(ip.to_string().starts_with("ip"));
let ip2 = Endpoint::from_str(&ip.to_string()).unwrap();
assert_eq!(ip, ip2);
let inet = crate::vl1::InetAddress::from_ip_port(&v, 1234);
for e in [(Endpoint::IpTcp(inet.clone()), "tcp"), (Endpoint::IpUdp(inet.clone()), "udp")] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
let mac = crate::vl1::MAC::from_u64(rand::random()).unwrap();
for e in [(Endpoint::Ethernet(mac.clone()), "eth"), (Endpoint::WifiDirect(mac.clone()), "wifip2p"), (Endpoint::Bluetooth(mac.clone()), "bt")] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
for e in [(Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash), "zt"), (Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash), "zte")] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
assert_eq!(Endpoint::Nil.to_string(), "nil");
}
}
}

View file

@ -861,8 +861,6 @@ mod tests {
use crate::util::marshalable::Marshalable;
use crate::vl1::identity::*;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
#[allow(unused_imports)]
use zerotier_core_crypto::hex;
#[test]
@ -974,24 +972,4 @@ mod tests {
assert!(Identity::from_str(ids.as_str()).unwrap() == id);
}
}
#[test]
fn benchmark_generate() {
let mut count = 0;
let run_time = Duration::from_secs(5);
let start = SystemTime::now();
let mut end;
let mut duration;
loop {
let _id = Identity::generate();
//println!("{}", _id.to_string());
end = SystemTime::now();
duration = end.duration_since(start).unwrap();
count += 1;
if duration >= run_time {
break;
}
}
println!("benchmark: V1 identity generation: {} ms / identity (average)", (duration.as_millis() as f64) / (count as f64));
}
}