More topology stuff for VL2.

This commit is contained in:
Adam Ierymenko 2023-03-09 18:38:37 -05:00
parent c05039f30d
commit 86ffb2861b
5 changed files with 170 additions and 9 deletions

View file

@ -1,3 +1,61 @@
pub struct Member {}
use std::borrow::Cow;
pub struct Topology {}
use zerotier_utils::blob::Blob;
use zerotier_utils::flatsortedmap::FlatSortedMap;
use serde::{Deserialize, Serialize};
use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
use crate::vl1::inetaddress::InetAddress;
use crate::vl2::rule::Rule;
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)]
pub struct Member<'a> {
#[serde(skip_serializing_if = "u64_zero")]
#[serde(default)]
pub flags: u64,
#[serde(skip_serializing_if = "cow_str_is_empty")]
#[serde(default)]
pub name: Cow<'a, str>,
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)]
pub struct Topology<'a> {
pub timestamp: i64,
#[serde(skip_serializing_if = "cow_str_is_empty")]
#[serde(default)]
pub name: Cow<'a, str>,
#[serde(skip_serializing_if = "slice_is_empty")]
#[serde(default)]
pub rules: Cow<'a, [Rule]>,
#[serde(skip_serializing_if = "FlatSortedMap::is_empty")]
#[serde(default)]
pub dns_resolvers: FlatSortedMap<'a, Cow<'a, str>, InetAddress>,
#[serde(skip_serializing_if = "FlatSortedMap::is_empty")]
#[serde(default)]
pub dns_names: FlatSortedMap<'a, Cow<'a, str>, InetAddress>,
#[serde(skip_serializing_if = "FlatSortedMap::is_empty")]
#[serde(default)]
pub members: FlatSortedMap<'a, Blob<IDENTITY_FINGERPRINT_SIZE>, Member<'a>>,
}
#[inline(always)]
fn u64_zero(i: &u64) -> bool {
*i == 0
}
#[inline(always)]
fn cow_str_is_empty<'a>(s: &Cow<'a, str>) -> bool {
s.is_empty()
}
#[inline(always)]
fn slice_is_empty<T, S: AsRef<[T]>>(x: &S) -> bool {
x.as_ref().is_empty()
}

View file

@ -31,11 +31,6 @@ pub struct NetworkConfig {
#[serde(default)]
pub name: String,
/// A human-readable message for members of this network (V2 only)
#[serde(skip_serializing_if = "String::is_empty")]
#[serde(default)]
pub motd: String,
/// True if network has access control (the default)
pub private: bool,
@ -95,7 +90,6 @@ impl NetworkConfig {
network_id,
issued_to,
name: String::new(),
motd: String::new(),
private: true,
timestamp: 0,
mtu: 0,

View file

@ -7,6 +7,7 @@
*/
use std::fmt::Debug;
use std::hash::Hash;
use serde::ser::SerializeTuple;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -72,6 +73,27 @@ impl<const L: usize> ToString for Blob<L> {
}
}
impl<const L: usize> PartialOrd for Blob<L> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<const L: usize> Ord for Blob<L> {
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<const L: usize> Hash for Blob<L> {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<const L: usize> Debug for Blob<L> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -118,7 +140,7 @@ impl<'de, const L: usize> serde::de::Visitor<'de> for BlobVisitor<L> {
impl<'de, const L: usize> Deserialize<'de> for Blob<L> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Blob<L>, D::Error>
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{

View file

@ -0,0 +1,86 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c) ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::borrow::Cow;
use std::iter::{FromIterator, Iterator};
use serde::{Deserialize, Serialize};
/// A simple flat sorted map backed by a vector and binary search.
///
/// This doesn't support gradual adding of keys or removal of keys, but only construction
/// from an iterator of keys and values. It also implements Serialize and Deserialize and
/// is mainly intended for memory and space efficient serializable lookup tables.
///
/// If the iterator supplies more than one key with different values, which of these is
/// included is undefined.
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
#[repr(transparent)]
pub struct FlatSortedMap<'a, K: Eq + Ord + Clone, V: Clone>(Cow<'a, [(K, V)]>);
impl<'a, K: Eq + Ord + Clone, V: Clone> FromIterator<(K, V)> for FlatSortedMap<'a, K, V> {
#[inline]
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut tmp = Vec::from_iter(iter);
tmp.sort_unstable_by(|a, b| a.0.cmp(&b.0));
tmp.dedup_by(|a, b| a.0.eq(&b.0));
Self(Cow::Owned(tmp))
}
}
impl<'a, K: Eq + Ord + Clone, V: Clone> Default for FlatSortedMap<'a, K, V> {
#[inline(always)]
fn default() -> Self {
Self(Cow::Owned(Vec::new()))
}
}
impl<'a, K: Eq + Ord + Clone, V: Clone> FlatSortedMap<'a, K, V> {
#[inline]
pub fn get(&self, k: &K) -> Option<&V> {
if let Ok(idx) = self.0.binary_search_by(|a| a.0.cmp(k)) {
Some(unsafe { &self.0.get_unchecked(idx).1 })
} else {
None
}
}
#[inline]
pub fn contains(&self, k: &K) -> bool {
self.0.binary_search_by(|a| a.0.cmp(k)).is_ok()
}
/// Returns true if this map is valid, meaning that it contains only one of each key and is sorted.
#[inline]
pub fn is_valid(&self) -> bool {
let l = self.0.len();
if l > 1 {
for i in 1..l {
if unsafe { !self.0.get_unchecked(i - 1).0.cmp(&self.0.get_unchecked(i).0).is_lt() } {
return false;
}
}
}
return true;
}
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = &(K, V)> {
self.0.iter()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}

View file

@ -14,6 +14,7 @@ pub mod dictionary;
pub mod error;
#[allow(unused)]
pub mod exitcode;
pub mod flatsortedmap;
pub mod gate;
pub mod gatherarray;
pub mod hex;