A bunch of cleanup, bug fix, just use unordered_map, etc.

This commit is contained in:
Adam Ierymenko 2020-03-30 17:28:23 -07:00
parent 0d05e4bcae
commit 9428fc53f6
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
31 changed files with 449 additions and 447 deletions

View file

@ -1370,7 +1370,7 @@ void EmbeddedNetworkController::_request(
++caprc;
}
}
nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc);
nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId,nwid,now,capr,caprc);
if (nc->capabilities[nc->capabilityCount].sign(_signingId,identity.address()))
++nc->capabilityCount;
if (nc->capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES)

View file

@ -51,7 +51,7 @@ ZT_INLINE void s_bmul64(const uint64_t x,const uint64_t y,uint64_t &r_high,uint6
r |= z & m4;
z = (x1 * y5) ^ (x2 * y4) ^ (x3 * y3) ^ (x4 * y2) ^ (x5 * y1);
r |= z & m5;
r_high = (uint64_t)(r >> 64);
r_high = (uint64_t)(r >> 64U);
r_low = (uint64_t)r;
}

View file

@ -130,6 +130,7 @@ public:
}
class GMACSIVEncryptor;
class GMACSIVDecryptor;
/**
* Streaming GMAC calculator
@ -137,6 +138,7 @@ public:
class GMAC
{
friend class GMACSIVEncryptor;
friend class GMACSIVDecryptor;
public:
/**
@ -209,6 +211,7 @@ public:
class CTR
{
friend class GMACSIVEncryptor;
friend class GMACSIVDecryptor;
public:
ZT_INLINE CTR(const AES &aes) noexcept : _aes(aes) {}
@ -250,7 +253,7 @@ public:
};
/**
* Encrypt with AES-GMAC-SIV
* Encryptor for GMAC-SIV
*/
class GMACSIVEncryptor
{
@ -374,6 +377,101 @@ public:
AES::CTR _ctr;
};
/**
* Decryptor for GMAC-SIV
*/
class GMACSIVDecryptor
{
public:
ZT_INLINE GMACSIVDecryptor(const AES &k0,const AES &k1) noexcept :
_ctr(k1),
_gmac(k0) {}
/**
* Initialize decryptor for a new message
*
* @param tag 128-bit combined IV/MAC originally created by GMAC-SIV encryption
* @param output Buffer in which to write output plaintext (must be large enough!)
*/
ZT_INLINE void init(const uint64_t tag[2],void *const output) noexcept
{
// Init CTR with the most significant 96 bits of the tag (as in encryption).
uint64_t tmp[2];
tmp[0] = tag[0];
#if __BYTE_ORDER == __BIG_ENDIAN
tmp[1] = tag[1] & 0xffffffff00000000ULL;
#else
tmp[1] = tag[1] & 0x00000000ffffffffULL;
#endif
_ctr.init(reinterpret_cast<const uint8_t *>(tmp),output);
// Decrypt the opaque tag to yield the original IV and 64-bit truncated MAC.
_ctr._aes.decrypt(tag,_ivMac);
// Initialize GMAC with the original IV.
tmp[0] = _ivMac[0];
tmp[1] = 0;
_gmac.init(reinterpret_cast<const uint8_t *>(tmp));
_output = output;
_decryptedLen = 0;
}
/**
* Process AAD (additional authenticated data) that wasn't encrypted
*
* @param aad Additional authenticated data
* @param len Length of AAD in bytes
*/
ZT_INLINE void aad(const void *const aad,unsigned int len) noexcept
{
_gmac.update(aad,len);
len &= 0xfU;
if (len != 0)
_gmac.update(Utils::ZERO256,16 - len);
}
/**
* Feed ciphertext into the decryptor
*
* Unlike encryption, GMAC-SIV decryption requires only one pass.
*
* @param input Input ciphertext
* @param len Length of ciphertext
*/
ZT_INLINE void update(const void *const input,const unsigned int len) noexcept
{
_ctr.crypt(input,len);
_decryptedLen += len;
}
/**
* Flush decryption, compute MAC, and verify
*
* @return True if resulting plaintext (and AAD) pass message authentication check
*/
ZT_INLINE bool finish() noexcept
{
// Flush any remaining bytes from CTR.
_ctr.finish();
// Feed plaintext through GMAC.
_gmac.update(_output,_decryptedLen);
uint64_t gmacTag[2];
_gmac.finish(reinterpret_cast<uint8_t *>(gmacTag));
// MAC passes if its first 64 bits equals the MAC we got by decrypting the tag.
return gmacTag[0] == _ivMac[1];
}
private:
uint64_t _ivMac[2];
AES::CTR _ctr;
AES::GMAC _gmac;
void *_output;
unsigned int _decryptedLen;
};
private:
static const uint32_t Te0[256];
static const uint32_t Te1[256];

View file

@ -329,7 +329,7 @@ public:
template<typename T>
ZT_INLINE int rO(int &ii,T &obj) const noexcept
{
if (ii < ZT_BUF_MEM_SIZE) {
if (likely(ii < ZT_BUF_MEM_SIZE)) {
int ms = obj.unmarshal(unsafeData + ii,ZT_BUF_MEM_SIZE - ii);
if (ms > 0)
ii += ms;
@ -353,7 +353,7 @@ public:
{
const char *const s = (const char *)(unsafeData + ii);
const int sii = ii;
while (ii < ZT_BUF_MEM_SIZE) {
while (likely(ii < ZT_BUF_MEM_SIZE)) {
if (unsafeData[ii++] == 0) {
Utils::copy(buf,s,ii - sii);
return buf;
@ -398,7 +398,7 @@ public:
*/
ZT_INLINE uint8_t *rB(int &ii,void *const bytes,const unsigned int len) const noexcept
{
if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) {
if (likely(((ii += (int)len) <= ZT_BUF_MEM_SIZE))) {
Utils::copy(bytes,unsafeData + ii,len);
return reinterpret_cast<uint8_t *>(bytes);
}
@ -586,7 +586,7 @@ public:
ZT_INLINE void wO(int &ii,T &t) noexcept
{
const int s = ii;
if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) {
if (likely((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE)) {
int ms = t.marshal(unsafeData + s);
if (ms > 0)
ii += ms;
@ -624,7 +624,7 @@ public:
ZT_INLINE void wB(int &ii,const void *const bytes,const unsigned int len) noexcept
{
const int s = ii;
if ((ii += (int)len) <= ZT_BUF_MEM_SIZE)
if (likely((ii += (int)len) <= ZT_BUF_MEM_SIZE))
Utils::copy(unsafeData + s,bytes,len);
}

View file

@ -2474,24 +2474,28 @@ void C25519::_calcPubDH(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv
void C25519::_calcPubED(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],const uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
{
unsigned char extsk[64];
sc25519 scsk;
ge25519 gepk;
struct {
uint8_t extsk[64];
sc25519 scsk;
ge25519 gepk;
} s;
// Second 32 bytes of pub and priv are the keys for ed25519
// signing and verification.
SHA512(extsk,priv + 32,32);
extsk[0] &= 248;
extsk[31] &= 127;
extsk[31] |= 64;
sc25519_from32bytes(&scsk,extsk);
ge25519_scalarmult_base(&gepk,&scsk);
ge25519_pack(pub + 32,&gepk);
SHA512(s.extsk,priv + 32,32);
s.extsk[0] &= 248U;
s.extsk[31] &= 127U;
s.extsk[31] |= 64U;
sc25519_from32bytes(&s.scsk,s.extsk);
ge25519_scalarmult_base(&s.gepk,&s.scsk);
ge25519_pack(pub + 32,&s.gepk);
// In NaCl, the public key is crammed into the next 32 bytes
// of the private key for signing since both keys are required
// to sign. In this version we just get it from kp.pub, so we
// leave that out of private.
Utils::burn(&s,sizeof(s));
}
} // namespace ZeroTier

View file

@ -17,12 +17,12 @@ set(core_headers
Expect.hpp
FCV.hpp
Fingerprint.hpp
FlatMap.hpp
Identity.hpp
InetAddress.hpp
Locator.hpp
LZ4.hpp
MAC.hpp
Map.hpp
Membership.hpp
MulticastGroup.hpp
Mutex.hpp

View file

@ -20,7 +20,7 @@
#include "Mutex.hpp"
#include "Path.hpp"
#include "FCV.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include <cstring>
#include <cstdlib>
@ -164,7 +164,7 @@ public:
std::vector<std::pair<int64_t,uint64_t> > messagesByLastUsedTime;
messagesByLastUsedTime.reserve(_messages.size());
for(typename FlatMap< uint64_t,_E >::const_iterator i(_messages.begin());i!=_messages.end();++i)
for(typename Map< uint64_t,_E >::const_iterator i(_messages.begin());i!=_messages.end();++i)
messagesByLastUsedTime.push_back(std::pair<int64_t,uint64_t>(i->second.lastUsed,i->first));
std::sort(messagesByLastUsedTime.begin(),messagesByLastUsedTime.end());
@ -286,6 +286,7 @@ public:
}
private:
// _E is an entry in the message queue.
struct _E
{
ZT_INLINE _E() noexcept :
@ -312,6 +313,19 @@ private:
}
}
ZT_INLINE _E &operator=(const _E &e)
{
if (this != &e) {
id = e.id;
lastUsed = e.lastUsed;
totalFragmentsExpected = e.totalFragmentsExpected;
fragmentsReceived = e.fragmentsReceived;
via = e.via;
message = e.message;
}
return *this;
}
uint64_t id;
volatile int64_t lastUsed;
unsigned int totalFragmentsExpected;
@ -321,7 +335,7 @@ private:
Mutex lock;
};
FlatMap< uint64_t,_E > _messages;
Map< uint64_t,_E > _messages;
RWMutex _messages_l;
};

View file

@ -15,19 +15,6 @@
namespace ZeroTier {
static uint64_t _toKey(const char *k)
{
uint64_t n = 0;
unsigned int i = 0;
for(;;) {
char c = k[i];
if (!c) break;
reinterpret_cast<uint8_t *>(&n)[i & 7U] ^= (uint8_t)c;
++i;
}
return n;
}
Dictionary::Dictionary()
{
}
@ -80,7 +67,7 @@ void Dictionary::add(const char *k,uint64_t v)
void Dictionary::add(const char *k,const Address &v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(11);
e.resize(ZT_ADDRESS_STRING_SIZE_MAX);
v.toString((char *)e.data());
}
@ -109,7 +96,6 @@ void Dictionary::add(const char *k,const void *data,unsigned int len)
bool Dictionary::getB(const char *k,bool dfl) const
{
bool v = dfl;
const std::vector<uint8_t> &e = (*this)[k];
if (!e.empty()) {
switch ((char)e[0]) {
@ -123,7 +109,7 @@ bool Dictionary::getB(const char *k,bool dfl) const
return false;
}
}
return v;
return dfl;
}
uint64_t Dictionary::getUI(const char *k,uint64_t dfl) const
@ -173,7 +159,6 @@ void Dictionary::encode(std::vector<uint8_t> &out) const
for(std::map< uint64_t,std::vector<uint8_t> >::const_iterator ti(_t.begin());ti!=_t.end();++ti) {
str[0] = ti->first;
const char *k = (const char *)str;
for(;;) {
char kc = *(k++);
if (!kc) break;

View file

@ -30,7 +30,7 @@ namespace ZeroTier {
*
* This data structure is used for network configurations, node meta-data,
* and other open-definition protocol objects. It consists of a key-value
* store with short (under 8 characters) keys that map to strings, blobs,
* store with short (max: 8 characters) keys that map to strings, blobs,
* or integers with the latter being by convention in hex format.
*
* If this seems a little odd, it is. It dates back to the very first alpha
@ -168,6 +168,18 @@ public:
bool decode(const void *data,unsigned int len);
private:
// This just packs up to 8 character bytes into a 64-bit word. There is no need
// for this to be portable in terms of endian-ness. It's just for fast key lookup.
static ZT_INLINE uint64_t _toKey(const char *k)
{
uint64_t key = 0;
for(int i=0;i<8;++i) {
if ((reinterpret_cast<uint8_t *>(&key)[i] = *(k++)) == 0)
break;
}
return key;
}
std::map< uint64_t,std::vector<uint8_t> > _t;
};

View file

@ -80,7 +80,7 @@ private:
const uint64_t _salt;
// Each bucket contains a timestamp in units of the expect duration.
std::atomic<int32_t> _packetIdSent[ZT_EXPECT_TTL];
std::atomic<int32_t> _packetIdSent[ZT_EXPECT_BUCKETS];
};
} // namespace ZeroTier

View file

@ -1,245 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_FLATMAP_HPP
#define ZT_FLATMAP_HPP
#include "Constants.hpp"
#include "Utils.hpp"
#include <list>
#include <vector>
#include <utility>
namespace ZeroTier {
/**
* A very simple and fast flat hash table
*
* This is designed to prioritize performance over memory, though with
* endpoint ZeroTier nodes this is used in cases where the data set is not
* that large (e.g. networks, peers) and so memory consumption should not
* be much.
*
* It creates a flat hash table that maps keys to values and doubles its
* size whenever a collision occurs. The size is always a power of two so
* that only very fast operations like add, XOR, and bit mask can be used
* for hash table lookup.
*
* It also saves actual key/value pairs in a linked list for relatively
* fast iteration compared to a hash table where you have to iterate through
* a bunch of empty buckets. Iteration order should be considered undefined.
*
* @tparam K Key type
* @tparam V Value type
* @tparam IC Initial capacity (must be a power of two)
*/
template<typename K,typename V,unsigned long IC = 16>
class FlatMap
{
public:
typedef K key_type;
typedef V mapped_type;
typedef std::pair<K,V> value_type;
typedef typename std::list< std::pair<K,V> >::iterator iterator;
typedef typename std::list< std::pair<K,V> >::const_iterator const_iterator;
ZT_INLINE FlatMap() :
_d(),
_null(),
_b(new iterator[IC]),
_s(IC),
_m(IC - 1)
{
}
ZT_INLINE FlatMap(const FlatMap &m) :
_d(m._d),
_null(),
_b(new iterator[m._s]),
_s(m._s),
_m(m._m)
{
try {
for (iterator i=_d.begin();i!=_d.end();++i)
_b[_hc(i->first) & _m] = i;
} catch ( ... ) {
delete [] _b;
throw;
}
}
ZT_INLINE ~FlatMap()
{
delete [] _b;
}
ZT_INLINE FlatMap &operator=(const FlatMap &m)
{
_d = m._d;
delete [] _b;
_b = nullptr;
_b = new iterator[m._s];
_s = m._s;
_m = m._m;
for(iterator i=_d.begin();i!=_d.end();++i)
_b[_hc(i->first) & _m] = i;
}
ZT_INLINE void clear()
{
_d.clear();
delete [] _b;
_b = nullptr;
_b = new iterator[IC];
_s = IC;
_m = IC - 1;
}
ZT_INLINE V *get(const K &key) noexcept
{
const unsigned long hb = _hc(key) & _m;
if ((_b[hb] != _null)&&(_b[hb]->first == key))
return &(_b[hb]->second);
return nullptr;
}
ZT_INLINE const V *get(const K &key) const noexcept
{
const unsigned long hb = _hc(key) & _m;
if ((_b[hb] != _null)&&(_b[hb]->first == key))
return &(_b[hb]->second);
return nullptr;
}
ZT_INLINE iterator find(const K &key) noexcept
{
const unsigned long hb = _hc(key) & _m;
if ((_b[hb] != _null)&&(_b[hb]->first == key))
return _b[hb];
return _d.end();
}
ZT_INLINE const_iterator find(const K &key) const noexcept
{
const unsigned long hb = _hc(key) & _m;
if ((_b[hb] != _null)&&(_b[hb]->first == key))
return _b[hb];
return _d.end();
}
ZT_INLINE V &set(const K &key,const V &value)
{
unsigned long hb = _hc(key) & _m;
if (_b[hb] == _null) {
#ifdef __CPP11__
_d.emplace_back(key,value);
#else
_d.push_back(std::pair<K,V>(key,value));
#endif
return (_b[hb] = --_d.end())->second;
} else {
if (_b[hb]->first == key) {
_b[hb]->second = value;
return _b[hb]->second;
}
#ifdef __CPP11__
_d.emplace_back(key,value);
#else
_d.push_back(std::pair<K,V>(key,value));
#endif
_grow();
return _b[_hc(key) & _m]->second;
}
}
ZT_INLINE V &operator[](const K &key)
{
unsigned long hb = _hc(key) & _m;
if (_b[hb] == _null) {
#ifdef __CPP11__
_d.emplace_back(key,V());
#else
_d.push_back(std::pair<K,V>(key,V()));
#endif
return (_b[hb] = --_d.end())->second;
} else {
if (_b[hb]->first == key)
return _b[hb]->second;
#ifdef __CPP11__
_d.emplace_back(key,V());
#else
_d.push_back(std::pair<K,V>(key,V()));
#endif
_grow();
return _b[_hc(key) & _m]->second;
}
}
ZT_INLINE void erase(const iterator &i)
{
_b[_hc(i->first) & _m] = _null;
_d.erase(i);
}
ZT_INLINE void erase(const K &key)
{
iterator e(find(key));
if (e != end())
erase(e);
}
ZT_INLINE iterator begin() noexcept { return _d.begin(); }
ZT_INLINE iterator end() noexcept { return _d.end(); }
ZT_INLINE const_iterator begin() const noexcept { return _d.begin(); }
ZT_INLINE const_iterator end() const noexcept { return _d.end(); }
ZT_INLINE unsigned long size() const { return (unsigned long)_d.size(); }
ZT_INLINE bool empty() const { return _d.empty(); }
ZT_INLINE unsigned long hashSize() const { return _s; }
private:
template<typename O>
static ZT_INLINE unsigned long _hc(const O &obj) { return (unsigned long)obj.hashCode(); }
static ZT_INLINE unsigned long _hc(const uint64_t i) noexcept { return (unsigned long)(i + (i >> 32U)); }
static ZT_INLINE unsigned long _hc(const int64_t i) noexcept { return (unsigned long)((uint64_t)i + ((uint64_t)i >> 32U)); }
static ZT_INLINE unsigned long _hc(const uint32_t i) noexcept { return Utils::hash32(i); }
ZT_INLINE void _grow()
{
unsigned long hb;
enlarge_again:
delete [] _b;
_b = nullptr; // in case 'new' throws
_b = new iterator[_s <<= 1U];
_m = _s - 1;
for(iterator i=_d.begin();i!=_d.end();++i) {
hb = _hc(i->first) & _m;
if (_b[hb] == _null) {
_b[hb] = i;
} else {
goto enlarge_again;
}
}
}
std::list< std::pair<K,V> > _d;
const iterator _null; // prototype of a "null" / default iterator
iterator *_b;
unsigned long _s;
unsigned long _m;
};
} // namespace ZeroTier
#endif

View file

@ -85,16 +85,9 @@ struct identityV0ProofOfWorkCriteria
bool identityV1ProofOfWorkCriteria(const void *in,const unsigned int len)
{
uint64_t b[98304]; // 768 KiB of working memory
uint64_t polykey[4];
SHA512(b,in,len);
// Poly1305 key, used in final hash at the end.
polykey[0] = b[0];
polykey[1] = b[1];
polykey[2] = b[2];
polykey[3] = b[3];
#if __BYTE_ORDER == __BIG_ENDIAN
b[0] = Utils::swapBytes(b[0]);
b[1] = Utils::swapBytes(b[1]);
@ -152,9 +145,7 @@ bool identityV1ProofOfWorkCriteria(const void *in,const unsigned int len)
}
#endif
// Use poly1305 to compute a very fast digest of 'b'. This doesn't have to be
// cryptographic per se, just have good hashing properties.
poly1305(b,b,sizeof(b),polykey);
SHA384(b,b,sizeof(b),in,len);
// Criterion: add two 64-bit components of poly1305 hash, must be zero mod 180.
// As with the rest of this bits are used in little-endian byte order. The value

94
node/Map.hpp Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_MAP_HPP
#define ZT_MAP_HPP
#include "Constants.hpp"
#include "Utils.hpp"
#ifdef __CPP11__
#include <unordered_map>
#else
#include <map>
#endif
namespace ZeroTier {
#ifdef __CPP11__
struct _MapHasher
{
template<typename O>
std::size_t operator()(const O &obj) const noexcept { return (std::size_t)obj.hashCode() ^ (std::size_t)Utils::s_mapNonce; }
std::size_t operator()(const uint64_t i) const noexcept { return (std::size_t)Utils::hash64(i ^ Utils::s_mapNonce); }
std::size_t operator()(const int64_t i) const noexcept { return (std::size_t)Utils::hash64((uint64_t)i ^ Utils::s_mapNonce); }
std::size_t operator()(const uint32_t i) const noexcept { return (std::size_t)Utils::hash32(i ^ (uint32_t)Utils::s_mapNonce); }
std::size_t operator()(const int32_t i) const noexcept { return (std::size_t)Utils::hash32((uint32_t)i ^ (uint32_t)Utils::s_mapNonce); }
};
template<typename K,typename V>
class Map : public std::unordered_map<K,V,_MapHasher>
{
public:
ZT_INLINE V *get(const K &key) noexcept
{
typename Map::iterator i(this->find(key));
if (i == this->end())
return nullptr;
return &(i->second);
}
ZT_INLINE const V *get(const K &key) const noexcept
{
typename Map::const_iterator i(this->find(key));
if (i == this->end())
return nullptr;
return &(i->second);
}
ZT_INLINE void set(const K &key,const V &value)
{
this->emplace(key,value);
}
};
#else
template<typename K,typename V>
class Map : public std::map<K,V>
{
public:
ZT_INLINE V *get(const K &key) noexcept
{
typename Map::iterator i(this->find(key));
if (i == this->end())
return nullptr;
return &(i->second);
}
ZT_INLINE const V *get(const K &key) const noexcept
{
typename Map::const_iterator i(this->find(key));
if (i == this->end())
return nullptr;
return &(i->second);
}
ZT_INLINE void set(const K &key,const V &value)
{
this->emplace(key,value);
}
};
#endif
} // ZeroTier
#endif

View file

@ -159,8 +159,8 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
// 3/5 of the credential types have identical addCredential() code
template<typename C>
static ZT_INLINE Membership::AddCredentialResult _addCredImpl(
FlatMap<uint32_t,C> &remoteCreds,
const FlatMap<uint64_t,int64_t> &revocations,
Map<uint32_t,C> &remoteCreds,
const Map<uint64_t,int64_t> &revocations,
const RuntimeEnvironment *const RR,
void *const tPtr,
const Identity &sourcePeerIdentity,

View file

@ -18,7 +18,7 @@
#include "Constants.hpp"
#include "Credential.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include "CertificateOfMembership.hpp"
#include "Capability.hpp"
#include "Tag.hpp"
@ -106,7 +106,7 @@ public:
{
if (_isUnspoofableAddress(nconf,r))
return true;
for(FlatMap< uint32_t,CertificateOfOwnership >::const_iterator i(_remoteCoos.begin());i!=_remoteCoos.end();++i) {
for(Map< uint32_t,CertificateOfOwnership >::const_iterator i(_remoteCoos.begin());i!=_remoteCoos.end();++i) {
if (_isCredentialTimestampValid(nconf,i->second)&&(i->second.owns(r)))
return true;
}
@ -176,9 +176,9 @@ private:
}
template<typename C>
ZT_INLINE void _cleanCredImpl(const NetworkConfig &nconf,FlatMap<uint32_t,C> &remoteCreds)
ZT_INLINE void _cleanCredImpl(const NetworkConfig &nconf,Map<uint32_t,C> &remoteCreds)
{
for(typename FlatMap<uint32_t,C>::iterator i(remoteCreds.begin());i!=remoteCreds.end();) {
for(typename Map<uint32_t,C>::iterator i(remoteCreds.begin());i!=remoteCreds.end();) {
if (!_isCredentialTimestampValid(nconf,i->second))
remoteCreds.erase(i++);
else ++i;
@ -198,12 +198,12 @@ private:
CertificateOfMembership _com;
// Revocations by credentialKey()
FlatMap< uint64_t,int64_t > _revocations;
Map< uint64_t,int64_t > _revocations;
// Remote credentials that we have received from this member (and that are valid)
FlatMap< uint32_t,Tag > _remoteTags;
FlatMap< uint32_t,Capability > _remoteCaps;
FlatMap< uint32_t,CertificateOfOwnership > _remoteCoos;
Map< uint32_t,Tag > _remoteTags;
Map< uint32_t,Capability > _remoteCaps;
Map< uint32_t,CertificateOfOwnership > _remoteCoos;
public:
class CapabilityIterator
@ -219,7 +219,7 @@ public:
ZT_INLINE Capability *next() noexcept
{
while (_hti != _m._remoteCaps.end()) {
FlatMap< uint32_t,Capability >::iterator i(_hti++);
Map< uint32_t,Capability >::iterator i(_hti++);
if (_m._isCredentialTimestampValid(_nconf,i->second))
return &(i->second);
}
@ -227,7 +227,7 @@ public:
}
private:
FlatMap< uint32_t,Capability >::iterator _hti;
Map< uint32_t,Capability >::iterator _hti;
Membership &_m;
const NetworkConfig &_nconf;
};

View file

@ -1099,7 +1099,7 @@ void Network::doPeriodicTasks(void *tPtr,const int64_t now)
{
Mutex::Lock l1(_memberships_l);
for(FlatMap<Address,Membership>::iterator i(_memberships.begin());i!=_memberships.end();++i)
for(Map<Address,Membership>::iterator i(_memberships.begin());i!=_memberships.end();++i)
i->second.clean(now,_config);
{
@ -1128,12 +1128,12 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
// Anti-DOS circuit breaker to prevent nodes from spamming us with absurd numbers of bridge routes
while (_remoteBridgeRoutes.size() > ZT_MAX_BRIDGE_ROUTES) {
FlatMap< Address,unsigned long > counts;
Map< Address,unsigned long > counts;
Address maxAddr;
unsigned long maxCount = 0;
// Find the address responsible for the most entries
for(FlatMap<MAC,Address>::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();++i) {
for(Map<MAC,Address>::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();++i) {
const unsigned long c = ++counts[i->second];
if (c > maxCount) {
maxCount = c;
@ -1142,7 +1142,7 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
}
// Kill this address from our table, since it's most likely spamming us
for(FlatMap<MAC,Address>::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();) {
for(Map<MAC,Address>::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();) {
if (i->second == maxAddr)
_remoteBridgeRoutes.erase(i++);
else ++i;
@ -1529,7 +1529,7 @@ std::vector<MulticastGroup> Network::_allMulticastGroups() const
std::vector<MulticastGroup> mgs;
mgs.reserve(_myMulticastGroups.size() + _multicastGroupsBehindMe.size() + 1);
mgs.insert(mgs.end(),_myMulticastGroups.begin(),_myMulticastGroups.end());
for(FlatMap< MulticastGroup,uint64_t >::const_iterator i(_multicastGroupsBehindMe.begin());i!=_multicastGroupsBehindMe.end();++i)
for(Map< MulticastGroup,uint64_t >::const_iterator i(_multicastGroupsBehindMe.begin());i!=_multicastGroupsBehindMe.end();++i)
mgs.push_back(i->first);
if ((_config)&&(_config.enableBroadcast()))
mgs.push_back(Network::BROADCAST);

View file

@ -25,7 +25,7 @@
#include "Membership.hpp"
#include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include <cstdint>
#include <string>
@ -326,7 +326,7 @@ public:
ZT_INLINE void eachMember(F f)
{
Mutex::Lock ml(_memberships_l);
for(FlatMap<Address,Membership>::iterator i(_memberships.begin());i!=_memberships.end();++i) {
for(Map<Address,Membership>::iterator i(_memberships.begin());i!=_memberships.end();++i) {
if (!f(i->first,i->second))
break;
}
@ -353,8 +353,8 @@ private:
bool _portInitialized;
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
FlatMap< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
FlatMap< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
Map< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
Map< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
NetworkConfig _config;
std::atomic<int64_t> _lastConfigUpdate;
@ -377,7 +377,7 @@ private:
NETCONF_FAILURE_INIT_FAILED
} _netconfFailure;
FlatMap<Address,Membership> _memberships;
Map<Address,Membership> _memberships;
Mutex _myMulticastGroups_l;
Mutex _remoteBridgeRoutes_l;

View file

@ -268,7 +268,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
_lastHousekeepingRun = now;
{
RWMutex::RLock l(_networks_m);
for(FlatMap< uint64_t,SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i)
for(Map< uint64_t,SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i)
i->second->doPeriodicTasks(tPtr,now);
}
}
@ -281,7 +281,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
// or trust nodes without doing an extra cert check.
{
_localControllerAuthorizations_m.lock();
for(FlatMap<_LocalControllerAuth,int64_t>::iterator i(_localControllerAuthorizations.begin());i!=_localControllerAuthorizations.end();) {
for(Map<_LocalControllerAuth,int64_t>::iterator i(_localControllerAuthorizations.begin());i!=_localControllerAuthorizations.end();) {
if ((i->second - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3))
_localControllerAuthorizations.erase(i++);
else ++i;
@ -353,7 +353,7 @@ ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
ZT_VirtualNetworkConfig ctmp;
_networks_m.lock();
FlatMap< uint64_t,SharedPtr<Network> >::iterator nwi(_networks.find(nwid));
Map< uint64_t,SharedPtr<Network> >::iterator nwi(_networks.find(nwid));
if (nwi == _networks.end()) {
_networks_m.unlock();
return ZT_RESULT_OK;
@ -507,7 +507,7 @@ ZT_VirtualNetworkList *Node::networks() const
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
for(FlatMap< uint64_t,SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i)
for(Map< uint64_t,SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i)
i->second->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;
@ -596,7 +596,7 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const i
{
{
RWMutex::RLock l(_networks_m);
for (FlatMap< uint64_t,SharedPtr<Network> >::iterator i(_networks.begin()); i != _networks.end(); ++i) {
for (Map< uint64_t,SharedPtr<Network> >::iterator i(_networks.begin()); i != _networks.end(); ++i) {
for (unsigned int k = 0,j = i->second->config().staticIpCount; k < j; ++k) {
if (i->second->config().staticIps[k].containsAddress(remoteAddress))
return false;

View file

@ -24,7 +24,7 @@
#include "Salsa20.hpp"
#include "NetworkController.hpp"
#include "Buf.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include <cstdio>
#include <cstdlib>
@ -361,13 +361,14 @@ private:
ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)(nwid + address); }
ZT_INLINE bool operator==(const _LocalControllerAuth &a) const noexcept { return ((a.nwid == nwid) && (a.address == address)); }
ZT_INLINE bool operator!=(const _LocalControllerAuth &a) const noexcept { return ((a.nwid != nwid) || (a.address != address)); }
ZT_INLINE bool operator<(const _LocalControllerAuth &a) const noexcept { return ((a.nwid < nwid) || ((a.nwid == nwid)&&(a.address < address))); }
};
FlatMap<_LocalControllerAuth,int64_t> _localControllerAuthorizations;
Map<_LocalControllerAuth,int64_t> _localControllerAuthorizations;
Mutex _localControllerAuthorizations_m;
// Networks are stored in a flat hash table that is resized on any network ID collision. This makes
// network lookup by network ID a few bitwise ops and an array index.
FlatMap< uint64_t,SharedPtr<Network> > _networks;
Map< uint64_t,SharedPtr<Network> > _networks;
RWMutex _networks_m;
// These are local interface addresses that have been configured via the API

View file

@ -69,7 +69,7 @@ void SelfAwareness::iam(void *tPtr,const Identity &reporter,const int64_t receiv
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
// due to multiple reports of endpoint change.
// Don't use 'entry' after this since hash table gets modified.
for(FlatMap<PhySurfaceKey,PhySurfaceEntry>::iterator i(_phy.begin());i!=_phy.end();) {
for(Map<PhySurfaceKey,PhySurfaceEntry>::iterator i(_phy.begin());i!=_phy.end();) {
if ((i->first.scope == scope)&&(i->first.reporterPhysicalAddress != reporterPhysicalAddress))
_phy.erase(i++);
else ++i;
@ -91,7 +91,7 @@ void SelfAwareness::iam(void *tPtr,const Identity &reporter,const int64_t receiv
void SelfAwareness::clean(int64_t now)
{
Mutex::Lock l(_phy_l);
for(FlatMap<PhySurfaceKey,PhySurfaceEntry>::iterator i(_phy.begin());i!=_phy.end();) {
for(Map<PhySurfaceKey,PhySurfaceEntry>::iterator i(_phy.begin());i!=_phy.end();) {
if ((now - i->second.ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT)
_phy.erase(i++);
else ++i;
@ -101,17 +101,17 @@ void SelfAwareness::clean(int64_t now)
std::multimap<unsigned long,InetAddress> SelfAwareness::externalAddresses(const int64_t now) const
{
std::multimap<unsigned long,InetAddress> r;
FlatMap<InetAddress,unsigned long,256> counts;
Map<InetAddress,unsigned long> counts;
{
Mutex::Lock l(_phy_l);
for(FlatMap<PhySurfaceKey,PhySurfaceEntry>::const_iterator i(_phy.begin());i!=_phy.end();++i) {
for(Map<PhySurfaceKey,PhySurfaceEntry>::const_iterator i(_phy.begin());i!=_phy.end();++i) {
if ((now - i->second.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT)
++counts[i->second.mySurface];
}
}
for(FlatMap<InetAddress,unsigned long,256>::iterator i(counts.begin());i!=counts.end();++i)
for(Map<InetAddress,unsigned long>::iterator i(counts.begin());i!=counts.end();++i)
r.insert(std::pair<unsigned long,InetAddress>(i->second,i->first));
return r;

View file

@ -16,7 +16,7 @@
#include "Constants.hpp"
#include "InetAddress.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include "Address.hpp"
#include "Mutex.hpp"
@ -72,13 +72,30 @@ private:
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
ZT_INLINE PhySurfaceKey() {}
ZT_INLINE PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
ZT_INLINE PhySurfaceKey() noexcept {}
ZT_INLINE PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) noexcept : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
ZT_INLINE unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)receivedOnLocalSocket + (unsigned long)scope); }
ZT_INLINE unsigned long hashCode() const noexcept { return ((unsigned long)reporter.toInt() + (unsigned long)receivedOnLocalSocket + (unsigned long)scope); }
ZT_INLINE bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter) && (receivedOnLocalSocket == k.receivedOnLocalSocket) && (reporterPhysicalAddress == k.reporterPhysicalAddress) && (scope == k.scope)); }
ZT_INLINE bool operator!=(const PhySurfaceKey &k) const { return (!(*this == k)); }
ZT_INLINE bool operator==(const PhySurfaceKey &k) const noexcept { return ((reporter == k.reporter) && (receivedOnLocalSocket == k.receivedOnLocalSocket) && (reporterPhysicalAddress == k.reporterPhysicalAddress) && (scope == k.scope)); }
ZT_INLINE bool operator!=(const PhySurfaceKey &k) const noexcept { return (!(*this == k)); }
ZT_INLINE bool operator<(const PhySurfaceKey &k) const noexcept
{
if (reporter < k.reporter) {
return true;
} else if (reporter == k.reporter) {
if (receivedOnLocalSocket < k.receivedOnLocalSocket) {
return true;
} else if (receivedOnLocalSocket == k.receivedOnLocalSocket) {
if (reporterPhysicalAddress < k.reporterPhysicalAddress) {
return true;
} else if (reporterPhysicalAddress == k.reporterPhysicalAddress) {
return scope < k.scope;
}
}
}
return false;
}
};
struct PhySurfaceEntry
@ -87,12 +104,12 @@ private:
uint64_t ts;
bool trusted;
ZT_INLINE PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
ZT_INLINE PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
ZT_INLINE PhySurfaceEntry() noexcept : mySurface(),ts(0),trusted(false) {}
ZT_INLINE PhySurfaceEntry(const InetAddress &a,const uint64_t t) noexcept : mySurface(a),ts(t),trusted(false) {}
};
const RuntimeEnvironment *RR;
FlatMap< PhySurfaceKey,PhySurfaceEntry > _phy;
Map< PhySurfaceKey,PhySurfaceEntry > _phy;
Mutex _phy_l;
};

View file

@ -25,6 +25,10 @@ namespace ZeroTier {
* Speck does not specify a mandatory endian-ness. This implementation is
* little-endian for higher performance on the majority of platforms.
*
* This is only used as part of the work function for V1 identity generation
* and for the built-in secure random source on systems that lack AES
* hardware acceleration.
*
* @tparam R Number of rounds (default: 32)
*/
template<int R = 32>
@ -96,7 +100,10 @@ public:
}
/**
* Encrypt 512 bits in parallel with the same key
* Encrypt 512 bits in parallel with the same key.
*
* Parallel in this case assumes instruction level parallelism, but even without that
* it may be faster due to cache/memory effects.
*/
ZT_INLINE void encryptXYXYXYXY(uint64_t &x0,uint64_t &y0,uint64_t &x1,uint64_t &y1,uint64_t &x2,uint64_t &y2,uint64_t &x3,uint64_t &y3) const noexcept
{

View file

@ -38,7 +38,7 @@
#include "SHA512.hpp"
#include "Defragmenter.hpp"
#include "Fingerprint.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
#include <cstdint>
#include <cstring>
@ -176,7 +176,7 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
};
#define IDENTITY_V0_KNOWN_GOOD_0 "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
#define IDENTITY_V1_KNOWN_GOOD_0 "8013cb9738:1:avsyitc474r2ylspwjkjvlermiiouaxy3bzzlqtcvafo2gv3abpeyov2zjfyx4vbkxghcoidpjidszt2ibsn4nmmvtpj42clzsxkyt5dajljk2i3x56picaeapayrv5xvd6x6ucgzvei7773xnoj6tyxwcjvqwbhz3joxeb74kdrfq2247hm7ly:54cckqx5fmd54zkmxt6lz2bxn2elws2ju2kv44cqpy7b22n5dlz256iz7j44jc4wyqj4mppyhknbayf2iulu7g4mysgeee5zrtp4zfwqtjnqkn2xpemap5cfm33kldc5kpukg33nqaizcgvekobj6omt3uwro5xboopvgiwdejwmgmpfevja"
#define IDENTITY_V1_KNOWN_GOOD_0 "2d48f7a238:1:gltupn4yrt226o3vebl7m7m5hpndhvfz66nzx6gwgtgbsgs5xr7dpz5aiv636zijrxayuu2ydpff4zgho7o6gpvx62njwkavqordxcceajs2fif4y2ytofpyr25mmxmanbf4fmdiitiq2b53nmx4ckjcmtyqrkqye2jkdainmkqbtil3dhyuiwa:xg73bkrxptymo7kyyd6efu2o7ziemyu3lpgtip53ejsqukt6l2gebq5uofzt6cd2455st5iwrdgc2ft3twkdzrkunu6x5imdz6jt27qopsvqpdijx5cqgukpjxrtyx73j42socym5pi5hy2ir5yma7by4gmtjgvvu3sxbb3qv2yuicykyz2q"
// --------------------------------------------------------------------------------------------------------------------
@ -466,34 +466,41 @@ extern "C" const char *ZTT_general()
}
{
ZT_T_PRINTF("[general] Testing FlatMap... ");
FlatMap<uint64_t,uint64_t> tm;
for(uint64_t i=0;i<10000;++i)
ZT_T_PRINTF("[general] Testing Map... ");
Map<uint64_t,uint64_t> tm;
for(uint64_t i=0;i<100000;++i)
tm.set(i,i);
for(uint64_t i=0;i<10000;++i) {
for(uint64_t i=0;i<100000;++i) {
uint64_t *v = tm.get(i);
if ((!v)||(*v != i)) {
ZT_T_PRINTF("FAILED (get() failed)" ZT_EOL_S);
return "FlatMap::get() failed";
return "Map::get() failed";
}
}
for(FlatMap<uint64_t,uint64_t>::iterator i(tm.begin());i!=tm.end();) {
for(Map<uint64_t,uint64_t>::iterator i(tm.begin());i!=tm.end();) {
if ((i->first & 1U) == 0)
tm.erase(i++);
else ++i;
}
if (tm.size() != 5000) {
if (tm.size() != 50000) {
ZT_T_PRINTF("FAILED (erase() failed (1))" ZT_EOL_S);
return "FlatMap::erase() failed (1)";
return "Map::erase() failed (1)";
}
for(uint64_t i=0;i<10000;++i) {
for(uint64_t i=0;i<100000;++i) {
uint64_t *v = tm.get(i);
if (((i & 1U) == 0)&&(v)) {
ZT_T_PRINTF("FAILED (erase() failed (2))" ZT_EOL_S);
return "FlatMap::erase() failed (2)";
if ((i & 1U) == 0) {
if (v) {
ZT_T_PRINTF("FAILED (erase() failed (2))" ZT_EOL_S);
return "Map::erase() failed (2)";
}
} else {
if (!v) {
ZT_T_PRINTF("FAILED (erase() failed (3))" ZT_EOL_S);
return "Map::erase() failed (3)";
}
}
}
ZT_T_PRINTF("OK (hash size: %lu)" ZT_EOL_S,tm.hashSize());
ZT_T_PRINTF("OK" ZT_EOL_S);
}
{

View file

@ -99,7 +99,7 @@ void Topology::getAllPeers(std::vector< SharedPtr<Peer> > &allPeers) const
RWMutex::RLock l(_peers_l);
allPeers.clear();
allPeers.reserve(_peers.size());
for(FlatMap< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
for(Map< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
allPeers.push_back(i->second);
}
@ -198,7 +198,7 @@ void Topology::doPeriodicTasks(void *tPtr,const int64_t now)
{
{
RWMutex::Lock l1(_peers_l);
for(FlatMap< Address,SharedPtr<Peer> >::iterator i(_peers.begin());i!=_peers.end();) {
for(Map< Address,SharedPtr<Peer> >::iterator i(_peers.begin());i!=_peers.end();) {
if ( (!i->second->alive(now)) && (_roots.count(i->second->identity()) == 0) ) {
i->second->save(tPtr);
_peersByIncomingProbe.erase(i->second->incomingProbe());
@ -209,7 +209,7 @@ void Topology::doPeriodicTasks(void *tPtr,const int64_t now)
}
{
RWMutex::Lock l1(_paths_l);
for(FlatMap< uint64_t,SharedPtr<Path> >::iterator i(_paths.begin());i!=_paths.end();) {
for(Map< uint64_t,SharedPtr<Path> >::iterator i(_paths.begin());i!=_paths.end();) {
if ((i->second.references() <= 1)&&(!i->second->alive(now)))
_paths.erase(i++);
else ++i;
@ -220,7 +220,7 @@ void Topology::doPeriodicTasks(void *tPtr,const int64_t now)
void Topology::saveAll(void *tPtr)
{
RWMutex::RLock l(_peers_l);
for(FlatMap< Address,SharedPtr<Peer> >::iterator i(_peers.begin());i!=_peers.end();++i)
for(Map< Address,SharedPtr<Peer> >::iterator i(_peers.begin());i!=_peers.end();++i)
i->second->save(tPtr);
}

View file

@ -30,7 +30,7 @@
#include "SharedPtr.hpp"
#include "ScopedPtr.hpp"
#include "Fingerprint.hpp"
#include "FlatMap.hpp"
#include "Map.hpp"
namespace ZeroTier {
@ -127,7 +127,7 @@ public:
*/
ZT_INLINE SharedPtr<Path> path(const int64_t l,const InetAddress &r)
{
const uint64_t k = _pathHash(l,r);
const uint64_t k = _getPathKey(l,r);
{
RWMutex::RLock lck(_paths_l);
SharedPtr<Path> *const p = _paths.get(k);
@ -179,7 +179,7 @@ public:
ZT_INLINE void eachPeer(F f) const
{
RWMutex::RLock l(_peers_l);
for(FlatMap< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
for(Map< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
f(i->second);
}
@ -204,7 +204,7 @@ public:
std::sort(rootPeerPtrs.begin(),rootPeerPtrs.end());
try {
for(FlatMap< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
for(Map< Address,SharedPtr<Peer> >::const_iterator i(_peers.begin());i!=_peers.end();++i)
f(i->second,std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)i->second.ptr()));
} catch ( ... ) {} // should not throw
}
@ -219,7 +219,7 @@ public:
ZT_INLINE void eachPath(F f) const
{
RWMutex::RLock l(_paths_l);
for(FlatMap< uint64_t,SharedPtr<Path> >::const_iterator i(_paths.begin());i!=_paths.end();++i)
for(Map< uint64_t,SharedPtr<Path> >::const_iterator i(_paths.begin());i!=_paths.end();++i)
f(i->second);
}
@ -324,11 +324,11 @@ private:
// This is a secure random integer created at startup to salt the calculation of path hash map keys
static const uint64_t s_pathHashSalt;
// Get a hash key for looking up paths by their local port and destination address
ZT_INLINE uint64_t _pathHash(int64_t l,const InetAddress &r) const
// This gets an integer key from an InetAddress for looking up paths.
ZT_INLINE uint64_t _getPathKey(int64_t l,const InetAddress &r) const
{
if (r.family() == AF_INET) {
return Utils::hash64(s_pathHashSalt ^ (uint64_t)(reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr)) + (uint64_t)Utils::ntoh(reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port) + (uint64_t)l;
return s_pathHashSalt + (uint64_t)(reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr) + (uint64_t)Utils::ntoh(reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port) + (uint64_t)l;
} else if (r.family() == AF_INET6) {
#ifdef ZT_NO_UNALIGNED_ACCESS
uint64_t h = s_pathHashSalt;
@ -338,7 +338,7 @@ private:
h ^= (h >> 6U);
}
#else
uint64_t h = Utils::hash64(s_pathHashSalt ^ (reinterpret_cast<const uint64_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr)[0] + reinterpret_cast<const uint64_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr)[1]));
uint64_t h = s_pathHashSalt + (reinterpret_cast<const uint64_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr)[0] + reinterpret_cast<const uint64_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr)[1]);
#endif
return h + (uint64_t)Utils::ntoh(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) + (uint64_t)l;
} else {
@ -354,10 +354,10 @@ private:
std::pair< InetAddress,ZT_PhysicalPathConfiguration > _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
unsigned int _numConfiguredPhysicalPaths;
FlatMap< Address,SharedPtr<Peer> > _peers;
FlatMap< uint64_t,SharedPtr<Peer> > _peersByIncomingProbe;
FlatMap< Fingerprint,SharedPtr<Peer> > _peersByIdentityHash;
FlatMap< uint64_t,SharedPtr<Path> > _paths;
Map< Address,SharedPtr<Peer> > _peers;
Map< uint64_t,SharedPtr<Peer> > _peersByIncomingProbe;
Map< Fingerprint,SharedPtr<Peer> > _peersByIdentityHash;
Map< uint64_t,SharedPtr<Path> > _paths;
std::set< Identity > _roots; // locked by _peers_l
std::vector< SharedPtr<Peer> > _rootPeers; // locked by _peers_l
};

View file

@ -19,6 +19,7 @@
#include "Mutex.hpp"
#include "AES.hpp"
#include "SHA512.hpp"
#include "Speck128.hpp"
#ifdef __UNIX_LIKE__
#include <unistd.h>
@ -59,6 +60,7 @@ const CPUIDRegisters CPUID;
const uint64_t ZERO256[4] = { 0,0,0,0 };
const char HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
const uint64_t s_mapNonce = getSecureRandomU64();
bool secureEq(const void *a,const void *b,unsigned int len) noexcept
{
@ -223,32 +225,40 @@ unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen
return l;
}
void getSecureRandom(void *buf,unsigned int bytes) noexcept
#define ZT_GETSECURERANDOM_STATE_SIZE 64
#define ZT_GETSECURERANDOM_BUF_SIZE 4096
void getSecureRandom(void *const buf,const unsigned int bytes) noexcept
{
static Mutex globalLock;
static bool initialized = false;
static uint64_t randomState[16]; // secret state
static uint64_t randomBuf[8192]; // next batch of random bytes
static uint64_t randomState[ZT_GETSECURERANDOM_STATE_SIZE]; // secret state
static uint64_t randomBuf[ZT_GETSECURERANDOM_BUF_SIZE]; // next batch of random bytes
static unsigned long randomPtr = sizeof(randomBuf); // refresh on first iteration
// This secure random function gets entropy from the system random source (e.g. /dev/urandom),
// CPU random instructions if present, and other sources and uses them to initialize a SHA/AES
// based CSPRNG with a large state. System random sources are not used directly to mitigate
// against cases where the system random source is broken in some way, which does happen from
// time to time.
Mutex::Lock gl(globalLock);
// This could be a lot faster if we're not going to need a new block.
if ((randomPtr + (unsigned long)bytes) <= sizeof(randomBuf)) {
Utils::copy(buf,reinterpret_cast<uint8_t *>(randomBuf) + randomPtr,bytes);
randomPtr += bytes;
return;
}
for(unsigned int i=0;i<bytes;++i) {
if (randomPtr >= sizeof(randomBuf)) {
// Generate a new block of random data if we're at the end of the current block.
// Note that randomPtr is a byte pointer not a word pointer so we compare with sizeof.
if (randomPtr >= (unsigned long)sizeof(randomBuf)) {
randomPtr = 0;
if (!initialized) {
initialized = true;
// Fill both randomState and randomBuf from system random source. Failure here
// is fatal to the running application and indicates a serious system problem.
// This is some of the only OS-specific code in the core.
Utils::memoryLock(randomState,sizeof(randomState));
Utils::memoryLock(randomBuf,sizeof(randomBuf));
// Fill randomState with entropy from the system. If this doesn't work this is a hard fail.
Utils::zero<sizeof(randomState)>(randomState);
#ifdef __WINDOWS__
HCRYPTPROV cryptProvider = NULL;
if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
@ -259,10 +269,6 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
fprintf(stderr,"FATAL: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
exit(1);
}
if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
fprintf(stderr,"FATAL: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
exit(1);
}
CryptReleaseContext(cryptProvider,0);
#else
int devURandomFd = ::open("/dev/urandom",O_RDONLY);
@ -275,25 +281,20 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
fprintf(stderr,"FATAL: Utils::getSecureRandom() unable to read from /dev/urandom\n");
exit(1);
}
if ((long)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (long)sizeof(randomBuf)) {
::close(devURandomFd);
fprintf(stderr,"FATAL: Utils::getSecureRandom() unable to read from /dev/urandom\n");
exit(1);
}
close(devURandomFd);
#endif
// Mix in additional entropy from time, the address of 'buf', rdrand if present, etc.
randomState[0] ^= (uint64_t)time(nullptr);
randomState[1] ^= (uint64_t)((uintptr_t)buf);
// Mix in additional entropy from time, the address of 'buf', CPU RDRAND if present, etc.
randomState[0] += (uint64_t)time(nullptr);
randomState[1] += (uint64_t)((uintptr_t)buf);
#ifdef __UNIX_LIKE__
randomState[2] ^= (uint64_t)getpid();
randomState[3] ^= (uint64_t)getppid();
randomState[2] += (uint64_t)getpid();
randomState[3] += (uint64_t)getppid();
#endif
#ifdef ZT_ARCH_X64
if (CPUID.rdrand) {
uint64_t tmp = 0;
for(int k=0;k<16;++k) {
for(int k=0;k<ZT_GETSECURERANDOM_STATE_SIZE;++k) {
_rdrand64_step((unsigned long long *)&tmp);
randomState[k] ^= tmp;
}
@ -301,25 +302,31 @@ void getSecureRandom(void *buf,unsigned int bytes) noexcept
#endif
}
// Generate a new randomBuf:
//
// (1) Generate next randomState by perturbing, hashing, and replacing the first 384 bits with the hash.
// (2) Initialize AES using the first 256 bits of the new randomState as its key.
// (3) Initialize a 128-bit counter field using the following 128 bits of randomState.
// (4) Encrypt randomBuf with AES-CTR (machine-endian counter since spec conformance doesn't matter).
// Perturb state, hash, and overwrite the first 64 bytes with this hash.
++randomState[ZT_GETSECURERANDOM_STATE_SIZE-1];
SHA512(randomState,randomState,sizeof(randomState));
++randomState[15];
SHA384(randomState,randomState,sizeof(randomState));
AES aes(randomState);
uint64_t ctr[2],tmp[2];
ctr[0] = randomState[4];
ctr[1] = randomState[5]; // AES key + CTR/nonce = part replaced each time by SHA384
for(int k=0;k<8192;k+=2) {
++ctr[0];
aes.encrypt(ctr,tmp);
randomBuf[k] ^= tmp[0];
randomBuf[k+1] ^= tmp[1];
// Use the part of the state that was overwritten with new state to key a
// stream cipher and re-fill the buffer. Use AES if we're HW accel or use
// Speck if not since it's way faster on tiny chips without AES units.
if (AES::accelerated()) {
AES aes(randomState);
uint64_t ctr[2];
ctr[0] = randomState[4];
ctr[1] = randomState[5];
for (int k = 0;k < ZT_GETSECURERANDOM_BUF_SIZE;k += 2) {
++ctr[0];
aes.encrypt(ctr,randomBuf + k);
}
} else {
Speck128<> speck(randomState);
uint64_t ctr[2];
ctr[0] = randomState[4];
ctr[1] = randomState[5];
for (int k = 0;k < ZT_GETSECURERANDOM_BUF_SIZE;k += 2) {
++ctr[0];
speck.encrypt(ctr,randomBuf + k);
}
}
}
@ -336,7 +343,7 @@ uint64_t getSecureRandomU64() noexcept
int b32e(const uint8_t *data,int length,char *result,int bufSize) noexcept
{
if (length < 0 || length > (1 << 28)) {
if (length < 0 || length > (1 << 28U)) {
result[0] = (char)0;
return -1;
}
@ -413,6 +420,7 @@ int b32d(const char *encoded,uint8_t *result,int bufSize) noexcept
uint64_t random() noexcept
{
// https://en.wikipedia.org/wiki/Xorshift#xoshiro256**
static volatile uint64_t s_s0 = getSecureRandomU64();
static volatile uint64_t s_s1 = getSecureRandomU64();
static volatile uint64_t s_s2 = getSecureRandomU64();

View file

@ -68,6 +68,11 @@ extern const uint64_t ZERO256[4];
*/
extern const char HEXCHARS[16];
/**
* A random integer generated at startup for Map's hash bucket calculation.
*/
extern const uint64_t s_mapNonce;
/**
* Lock memory to prevent swapping out to secondary storage (if possible)
*
@ -228,7 +233,11 @@ int b32e(const uint8_t *data,int length,char *result,int bufSize) noexcept;
int b32d(const char *encoded, uint8_t *result, int bufSize) noexcept;
/**
* Get a non-cryptographic random integer
* Get a non-cryptographic random integer.
*
* This should never be used for cryptographic use cases, not even for choosing
* message nonce/IV values if they should not repeat. It should only be used when
* a fast and potentially "dirty" random source is needed.
*/
uint64_t random() noexcept;
@ -246,7 +255,7 @@ uint64_t random() noexcept;
bool scopy(char *dest,unsigned int len,const char *src) noexcept;
/**
* Mix bits in a 64-bit integer (non-cryptographic)
* Mix bits in a 64-bit integer (non-cryptographic, for hash tables)
*
* https://nullprogram.com/blog/2018/07/31/
*
@ -264,16 +273,20 @@ static ZT_INLINE uint64_t hash64(uint64_t x) noexcept
}
/**
* Mix bits in a 32-bit integer
* Mix bits in a 32-bit integer (non-cryptographic, for hash tables)
*
* https://nullprogram.com/blog/2018/07/31/
*
* @param x Integer to mix
* @return Hashed value
*/
static ZT_INLINE uint32_t hash32(uint32_t x) noexcept
{
x = ((x >> 16U) ^ x) * 0x45d9f3b;
x = ((x >> 16U) ^ x) * 0x45d9f3b;
x = (x >> 16U) ^ x;
x ^= x >> 16U;
x *= 0x7feb352dU;
x ^= x >> 15U;
x *= 0x846ca68bU;
x ^= x >> 16U;
return x;
}
@ -598,7 +611,7 @@ static ZT_INLINE void storeLittleEndian(void *const p,const I i) noexcept
}
/**
* Copy memory block whose size is known at compile time
* Copy memory block whose size is known at compile time.
*
* @tparam L Size of memory
* @param dest Destination memory

View file

@ -11,7 +11,6 @@
*/
/****/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -24,7 +23,7 @@ static const uint8_t ARP_REQUEST_HEADER[8] = { 0x00,0x01,0x08,0x00,0x06,0x04,0x0
static const uint8_t ARP_RESPONSE_HEADER[8] = { 0x00,0x01,0x08,0x00,0x06,0x04,0x00,0x02 };
Arp::Arp() :
_cache(256),
_cache(),
_lastCleaned(OSUtils::now())
{
}
@ -78,12 +77,10 @@ uint32_t Arp::processIncomingArp(const void *arp,unsigned int len,void *response
if ((now - _lastCleaned) >= ZT_ARP_EXPIRE) {
_lastCleaned = now;
Hashtable< uint32_t,_ArpEntry >::Iterator i(_cache);
uint32_t *k = (uint32_t *)0;
_ArpEntry *v = (_ArpEntry *)0;
while (i.next(k,v)) {
if ((!v->local)&&((now - v->lastResponseReceived) >= ZT_ARP_EXPIRE))
_cache.erase(*k);
for(Map< uint32_t,_ArpEntry >::iterator i(_cache.begin());i!=_cache.end();) {
if ((!i->second.local)&&((now - i->second.lastResponseReceived) >= ZT_ARP_EXPIRE))
_cache.erase(i++);
else ++i;
}
}

View file

@ -20,7 +20,7 @@
#include <vector>
#include "../node/Constants.hpp"
#include "../node/Hashtable.hpp"
#include "../node/Map.hpp"
#include "../node/MAC.hpp"
/**
@ -135,7 +135,7 @@ private:
bool local; // True if this is a local ARP entry
};
Hashtable< uint32_t,_ArpEntry > _cache;
Map< uint32_t,_ArpEntry > _cache;
uint64_t _lastCleaned;
};

View file

@ -156,7 +156,7 @@ struct _neighbor_advertisement {
};
NeighborDiscovery::NeighborDiscovery()
: _cache(256)
: _cache()
, _lastCleaned(OSUtils::now())
{}
@ -211,13 +211,12 @@ sockaddr_storage NeighborDiscovery::processIncomingND(const uint8_t *nd, unsigne
if ((now - _lastCleaned) >= ZT_ND_EXPIRE) {
_lastCleaned = now;
Hashtable<InetAddress, _NDEntry>::Iterator i(_cache);
InetAddress *k = nullptr;
_NDEntry *v = nullptr;
while (i.next(k, v)) {
if(!v->local && (now - v->lastResponseReceived) >= ZT_ND_EXPIRE) {
_cache.erase(*k);
}
for(Map<InetAddress,_NDEntry>::iterator i(_cache.begin());i!=_cache.end();) {
if(!i->second.local && (now - i->second.lastResponseReceived) >= ZT_ND_EXPIRE) {
_cache.erase(i++);
} else {
++i;
}
}
}

View file

@ -14,7 +14,7 @@
#ifndef ZT_NEIGHBORDISCOVERY_HPP
#define ZT_NEIGHBORDISCOVERY_HPP
#include "../node/Hashtable.hpp"
#include "../node/Map.hpp"
#include "../node/MAC.hpp"
#include "../node/InetAddress.hpp"
@ -58,7 +58,7 @@ private:
bool local;
};
Hashtable<InetAddress, _NDEntry> _cache;
Map< InetAddress,_NDEntry > _cache;
uint64_t _lastCleaned;
};