mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 08:27:39 +02:00
Implement usage flags in certificate, make root sets require a usage flag and a more general local trust flag, and clean up Identity return in Rust Node.
This commit is contained in:
parent
24ab618123
commit
251cc082f2
18 changed files with 512 additions and 169 deletions
|
@ -40,7 +40,7 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
|
|||
m_clear();
|
||||
|
||||
Utils::copy< sizeof(this->serialNo) >(this->serialNo, cert.serialNo);
|
||||
this->flags = cert.flags;
|
||||
this->usageFlags = cert.usageFlags;
|
||||
this->timestamp = cert.timestamp;
|
||||
this->validity[0] = cert.validity[0];
|
||||
this->validity[1] = cert.validity[1];
|
||||
|
@ -180,8 +180,8 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
|
|||
* purposes the keys must be always be in order.
|
||||
*/
|
||||
|
||||
if (this->flags != 0)
|
||||
d.add("f", this->flags);
|
||||
if (this->usageFlags != 0)
|
||||
d.add("f", this->usageFlags);
|
||||
if (this->timestamp > 0)
|
||||
d.add("t", (uint64_t)this->timestamp);
|
||||
if (this->validity[0] > 0)
|
||||
|
@ -223,7 +223,7 @@ bool Certificate::decode(const void *const data, const unsigned int len)
|
|||
|
||||
m_clear();
|
||||
|
||||
this->flags = d.getUI("f");
|
||||
this->usageFlags = d.getUI("f");
|
||||
this->timestamp = (int64_t)d.getUI("t");
|
||||
this->validity[0] = (int64_t)d.getUI("v#0");
|
||||
this->validity[1] = (int64_t)d.getUI("v#1");
|
||||
|
|
|
@ -300,7 +300,7 @@ static bool ZTT_deepCompareCertificateName(const ZT_Certificate_Name &a, const Z
|
|||
static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate &b)
|
||||
{
|
||||
if (
|
||||
(a.flags != b.flags) ||
|
||||
(a.usageFlags != b.usageFlags) ||
|
||||
(a.timestamp != b.timestamp) ||
|
||||
(a.validity[0] != b.validity[0]) ||
|
||||
(a.validity[1] != b.validity[1]) ||
|
||||
|
|
|
@ -39,19 +39,22 @@ Map< Identity, SharedPtr< const Locator > > TrustStore::roots()
|
|||
for (Map< Fingerprint, Vector< SharedPtr< Entry > > >::const_iterator cv(m_bySubjectIdentity.begin()); cv != m_bySubjectIdentity.end(); ++cv) {
|
||||
for (Vector< SharedPtr< Entry > >::const_iterator c(cv->second.begin()); c != cv->second.end(); ++c) {
|
||||
|
||||
if (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) != 0) {
|
||||
// A root set cert must be marked for this use and authorized to influence this node's config.
|
||||
if ((((*c)->m_certificate.usageFlags & ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET) != 0) && (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG) != 0)) {
|
||||
|
||||
// Add all identities to the root set, and for each entry in the set make sure we have the latest locator if there's more than one cert with one.
|
||||
for (unsigned int j = 0; j < (*c)->certificate().subject.identityCount; ++j) {
|
||||
const Identity *const id = reinterpret_cast<const Identity *>((*c)->certificate().subject.identities[j].identity);
|
||||
if ((id) && (*id)) { // sanity check
|
||||
SharedPtr< const Locator > &existingLoc = r[*id];
|
||||
const Locator *const loc = reinterpret_cast<const Locator *>((*c)->certificate().subject.identities[j].locator);
|
||||
if (loc) {
|
||||
// If more than one certificate names a root, this ensures that the newest locator is used.
|
||||
if ((!existingLoc) || (existingLoc->revision() < loc->revision()))
|
||||
existingLoc.set(new Locator(*loc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -198,10 +201,13 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
|
|||
m_bySubjectUniqueId.clear();
|
||||
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
|
||||
if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) {
|
||||
|
||||
const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize;
|
||||
if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) {
|
||||
SharedPtr< Entry > &entry = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)];
|
||||
if (entry) {
|
||||
|
||||
// If there's already an entry, see if there's a newer certificate for this subject.
|
||||
if (c->second->m_certificate.subject.timestamp > entry->m_certificate.subject.timestamp) {
|
||||
entry->m_subjectDeprecated = true;
|
||||
entry = c->second;
|
||||
|
@ -216,10 +222,12 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
|
|||
c->second->m_subjectDeprecated = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
entry = c->second;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,17 +236,17 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
|
|||
m_bySubjectIdentity.clear();
|
||||
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
|
||||
if ((c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) && (!c->second->m_subjectDeprecated)) {
|
||||
|
||||
for (unsigned int i = 0; i < c->second->m_certificate.subject.identityCount; ++i) {
|
||||
const Identity *const id = reinterpret_cast<const Identity *>(c->second->m_certificate.subject.identities[i].identity);
|
||||
if ((id) && (*id)) // sanity check
|
||||
m_bySubjectIdentity[id->fingerprint()].push_back(c->second);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Purge error certificates and return them if 'purge' is non-NULL. This purges error certificates
|
||||
// and deprecated certificates not on a trust path. Deprecated certificates on a trust path remain
|
||||
// as they are still technically valid and other possibly wanted certificates depend on them.
|
||||
// If purge is set, erase and return error and deprecated certs (that are not on a trust path).
|
||||
if (purge) {
|
||||
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
|
||||
if ( (c->second->error() != ZT_CERTIFICATE_ERROR_NONE) || ((c->second->m_subjectDeprecated) && (!c->second->m_onTrustPath)) ) {
|
||||
|
@ -259,15 +267,15 @@ Vector< uint8_t > TrustStore::save() const
|
|||
|
||||
int compSize;
|
||||
{
|
||||
Vector< uint8_t > b;
|
||||
b.reserve(65536);
|
||||
|
||||
RWMutex::RLock l(m_lock);
|
||||
|
||||
Vector< uint8_t > b;
|
||||
b.reserve(4096);
|
||||
|
||||
// A version byte.
|
||||
b.push_back(0);
|
||||
|
||||
// <size> <certificate> <trust> tuples terminated by a 0 size.
|
||||
// <size[2]> <certificate[...]> <trust[2]> tuples terminated by a 0 size.
|
||||
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
|
||||
const Vector< uint8_t > cdata(c->second->certificate().encode());
|
||||
const unsigned long size = (uint32_t)cdata.size();
|
||||
|
@ -276,8 +284,6 @@ Vector< uint8_t > TrustStore::save() const
|
|||
b.push_back((uint8_t)size);
|
||||
b.insert(b.end(), cdata.begin(), cdata.end());
|
||||
const uint32_t localTrust = (uint32_t)c->second->localTrust();
|
||||
b.push_back((uint8_t)(localTrust >> 24U));
|
||||
b.push_back((uint8_t)(localTrust >> 16U));
|
||||
b.push_back((uint8_t)(localTrust >> 8U));
|
||||
b.push_back((uint8_t)localTrust);
|
||||
}
|
||||
|
@ -291,15 +297,8 @@ Vector< uint8_t > TrustStore::save() const
|
|||
return Vector< uint8_t >();
|
||||
|
||||
const uint32_t uncompSize = (uint32_t)b.size();
|
||||
const uint32_t cksum = Utils::fnv1a32(b.data(), (unsigned int)uncompSize);
|
||||
comp[0] = (uint8_t)(uncompSize >> 24);
|
||||
comp[1] = (uint8_t)(uncompSize >> 16);
|
||||
comp[2] = (uint8_t)(uncompSize >> 8);
|
||||
comp[3] = (uint8_t)uncompSize;
|
||||
comp[4] = (uint8_t)(cksum >> 24);
|
||||
comp[5] = (uint8_t)(cksum >> 16);
|
||||
comp[6] = (uint8_t)(cksum >> 8);
|
||||
comp[7] = (uint8_t)cksum;
|
||||
Utils::storeBigEndian(comp.data(), uncompSize);
|
||||
Utils::storeBigEndian(comp.data() + 4, Utils::fnv1a32(b.data(), (unsigned int)uncompSize));
|
||||
compSize += 8;
|
||||
}
|
||||
|
||||
|
@ -314,8 +313,8 @@ int TrustStore::load(const Vector< uint8_t > &data)
|
|||
if (data.size() < 8)
|
||||
return -1;
|
||||
|
||||
const unsigned long uncompSize = Utils::loadBigEndian< uint32_t >(data.data());
|
||||
if ((uncompSize == 0) || (uncompSize > (data.size() * 128)))
|
||||
const unsigned int uncompSize = Utils::loadBigEndian< uint32_t >(data.data());
|
||||
if ((uncompSize == 0) || (uncompSize > (unsigned int)(data.size() * 128)))
|
||||
return -1;
|
||||
|
||||
Vector< uint8_t > uncomp;
|
||||
|
@ -336,20 +335,20 @@ int TrustStore::load(const Vector< uint8_t > &data)
|
|||
for (;;) {
|
||||
if ((b + 2) > eof)
|
||||
break;
|
||||
const uint32_t cdataSize = Utils::loadBigEndian< uint16_t >(b);
|
||||
const uint32_t certDataSize = Utils::loadBigEndian< uint16_t >(b);
|
||||
b += 2;
|
||||
|
||||
if (cdataSize == 0)
|
||||
if (certDataSize == 0)
|
||||
break;
|
||||
|
||||
if ((b + cdataSize + 4) > eof)
|
||||
if ((b + certDataSize + 2) > eof) // certificate length + 2 bytes for trust flags
|
||||
break;
|
||||
Certificate c;
|
||||
if (c.decode(b, (unsigned int)cdataSize)) {
|
||||
b += cdataSize;
|
||||
const uint32_t localTrust = Utils::loadBigEndian< uint32_t >(b);
|
||||
b += 4;
|
||||
this->add(c, (unsigned int)localTrust);
|
||||
if (c.decode(b, (unsigned int)certDataSize)) {
|
||||
b += certDataSize;
|
||||
this->add(c, Utils::loadBigEndian< uint16_t >(b));
|
||||
b += 2;
|
||||
|
||||
++readCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ public:
|
|||
Entry &operator=(const Entry &) { return *this; }
|
||||
|
||||
ZT_INLINE Entry(RWMutex &l, const Certificate &cert, const unsigned int lt) noexcept:
|
||||
__refCount(0),
|
||||
m_lock(l),
|
||||
m_certificate(cert),
|
||||
m_localTrust(lt),
|
||||
|
@ -109,11 +110,12 @@ public:
|
|||
m_onTrustPath(false)
|
||||
{}
|
||||
|
||||
std::atomic< int > __refCount;
|
||||
|
||||
RWMutex &m_lock;
|
||||
const Certificate m_certificate;
|
||||
unsigned int m_localTrust;
|
||||
ZT_CertificateError m_error;
|
||||
std::atomic< int > __refCount;
|
||||
bool m_subjectDeprecated;
|
||||
bool m_onTrustPath;
|
||||
};
|
||||
|
|
|
@ -328,9 +328,81 @@ typedef struct
|
|||
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA 0x0001U
|
||||
|
||||
/**
|
||||
* Certificate's subject describes a set of roots (local trust flag)
|
||||
* Certificate can affect configuration of the node.
|
||||
*
|
||||
* An example is a ZeroTier root set certificate which adds root servers.
|
||||
*/
|
||||
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET 0x0002U
|
||||
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG 0x0002U
|
||||
|
||||
/*
|
||||
* Certificate usage flags
|
||||
*
|
||||
* Not all of these are used in ZeroTier. Some are simply reserved because
|
||||
* same named usage attributes exist in X509 and we want to be future proof
|
||||
* against future desires to translate over X509 functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Certificate can sign things (general).
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE 0x00000001U
|
||||
|
||||
/**
|
||||
* Certificate can verify signatures to verify actions.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_NON_REPUDIATION 0x00000002U
|
||||
|
||||
/**
|
||||
* Certificate's key can encipher other keys.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT 0x00000004U
|
||||
|
||||
/**
|
||||
* Certificate's key can encipher data.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT 0x00000008U
|
||||
|
||||
/**
|
||||
* Certificate's key can be used for Diffie-Hellman style key agreemtn.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_KEY_AGREEMENT 0x00000010U
|
||||
|
||||
/**
|
||||
* Certificate can sign other certificates.
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING 0x00000020U
|
||||
|
||||
/**
|
||||
* Certificate can revoke signatures.
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_CRL_SIGNING 0x00000040U
|
||||
|
||||
/**
|
||||
* Certificate can sign executable code.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE 0x00000080U
|
||||
|
||||
/**
|
||||
* Certificate's public key can be used for a timestamp service.
|
||||
*
|
||||
* (not used in ZeroTier)
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_TIMESTAMPING 0x00000100U
|
||||
|
||||
/**
|
||||
* Certificate can enumerate a set of ZeroTier root nodes.
|
||||
*/
|
||||
#define ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET 0x00000200U
|
||||
|
||||
/**
|
||||
* Errors returned by functions that verify or handle certificates.
|
||||
|
@ -572,9 +644,9 @@ typedef struct
|
|||
uint8_t serialNo[ZT_CERTIFICATE_HASH_SIZE];
|
||||
|
||||
/**
|
||||
* Flags indicating certificate usage and any other attributes.
|
||||
* Certificate usage flags.
|
||||
*/
|
||||
uint64_t flags;
|
||||
uint64_t usageFlags;
|
||||
|
||||
/**
|
||||
* Certificate timestamp in milliseconds since epoch.
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
*/
|
||||
/****/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
|
||||
pub struct Address(pub u64);
|
||||
|
||||
|
@ -28,14 +30,10 @@ impl Address {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Address {
|
||||
impl Hash for Address {
|
||||
#[inline(always)]
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
if bytes.len() >= 5 {
|
||||
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
|
||||
} else {
|
||||
Address(0)
|
||||
}
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,14 +46,25 @@ impl ToString for Address {
|
|||
impl From<u64> for Address {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> Self {
|
||||
Address(i)
|
||||
Address(i & 0xffffffffff)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Address {
|
||||
#[inline(always)]
|
||||
fn from(s: &str) -> Self {
|
||||
Address(u64::from_str_radix(s, 16).unwrap_or(0))
|
||||
Address::from(u64::from_str_radix(s, 16).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
if bytes.len() >= 5 {
|
||||
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
|
||||
} else {
|
||||
Address(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +86,7 @@ impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
|||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) }
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Address {
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
|
||||
use std::ffi::CString;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::zeroed;
|
||||
use std::mem::{zeroed, MaybeUninit};
|
||||
use std::os::raw::{c_char, c_uint, c_void};
|
||||
use std::pin::Pin;
|
||||
use std::ptr::{copy_nonoverlapping, null, null_mut};
|
||||
use std::ptr::{copy_nonoverlapping, null, null_mut, read_unaligned, write_bytes};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
#[allow(unused_imports)]
|
||||
|
@ -32,13 +32,39 @@ pub const CERTIFICATE_MAX_STRING_LENGTH: isize = ztcore::ZT_CERTIFICATE_MAX_STRI
|
|||
/// Certificate local trust bit field flag: this certificate self-signs a root CA.
|
||||
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
|
||||
|
||||
/// Certificate local trust bit field flag: this certificate specifies a set of ZeroTier roots.
|
||||
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET;
|
||||
pub const CERTIFICATE_USAGE_DIGITAL_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE as u64;
|
||||
pub const CERTIFICATE_USAGE_NON_REPUDIATION: u64 = ztcore::ZT_CERTIFICATE_USAGE_NON_REPUDIATION as u64;
|
||||
pub const CERTIFICATE_USAGE_KEY_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_DATA_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_KEY_AGREEMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_AGREEMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_CERTIFICATE_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING as u64;
|
||||
pub const CERTIFICATE_USAGE_CRL_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CRL_SIGNING as u64;
|
||||
pub const CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE as u64;
|
||||
pub const CERTIFICATE_USAGE_TIMESTAMPING: u64 = ztcore::ZT_CERTIFICATE_USAGE_TIMESTAMPING as u64;
|
||||
|
||||
/// All certificate usage flags and their corresponding canonical abbreviations.
|
||||
pub const ALL_CERTIFICATE_USAGE_FLAGS: [(u64, &'static str); 9] = [
|
||||
(CERTIFICATE_USAGE_DIGITAL_SIGNATURE, "ds"),
|
||||
(CERTIFICATE_USAGE_NON_REPUDIATION, "nr"),
|
||||
(CERTIFICATE_USAGE_KEY_ENCIPHERMENT, "ke"),
|
||||
(CERTIFICATE_USAGE_DATA_ENCIPHERMENT, "de"),
|
||||
(CERTIFICATE_USAGE_KEY_AGREEMENT, "ka"),
|
||||
(CERTIFICATE_USAGE_CERTIFICATE_SIGNING, "cs"),
|
||||
(CERTIFICATE_USAGE_CRL_SIGNING, "crl"),
|
||||
(CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE, "es"),
|
||||
(CERTIFICATE_USAGE_TIMESTAMPING, "ts"),
|
||||
];
|
||||
|
||||
#[inline(always)]
|
||||
fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] {
|
||||
let mut a = [0_u8; L];
|
||||
unsafe { copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr(), v.len().min(L)) };
|
||||
a
|
||||
unsafe {
|
||||
let mut a: MaybeUninit<[u8; L]> = MaybeUninit::uninit();
|
||||
copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr().cast::<u8>(), v.len().min(L));
|
||||
if v.len() < L {
|
||||
write_bytes(a.as_mut_ptr().cast::<u8>(), 0, L - v.len());
|
||||
}
|
||||
a.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -62,25 +88,44 @@ impl From<i32> for CertificatePublicKeyAlgorithm {
|
|||
pub struct CertificateSerialNo(pub [u8; 48]);
|
||||
|
||||
impl CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0; 48]) }
|
||||
pub fn new_from_string(s: &str) -> Result<CertificateSerialNo, ResultCode> { hex::decode(s).map_or_else(|_| { Err(ResultCode::ErrorBadParameter) }, |b| { Ok(CertificateSerialNo::from(b)) }) }
|
||||
}
|
||||
pub const SIZE: usize = 48;
|
||||
pub const SIZE_HEX: usize = 96;
|
||||
|
||||
impl<A: AsRef<[u8]>> From<A> for CertificateSerialNo {
|
||||
fn from(a: A) -> CertificateSerialNo {
|
||||
let mut sn = CertificateSerialNo::new();
|
||||
let aa = a.as_ref();
|
||||
for i in 0..aa.len() {
|
||||
sn.0[i] = aa[i];
|
||||
}
|
||||
sn
|
||||
#[inline(always)]
|
||||
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0_u8; 48]) }
|
||||
|
||||
/// Create a new certificate serial from a hex string, returning None if invalid.
|
||||
/// The from() alternative for converting from a string returns an a nil (all zero) serial on error.
|
||||
pub fn new_from_string(s: &str) -> Option<CertificateSerialNo> {
|
||||
hex::decode(s).map_or(None, |b| {
|
||||
if b.len() == Self::SIZE {
|
||||
Some(CertificateSerialNo(vec_to_array::<48>(&b)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if serial is all zeroes.
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
is_all_zeroes(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state); }
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Serials are SHA384 hashes, so we can just use the first 8 bytes as-is.
|
||||
state.write_u64(unsafe { read_unaligned::<u64>(self.0.as_ptr().cast()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> From<S> for CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
fn from(s: S) -> CertificateSerialNo {
|
||||
Self::new_from_string(s.as_ref()).unwrap_or_else(|| CertificateSerialNo::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CertificateSerialNo {
|
||||
|
@ -95,7 +140,7 @@ struct CertificateSerialNoVisitor;
|
|||
impl<'de> serde::de::Visitor<'de> for CertificateSerialNoVisitor {
|
||||
type Value = CertificateSerialNo;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("object") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|_| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) },|id| { Ok(id as Self::Value) }) }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) },|serial| { Ok(serial as Self::Value) }) }
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for CertificateSerialNo {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateSerialNoVisitor) }
|
||||
|
@ -117,22 +162,27 @@ pub enum CertificateError {
|
|||
Revoked = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_REVOKED as isize,
|
||||
}
|
||||
|
||||
impl CertificateError {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
CertificateError::None => "None",
|
||||
CertificateError::InvalidFormat => "InvalidFormat",
|
||||
CertificateError::InvalidIdentity => "InvalidIdentity",
|
||||
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
|
||||
CertificateError::InvalidChain => "InvalidChain",
|
||||
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
|
||||
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
|
||||
CertificateError::MissingRequiredFields => "MissingRequiredFields",
|
||||
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow",
|
||||
CertificateError::Revoked => "Revoked",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CertificateError {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
String::from(
|
||||
match self {
|
||||
CertificateError::None => "None",
|
||||
CertificateError::InvalidFormat => "InvalidFormat",
|
||||
CertificateError::InvalidIdentity => "InvalidIdentity",
|
||||
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
|
||||
CertificateError::InvalidChain => "InvalidChain",
|
||||
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
|
||||
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
|
||||
CertificateError::MissingRequiredFields => "MissingRequiredFields",
|
||||
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow",
|
||||
CertificateError::Revoked => "Revoked",
|
||||
}
|
||||
)
|
||||
String::from(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,10 +388,10 @@ pub struct CertificateSubject {
|
|||
#[serde(rename = "updateURLs")]
|
||||
pub update_urls: Vec<String>,
|
||||
pub name: CertificateName,
|
||||
#[serde(with = "Base64Standard")]
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
#[serde(rename = "uniqueId")]
|
||||
pub unique_id: Vec<u8>,
|
||||
#[serde(with = "Base64Standard")]
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
#[serde(rename = "uniqueIdSignature")]
|
||||
pub unique_id_signature: Vec<u8>,
|
||||
}
|
||||
|
@ -509,7 +559,8 @@ impl CertificateSubject {
|
|||
pub struct Certificate {
|
||||
#[serde(rename = "serialNo")]
|
||||
pub serial_no: CertificateSerialNo,
|
||||
pub flags: u64,
|
||||
#[serde(rename = "usageFlags")]
|
||||
pub usage_flags: u64,
|
||||
pub timestamp: i64,
|
||||
pub validity: [i64; 2],
|
||||
pub subject: CertificateSubject,
|
||||
|
@ -520,7 +571,7 @@ pub struct Certificate {
|
|||
pub public_key: Vec<u8>,
|
||||
#[serde(rename = "extendedAttributes")]
|
||||
pub extended_attributes: Vec<u8>,
|
||||
#[serde(with = "Base64Standard")]
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
pub signature: Vec<u8>,
|
||||
#[serde(rename = "maxPathLength")]
|
||||
pub max_path_length: u32,
|
||||
|
@ -557,7 +608,7 @@ impl Certificate {
|
|||
pub fn new() -> Certificate {
|
||||
Certificate {
|
||||
serial_no: CertificateSerialNo::new(),
|
||||
flags: 0,
|
||||
usage_flags: 0,
|
||||
timestamp: 0,
|
||||
validity: [0, i64::MAX],
|
||||
subject: CertificateSubject::new(),
|
||||
|
@ -573,7 +624,7 @@ impl Certificate {
|
|||
pub(crate) unsafe fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate {
|
||||
return Certificate {
|
||||
serial_no: CertificateSerialNo(c.serialNo),
|
||||
flags: c.flags,
|
||||
usage_flags: c.usageFlags,
|
||||
timestamp: c.timestamp,
|
||||
validity: c.validity,
|
||||
subject: CertificateSubject::new_from_capi(&c.subject),
|
||||
|
@ -591,7 +642,7 @@ impl Certificate {
|
|||
CertificateCAPIContainer {
|
||||
certificate: ztcore::ZT_Certificate {
|
||||
serialNo: self.serial_no.0,
|
||||
flags: self.flags,
|
||||
usageFlags: self.usage_flags,
|
||||
timestamp: self.timestamp,
|
||||
validity: self.validity,
|
||||
subject: subject.subject,
|
||||
|
@ -689,7 +740,7 @@ mod tests {
|
|||
|
||||
let mut cert = Certificate{
|
||||
serial_no: CertificateSerialNo::new(),
|
||||
flags: 1,
|
||||
usage_flags: 1,
|
||||
timestamp: 2,
|
||||
validity: [ 1,10 ],
|
||||
subject: CertificateSubject::new(),
|
||||
|
|
|
@ -11,15 +11,17 @@
|
|||
*/
|
||||
/****/
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::CString;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
use std::os::raw::*;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum IdentityType {
|
||||
|
@ -27,9 +29,15 @@ pub enum IdentityType {
|
|||
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 as isize,
|
||||
}
|
||||
|
||||
impl IdentityType {
|
||||
fn to_str(&self) -> &'static str {
|
||||
if *self == IdentityType::Curve25519 { "c25519" } else { "p384" }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IdentityType {
|
||||
fn to_string(&self) -> String {
|
||||
String::from(if *self == IdentityType::Curve25519 { "c25519" } else { "p384" })
|
||||
String::from(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,17 +51,25 @@ pub struct Identity {
|
|||
impl Identity {
|
||||
pub(crate) fn new_from_capi(id: *const ztcore::ZT_Identity, requires_delete: bool) -> Identity {
|
||||
unsafe {
|
||||
let idt = ztcore::ZT_Identity_type(id);
|
||||
let a = ztcore::ZT_Identity_address(id);
|
||||
return Identity {
|
||||
type_: FromPrimitive::from_i32(idt as i32).unwrap(),
|
||||
address: Address(a),
|
||||
Identity {
|
||||
type_: FromPrimitive::from_i32(ztcore::ZT_Identity_type(id) as i32).unwrap(),
|
||||
address: Address(ztcore::ZT_Identity_address(id)),
|
||||
capi: id,
|
||||
requires_delete,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync type and address fields with C API.
|
||||
/// This isn't truly unsafe because these are primitive types, but it's marked as such
|
||||
/// because it breaks the rules slightly. It's used in Node to make sure its identity
|
||||
/// wrapper matches the one in C++-land even if the latter changes.
|
||||
pub(crate) unsafe fn sync_type_and_address_with_capi(&self) {
|
||||
let s: *mut Identity = transmute(self as *const Identity);
|
||||
(*s).type_ = FromPrimitive::from_i32(ztcore::ZT_Identity_type(self.capi) as i32).unwrap();
|
||||
(*s).address.0 = ztcore::ZT_Identity_address(self.capi);
|
||||
}
|
||||
|
||||
/// Generate a new identity.
|
||||
/// This is time consuming due to one time proof of work. It can take several seconds.
|
||||
pub fn new_generate(id_type: IdentityType) -> Result<Identity, ResultCode> {
|
||||
|
@ -143,6 +159,13 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for Identity {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.address.0 | (self.type_.to_u64().unwrap_or(0) << 40)).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Identity {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
|
|
@ -58,7 +58,7 @@ pub use virtualnetworkconfig::*;
|
|||
pub use multicastgroup::MulticastGroup;
|
||||
pub use dictionary::*;
|
||||
|
||||
base64_serde_type!(Base64Standard, base64::URL_SAFE_NO_PAD);
|
||||
base64_serde_type!(Base64URLSafeNoPad, base64::URL_SAFE_NO_PAD);
|
||||
|
||||
/// Recommended minimum thread stack size for background threads.
|
||||
pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 524288;
|
||||
|
@ -84,6 +84,16 @@ pub const DEFAULT_UDP_MTU: u32 = ztcore::ZT_DEFAULT_UDP_MTU;
|
|||
/// Maximum UDP MTU (we never actually get this high).
|
||||
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU;
|
||||
|
||||
/// Base64 encode using the URL-safe with no padding configuration.
|
||||
pub fn base64_encode<T: AsRef<[u8]>>(t: &T) -> String {
|
||||
base64::encode_config(t, base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
/// Base64 decode using the URL-safe with no padding configuration.
|
||||
pub fn base64_decode<T: AsRef<[u8]>>(t: &T) -> Result<Vec<u8>, base64::DecodeError> {
|
||||
base64::decode_config(t, base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case,non_upper_case_globals)]
|
||||
pub mod RulePacketCharacteristicFlags {
|
||||
pub const Inbound: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_INBOUND as u64;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
*/
|
||||
/****/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||
pub struct NetworkId(pub u64);
|
||||
|
||||
|
@ -21,6 +23,13 @@ impl Default for NetworkId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for NetworkId {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for NetworkId {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{:0>16x}", self.0)
|
||||
|
|
|
@ -12,19 +12,20 @@
|
|||
/****/
|
||||
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::intrinsics::copy_nonoverlapping;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
|
||||
use std::pin::Pin;
|
||||
use std::ptr::{null_mut, slice_from_raw_parts};
|
||||
use std::ptr::{null_mut, slice_from_raw_parts, copy_nonoverlapping};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: i64 = 200;
|
||||
|
||||
|
@ -51,7 +52,8 @@ pub enum StateObjectType {
|
|||
}
|
||||
|
||||
impl StateObjectType {
|
||||
/// True if this state object should be protected.
|
||||
/// True if this state object should be protected in a data store.
|
||||
/// This could mean its file permissions should be locked down so they're only readable by the service, for example.
|
||||
#[inline(always)]
|
||||
pub fn is_secret(&self) -> bool {
|
||||
*self == StateObjectType::IdentitySecret || *self == StateObjectType::TrustStore
|
||||
|
@ -71,8 +73,8 @@ pub struct NodeStatus {
|
|||
}
|
||||
|
||||
/// An event handler that receives events, frames, and packets from the core.
|
||||
/// Note that these handlers can be called concurrently from any thread and
|
||||
/// must be thread safe.
|
||||
/// Note that if multiple threads are calling into Node these may be called by any of these threads
|
||||
/// at any time and must be thread safe.
|
||||
pub trait NodeEventHandler<N: Sync + Send + 'static> {
|
||||
/// Called when a configuration change or update should be applied to a network.
|
||||
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &N, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>);
|
||||
|
@ -99,7 +101,7 @@ pub trait NodeEventHandler<N: Sync + Send + 'static> {
|
|||
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>;
|
||||
}
|
||||
|
||||
pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
|
||||
pub(crate) struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
|
||||
event_handler: T,
|
||||
capi: *mut ztcore::ZT_Node,
|
||||
networks_by_id: Mutex<HashMap<u64, Pin<Box<N>>>>,
|
||||
|
@ -120,6 +122,7 @@ pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send
|
|||
/// multithreaded or async.
|
||||
pub struct Node<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
|
||||
intl: Pin<Box<NodeIntl<T, N, H>>>,
|
||||
identity_wrapper: Option<Identity>,
|
||||
event_handler_placeholder: PhantomData<H>,
|
||||
}
|
||||
|
||||
|
@ -323,6 +326,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
recent_clock: PortableAtomicI64::new(clock),
|
||||
recent_ticks: PortableAtomicI64::new(ticks),
|
||||
}),
|
||||
identity_wrapper: None,
|
||||
event_handler_placeholder: PhantomData::default(),
|
||||
};
|
||||
|
||||
|
@ -342,6 +346,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
|
||||
if rc == 0 {
|
||||
assert!(!n.intl.capi.is_null());
|
||||
n.identity_wrapper.replace(Identity::new_from_capi(unsafe { ztcore::ZT_Node_identity(n.intl.capi) }, false));
|
||||
Ok(n)
|
||||
} else {
|
||||
Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal))
|
||||
|
@ -352,6 +357,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds
|
||||
/// since the node was created, and after this runs it returns the amount of time the caller
|
||||
/// should wait before calling it again.
|
||||
#[inline(always)]
|
||||
pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 {
|
||||
self.intl.recent_clock.set(clock);
|
||||
self.intl.recent_ticks.set(ticks);
|
||||
|
@ -392,7 +398,9 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
self.intl.networks_by_id.lock().unwrap().remove(&nwid.0).map_or_else(|| {
|
||||
ResultCode::ErrorNetworkNotFound
|
||||
}, |_| {
|
||||
unsafe { ResultCode::from_i32(ztcore::ZT_Node_leave(self.intl.capi, clock, ticks, null_mut(), null_mut(), nwid.0) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
|
||||
unsafe {
|
||||
ResultCode::from_i32(ztcore::ZT_Node_leave(self.intl.capi, clock, ticks, null_mut(), null_mut(), nwid.0) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -410,11 +418,14 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the address of this node.
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
unsafe { Address(ztcore::ZT_Node_address(self.intl.capi) as u64) }
|
||||
}
|
||||
|
||||
/// Handle a wire packet read from the physical network.
|
||||
/// This is physical -> virtual.
|
||||
#[inline(always)]
|
||||
pub fn process_wire_packet(&self, clock: i64, ticks: i64, local_socket: i64, remote_address: &InetAddress, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
|
||||
let intl = &*self.intl;
|
||||
|
@ -423,6 +434,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
rc
|
||||
}
|
||||
|
||||
/// Handle a packet sent via a virtual network interface such as a tun/tap device.
|
||||
/// This is virtual -> physical.
|
||||
#[inline(always)]
|
||||
pub fn process_virtual_network_frame(&self, clock: i64, ticks: i64, nwid: &NetworkId, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
|
||||
let intl = &*self.intl;
|
||||
|
@ -431,30 +444,27 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
rc
|
||||
}
|
||||
|
||||
/// Subscribe to a multicast group on a given network.
|
||||
#[inline(always)]
|
||||
pub fn multicast_subscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
|
||||
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
|
||||
}
|
||||
|
||||
/// Unsubscribe from a multicast group on a given network.
|
||||
#[inline(always)]
|
||||
pub fn multicast_unsubscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
|
||||
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
|
||||
}
|
||||
|
||||
/// Get a copy of this node's identity.
|
||||
/// Get this node's identity.
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> Identity {
|
||||
unsafe { self.identity_fast().clone() }
|
||||
}
|
||||
|
||||
/// Get an identity that simply holds a pointer to the underlying node's identity.
|
||||
/// This is unsafe because the identity object becomes invalid if the node ceases
|
||||
/// to exist the Identity becomes invalid. Use clone() on it to get a copy.
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn identity_fast(&self) -> Identity {
|
||||
Identity::new_from_capi(ztcore::ZT_Node_identity(self.intl.capi), false)
|
||||
pub fn identity(&self) -> &Identity {
|
||||
let id = self.identity_wrapper.as_ref().unwrap();
|
||||
unsafe { id.sync_type_and_address_with_capi() };
|
||||
id
|
||||
}
|
||||
|
||||
/// Get status information for this node.
|
||||
pub fn status(&self, clock: i64, ticks: i64) -> NodeStatus {
|
||||
let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed();
|
||||
unsafe {
|
||||
|
@ -473,6 +483,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a list of all peers with which this node is communicating or has very recently communicated.
|
||||
/// The presence of a peer on this list doesn't mean it has actual access to any network.
|
||||
pub fn peers(&self, clock: i64, ticks: i64) -> Vec<Peer> {
|
||||
let mut p: Vec<Peer> = Vec::new();
|
||||
unsafe {
|
||||
|
@ -489,6 +501,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
p
|
||||
}
|
||||
|
||||
/// Get the networks this node has joined.
|
||||
pub fn networks(&self) -> Vec<VirtualNetworkConfig> {
|
||||
let mut n: Vec<VirtualNetworkConfig> = Vec::new();
|
||||
unsafe {
|
||||
|
@ -505,6 +518,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
|
|||
n
|
||||
}
|
||||
|
||||
/// Get the certificates in this node's trust store and their local trust flags.
|
||||
pub fn certificates(&self, clock: i64, ticks: i64) -> Vec<(Certificate, u32)> {
|
||||
let mut c: Vec<(Certificate, u32)> = Vec::new();
|
||||
unsafe {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
/****/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Endpoint;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
|
|
|
@ -19,13 +19,163 @@ use dialoguer::Input;
|
|||
use zerotier_core::*;
|
||||
|
||||
use crate::store::Store;
|
||||
use crate::utils::{read_limit, ms_since_epoch, to_json_pretty};
|
||||
use crate::GlobalFlags;
|
||||
|
||||
/// Dump a certificate in human-readable format to stdout.
|
||||
fn dump_cert(certificate: &Certificate) {
|
||||
let mut subject_identities = String::new();
|
||||
let mut subject_networks = String::new();
|
||||
let mut subject_update_urls = String::new();
|
||||
let mut usage_flags = String::new();
|
||||
|
||||
fn string_or_dash(s: &str) -> &str {
|
||||
if s.is_empty() {
|
||||
"-"
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
if certificate.subject.identities.is_empty() {
|
||||
subject_identities.push_str(": (none)");
|
||||
} else {
|
||||
for x in certificate.subject.identities.iter() {
|
||||
subject_identities.push_str("\n ");
|
||||
subject_identities.push_str(x.identity.to_string().as_str());
|
||||
if x.locator.is_some() {
|
||||
subject_identities.push_str(" ");
|
||||
subject_identities.push_str(x.locator.as_ref().unwrap().to_string().as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if certificate.subject.networks.is_empty() {
|
||||
subject_networks.push_str(": (none)");
|
||||
} else {
|
||||
for x in certificate.subject.networks.iter() {
|
||||
subject_networks.push_str("\n ");
|
||||
subject_networks.push_str(x.id.to_string().as_str());
|
||||
if x.controller.is_some() {
|
||||
subject_networks.push_str(" at ");
|
||||
subject_networks.push_str(x.controller.as_ref().unwrap().to_string().as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if certificate.subject.update_urls.is_empty() {
|
||||
subject_update_urls.push_str(": (none)");
|
||||
} else {
|
||||
for x in certificate.subject.update_urls.iter() {
|
||||
subject_update_urls.push_str("\n ");
|
||||
subject_update_urls.push_str(x.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
if certificate.usage_flags != 0 {
|
||||
usage_flags.push_str(" (");
|
||||
for f in ALL_CERTIFICATE_USAGE_FLAGS.iter() {
|
||||
if (certificate.usage_flags & (*f).0) != 0 {
|
||||
if !usage_flags.is_empty() {
|
||||
usage_flags.push(',');
|
||||
}
|
||||
usage_flags.push_str((*f).1);
|
||||
}
|
||||
}
|
||||
usage_flags.push(')');
|
||||
}
|
||||
|
||||
println!(r###"Serial Number (SHA384): {}
|
||||
Usage Flags: 0x{:0>8x}{}
|
||||
Timestamp: {}
|
||||
Validity: {} to {}
|
||||
Subject
|
||||
Timestamp: {}
|
||||
Identities{}
|
||||
Networks{}
|
||||
Update URLs{}
|
||||
Name
|
||||
Serial: {}
|
||||
Common Name: {}
|
||||
Country: {}
|
||||
Organization: {}
|
||||
Unit: {}
|
||||
Locality: {}
|
||||
State/Province: {}
|
||||
Street Address: {}
|
||||
Postal Code: {}
|
||||
E-Mail: {}
|
||||
URL: {}
|
||||
Host: {}
|
||||
Unique ID: {}
|
||||
Unique ID Signature: {}
|
||||
Issuer: {}
|
||||
Issuer Public Key: {}
|
||||
Public Key: {}
|
||||
Extended Attributes: {} bytes
|
||||
Signature: {}
|
||||
Maximum Path Length: {}{}"###,
|
||||
certificate.serial_no.to_string(),
|
||||
certificate.usage_flags, usage_flags,
|
||||
certificate.timestamp,
|
||||
certificate.validity[0], certificate.validity[1],
|
||||
certificate.subject.timestamp,
|
||||
subject_identities,
|
||||
subject_networks,
|
||||
subject_update_urls,
|
||||
string_or_dash(certificate.subject.name.serial_no.as_str()),
|
||||
string_or_dash(certificate.subject.name.common_name.as_str()),
|
||||
string_or_dash(certificate.subject.name.country.as_str()),
|
||||
string_or_dash(certificate.subject.name.organization.as_str()),
|
||||
string_or_dash(certificate.subject.name.unit.as_str()),
|
||||
string_or_dash(certificate.subject.name.locality.as_str()),
|
||||
string_or_dash(certificate.subject.name.province.as_str()),
|
||||
string_or_dash(certificate.subject.name.street_address.as_str()),
|
||||
string_or_dash(certificate.subject.name.postal_code.as_str()),
|
||||
string_or_dash(certificate.subject.name.email.as_str()),
|
||||
string_or_dash(certificate.subject.name.url.as_str()),
|
||||
string_or_dash(certificate.subject.name.host.as_str()),
|
||||
string_or_dash(base64_encode(&certificate.subject.unique_id).as_str()),
|
||||
string_or_dash(base64_encode(&certificate.subject.unique_id_signature).as_str()),
|
||||
certificate.issuer.to_string(),
|
||||
string_or_dash(base64_encode(&certificate.issuer_public_key).as_str()),
|
||||
string_or_dash(base64_encode(&certificate.public_key).as_str()),
|
||||
certificate.extended_attributes.len(),
|
||||
string_or_dash(base64_encode(&certificate.signature).as_str()),
|
||||
certificate.max_path_length, if certificate.max_path_length == 0 { " (leaf)" } else { " (CA or sub-CA)" });
|
||||
}
|
||||
|
||||
fn list(store: &Arc<Store>) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn show<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
|
||||
0
|
||||
fn show<'a>(store: &Arc<Store>, global_flags: &GlobalFlags, cli_args: &ArgMatches<'a>) -> i32 {
|
||||
let serial_or_path = cli_args.value_of("serialorpath").unwrap().trim();
|
||||
CertificateSerialNo::new_from_string(serial_or_path).map_or_else(|| {
|
||||
read_limit(serial_or_path, 65536).map_or_else(|e| {
|
||||
println!("ERROR: unable to read certificate from '{}': {}", serial_or_path, e.to_string());
|
||||
1
|
||||
}, |cert_json| {
|
||||
serde_json::from_slice::<Certificate>(cert_json.as_ref()).map_or_else(|e| {
|
||||
println!("ERROR: unable to decode certificate from '{}': {}", serial_or_path, e.to_string());
|
||||
1
|
||||
}, |certificate| {
|
||||
if global_flags.json_output {
|
||||
println!("{}", to_json_pretty(&certificate));
|
||||
} else {
|
||||
dump_cert(&certificate);
|
||||
}
|
||||
let cv = certificate.verify(ms_since_epoch());
|
||||
if cv != CertificateError::None {
|
||||
println!("\nWARNING: certificate validity check failed: {}", cv.to_str());
|
||||
}
|
||||
0
|
||||
})
|
||||
})
|
||||
}, |serial| {
|
||||
// TODO: query node
|
||||
0
|
||||
})
|
||||
}
|
||||
|
||||
fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
|
||||
|
@ -35,13 +185,13 @@ fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
|
|||
1
|
||||
} else {
|
||||
let (_, privk) = key_pair.ok().unwrap();
|
||||
let privk_hex = hex::encode(privk);
|
||||
let privk_base64 = base64_encode(&privk);
|
||||
let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") });
|
||||
if path.is_empty() {
|
||||
println!("{}", privk_hex);
|
||||
println!("{}", privk_base64);
|
||||
0
|
||||
} else {
|
||||
std::fs::write(path, privk_hex.as_bytes()).map_or_else(|e| {
|
||||
std::fs::write(path, privk_base64.as_bytes()).map_or_else(|e| {
|
||||
eprintln!("FATAL: error writing '{}': {}", path, e.to_string());
|
||||
e.raw_os_error().unwrap_or(1)
|
||||
}, |_| {
|
||||
|
@ -251,10 +401,6 @@ fn verify<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
fn dump<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn import<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
|
||||
0
|
||||
}
|
||||
|
@ -271,19 +417,18 @@ fn delete<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
pub(crate) fn run(store: Arc<Store>, cli_args: &ArgMatches) -> i32 {
|
||||
pub(crate) fn run(store: Arc<Store>, global_flags: GlobalFlags, cli_args: &ArgMatches) -> i32 {
|
||||
match cli_args.subcommand() {
|
||||
("list", None) => list(&store),
|
||||
("show", Some(sub_cli_args)) => show(&store, sub_cli_args),
|
||||
("show", Some(sub_cli_args)) => show(&store, &global_flags, sub_cli_args),
|
||||
("newsuid", sub_cli_args) => newsuid(sub_cli_args),
|
||||
("newcsr", Some(sub_cli_args)) => newcsr(sub_cli_args),
|
||||
("sign", Some(sub_cli_args)) => sign(&store, sub_cli_args),
|
||||
("verify", Some(sub_cli_args)) => verify(&store, sub_cli_args),
|
||||
("dump", Some(sub_cli_args)) => dump(&store, sub_cli_args),
|
||||
("import", Some(sub_cli_args)) => import(&store, sub_cli_args),
|
||||
("factoryreset", None) => factoryreset(&store),
|
||||
("export", Some(sub_cli_args)) => export(&store, sub_cli_args),
|
||||
("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args),
|
||||
("factoryreset", None) => factoryreset(&store),
|
||||
_ => {
|
||||
crate::print_help();
|
||||
1
|
||||
|
|
|
@ -96,7 +96,7 @@ mod tests {
|
|||
fn test_getifaddrs() {
|
||||
println!("starting getifaddrs...");
|
||||
crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| {
|
||||
println!(" device: {} ip: {}", dev, a.to_string())
|
||||
println!(" {} {}", dev, a.to_string())
|
||||
});
|
||||
println!("done.")
|
||||
}
|
||||
|
|
|
@ -85,12 +85,12 @@ Common Operations:
|
|||
· defaultroute <boolean> Can default route be overridden?
|
||||
|
||||
· join [-...] <network> Join a virtual network
|
||||
-c <identity | fingerprint> Controller identity / fingerprint
|
||||
-c <?identity | fingerprint> Controller identity / fingerprint
|
||||
· leave <network> Leave a virtual network
|
||||
|
||||
Advanced Operations:
|
||||
|
||||
service Start this node
|
||||
service Start node
|
||||
(usually not invoked directly)
|
||||
|
||||
controller <command> [option]
|
||||
|
@ -103,39 +103,35 @@ Advanced Operations:
|
|||
|
||||
identity <command> [args]
|
||||
new [c25519 | p384] Create identity (default: c25519)
|
||||
getpublic <identity> Extract public part of identity
|
||||
fingerprint <identity> Get an identity's fingerprint
|
||||
validate <identity> Locally validate an identity
|
||||
sign <identity> <file> Sign a file with an identity's key
|
||||
verify <identity> <file> <sig> Verify a signature
|
||||
getpublic <?identity> Extract public part of identity
|
||||
fingerprint <?identity> Get an identity's fingerprint
|
||||
validate <?identity> Locally validate an identity
|
||||
sign <?identity> <@file> Sign a file with an identity's key
|
||||
verify <?identity> <@file> <sig> Verify a signature
|
||||
|
||||
locator <command> [args]
|
||||
new [-...] <identity> <endpoint> [...] Create new signed locator
|
||||
new [-...] <?identity> <endpoint,...> Create new signed locator
|
||||
-r <revision> Revision number (default: time)
|
||||
verify <identity> <locator> Verify locator signature
|
||||
show <locator> Show contents of a locator
|
||||
verify <?identity> <?locator> Verify locator signature
|
||||
show <?locator> Show contents of a locator
|
||||
|
||||
cert <command> [args]
|
||||
· list List certificates at local node
|
||||
· show <serial> Show certificate details
|
||||
newsuid [suid secret out] Create a subject unique ID secret
|
||||
newcsr <csr out> <secret out> Create a CSR (interactive)
|
||||
sign <csr> <identity> [cert out] Sign a CSR to create a certificate
|
||||
verify <cert> Verify certificate (not chain)
|
||||
dump <cert> Verify and print certificate
|
||||
· import <cert> [trust,trust,...] Import certificate into this node
|
||||
trust flag: rootca Certificate is a root CA
|
||||
trust flag: ztrootset ZeroTier root node set
|
||||
· factoryreset Re-import compiled-in default certs
|
||||
· export <serial> [path] Export a certificate from this node
|
||||
show <@cert|·serial> Show certificate details
|
||||
newsuid [@secret] Create a subject unique ID secret
|
||||
newcsr <@csr> <@secret> Create a CSR (interactive)
|
||||
sign <@csr> <@secret> <@cert> Sign a CSR to create a certificate
|
||||
verify <@cert> Internally verify certificate
|
||||
· import <@cert> [trust,trust,...] Import certificate into this node
|
||||
trust flag: rootca Root (or self-signed) CA
|
||||
trust flag: config Can influence node configuration
|
||||
· export <serial> [@cert] Export a certificate from this node
|
||||
· delete <serial|ALL> Delete certificate from this node
|
||||
· factoryreset Re-import compiled-in default certs
|
||||
|
||||
· Command requires a running node and access to a local API token.
|
||||
|
||||
An <address> may be specified as a 10-digit short ZeroTier address, a
|
||||
fingerprint containing both an address and a SHA384 hash, or an identity.
|
||||
Identities and locators can be specified as either paths to files on the
|
||||
filesystem or verbatim objects in string format. This is auto-detected.
|
||||
· Command (or command with argument type) requires a running node.
|
||||
@ Argument is a path to an object, not the object itself.
|
||||
? Argument can be either an inline value or a path to it.
|
||||
"###, ver.0, ver.1, ver.2)
|
||||
}
|
||||
|
||||
|
@ -178,10 +174,11 @@ fn make_store(cli_args: &ArgMatches) -> Arc<Store> {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalFlags {
|
||||
pub(crate) struct GlobalFlags {
|
||||
pub json_output: bool,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags {
|
||||
GlobalFlags {
|
||||
json_output: cli_args.is_present("json")
|
||||
|
@ -276,7 +273,7 @@ fn main() {
|
|||
.subcommand(App::new("cert")
|
||||
.subcommand(App::new("list"))
|
||||
.subcommand(App::new("show")
|
||||
.arg(Arg::with_name("serial").index(1).required(true)))
|
||||
.arg(Arg::with_name("serialorpath").index(1).required(true)))
|
||||
.subcommand(App::new("newsuid")
|
||||
.arg(Arg::with_name("path").index(1).required(false)))
|
||||
.subcommand(App::new("newcsr")
|
||||
|
@ -284,7 +281,7 @@ fn main() {
|
|||
.arg(Arg::with_name("secretpath").index(2).required(true)))
|
||||
.subcommand(App::new("sign")
|
||||
.arg(Arg::with_name("csr").index(1).required(true))
|
||||
.arg(Arg::with_name("identity").index(2).required(true))
|
||||
.arg(Arg::with_name("secretpath").index(2).required(true))
|
||||
.arg(Arg::with_name("output").index(3).required(false)))
|
||||
.subcommand(App::new("verify")
|
||||
.arg(Arg::with_name("cert").index(1).required(true)))
|
||||
|
@ -340,7 +337,7 @@ fn main() {
|
|||
("controller", Some(sub_cli_args)) => { 0 }
|
||||
("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args),
|
||||
("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args),
|
||||
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), sub_cli_args),
|
||||
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), get_global_flags(&cli_args), sub_cli_args),
|
||||
_ => {
|
||||
print_help();
|
||||
1
|
||||
|
|
|
@ -219,7 +219,7 @@ impl Service {
|
|||
uptime: ms_monotonic() - self.startup_time_monotonic,
|
||||
config: (*self.local_config()).clone(),
|
||||
online: self.online(),
|
||||
public_identity: node.identity(),
|
||||
public_identity: node.identity().clone(),
|
||||
version: format!("{}.{}.{}", ver.0, ver.1, ver.2),
|
||||
version_major: ver.0,
|
||||
version_minor: ver.1,
|
||||
|
@ -237,7 +237,7 @@ unsafe impl Send for Service {}
|
|||
unsafe impl Sync for Service {}
|
||||
|
||||
async fn run_async(store: Arc<Store>, local_config: Arc<LocalConfig>) -> i32 {
|
||||
let mut process_exit_value: i32 = 0;
|
||||
let process_exit_value: i32 = 0;
|
||||
|
||||
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
|
||||
let mut http_listeners: BTreeMap<InetAddress, HttpListener> = BTreeMap::new();
|
||||
|
|
|
@ -123,6 +123,16 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
|
||||
pub(crate) fn to_json<O: serde::Serialize>(o: &O) -> String {
|
||||
serde_json::to_string(o).unwrap_or("null".into())
|
||||
}
|
||||
|
||||
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
|
||||
pub(crate) fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
|
||||
serde_json::to_string_pretty(o).unwrap_or("null".into())
|
||||
}
|
||||
|
||||
/// Recursively patch a JSON object.
|
||||
/// This is slightly different from a usual JSON merge. For objects in the target their fields
|
||||
/// are updated by recursively calling json_patch if the same field is present in the source.
|
||||
|
|
|
@ -32,7 +32,7 @@ pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGro
|
|||
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family as i32 == osdep::AF_LINK as i32 {
|
||||
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
|
||||
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
|
||||
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && osdep::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::osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
|
||||
let mi = la.sdl_nlen as usize;
|
||||
groups.insert(MulticastGroup{
|
||||
mac: MAC((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),
|
||||
|
|
Loading…
Add table
Reference in a new issue