diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 287bc81df..9b5b537a7 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -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) diff --git a/node/AES.cpp b/node/AES.cpp index d6df2b49e..164d65d09 100644 --- a/node/AES.cpp +++ b/node/AES.cpp @@ -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; } diff --git a/node/AES.hpp b/node/AES.hpp index 667fa4530..7b9f1b7e7 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -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(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(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(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]; diff --git a/node/Buf.hpp b/node/Buf.hpp index 4d9186964..e30ea278a 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -329,7 +329,7 @@ public: template 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(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); } diff --git a/node/C25519.cpp b/node/C25519.cpp index 4f630a971..487c095a4 100644 --- a/node/C25519.cpp +++ b/node/C25519.cpp @@ -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 diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index fcbd38350..705f54419 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -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 diff --git a/node/Defragmenter.hpp b/node/Defragmenter.hpp index 0e979610f..15665106d 100644 --- a/node/Defragmenter.hpp +++ b/node/Defragmenter.hpp @@ -20,7 +20,7 @@ #include "Mutex.hpp" #include "Path.hpp" #include "FCV.hpp" -#include "FlatMap.hpp" +#include "Map.hpp" #include #include @@ -164,7 +164,7 @@ public: std::vector > 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(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; }; diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp index 7167aff8c..c3b9b3965 100644 --- a/node/Dictionary.cpp +++ b/node/Dictionary.cpp @@ -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(&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 &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 &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 &out) const for(std::map< uint64_t,std::vector >::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; diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 5476f209a..a26246b70 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -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(&key)[i] = *(k++)) == 0) + break; + } + return key; + } + std::map< uint64_t,std::vector > _t; }; diff --git a/node/Expect.hpp b/node/Expect.hpp index 1a869ccd9..feaafd6ee 100644 --- a/node/Expect.hpp +++ b/node/Expect.hpp @@ -80,7 +80,7 @@ private: const uint64_t _salt; // Each bucket contains a timestamp in units of the expect duration. - std::atomic _packetIdSent[ZT_EXPECT_TTL]; + std::atomic _packetIdSent[ZT_EXPECT_BUCKETS]; }; } // namespace ZeroTier diff --git a/node/FlatMap.hpp b/node/FlatMap.hpp deleted file mode 100644 index 6809d2c5b..000000000 --- a/node/FlatMap.hpp +++ /dev/null @@ -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 -#include -#include - -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 -class FlatMap -{ -public: - typedef K key_type; - typedef V mapped_type; - typedef std::pair value_type; - typedef typename std::list< std::pair >::iterator iterator; - typedef typename std::list< std::pair >::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(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(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(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(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 - 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 > _d; - const iterator _null; // prototype of a "null" / default iterator - iterator *_b; - unsigned long _s; - unsigned long _m; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/Identity.cpp b/node/Identity.cpp index 49cc042b8..434b2d91a 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -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 diff --git a/node/Map.hpp b/node/Map.hpp new file mode 100644 index 000000000..87925808c --- /dev/null +++ b/node/Map.hpp @@ -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 +#else +#include +#endif + +namespace ZeroTier { + +#ifdef __CPP11__ +struct _MapHasher +{ + template + 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 +class Map : public std::unordered_map +{ +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 +class Map : public std::map +{ +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 diff --git a/node/Membership.cpp b/node/Membership.cpp index 31cd95de3..448925c4f 100644 --- a/node/Membership.cpp +++ b/node/Membership.cpp @@ -159,8 +159,8 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme // 3/5 of the credential types have identical addCredential() code template static ZT_INLINE Membership::AddCredentialResult _addCredImpl( - FlatMap &remoteCreds, - const FlatMap &revocations, + Map &remoteCreds, + const Map &revocations, const RuntimeEnvironment *const RR, void *const tPtr, const Identity &sourcePeerIdentity, diff --git a/node/Membership.hpp b/node/Membership.hpp index f35f49ce1..6e2676499 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -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 - ZT_INLINE void _cleanCredImpl(const NetworkConfig &nconf,FlatMap &remoteCreds) + ZT_INLINE void _cleanCredImpl(const NetworkConfig &nconf,Map &remoteCreds) { - for(typename FlatMap::iterator i(remoteCreds.begin());i!=remoteCreds.end();) { + for(typename Map::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; }; diff --git a/node/Network.cpp b/node/Network.cpp index 0e6568103..71277a5e0 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1099,7 +1099,7 @@ void Network::doPeriodicTasks(void *tPtr,const int64_t now) { Mutex::Lock l1(_memberships_l); - for(FlatMap::iterator i(_memberships.begin());i!=_memberships.end();++i) + for(Map::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::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();++i) { + for(Map::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::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();) { + for(Map::iterator i(_remoteBridgeRoutes.begin());i!=_remoteBridgeRoutes.end();) { if (i->second == maxAddr) _remoteBridgeRoutes.erase(i++); else ++i; @@ -1529,7 +1529,7 @@ std::vector Network::_allMulticastGroups() const std::vector 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); diff --git a/node/Network.hpp b/node/Network.hpp index e5280eae6..c89056e19 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -25,7 +25,7 @@ #include "Membership.hpp" #include "NetworkConfig.hpp" #include "CertificateOfMembership.hpp" -#include "FlatMap.hpp" +#include "Map.hpp" #include #include @@ -326,7 +326,7 @@ public: ZT_INLINE void eachMember(F f) { Mutex::Lock ml(_memberships_l); - for(FlatMap::iterator i(_memberships.begin());i!=_memberships.end();++i) { + for(Map::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 _lastConfigUpdate; @@ -377,7 +377,7 @@ private: NETCONF_FAILURE_INIT_FAILED } _netconfFailure; - FlatMap _memberships; + Map _memberships; Mutex _myMulticastGroups_l; Mutex _remoteBridgeRoutes_l; diff --git a/node/Node.cpp b/node/Node.cpp index 0ca1c1b6b..8703b4800 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -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 >::const_iterator i(_networks.begin());i!=_networks.end();++i) + for(Map< uint64_t,SharedPtr >::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 >::iterator nwi(_networks.find(nwid)); + Map< uint64_t,SharedPtr >::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 >::const_iterator i(_networks.begin());i!=_networks.end();++i) + for(Map< uint64_t,SharedPtr >::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 >::iterator i(_networks.begin()); i != _networks.end(); ++i) { + for (Map< uint64_t,SharedPtr >::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; diff --git a/node/Node.hpp b/node/Node.hpp index 8cb222ac4..822643fa0 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -24,7 +24,7 @@ #include "Salsa20.hpp" #include "NetworkController.hpp" #include "Buf.hpp" -#include "FlatMap.hpp" +#include "Map.hpp" #include #include @@ -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 > _networks; + Map< uint64_t,SharedPtr > _networks; RWMutex _networks_m; // These are local interface addresses that have been configured via the API diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index cd76879d6..63ce0b8af 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -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::iterator i(_phy.begin());i!=_phy.end();) { + for(Map::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::iterator i(_phy.begin());i!=_phy.end();) { + for(Map::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 SelfAwareness::externalAddresses(const int64_t now) const { std::multimap r; - FlatMap counts; + Map counts; { Mutex::Lock l(_phy_l); - for(FlatMap::const_iterator i(_phy.begin());i!=_phy.end();++i) { + for(Map::const_iterator i(_phy.begin());i!=_phy.end();++i) { if ((now - i->second.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) ++counts[i->second.mySurface]; } } - for(FlatMap::iterator i(counts.begin());i!=counts.end();++i) + for(Map::iterator i(counts.begin());i!=counts.end();++i) r.insert(std::pair(i->second,i->first)); return r; diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp index 3406a9bd5..5922c8322 100644 --- a/node/SelfAwareness.hpp +++ b/node/SelfAwareness.hpp @@ -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; }; diff --git a/node/Speck128.hpp b/node/Speck128.hpp index b34ac6d8f..7a6874af5 100644 --- a/node/Speck128.hpp +++ b/node/Speck128.hpp @@ -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 @@ -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 { diff --git a/node/Tests.cpp b/node/Tests.cpp index adfa7b5e0..d60bd12f7 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -38,7 +38,7 @@ #include "SHA512.hpp" #include "Defragmenter.hpp" #include "Fingerprint.hpp" -#include "FlatMap.hpp" +#include "Map.hpp" #include #include @@ -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 tm; - for(uint64_t i=0;i<10000;++i) + ZT_T_PRINTF("[general] Testing Map... "); + Map 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::iterator i(tm.begin());i!=tm.end();) { + for(Map::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); } { diff --git a/node/Topology.cpp b/node/Topology.cpp index 0c11c87f7..60872135e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -99,7 +99,7 @@ void Topology::getAllPeers(std::vector< SharedPtr > &allPeers) const RWMutex::RLock l(_peers_l); allPeers.clear(); allPeers.reserve(_peers.size()); - for(FlatMap< Address,SharedPtr >::const_iterator i(_peers.begin());i!=_peers.end();++i) + for(Map< Address,SharedPtr >::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 >::iterator i(_peers.begin());i!=_peers.end();) { + for(Map< Address,SharedPtr >::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 >::iterator i(_paths.begin());i!=_paths.end();) { + for(Map< uint64_t,SharedPtr >::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 >::iterator i(_peers.begin());i!=_peers.end();++i) + for(Map< Address,SharedPtr >::iterator i(_peers.begin());i!=_peers.end();++i) i->second->save(tPtr); } diff --git a/node/Topology.hpp b/node/Topology.hpp index 6934c31b0..6b4b5d2b6 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -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(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 *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 >::const_iterator i(_peers.begin());i!=_peers.end();++i) + for(Map< Address,SharedPtr >::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 >::const_iterator i(_peers.begin());i!=_peers.end();++i) + for(Map< Address,SharedPtr >::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 >::const_iterator i(_paths.begin());i!=_paths.end();++i) + for(Map< uint64_t,SharedPtr >::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(&r)->sin_addr.s_addr)) + (uint64_t)Utils::ntoh(reinterpret_cast(&r)->sin_port) + (uint64_t)l; + return s_pathHashSalt + (uint64_t)(reinterpret_cast(&r)->sin_addr.s_addr) + (uint64_t)Utils::ntoh(reinterpret_cast(&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(reinterpret_cast(&r)->sin6_addr.s6_addr)[0] + reinterpret_cast(reinterpret_cast(&r)->sin6_addr.s6_addr)[1])); + uint64_t h = s_pathHashSalt + (reinterpret_cast(reinterpret_cast(&r)->sin6_addr.s6_addr)[0] + reinterpret_cast(reinterpret_cast(&r)->sin6_addr.s6_addr)[1]); #endif return h + (uint64_t)Utils::ntoh(reinterpret_cast(&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 > _peers; - FlatMap< uint64_t,SharedPtr > _peersByIncomingProbe; - FlatMap< Fingerprint,SharedPtr > _peersByIdentityHash; - FlatMap< uint64_t,SharedPtr > _paths; + Map< Address,SharedPtr > _peers; + Map< uint64_t,SharedPtr > _peersByIncomingProbe; + Map< Fingerprint,SharedPtr > _peersByIdentityHash; + Map< uint64_t,SharedPtr > _paths; std::set< Identity > _roots; // locked by _peers_l std::vector< SharedPtr > _rootPeers; // locked by _peers_l }; diff --git a/node/Utils.cpp b/node/Utils.cpp index c7ad82337..3e86a678c 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -19,6 +19,7 @@ #include "Mutex.hpp" #include "AES.hpp" #include "SHA512.hpp" +#include "Speck128.hpp" #ifdef __UNIX_LIKE__ #include @@ -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(randomBuf) + randomPtr,bytes); + randomPtr += bytes; + return; + } + for(unsigned int i=0;i= 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(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 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(); diff --git a/node/Utils.hpp b/node/Utils.hpp index 48b1833a5..bfe59f01d 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -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 diff --git a/osdep/Arp.cpp b/osdep/Arp.cpp index 6930b68ee..743aa4bd2 100644 --- a/osdep/Arp.cpp +++ b/osdep/Arp.cpp @@ -11,7 +11,6 @@ */ /****/ -#include #include #include @@ -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; } } diff --git a/osdep/Arp.hpp b/osdep/Arp.hpp index 2a9280f1d..cd9a9e310 100644 --- a/osdep/Arp.hpp +++ b/osdep/Arp.hpp @@ -20,7 +20,7 @@ #include #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; }; diff --git a/osdep/NeighborDiscovery.cpp b/osdep/NeighborDiscovery.cpp index 2adda8c93..c56a8f590 100644 --- a/osdep/NeighborDiscovery.cpp +++ b/osdep/NeighborDiscovery.cpp @@ -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::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::iterator i(_cache.begin());i!=_cache.end();) { + if(!i->second.local && (now - i->second.lastResponseReceived) >= ZT_ND_EXPIRE) { + _cache.erase(i++); + } else { + ++i; + } } } diff --git a/osdep/NeighborDiscovery.hpp b/osdep/NeighborDiscovery.hpp index 9d83a3090..c3f9f1cd9 100644 --- a/osdep/NeighborDiscovery.hpp +++ b/osdep/NeighborDiscovery.hpp @@ -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 _cache; + Map< InetAddress,_NDEntry > _cache; uint64_t _lastCleaned; };