Add a bunch of stuff to InetAddress, clean up, fix some build errors in service.

This commit is contained in:
Adam Ierymenko 2022-04-26 16:19:10 -04:00
parent 6d59dd77c4
commit 3bdefc1d83
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
20 changed files with 355 additions and 1526 deletions

View file

@ -76,9 +76,16 @@ impl P384PublicKey {
let r = BigNum::from_slice(&signature[0..48]);
let s = BigNum::from_slice(&signature[48..96]);
if r.is_ok() && s.is_ok() {
let sig = EcdsaSig::from_private_components(r.unwrap(), s.unwrap());
if sig.is_ok() {
return sig.unwrap().verify(&SHA384::hash(msg), self.key.as_ref()).unwrap_or(false);
let r = r.unwrap();
let s = s.unwrap();
let z = BigNum::from_u32(0).unwrap();
// Check that r and s are >=1 just in case the OpenSSL version or an OpenSSL API lookalike is
// vulnerable to this, since a bunch of vulnerabilities involving zero r/s just made the rounds.
if r.gt(&z) && s.gt(&z) {
let sig = EcdsaSig::from_private_components(r, s);
if sig.is_ok() {
return sig.unwrap().verify(&SHA384::hash(msg), self.key.as_ref()).unwrap_or(false);
}
}
}
}

View file

@ -18,7 +18,6 @@ lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-
dashmap = "^4"
parking_lot = "^0"
lazy_static = "^1"
highway = "^0"
serde = "^1"
serde_derive = "^1"
serde_json = "^1"

View file

@ -6,7 +6,6 @@
* https://www.zerotier.com/
*/
use crate::util::{array_range, array_range_mut};
use std::io::Write;
use std::mem::{size_of, MaybeUninit};
@ -77,7 +76,7 @@ impl<const L: usize> Buffer<L> {
#[inline(always)]
pub fn as_range_fixed<const START: usize, const LEN: usize>(&self) -> &[u8; LEN] {
array_range::<u8, L, START, LEN>(&self.1)
crate::util::byte_array_range::<L, START, LEN>(&self.1)
}
#[inline(always)]
@ -92,7 +91,7 @@ impl<const L: usize> Buffer<L> {
#[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)
crate::util::byte_array_range_mut::<L, START, LEN>(&mut self.1)
}
/// Get all bytes after a given position.

View file

@ -15,35 +15,18 @@ pub use zerotier_core_crypto::varint;
pub(crate) const ZEROES: [u8; 64] = [0_u8; 64];
/// Obtain a reference to a sub-array within an existing array.
/// Obtain a reference to a sub-array within an existing byte array.
#[inline(always)]
pub(crate) fn array_range<T, const A: usize, const START: usize, const LEN: usize>(a: &[T; A]) -> &[T; LEN] {
pub(crate) fn byte_array_range<const A: usize, const START: usize, const LEN: usize>(a: &[u8; A]) -> &[u8; LEN] {
assert!((START + LEN) <= A);
unsafe { &*a.as_ptr().add(START).cast::<[T; LEN]>() }
unsafe { &*a.as_ptr().add(START).cast::<[u8; LEN]>() }
}
/// Obtain a reference to a sub-array within an existing array.
/// Obtain a reference to a sub-array within an existing byte array.
#[inline(always)]
pub(crate) fn array_range_mut<T, const A: usize, const START: usize, const LEN: usize>(a: &mut [T; A]) -> &mut [T; LEN] {
pub(crate) fn byte_array_range_mut<const A: usize, const START: usize, const LEN: usize>(a: &mut [u8; A]) -> &mut [u8; LEN] {
assert!((START + LEN) <= A);
unsafe { &mut *a.as_mut_ptr().add(START).cast::<[T; LEN]>() }
}
/// Cast a u64 to a byte array.
#[inline(always)]
pub(crate) fn u64_as_bytes(i: &u64) -> &[u8; 8] {
unsafe { &*(i as *const u64).cast() }
}
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()];
}
/// 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.
#[inline(always)]
pub(crate) fn highwayhasher() -> highway::HighwayHasher {
highway::HighwayHasher::new(highway::Key(HIGHWAYHASHER_KEY.clone()))
unsafe { &mut *a.as_mut_ptr().add(START).cast::<[u8; LEN]>() }
}
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
@ -56,7 +39,8 @@ pub(crate) fn hash64_noncrypt(mut x: u64) -> u64 {
x ^ x.wrapping_shr(31)
}
/// A hasher for maps that just returns u64 values as-is.
/// A hasher for u64 maps that just returns u64 values as-is.
///
/// Used with things like ZeroTier addresses and network IDs that are already randomly distributed.
#[derive(Copy, Clone)]
pub(crate) struct U64NoOpHasher(u64);

View file

@ -56,14 +56,7 @@ impl Address {
#[inline(always)]
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
let b = buf.append_bytes_fixed_get_mut::<ADDRESS_SIZE>()?;
let i = self.0.get();
(*b)[0] = (i >> 32) as u8;
(*b)[1] = (i >> 24) as u8;
(*b)[2] = (i >> 16) as u8;
(*b)[3] = (i >> 8) as u8;
(*b)[4] = i as u8;
Ok(())
buf.append_bytes(&self.0.get().to_be_bytes()[8 - ADDRESS_SIZE..])
}
#[inline(always)]

View file

@ -20,7 +20,7 @@ use lazy_static::lazy_static;
use serde_derive::{Deserialize, Serialize};
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::*;
use zerotier_core_crypto::hex;
use zerotier_core_crypto::p384::*;
use zerotier_core_crypto::salsa::Salsa;
@ -811,6 +811,7 @@ mod tests {
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
use std::str::FromStr;
use std::time::{Duration, SystemTime};
#[allow(unused_imports)]
use zerotier_core_crypto::hex;
const GOOD_V0_IDENTITIES: [&'static str; 10] = [

View file

@ -9,10 +9,12 @@
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::mem::{size_of, transmute_copy, zeroed, MaybeUninit};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::ptr::{copy_nonoverlapping, null, slice_from_raw_parts, write_bytes};
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(windows)]
use winapi::um::winsock2;
@ -62,6 +64,10 @@ pub enum IpScope {
/// The ZeroTier core uses this in preference to std::net stuff so this can be
/// directly used via the C API or with C socket I/O functions.
///
/// This supports into() and from() the std::net types and also has a custom
/// serde serializer that serializes it in ZT protocol binary form for binary
/// formats and canonical ZT string form for string formats.
///
/// Unfortunately this is full of unsafe because it's a union, but the code is
/// not complex and doesn't allocate anything.
#[repr(C)]
@ -72,16 +78,168 @@ pub union InetAddress {
ss: sockaddr_storage, // some external code may expect the struct to be this full length
}
impl Into<IpAddr> for InetAddress {
fn into(self) -> IpAddr {
if self.is_ipv4() {
IpAddr::V4(Ipv4Addr::from(self.to_bytes::<4>()))
} else {
IpAddr::V6(Ipv6Addr::from(self.to_bytes::<16>()))
impl TryInto<IpAddr> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<IpAddr, Self::Error> {
match unsafe { self.sa.sa_family } {
AF_INET => Ok(IpAddr::V4(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() }))),
AF_INET6 => Ok(IpAddr::V6(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr }))),
_ => Err(crate::error::InvalidParameterError("not an IP address")),
}
}
}
impl TryInto<Ipv4Addr> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<Ipv4Addr, Self::Error> {
match unsafe { self.sa.sa_family } {
AF_INET => Ok(Ipv4Addr::from(unsafe { self.sin.sin_addr.s_addr.to_ne_bytes() })),
_ => Err(crate::error::InvalidParameterError("not an IPv4 address")),
}
}
}
impl TryInto<Ipv6Addr> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<Ipv6Addr, Self::Error> {
match unsafe { self.sa.sa_family } {
AF_INET6 => Ok(Ipv6Addr::from(unsafe { self.sin6.sin6_addr.s6_addr })),
_ => Err(crate::error::InvalidParameterError("not an IPv6 address")),
}
}
}
impl TryInto<SocketAddr> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<SocketAddr, Self::Error> {
unsafe {
match self.sa.sa_family {
AF_INET => Ok(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(self.sin.sin_addr.s_addr.to_ne_bytes()), u16::from_be(self.sin.sin_port as u16)))),
AF_INET6 => Ok(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(self.sin6.sin6_addr.s6_addr), u16::from_be(self.sin6.sin6_port as u16), 0, 0))),
_ => Err(crate::error::InvalidParameterError("not an IP address")),
}
}
}
}
impl TryInto<SocketAddrV4> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<SocketAddrV4, Self::Error> {
unsafe {
match self.sa.sa_family {
AF_INET => Ok(SocketAddrV4::new(Ipv4Addr::from(self.sin.sin_addr.s_addr.to_ne_bytes()), u16::from_be(self.sin.sin_port as u16))),
_ => Err(crate::error::InvalidParameterError("not an IPv4 address")),
}
}
}
}
impl TryInto<SocketAddrV6> for InetAddress {
type Error = crate::error::InvalidParameterError;
fn try_into(self) -> Result<SocketAddrV6, Self::Error> {
unsafe {
match self.sa.sa_family {
AF_INET6 => Ok(SocketAddrV6::new(Ipv6Addr::from(self.sin6.sin6_addr.s6_addr), u16::from_be(self.sin6.sin6_port as u16), 0, 0)),
_ => Err(crate::error::InvalidParameterError("not an IPv6 address")),
}
}
}
}
impl From<&IpAddr> for InetAddress {
fn from(ip: &IpAddr) -> Self {
match ip {
IpAddr::V4(ip4) => Self::from(ip4),
IpAddr::V6(ip6) => Self::from(ip6),
}
}
}
impl From<IpAddr> for InetAddress {
#[inline(always)]
fn from(ip: IpAddr) -> Self {
Self::from(&ip)
}
}
impl From<&Ipv4Addr> for InetAddress {
#[inline(always)]
fn from(ip4: &Ipv4Addr) -> Self {
Self::from_ip_port(&ip4.octets(), 0)
}
}
impl From<Ipv4Addr> for InetAddress {
#[inline(always)]
fn from(ip4: Ipv4Addr) -> Self {
Self::from_ip_port(&ip4.octets(), 0)
}
}
impl From<&Ipv6Addr> for InetAddress {
#[inline(always)]
fn from(ip6: &Ipv6Addr) -> Self {
Self::from_ip_port(&ip6.octets(), 0)
}
}
impl From<Ipv6Addr> for InetAddress {
#[inline(always)]
fn from(ip6: Ipv6Addr) -> Self {
Self::from_ip_port(&ip6.octets(), 0)
}
}
impl From<&SocketAddr> for InetAddress {
fn from(sa: &SocketAddr) -> Self {
match sa {
SocketAddr::V4(sa4) => Self::from(sa4),
SocketAddr::V6(sa6) => Self::from(sa6),
}
}
}
impl From<SocketAddr> for InetAddress {
#[inline(always)]
fn from(sa: SocketAddr) -> Self {
Self::from(&sa)
}
}
impl From<&SocketAddrV4> for InetAddress {
#[inline(always)]
fn from(sa: &SocketAddrV4) -> Self {
Self::from_ip_port(&sa.ip().octets(), sa.port())
}
}
impl From<SocketAddrV4> for InetAddress {
#[inline(always)]
fn from(sa: SocketAddrV4) -> Self {
Self::from_ip_port(&sa.ip().octets(), sa.port())
}
}
impl From<&SocketAddrV6> for InetAddress {
#[inline(always)]
fn from(sa: &SocketAddrV6) -> Self {
Self::from_ip_port(&sa.ip().octets(), sa.port())
}
}
impl From<SocketAddrV6> for InetAddress {
#[inline(always)]
fn from(sa: SocketAddrV6) -> Self {
Self::from_ip_port(&sa.ip().octets(), sa.port())
}
}
impl Clone for InetAddress {
#[inline(always)]
fn clone(&self) -> Self {
@ -91,8 +249,8 @@ impl Clone for InetAddress {
impl Default for InetAddress {
#[inline(always)]
fn default() -> InetAddress {
unsafe { zeroed() }
fn default() -> Self {
Self::new()
}
}
@ -102,6 +260,68 @@ impl std::fmt::Debug for InetAddress {
}
}
// Just has to be large enough to store any binary marshalled InetAddress
const TEMP_SERIALIZE_BUFFER_SIZE: usize = 24;
impl Serialize for InetAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(self.to_string().as_str())
} else {
let mut tmp: Buffer<TEMP_SERIALIZE_BUFFER_SIZE> = Buffer::new();
assert!(self.marshal(&mut tmp).is_ok());
serializer.serialize_bytes(tmp.as_bytes())
}
}
}
struct InetAddressVisitor;
impl<'de> serde::de::Visitor<'de> for InetAddressVisitor {
type Value = InetAddress;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an InetAddress")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if v.len() <= TEMP_SERIALIZE_BUFFER_SIZE {
let mut tmp: Buffer<TEMP_SERIALIZE_BUFFER_SIZE> = Buffer::new();
let _ = tmp.append_bytes(v);
let mut cursor = 0;
InetAddress::unmarshal(&tmp, &mut cursor).map_err(|e| E::custom(e.to_string()))
} else {
Err(E::custom("object too large"))
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
InetAddress::from_str(v).map_err(|e| E::custom(e.to_string()))
}
}
impl<'de> Deserialize<'de> for InetAddress {
fn deserialize<D>(deserializer: D) -> Result<InetAddress, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
deserializer.deserialize_str(InetAddressVisitor)
} else {
deserializer.deserialize_bytes(InetAddressVisitor)
}
}
}
impl InetAddress {
/// Get a new zero/nil InetAddress.
#[inline(always)]
@ -114,7 +334,7 @@ impl InetAddress {
#[inline(always)]
pub fn from_ip_port(ip: &[u8], port: u16) -> InetAddress {
unsafe {
let mut c = MaybeUninit::<InetAddress>::uninit().assume_init();
let mut c = MaybeUninit::<InetAddress>::uninit().assume_init(); // gets zeroed in set()
c.set(ip, port);
c
}
@ -178,6 +398,13 @@ impl InetAddress {
unsafe { self.sa.sa_family as u8 == AF_INET6 }
}
/// Check if this is either an IPv4 or an IPv6 address.
#[inline(always)]
pub fn is_ip(&self) -> bool {
let family = unsafe { self.sa.sa_family };
family == AF_INET || family == AF_INET6
}
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
#[inline(always)]
#[cfg(not(target_os = "linux"))]
@ -242,21 +469,6 @@ impl InetAddress {
}
}
pub fn to_bytes<const LEN: usize>(&self) -> [u8; LEN] {
if LEN != 4 && LEN != 16 {
panic!("LEN was not 4 or 16, cannot convert");
}
let mut res = [0u8; LEN];
if LEN == 4 && !self.is_ipv4() {
panic!("Non-IPv4 address expected for 4 byte array");
}
res.copy_from_slice(&self.ip_bytes()[0..LEN]);
res
}
/// Get raw IP bytes packed into a u128.
/// Bytes are packed in native endian so the resulting u128 may not be the same between systems.
/// This value is intended for local lookup use only.
@ -499,39 +711,44 @@ impl ToString for InetAddress {
impl FromStr for InetAddress {
type Err = InvalidFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn from_str(ip_string: &str) -> Result<Self, Self::Err> {
let mut addr = InetAddress::new();
let (ip_str, port) = s.find('/').map_or_else(
|| (s, 0),
|pos| {
let ss = s.split_at(pos);
let mut port_str = ss.1;
if port_str.starts_with('/') {
port_str = &port_str[1..];
}
(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 {
match ip {
IpAddr::V4(v4) => {
addr.sin.sin_family = AF_INET.into();
addr.sin.sin_port = port.into();
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
}
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);
let s = ip_string.trim();
if !s.is_empty() {
let (ip_str, port) = s.find('/').map_or_else(
|| (s, 0),
|pos| {
let ss = s.split_at(pos);
let mut port_str = ss.1;
if port_str.starts_with('/') {
port_str = &port_str[1..];
}
(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 {
match ip {
IpAddr::V4(v4) => {
addr.sin.sin_family = AF_INET.into();
addr.sin.sin_port = port.into();
copy_nonoverlapping(v4.octets().as_ptr(), (&mut (addr.sin.sin_addr.s_addr) as *mut u32).cast(), 4);
}
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)
},
)
} else {
Ok(addr)
}
}
}
@ -643,6 +860,8 @@ impl Hash for InetAddress {
}
}
unsafe impl Send for InetAddress {}
#[cfg(test)]
mod tests {
use std::mem::size_of;

View file

@ -57,15 +57,7 @@ impl MAC {
#[inline(always)]
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
let b = buf.append_bytes_fixed_get_mut::<6>()?;
let i = self.0.get();
(*b)[0] = (i >> 40) as u8;
(*b)[1] = (i >> 32) as u8;
(*b)[2] = (i >> 24) as u8;
(*b)[3] = (i >> 16) as u8;
(*b)[4] = (i >> 8) as u8;
(*b)[5] = i as u8;
Ok(())
buf.append_bytes(&self.0.get().to_be_bytes()[2..])
}
#[inline(always)]

View file

@ -21,7 +21,6 @@ pub(crate) mod peer;
#[allow(unused)]
pub(crate) mod protocol;
pub(crate) mod symmetricsecret;
pub(crate) mod system_interface;
pub(crate) mod whoisqueue;
pub use address::Address;
@ -33,6 +32,5 @@ pub use mac::MAC;
pub use node::{Node, SystemInterface};
pub use path::Path;
pub use peer::Peer;
pub use system_interface::VL1SystemInterface;
pub use protocol::{PACKET_FRAGMENT_COUNT_MAX, PACKET_SIZE_MAX};

View file

@ -82,8 +82,8 @@ pub trait SystemInterface: Sync + Send {
/// Interface between VL1 and higher/inner protocol layers.
///
/// This is implemented by Switch in VL2. It's usually not used outside of VL2 in the core but
/// it could also be implemented for testing or "off label" use of VL1.
pub trait VL1VirtualInterface: Sync + Send {
/// it could also be implemented for testing or "off label" use of VL1 to carry different protocols.
pub trait InnerProtocolInterface: Sync + Send {
/// Handle a packet, returning true if it was handled by the next layer.
///
/// Do not attempt to handle OK or ERROR. Instead implement handle_ok() and handle_error().
@ -232,7 +232,7 @@ impl Node {
}
/// Called when a packet is received on the physical wire.
pub fn wire_receive<SI: SystemInterface, PH: VL1VirtualInterface>(&self, si: &SI, ph: &PH, source_endpoint: &Endpoint, source_local_socket: Option<NonZeroI64>, source_local_interface: Option<NonZeroI64>, mut data: PacketBuffer) {
pub fn wire_receive<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, source_endpoint: &Endpoint, source_local_socket: Option<NonZeroI64>, source_local_interface: Option<NonZeroI64>, mut data: PacketBuffer) {
if let Ok(fragment_header) = data.struct_mut_at::<FragmentHeader>(0) {
if let Some(dest) = Address::from_bytes(&fragment_header.dest) {
let time_ticks = si.time_ticks();

View file

@ -8,16 +8,14 @@
use std::collections::HashMap;
use std::hash::Hasher;
use std::io::Write;
use std::num::NonZeroI64;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use highway::HighwayHash;
use parking_lot::Mutex;
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
use crate::util::{array_range, highwayhasher, U64NoOpHasher};
use crate::util::*;
use crate::vl1::fragmentedpacket::FragmentedPacket;
use crate::vl1::node::SystemInterface;
use crate::vl1::protocol::*;
@ -27,10 +25,13 @@ use crate::PacketBuffer;
/// Keepalive interval for paths in milliseconds.
pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
// A bunch of random values used to randomize the local_lookup_key() function's mappings of addresses to 128-bit internal keys.
lazy_static! {
static ref RANDOM_64BIT_SALT_0: u64 = zerotier_core_crypto::random::next_u64_secure();
static ref RANDOM_64BIT_SALT_1: u64 = zerotier_core_crypto::random::next_u64_secure();
static ref RANDOM_64BIT_SALT_2: u64 = zerotier_core_crypto::random::next_u64_secure();
static ref RANDOM_128BIT_SALT_0: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128);
static ref RANDOM_128BIT_SALT_1: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128);
}
/// A remote endpoint paired with a local socket and a local interface.
@ -55,7 +56,7 @@ impl Path {
let lsi = (local_socket as u128).wrapping_shl(64) | (local_interface as u128);
match endpoint {
Endpoint::Nil => 0,
Endpoint::ZeroTier(_, h) => u128::from_ne_bytes(*array_range::<u8, SHA384_HASH_SIZE, 0, 16>(h)),
Endpoint::ZeroTier(_, h) => u128::from_ne_bytes(*byte_array_range::<SHA384_HASH_SIZE, 0, 16>(h)),
Endpoint::Ethernet(m) => (m.to_u64() | 0x0100000000000000) as u128 ^ lsi,
Endpoint::WifiDirect(m) => (m.to_u64() | 0x0200000000000000) as u128 ^ lsi,
Endpoint::Bluetooth(m) => (m.to_u64() | 0x0400000000000000) as u128 ^ lsi,
@ -63,24 +64,23 @@ impl Path {
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::Http(s) => {
let mut hh = highwayhasher();
let _ = hh.write_all(s.as_bytes());
let _ = hh.write_u64(local_socket);
let _ = hh.write_u64(local_interface);
u128::from_ne_bytes(unsafe { *hh.finalize128().as_ptr().cast() })
let mut hh = std::collections::hash_map::DefaultHasher::new();
hh.write_u64(local_socket);
hh.write_u64(local_interface);
hh.write(s.as_bytes());
RANDOM_128BIT_SALT_0.wrapping_add(hh.finish() as u128)
}
Endpoint::WebRTC(b) => {
let mut hh = highwayhasher();
let _ = hh.write_u64(local_socket);
let _ = hh.write_u64(local_interface);
let _ = hh.write_all(b.as_slice());
u128::from_ne_bytes(unsafe { *hh.finalize128().as_ptr().cast() })
let mut hh = std::collections::hash_map::DefaultHasher::new();
hh.write_u64(local_socket);
hh.write_u64(local_interface);
hh.write(b.as_slice());
RANDOM_128BIT_SALT_1.wrapping_add(hh.finish() as u128)
}
Endpoint::ZeroTierEncap(_, h) => u128::from_ne_bytes(*array_range::<u8, SHA384_HASH_SIZE, 16, 16>(h)),
Endpoint::ZeroTierEncap(_, h) => u128::from_ne_bytes(*byte_array_range::<SHA384_HASH_SIZE, 16, 16>(h)),
}
}
#[inline(always)]
pub fn new(endpoint: Endpoint, local_socket: Option<NonZeroI64>, local_interface: Option<NonZeroI64>) -> Self {
Self {
endpoint: Mutex::new(Arc::new(endpoint)),
@ -119,7 +119,6 @@ impl Path {
/// 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.
#[inline(always)]
pub(crate) fn receive_fragment(&self, packet_id: u64, fragment_no: u8, fragment_expecting_count: u8, packet: PacketBuffer, time_ticks: i64) -> Option<FragmentedPacket> {
let mut fp = self.fragmented_packets.lock();
@ -150,7 +149,6 @@ impl Path {
self.last_receive_time_ticks.store(time_ticks, Ordering::Relaxed);
}
#[inline(always)]
pub(crate) fn log_receive_authenticated_packet(&self, _bytes: usize, source_endpoint: &Endpoint) {
let mut replace = false;
match source_endpoint {
@ -180,7 +178,6 @@ impl Path {
pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL;
#[inline(always)]
pub(crate) fn call_every_interval<SI: SystemInterface>(&self, _si: &SI, time_ticks: i64) {
self.fragmented_packets.lock().retain(|_, frag| (time_ticks - frag.ts_ticks) < PACKET_FRAGMENT_EXPIRATION);
}

View file

@ -18,13 +18,13 @@ use zerotier_core_crypto::aes_gmac_siv::AesCtr;
use zerotier_core_crypto::hash::*;
use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha384;
use zerotier_core_crypto::poly1305::Poly1305;
use zerotier_core_crypto::random::{fill_bytes_secure, get_bytes_secure, next_u64_secure};
use zerotier_core_crypto::random::{get_bytes_secure, next_u64_secure};
use zerotier_core_crypto::salsa::Salsa;
use zerotier_core_crypto::secret::Secret;
use crate::util::buffer::Buffer;
use crate::util::{array_range, u64_as_bytes};
use crate::vl1::hybridkey::{HybridKeyPair, HybridPublicKey};
use crate::util::byte_array_range;
use crate::vl1::hybridkey::HybridKeyPair;
use crate::vl1::identity::{IDENTITY_ALGORITHM_ALL, IDENTITY_ALGORITHM_X25519};
use crate::vl1::node::*;
use crate::vl1::protocol::*;
@ -172,7 +172,7 @@ fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8],
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
// decrypted tag.
Some(u64::from_ne_bytes(*array_range::<u8, 16, 0, 8>(tag)))
Some(u64::from_ne_bytes(*byte_array_range::<16, 0, 8>(tag)))
})
}
@ -221,7 +221,7 @@ impl Peer {
///
/// If the packet comes in multiple fragments, the fragments slice should contain all
/// those fragments after the main packet header and first chunk.
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>]) {
pub(crate) fn receive<SI: SystemInterface, VI: InnerProtocolInterface>(&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 mut payload: Buffer<PACKET_SIZE_MAX> = unsafe { Buffer::new_without_memzero() };
@ -502,7 +502,7 @@ impl Peer {
// Add extended HMAC-SHA512 authentication.
let mut hmac = HMACSHA512::new(self.identity_symmetric_key.packet_hmac_key.as_bytes());
hmac.update(u64_as_bytes(&message_id));
hmac.update(&message_id.to_ne_bytes());
hmac.update(&packet.as_bytes()[PACKET_HEADER_SIZE..]);
assert!(packet.append_bytes_fixed(&hmac.finish()).is_ok());
@ -537,7 +537,7 @@ impl Peer {
fn receive_hello<SI: SystemInterface>(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
#[inline(always)]
fn receive_error<SI: SystemInterface, PH: VL1VirtualInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
fn receive_error<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
let mut cursor: usize = 0;
let _ = payload.read_struct::<message_component_structs::ErrorHeader>(&mut cursor).map(|error_header| {
let in_re_message_id = u64::from_ne_bytes(error_header.in_re_message_id);
@ -553,7 +553,7 @@ impl Peer {
}
#[inline(always)]
fn receive_ok<SI: SystemInterface, PH: VL1VirtualInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
fn receive_ok<SI: SystemInterface, PH: InnerProtocolInterface>(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
let mut cursor: usize = 0;
let _ = payload.read_struct::<message_component_structs::OkHeader>(&mut cursor).map(|ok_header| {
let in_re_message_id = u64::from_ne_bytes(ok_header.in_re_message_id);

View file

@ -1,17 +0,0 @@
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;
}

View file

@ -9,7 +9,7 @@
use std::sync::Arc;
use crate::util::buffer::Buffer;
use crate::vl1::node::VL1VirtualInterface;
use crate::vl1::node::InnerProtocolInterface;
use crate::vl1::protocol::*;
use crate::vl1::{Identity, Path, Peer};
@ -17,7 +17,7 @@ pub trait SwitchInterface: Sync + Send {}
pub struct Switch {}
impl VL1VirtualInterface for Switch {
impl InnerProtocolInterface for Switch {
fn handle_packet(&self, peer: &Peer, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, verb: u8, payload: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
false
}

File diff suppressed because it is too large Load diff

View file

@ -17,22 +17,12 @@ zerotier-network-hypervisor = { path = "../zerotier-network-hypervisor" }
zerotier-core-crypto = { path = "../zerotier-core-crypto" }
serde = { version = "^1", features = ["derive"], default-features = false }
serde_json = { version = "^1", features = [], default-features = false }
clap = { version = "^2", features = ["suggestions", "wrap_help"] }
colored = "^2"
num_cpus = "^1"
parking_lot = "^0"
smol = "^1"
tide = { version = "^0", features = ["h1-server"], default-features = false }
digest_auth = "^0"
chrono = "^0"
hex = "^0"
rand = "^0"
lazy_static = "^1"
[target."cfg(windows)".dependencies]
winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
[target."cfg(not(windows))".dependencies]
libc = "^0"
[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies]
mach = "^0"

View file

@ -128,7 +128,7 @@ impl Store {
if token2.is_empty() {
if generate_if_missing {
let mut rb = [0_u8; 32];
unsafe { rb.fill_with(rand::random) };
zerotier_core_crypto::random::fill_bytes_secure(&mut rb);
token.reserve(rb.len());
for b in rb.iter() {
if *b > 127_u8 {

View file

@ -25,24 +25,7 @@ pub fn ms_since_epoch() -> i64 {
std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn ms_monotonic() -> i64 {
unsafe {
let mut tb: mach::mach_time::mach_timebase_info_data_t = std::mem::zeroed();
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
(((mt as u128) * tb.numer as u128 * 1000000_u128) / (tb.denom as u128)) as i64
// milliseconds since X
} else {
panic!("FATAL: mach_timebase_info() failed");
}
}
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn ms_monotonic() -> i64 {
std::time::SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
}
pub fn ms_monotonic() -> i64 {}
pub fn parse_bool(v: &str) -> Result<bool, String> {
if !v.is_empty() {

View file

@ -43,7 +43,7 @@ pub fn get_l2_multicast_subscriptions(dev: &str) -> HashSet<MAC> {
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family as i32 == libc::AF_LINK as i32 {
let in_: &libc::sockaddr_dl = &*((*i).ifma_name.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_() && libc::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
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));
}

View file

@ -31,18 +31,16 @@
*/
use std::cell::Cell;
use std::collections::HashSet;
use std::error::Error;
use std::collections::{BTreeSet, HashSet};
use std::ffi::CString;
use std::mem::{transmute, zeroed};
use std::os::raw::{c_char, c_int, c_short, c_uchar, c_uint, c_void};
use std::process::Command;
use std::ptr::{copy_nonoverlapping, null_mut};
use std::sync::Mutex;
use std::ptr::copy_nonoverlapping;
use std::thread::JoinHandle;
use lazy_static::lazy_static;
use num_traits::cast::AsPrimitive;
use parking_lot::Mutex;
use zerotier_network_hypervisor::vl1::{InetAddress, MAC};
@ -178,6 +176,7 @@ struct in6_ifreq {
*/
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
struct icmp6_ifstat {
ifs6_in_msg: u64,
@ -217,6 +216,7 @@ struct icmp6_ifstat {
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
struct in6_ifstat {
ifs6_in_receive: u64,
@ -247,6 +247,7 @@ struct in6_ifstat {
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
struct in6_addrlifetime {
ia6t_expire: libc::time_t,
@ -422,6 +423,7 @@ struct sockaddr_ndrv {
*/
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
struct ifkpi {
ifk_module_id: c_uint,
@ -430,6 +432,7 @@ struct ifkpi {
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(C)]
struct ifdevmtu {
ifdm_current: c_int,
@ -480,7 +483,7 @@ impl MacFethTap {
/// given will not remain valid after it returns. Also note that F will be called
/// from another thread that is spawned here, so all its bound references must
/// be "Send" and "Sync" e.g. Arc<>.
pub fn new<F: Fn(&[u8]) + Send + Sync + 'static>(nwid: &NetworkId, mac: &MAC, mtu: i32, metric: i32, eth_frame_func: F) -> Result<MacFethTap, String> {
pub fn new<F: Fn(&[u8]) + Send + Sync + 'static>(nwid: u64, mac: &MAC, mtu: i32, metric: i32, eth_frame_func: F) -> Result<MacFethTap, String> {
// This tracks BPF devices we are using so we don't try to reopen them, and also
// doubles as a global lock to ensure that only one feth tap is created at once per
// ZeroTier process per system.
@ -668,7 +671,7 @@ impl MacFethTap {
}
// Create BPF listener thread, which calls the supplied function on each incoming packet.
let t = std::thread::Builder::new().stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).spawn(move || {
let t = std::thread::Builder::new().stack_size(524288).spawn(move || {
let mut buf: [u8; BPF_BUFFER_SIZE] = [0_u8; BPF_BUFFER_SIZE];
let hdr_struct_size = std::mem::size_of::<libc::bpf_hdr>() as isize;
loop {
@ -808,7 +811,7 @@ impl VNIC for MacFethTap {
}
#[inline(always)]
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: &zerotier_network_hypervisor::vl1::MAC, dest_mac: &zerotier_network_hypervisor::vl1::MAC, ethertype: u16, _vlan_id: u16, data: *const u8, len: usize) -> bool {
let dm = dest_mac.0;
let sm = source_mac.0;
let mut hdr: [u8; 14] = [(dm >> 40) as u8, (dm >> 32) as u8, (dm >> 24) as u8, (dm >> 16) as u8, (dm >> 8) as u8, dm as u8, (sm >> 40) as u8, (sm >> 32) as u8, (sm >> 24) as u8, (sm >> 16) as u8, (sm >> 8) as u8, sm as u8, (ethertype >> 8) as u8, ethertype as u8];