diff --git a/node/Address.hpp b/node/Address.hpp index ec7ce2040..7634a1c36 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -18,8 +18,11 @@ #include #include #include +#include #include +#include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -33,24 +36,24 @@ namespace ZeroTier { class Address { public: - inline Address() : _a(0) {} - inline Address(const Address &a) : _a(a._a) {} - inline Address(uint64_t a) : _a(a & 0xffffffffffULL) {} + ZT_ALWAYS_INLINE Address() : _a(0) {} + ZT_ALWAYS_INLINE Address(const Address &a) : _a(a._a) {} + ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {} /** * @param bits Raw address -- 5 bytes, big-endian byte order * @param len Length of array */ - inline Address(const void *bits,unsigned int len) { setTo(bits,len); } + ZT_ALWAYS_INLINE Address(const void *bits,unsigned int len) { setTo(bits,len); } - inline Address &operator=(const Address &a) { _a = a._a; return *this; } - inline Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; } + ZT_ALWAYS_INLINE Address &operator=(const Address &a) { _a = a._a; return *this; } + ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; } /** * @param bits Raw address -- 5 bytes, big-endian byte order * @param len Length of array */ - inline void setTo(const void *bits,const unsigned int len) + ZT_ALWAYS_INLINE void setTo(const void *bits,const unsigned int len) { if (len < ZT_ADDRESS_LENGTH) { _a = 0; @@ -69,7 +72,7 @@ public: * @param bits Buffer to hold 5-byte address in big-endian byte order * @param len Length of array */ - inline void copyTo(void *const bits,const unsigned int len) const + ZT_ALWAYS_INLINE void copyTo(void *const bits,const unsigned int len) const { if (len < ZT_ADDRESS_LENGTH) return; @@ -87,7 +90,7 @@ public: * @param b Buffer to append to */ template - inline void appendTo(Buffer &b) const + ZT_ALWAYS_INLINE void appendTo(Buffer &b) const { unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH); *(p++) = (unsigned char)((_a >> 32) & 0xff); @@ -100,22 +103,22 @@ public: /** * @return Integer containing address (0 to 2^40) */ - inline uint64_t toInt() const { return _a; } + ZT_ALWAYS_INLINE uint64_t toInt() const { return _a; } /** * @return Hash code for use with Hashtable */ - inline unsigned long hashCode() const { return (unsigned long)_a; } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_a; } /** * @return Hexadecimal string */ - inline char *toString(char buf[11]) const { return Utils::hex10(_a,buf); } + ZT_ALWAYS_INLINE char *toString(char buf[11]) const { return Utils::hex10(_a,buf); } /** * @return True if this address is not zero */ - inline operator bool() const { return (_a != 0); } + ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); } /** * Check if this address is reserved @@ -126,33 +129,115 @@ public: * * @return True if address is reserved and may not be used */ - inline bool isReserved() const { return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); } + ZT_ALWAYS_INLINE bool isReserved() const { return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); } /** * @param i Value from 0 to 4 (inclusive) * @return Byte at said position (address interpreted in big-endian order) */ - inline uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); } + ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); } - inline operator unsigned int() const { return (unsigned int)_a; } - inline operator unsigned long() const { return (unsigned long)_a; } - inline operator unsigned long long() const { return (unsigned long long)_a; } + ZT_ALWAYS_INLINE operator unsigned int() const { return (unsigned int)_a; } + ZT_ALWAYS_INLINE operator unsigned long() const { return (unsigned long)_a; } + ZT_ALWAYS_INLINE operator unsigned long long() const { return (unsigned long long)_a; } - inline void zero() { _a = 0; } + ZT_ALWAYS_INLINE void zero() { _a = 0; } - inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); } - inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); } - inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); } - inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); } - inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); } - inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); } + ZT_ALWAYS_INLINE bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); } - inline bool operator==(const Address &a) const { return (_a == a._a); } - inline bool operator!=(const Address &a) const { return (_a != a._a); } - inline bool operator>(const Address &a) const { return (_a > a._a); } - inline bool operator<(const Address &a) const { return (_a < a._a); } - inline bool operator>=(const Address &a) const { return (_a >= a._a); } - inline bool operator<=(const Address &a) const { return (_a <= a._a); } + ZT_ALWAYS_INLINE bool operator==(const Address &a) const { return (_a == a._a); } + ZT_ALWAYS_INLINE bool operator!=(const Address &a) const { return (_a != a._a); } + ZT_ALWAYS_INLINE bool operator>(const Address &a) const { return (_a > a._a); } + ZT_ALWAYS_INLINE bool operator<(const Address &a) const { return (_a < a._a); } + ZT_ALWAYS_INLINE bool operator>=(const Address &a) const { return (_a >= a._a); } + ZT_ALWAYS_INLINE bool operator<=(const Address &a) const { return (_a <= a._a); } + + /** + * Create a list of the first N bits of a list of unique addresses with N as the minimum unique size + * + * The list is stored in a space-efficient packed bit format. + * + * @param start Starting Address iterator/pointer + * @param end Ending Address iterator/pointer + * @param list Pointer to location to write list + * @param listCapacityBytes Number of bytes available for list + * @return Number of bytes written or -1 on overflow or other error + * @tparam I Input iterator type + */ + template + static inline int createMinPrefixList(I start,I end,uint8_t *list,const int listCapacityBytes) + { + std::vector
sortedAddrs(start,end); + if (sortedAddrs.empty()) + return 0; + if (listCapacityBytes == 0) + return -1; + std::sort(sortedAddrs.begin(),sortedAddrs.end()); + + unsigned int bits = (unsigned int)fmaxf(log2f((float)(sortedAddrs.size() * 2)),3.0F); + uint64_t mask; +try_additional_bits: { + mask = 0xffffffffffffffffULL >> (64 - bits); + std::vector
::iterator a(sortedAddrs.begin()); + uint64_t aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + uint64_t lastMaskedAddress = aa; + while (a != sortedAddrs.end()) { + aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + if (aa == lastMaskedAddress) { + ++bits; + goto try_additional_bits; + } + lastMaskedAddress = aa; + } + } + + int l = 0; + unsigned int bitPtr = 0; + for(I a(start);a!=end;) { + uint64_t aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + unsigned int br = bits; + if (bitPtr > 0) { + unsigned int w = 8 - bitPtr; + if (w > br) w = br; + list[l] = (list[l] << w) | (((uint8_t)aa) & (0xff >> (8 - w))); + bitPtr += w; + if (bitPtr == 8) { + bitPtr = 0; + if (l >= listCapacityBytes) + return -1; + ++l; + } + aa >>= w; + br -= w; + } + while (br >= 8) { + if (l >= listCapacityBytes) + return -1; + list[l++] = (uint8_t)aa; + br -= 8; + aa >>= 8; + } + if (br > 0) { + list[l] = (uint8_t)aa; + bitPtr = br; + } + } + if (bitPtr > 0) { + if (l >= listCapacityBytes) + return -1; + ++l; + } + + return l; + } private: uint64_t _a; diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp index b5131ccb5..4e6b95315 100644 --- a/node/AtomicCounter.hpp +++ b/node/AtomicCounter.hpp @@ -28,9 +28,9 @@ namespace ZeroTier { class AtomicCounter { public: - inline AtomicCounter() { _v = 0; } + ZT_ALWAYS_INLINE AtomicCounter() { _v = 0; } - inline int load() const + ZT_ALWAYS_INLINE int load() const { #ifdef __GNUC__ return __sync_or_and_fetch(const_cast(&_v),0); @@ -39,7 +39,7 @@ public: #endif } - inline int operator++() + ZT_ALWAYS_INLINE int operator++() { #ifdef __GNUC__ return __sync_add_and_fetch(&_v,1); @@ -48,7 +48,7 @@ public: #endif } - inline int operator--() + ZT_ALWAYS_INLINE int operator--() { #ifdef __GNUC__ return __sync_sub_and_fetch(&_v,1); @@ -58,8 +58,8 @@ public: } private: - inline AtomicCounter(const AtomicCounter &) {} - inline const AtomicCounter &operator=(const AtomicCounter &) { return *this; } + ZT_ALWAYS_INLINE AtomicCounter(const AtomicCounter &) {} + ZT_ALWAYS_INLINE const AtomicCounter &operator=(const AtomicCounter &) { return *this; } #ifdef __GNUC__ int _v; diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index e25ff4318..99a3ae8d0 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -29,6 +29,7 @@ set(core_headers Network.hpp NetworkConfig.hpp Node.hpp + OS.hpp Packet.hpp Path.hpp Peer.hpp diff --git a/node/Identity.hpp b/node/Identity.hpp index cc8b5d71f..c28da306d 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -56,8 +56,8 @@ public: P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519/Ed25519 secondaries (2.x+) }; - inline Identity() { memset(reinterpret_cast(this),0,sizeof(Identity)); } - inline ~Identity() { Utils::burn(reinterpret_cast(&this->_priv),sizeof(this->_priv)); } + ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast(this),0,sizeof(Identity)); } + ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast(&this->_priv),sizeof(this->_priv)); } /** * Construct identity from string @@ -67,20 +67,17 @@ public: * * @param str Identity in canonical string format */ - inline Identity(const char *str) { fromString(str); } - - template - inline Identity(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } + ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); } /** * Set identity to NIL value (all zero) */ - inline void zero() { memset(reinterpret_cast(this),0,sizeof(Identity)); } + ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast(this),0,sizeof(Identity)); } /** * @return Identity type (undefined if identity is null or invalid) */ - inline Type type() const { return _type; } + ZT_ALWAYS_INLINE Type type() const { return _type; } /** * Generate a new identity (address, key pair) @@ -101,7 +98,7 @@ public: /** * @return True if this identity contains a private key */ - inline bool hasPrivate() const { return _hasPrivate; } + ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; } /** * This generates a SHA384 hash of this identity's keys. @@ -244,7 +241,7 @@ public: /** * @return This identity's address */ - inline const Address &address() const { return _address; } + ZT_ALWAYS_INLINE const Address &address() const { return _address; } /** * Serialize this identity (binary) @@ -371,9 +368,9 @@ public: /** * @return True if this identity contains something */ - inline operator bool() const { return (_address); } + ZT_ALWAYS_INLINE operator bool() const { return (_address); } - inline bool operator==(const Identity &id) const + ZT_ALWAYS_INLINE bool operator==(const Identity &id) const { if ((_address == id._address)&&(_type == id._type)) { switch(_type) { @@ -384,7 +381,7 @@ public: } return false; } - inline bool operator<(const Identity &id) const + ZT_ALWAYS_INLINE bool operator<(const Identity &id) const { if (_address < id._address) return true; @@ -401,15 +398,15 @@ public: } return false; } - inline bool operator!=(const Identity &id) const { return !(*this == id); } - inline bool operator>(const Identity &id) const { return (id < *this); } - inline bool operator<=(const Identity &id) const { return !(id < *this); } - inline bool operator>=(const Identity &id) const { return !(*this < id); } + ZT_ALWAYS_INLINE bool operator!=(const Identity &id) const { return !(*this == id); } + ZT_ALWAYS_INLINE bool operator>(const Identity &id) const { return (id < *this); } + ZT_ALWAYS_INLINE bool operator<=(const Identity &id) const { return !(id < *this); } + ZT_ALWAYS_INLINE bool operator>=(const Identity &id) const { return !(*this < id); } - inline unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); } // Marshal interface /////////////////////////////////////////////////////// - static inline int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } + static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } inline int marshal(uint8_t restrict data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const { _address.copyTo(data,ZT_ADDRESS_LENGTH); diff --git a/node/MAC.hpp b/node/MAC.hpp index 1044af72a..d200a7cad 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -31,40 +31,40 @@ namespace ZeroTier { class MAC { public: - inline MAC() : _m(0ULL) {} - inline MAC(const MAC &m) : _m(m._m) {} + ZT_ALWAYS_INLINE MAC() : _m(0ULL) {} + ZT_ALWAYS_INLINE MAC(const MAC &m) : _m(m._m) {} - inline MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) : + ZT_ALWAYS_INLINE MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) : _m( ((((uint64_t)a) & 0xffULL) << 40) | ((((uint64_t)b) & 0xffULL) << 32) | ((((uint64_t)c) & 0xffULL) << 24) | ((((uint64_t)d) & 0xffULL) << 16) | ((((uint64_t)e) & 0xffULL) << 8) | (((uint64_t)f) & 0xffULL) ) {} - inline MAC(const void *bits,unsigned int len) { setTo(bits,len); } - inline MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } - inline MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {} + ZT_ALWAYS_INLINE MAC(const void *bits,unsigned int len) { setTo(bits,len); } + ZT_ALWAYS_INLINE MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } + ZT_ALWAYS_INLINE MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {} /** * @return MAC in 64-bit integer */ - inline uint64_t toInt() const { return _m; } + ZT_ALWAYS_INLINE uint64_t toInt() const { return _m; } /** * Set MAC to zero */ - inline void zero() { _m = 0ULL; } + ZT_ALWAYS_INLINE void zero() { _m = 0ULL; } /** * @return True if MAC is non-zero */ - inline operator bool() const { return (_m != 0ULL); } + ZT_ALWAYS_INLINE operator bool() const { return (_m != 0ULL); } /** * @param bits Raw MAC in big-endian byte order * @param len Length, must be >= 6 or result is zero */ - inline void setTo(const void *bits,unsigned int len) + ZT_ALWAYS_INLINE void setTo(const void *bits,unsigned int len) { if (len < 6) { _m = 0ULL; @@ -83,7 +83,7 @@ public: * @param buf Destination buffer for MAC in big-endian byte order * @param len Length of buffer, must be >= 6 or nothing is copied */ - inline void copyTo(void *buf,unsigned int len) const + ZT_ALWAYS_INLINE void copyTo(void *buf,unsigned int len) const { if (len < 6) return; @@ -102,7 +102,7 @@ public: * @param b Buffer to append to */ template - inline void appendTo(Buffer &b) const + ZT_ALWAYS_INLINE void appendTo(Buffer &b) const { unsigned char *p = (unsigned char *)b.appendField(6); *(p++) = (unsigned char)((_m >> 40) & 0xff); @@ -116,17 +116,17 @@ public: /** * @return True if this is broadcast (all 0xff) */ - inline bool isBroadcast() const { return (_m == 0xffffffffffffULL); } + ZT_ALWAYS_INLINE bool isBroadcast() const { return (_m == 0xffffffffffffULL); } /** * @return True if this is a multicast MAC */ - inline bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); } + ZT_ALWAYS_INLINE bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); } /** * @param True if this is a locally-administered MAC */ - inline bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); } + ZT_ALWAYS_INLINE bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); } /** * Set this MAC to a MAC derived from an address and a network ID @@ -134,7 +134,7 @@ public: * @param ztaddr ZeroTier address * @param nwid 64-bit network ID */ - inline void fromAddress(const Address &ztaddr,uint64_t nwid) + ZT_ALWAYS_INLINE void fromAddress(const Address &ztaddr,uint64_t nwid) { uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40; m |= ztaddr.toInt(); // a is 40 bits @@ -153,7 +153,7 @@ public: * * @param nwid Network ID */ - inline Address toAddress(uint64_t nwid) const + ZT_ALWAYS_INLINE Address toAddress(uint64_t nwid) const { uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it @@ -168,7 +168,7 @@ public: * @param nwid Network ID * @return First octet of MAC for this network */ - static inline unsigned char firstOctetForNetwork(uint64_t nwid) + static ZT_ALWAYS_INLINE unsigned char firstOctetForNetwork(uint64_t nwid) { unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux @@ -178,16 +178,16 @@ public: * @param i Value from 0 to 5 (inclusive) * @return Byte at said position (address interpreted in big-endian order) */ - inline uint8_t operator[](unsigned int i) const { return (uint8_t)(_m >> (40 - (i * 8))); } + ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_m >> (40 - (i * 8))); } /** * @return 6, which is the number of bytes in a MAC, for container compliance */ - inline unsigned int size() const { return 6; } + ZT_ALWAYS_INLINE unsigned int size() const { return 6; } - inline unsigned long hashCode() const { return (unsigned long)_m; } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_m; } - inline char *toString(char buf[18]) const + ZT_ALWAYS_INLINE char *toString(char buf[18]) const { buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf]; buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf]; @@ -210,23 +210,23 @@ public: return buf; } - inline MAC &operator=(const MAC &m) + ZT_ALWAYS_INLINE MAC &operator=(const MAC &m) { _m = m._m; return *this; } - inline MAC &operator=(const uint64_t m) + ZT_ALWAYS_INLINE MAC &operator=(const uint64_t m) { _m = m & 0xffffffffffffULL; return *this; } - inline bool operator==(const MAC &m) const { return (_m == m._m); } - inline bool operator!=(const MAC &m) const { return (_m != m._m); } - inline bool operator<(const MAC &m) const { return (_m < m._m); } - inline bool operator<=(const MAC &m) const { return (_m <= m._m); } - inline bool operator>(const MAC &m) const { return (_m > m._m); } - inline bool operator>=(const MAC &m) const { return (_m >= m._m); } + ZT_ALWAYS_INLINE bool operator==(const MAC &m) const { return (_m == m._m); } + ZT_ALWAYS_INLINE bool operator!=(const MAC &m) const { return (_m != m._m); } + ZT_ALWAYS_INLINE bool operator<(const MAC &m) const { return (_m < m._m); } + ZT_ALWAYS_INLINE bool operator<=(const MAC &m) const { return (_m <= m._m); } + ZT_ALWAYS_INLINE bool operator>(const MAC &m) const { return (_m > m._m); } + ZT_ALWAYS_INLINE bool operator>=(const MAC &m) const { return (_m >= m._m); } private: uint64_t _m; diff --git a/node/Packet.hpp b/node/Packet.hpp index e7a22746c..8fff4be37 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -426,25 +426,27 @@ public: * <[...] physical destination address of packet> * [... begin encrypted region ...] * <[2] 16-bit reserved (legacy) field, always 0> - * <[2] 16-bit length of locator> - * <[...] locator for this node> * <[2] 16-bit length of meta-data dictionary> * <[...] meta-data dictionary> + * [... end encrypted region ...] + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> * - * HELLO is sent in the clear as it is how peers share their identity - * public keys. + * HELLO is sent with authentication but without the usual encryption so + * that peers can exchange identities. * * Destination address is the actual wire address to which the packet * was sent. See InetAddress::serialize() for format. * * Starting at "begin encrypted section" the reset of the packet is - * encrypted with Salsa20/12. This encryption is technically not - * absolutely required for security as nothing in this packet is - * very sensitive, but hiding the locator and other meta-data slightly - * improves privacy. + * encrypted with Salsa20/12. This is not the normal packet encryption + * and is technically not necessary as nothing in HELLO is secret. It + * exists merely to shield meta-data info from passive listeners to + * slightly improve privacy, and for backward compatibility with older + * nodes that required it. * - * The 16-bit zero after encryption starts is for backward compatibility - * with pre-2.0 nodes. + * HELLO (and its OK response) ends with a large 384-bit HMAC to allow + * identity exchanges to be authenticated with additional strength beyond + * ordinary packet authentication. * * OK payload: * <[8] HELLO timestamp field echo> @@ -453,7 +455,10 @@ public: * <[1] software minor version> * <[2] software revision> * <[...] physical destination address of packet> - * <[2] 16-bit reserved field, always 0> + * <[2] 16-bit reserved (legacy) field, always 0> + * <[2] 16-bit length of meta-data dictionary> + * <[...] meta-data dictionary> + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> * * With the exception of the timestamp, the other fields pertain to the * respondent who is sending OK and are not echoes. @@ -858,57 +863,20 @@ public: VERB_USER_MESSAGE = 0x14, /** - * Peer-to-peer propagated multicast packet: - * <[128] 1024-bit bloom filter> - * <[2] 16-bit perturbation coefficient to minimize bloom collisions> - * <[5] 40-bit start of range of recipient addresses> - * <[5] 40-bit end of range of recipient addresses> + * Encapsulate a ZeroTier packet for multicast distribution: * [... begin signed portion ...] * <[1] 8-bit flags> * <[5] 40-bit ZeroTier address of sender> - * <[8] 64-bit network ID> - * <[6] MAC address of multicast group> - * <[4] 32-bit ADI for multicast group> - * <[6] MAC address of sender> - * <[2] 16-bit ethertype> - * <[2] 16-bit length of ethernet payload> - * <[...] ethernet payload> + * <[2] 16-bit length of inner payload> + * <[1] inner payload verb> + * <[...] inner payload data> * [... end signed portion ...] - * <[2] 16-bit length of signature or 0 if unsigned> + * <[2] 16-bit length of signature or 0 if un-signed> * [<[...] optional signature of multicast>] - * - * This packet contains a multicast that is to be peer-to-peer replicated. - * The range of recipient addresses is a subset of the global list of - * subscribers to this multicast group. As the packet is propagated bits - * in the bloom filter will be set. The sender may attempt to select a - * perturbation coefficient to prevent collisions within the selected - * recipient range. + * <[...] address (min prefix) list> */ VERB_MULTICAST = 0x16, - /** - * Negotiate a new ephemeral key: - * <[48] SHA384 of ephemeral key we currently have for recipient> - * [<[...] sender's ephemeral key>] - * - * REKEY is used to negotiate ephemeral keys. The first byte is a step - * number from 0 to 2. Here's a new session initiated by Alice: - * - * Alice: REKEY[0x000...,AliceKey] -> Bob - * Bob: REKEY[SHA384(AliceKey),BobKey] -> Alice - * Alice: REKEY[SHA384(BobKey),(omitted)] -> Bob - * - * REKEY messages will continue until both sides have acknowledged each - * others' keys. Either Alice or Bob can send REKEY to negotiate a new - * ephemeral key pair at any time. - * - * OK isn't used because this is an ongoing handshake until both sides - * agree on a key. REKEY triggers a REKEY in reply if the hash for the - * recipient's ephemeral public key doesn't match the ephemeral key it - * wants to use. - */ - VERB_REKEY = 0x17, - /** * Encapsulate a full ZeroTier packet in another: * <[...] raw encapsulated packet> @@ -918,7 +886,7 @@ public: * where endpoint privacy is desired. Multiply nested ENCAP packets * could allow ZeroTier to act as an onion router. */ - VERB_ENCAP = 0x18 + VERB_ENCAP = 0x17 // protocol max: 0x1f }; diff --git a/node/SHA512.hpp b/node/SHA512.hpp index 1db7ed8a8..4ecc00fc5 100644 --- a/node/SHA512.hpp +++ b/node/SHA512.hpp @@ -42,21 +42,21 @@ namespace ZeroTier { #ifdef __APPLE__ #define ZT_HAVE_NATIVE_SHA512 1 -static inline void SHA512(void *digest,const void *data,unsigned int len) +static ZT_ALWAYS_INLINE void SHA512(void *digest,const void *data,unsigned int len) { CC_SHA512_CTX ctx; CC_SHA512_Init(&ctx); CC_SHA512_Update(&ctx,data,len); CC_SHA512_Final(reinterpret_cast(digest),&ctx); } -static inline void SHA384(void *digest,const void *data,unsigned int len) +static ZT_ALWAYS_INLINE void SHA384(void *digest,const void *data,unsigned int len) { CC_SHA512_CTX ctx; CC_SHA384_Init(&ctx); CC_SHA384_Update(&ctx,data,len); CC_SHA384_Final(reinterpret_cast(digest),&ctx); } -static inline void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1) +static ZT_ALWAYS_INLINE void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1) { CC_SHA512_CTX ctx; CC_SHA384_Init(&ctx); @@ -69,21 +69,21 @@ static inline void SHA384(void *digest,const void *data0,unsigned int len0,const #ifndef ZT_HAVE_NATIVE_SHA512 #ifdef ZT_USE_LIBCRYPTO #define ZT_HAVE_NATIVE_SHA512 1 -static inline void SHA512(void *digest,const void *data,unsigned int len) +static ZT_ALWAYS_INLINE void SHA512(void *digest,const void *data,unsigned int len) { SHA512_CTX ctx; SHA512_Init(&ctx); SHA512_Update(&ctx,data,len); SHA512_Final(reinterpret_cast(digest),&ctx); } -static inline void SHA384(void *digest,const void *data,unsigned int len) +static ZT_ALWAYS_INLINE void SHA384(void *digest,const void *data,unsigned int len) { SHA512_CTX ctx; SHA384_Init(&ctx); SHA384_Update(&ctx,data,len); SHA384_Final(reinterpret_cast(digest),&ctx); } -static inline void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1) +static ZT_ALWAYS_INLINE void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1) { SHA512_CTX ctx; SHA384_Init(&ctx); diff --git a/node/ScopedPtr.hpp b/node/ScopedPtr.hpp index 189a465ac..ff3972dad 100644 --- a/node/ScopedPtr.hpp +++ b/node/ScopedPtr.hpp @@ -27,21 +27,23 @@ template class ScopedPtr { public: - inline ScopedPtr(T *const p) : _p(p) {} - inline ~ScopedPtr() { delete _p; } + ZT_ALWAYS_INLINE ScopedPtr(T *const p) : _p(p) {} + ZT_ALWAYS_INLINE ~ScopedPtr() { delete _p; } - inline T *operator->() const { return _p; } - inline T &operator*() const { return *_p; } - inline operator bool() const { return (_p != (T *)0); } - inline T *ptr() const { return _p; } + ZT_ALWAYS_INLINE T *operator->() const { return _p; } + ZT_ALWAYS_INLINE T &operator*() const { return *_p; } + ZT_ALWAYS_INLINE operator bool() const { return (_p != (T *)0); } + ZT_ALWAYS_INLINE T *ptr() const { return _p; } - inline bool operator==(const ScopedPtr &p) const { return (_p == p._p); } - inline bool operator!=(const ScopedPtr &p) const { return (_p != p._p); } - inline bool operator==(T *const p) const { return (_p == p); } - inline bool operator!=(T *const p) const { return (_p != p); } + ZT_ALWAYS_INLINE bool operator==(const ScopedPtr &p) const { return (_p == p._p); } + ZT_ALWAYS_INLINE bool operator!=(const ScopedPtr &p) const { return (_p != p._p); } + ZT_ALWAYS_INLINE bool operator==(T *const p) const { return (_p == p); } + ZT_ALWAYS_INLINE bool operator!=(T *const p) const { return (_p != p); } private: - inline ScopedPtr() {} + ZT_ALWAYS_INLINE ScopedPtr() {} + ZT_ALWAYS_INLINE ScopedPtr(const ScopedPtr &p) : _p(nullptr) {} + ZT_ALWAYS_INLINE ScopedPtr &operator=(const ScopedPtr &p) { return *this; } T *const _p; }; diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp index 251a0648d..2c83ec997 100644 --- a/node/SharedPtr.hpp +++ b/node/SharedPtr.hpp @@ -30,11 +30,11 @@ template class SharedPtr { public: - inline SharedPtr() : _ptr((T *)0) {} - inline SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; } - inline SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {} + ZT_ALWAYS_INLINE SharedPtr() : _ptr((T *)0) {} + ZT_ALWAYS_INLINE SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; } + ZT_ALWAYS_INLINE SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {} - inline ~SharedPtr() + ZT_ALWAYS_INLINE ~SharedPtr() { if (_ptr) { if (--_ptr->__refCount <= 0) @@ -42,7 +42,7 @@ public: } } - inline SharedPtr &operator=(const SharedPtr &sp) + ZT_ALWAYS_INLINE SharedPtr &operator=(const SharedPtr &sp) { if (_ptr != sp._ptr) { T *p = sp._getAndInc(); @@ -63,7 +63,7 @@ public: * * @param ptr Naked pointer to assign */ - inline void set(T *ptr) + ZT_ALWAYS_INLINE void set(T *ptr) { zero(); ++ptr->__refCount; @@ -75,26 +75,26 @@ public: * * @param with Pointer to swap with */ - inline void swap(SharedPtr &with) + ZT_ALWAYS_INLINE void swap(SharedPtr &with) { T *tmp = _ptr; _ptr = with._ptr; with._ptr = tmp; } - inline operator bool() const { return (_ptr != (T *)0); } - inline T &operator*() const { return *_ptr; } - inline T *operator->() const { return _ptr; } + ZT_ALWAYS_INLINE operator bool() const { return (_ptr != (T *)0); } + ZT_ALWAYS_INLINE T &operator*() const { return *_ptr; } + ZT_ALWAYS_INLINE T *operator->() const { return _ptr; } /** * @return Raw pointer to held object */ - inline T *ptr() const { return _ptr; } + ZT_ALWAYS_INLINE T *ptr() const { return _ptr; } /** * Set this pointer to NULL */ - inline void zero() + ZT_ALWAYS_INLINE void zero() { if (_ptr) { if (--_ptr->__refCount <= 0) @@ -106,22 +106,22 @@ public: /** * @return Number of references according to this object's ref count or 0 if NULL */ - inline int references() + ZT_ALWAYS_INLINE int references() { if (_ptr) return _ptr->__refCount.load(); return 0; } - inline bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); } - inline bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); } - inline bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); } - inline bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); } - inline bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); } - inline bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); } + ZT_ALWAYS_INLINE bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); } + ZT_ALWAYS_INLINE bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); } + ZT_ALWAYS_INLINE bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); } + ZT_ALWAYS_INLINE bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); } + ZT_ALWAYS_INLINE bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); } + ZT_ALWAYS_INLINE bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); } private: - inline T *_getAndInc() const + ZT_ALWAYS_INLINE T *_getAndInc() const { if (_ptr) ++_ptr->__refCount; diff --git a/node/Str.hpp b/node/Str.hpp index 9870fecac..d7639af41 100644 --- a/node/Str.hpp +++ b/node/Str.hpp @@ -35,36 +35,36 @@ public: typedef char * iterator; typedef const char * const_iterator; - inline Str() { _l = 0; _s[0] = 0; } - inline Str(const Str &s) + ZT_ALWAYS_INLINE Str() { _l = 0; _s[0] = 0; } + ZT_ALWAYS_INLINE Str(const Str &s) { _l = s._l; memcpy(_s,s._s,_l+1); } - inline Str(const char *s) + ZT_ALWAYS_INLINE Str(const char *s) { _l = 0; _s[0] = 0; (*this) << s; } - inline Str(const std::string &s) + ZT_ALWAYS_INLINE Str(const std::string &s) { *this = s; } - inline Str &operator=(const Str &s) + ZT_ALWAYS_INLINE Str &operator=(const Str &s) { _l = s._l; memcpy(_s,s._s,_l+1); return *this; } - inline Str &operator=(const char *s) + ZT_ALWAYS_INLINE Str &operator=(const char *s) { _l = 0; _s[0] = 0; return ((*this) << s); } - inline Str &operator=(const std::string &s) + ZT_ALWAYS_INLINE Str &operator=(const std::string &s) { if (s.length() > ZT_STR_CAPACITY) { _l = 0; @@ -78,23 +78,23 @@ public: return *this; } - inline char operator[](const unsigned int i) const + ZT_ALWAYS_INLINE char operator[](const unsigned int i) const { if (unlikely(i >= (unsigned int)_l)) throw ZT_EXCEPTION_OUT_OF_BOUNDS; return _s[i]; } - inline void clear() { _l = 0; _s[0] = 0; } - inline const char *c_str() const { return _s; } - inline unsigned int length() const { return (unsigned int)_l; } - inline bool empty() const { return (_l == 0); } - inline iterator begin() { return (iterator)_s; } - inline iterator end() { return (iterator)(_s + (unsigned long)_l); } - inline const_iterator begin() const { return (const_iterator)_s; } - inline const_iterator end() const { return (const_iterator)(_s + (unsigned long)_l); } + ZT_ALWAYS_INLINE void clear() { _l = 0; _s[0] = 0; } + ZT_ALWAYS_INLINE const char *c_str() const { return _s; } + ZT_ALWAYS_INLINE unsigned int length() const { return (unsigned int)_l; } + ZT_ALWAYS_INLINE bool empty() const { return (_l == 0); } + ZT_ALWAYS_INLINE iterator begin() { return (iterator)_s; } + ZT_ALWAYS_INLINE iterator end() { return (iterator)(_s + (unsigned long)_l); } + ZT_ALWAYS_INLINE const_iterator begin() const { return (const_iterator)_s; } + ZT_ALWAYS_INLINE const_iterator end() const { return (const_iterator)(_s + (unsigned long)_l); } - inline Str &operator<<(const char *s) + ZT_ALWAYS_INLINE Str &operator<<(const char *s) { if (likely(s != (const char *)0)) { unsigned long l = _l; @@ -111,8 +111,8 @@ public: } return *this; } - inline Str &operator<<(const Str &s) { return ((*this) << s._s); } - inline Str &operator<<(const char c) + ZT_ALWAYS_INLINE Str &operator<<(const Str &s) { return ((*this) << s._s); } + ZT_ALWAYS_INLINE Str &operator<<(const char c) { if (unlikely(_l >= ZT_STR_CAPACITY)) { _s[ZT_STR_CAPACITY] = 0; @@ -122,29 +122,29 @@ public: _s[(unsigned long)_l] = 0; return *this; } - inline Str &operator<<(const unsigned long n) + ZT_ALWAYS_INLINE Str &operator<<(const unsigned long n) { char tmp[32]; Utils::decimal(n,tmp); return ((*this) << tmp); } - inline Str &operator<<(const unsigned int n) + ZT_ALWAYS_INLINE Str &operator<<(const unsigned int n) { char tmp[32]; Utils::decimal((unsigned long)n,tmp); return ((*this) << tmp); } - inline Str &operator<<(const Address &a) + ZT_ALWAYS_INLINE Str &operator<<(const Address &a) { char tmp[32]; return ((*this) << a.toString(tmp)); } - inline Str &operator<<(const InetAddress &a) + ZT_ALWAYS_INLINE Str &operator<<(const InetAddress &a) { char tmp[128]; return ((*this) << a.toString(tmp)); } - inline Str &operator<<(const MAC &a) + ZT_ALWAYS_INLINE Str &operator<<(const MAC &a) { char tmp[64]; return ((*this) << a.toString(tmp)); @@ -171,31 +171,23 @@ public: return *this; } - inline operator bool() const { return (_l != 0); } + ZT_ALWAYS_INLINE operator bool() const { return (_l != 0); } - inline bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); } - inline bool operator!=(const Str &s) const { return ((_l != s._l)||(memcmp(_s,s._s,_l) != 0)); } - inline bool operator<(const Str &s) const { return ( (_l < s._l) ? true : ((_l == s._l) ? (memcmp(_s,s._s,_l) < 0) : false) ); } - inline bool operator>(const Str &s) const { return (s < *this); } - inline bool operator<=(const Str &s) const { return !(s < *this); } - inline bool operator>=(const Str &s) const { return !(*this < s); } + ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); } + ZT_ALWAYS_INLINE bool operator!=(const Str &s) const { return ((_l != s._l)||(memcmp(_s,s._s,_l) != 0)); } + ZT_ALWAYS_INLINE bool operator<(const Str &s) const { return ( (_l < s._l) ? true : ((_l == s._l) ? (memcmp(_s,s._s,_l) < 0) : false) ); } + ZT_ALWAYS_INLINE bool operator>(const Str &s) const { return (s < *this); } + ZT_ALWAYS_INLINE bool operator<=(const Str &s) const { return !(s < *this); } + ZT_ALWAYS_INLINE bool operator>=(const Str &s) const { return !(*this < s); } - inline bool operator==(const char *s) const { return (strcmp(_s,s) == 0); } - inline bool operator!=(const char *s) const { return (strcmp(_s,s) != 0); } - inline bool operator<(const char *s) const { return (strcmp(_s,s) < 0); } - inline bool operator>(const char *s) const { return (strcmp(_s,s) > 0); } - inline bool operator<=(const char *s) const { return (strcmp(_s,s) <= 0); } - inline bool operator>=(const char *s) const { return (strcmp(_s,s) >= 0); } + ZT_ALWAYS_INLINE bool operator==(const char *s) const { return (strcmp(_s,s) == 0); } + ZT_ALWAYS_INLINE bool operator!=(const char *s) const { return (strcmp(_s,s) != 0); } + ZT_ALWAYS_INLINE bool operator<(const char *s) const { return (strcmp(_s,s) < 0); } + ZT_ALWAYS_INLINE bool operator>(const char *s) const { return (strcmp(_s,s) > 0); } + ZT_ALWAYS_INLINE bool operator<=(const char *s) const { return (strcmp(_s,s) <= 0); } + ZT_ALWAYS_INLINE bool operator>=(const char *s) const { return (strcmp(_s,s) >= 0); } - inline unsigned long hashCode() const - { - const char *p = _s; - unsigned long h = 0; - char c; - while ((c = *(p++))) - h = (31 * h) + (unsigned long)c; - return h; - } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return Utils::hashString(_s,_l); } private: uint16_t _l; diff --git a/node/Topology.hpp b/node/Topology.hpp index 4a084624d..c65901ef4 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -33,9 +32,9 @@ #include "Mutex.hpp" #include "InetAddress.hpp" #include "Hashtable.hpp" -#include "Locator.hpp" #include "SharedPtr.hpp" #include "ScopedPtr.hpp" +#include "Str.hpp" namespace ZeroTier { @@ -46,48 +45,17 @@ class RuntimeEnvironment; */ class Topology { -private: - struct _RootRankingFunction - { - inline _RootRankingFunction() : bestRoot(),bestRootLatency(0xffff) {} - inline bool operator()(const SharedPtr &peer,const std::vector &phy) - { - const unsigned int lat = peer->latency(now); - if ((!bestRoot)||((lat <= bestRootLatency)&&(peer->getAppropriatePath(now,false)))) { - bestRoot = peer; - bestRootLatency = lat; - } - return true; - } - int64_t now; - SharedPtr bestRoot; - unsigned int bestRootLatency; - }; - - inline void _updateRoots() - { - // assumes _roots_l is locked - _rootIdentities.clear(); - Hashtable< Str,SharedPtr >::Iterator i(_roots); - Str *k = (Str *)0; - SharedPtr< const Locator > *v = (SharedPtr< const Locator > *)0; - while (i.next(k,v)) { - if (*v) - _rootIdentities.set((*v)->id(),true); - } - } - public: - inline Topology(const RuntimeEnvironment *renv,const Identity &myId) : + ZT_ALWAYS_INLINE Topology(const RuntimeEnvironment *renv,const Identity &myId) : RR(renv), _myIdentity(myId), _numConfiguredPhysicalPaths(0), - _peers(64), - _paths(128), - _roots(8), - _rootIdentities(8), - _lastUpdatedBestRoot(0) {} - inline ~Topology() {} + _peers(128), + _paths(256) + { + } + + ZT_ALWAYS_INLINE ~Topology() {} /** * Add a peer to database @@ -95,21 +63,16 @@ public: * This will not replace existing peers. In that case the existing peer * record is returned. * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param peer Peer to add * @return New or existing peer (should replace 'peer') */ - inline SharedPtr add(const SharedPtr &peer) + ZT_ALWAYS_INLINE SharedPtr add(const SharedPtr &peer) { - SharedPtr np; - { - Mutex::Lock _l(_peers_l); - SharedPtr &hp = _peers[peer->address()]; - if (!hp) - hp = peer; - np = hp; - } - return np; + Mutex::Lock _l(_peers_l); + SharedPtr &hp = _peers[peer->address()]; + if (!hp) + hp = peer; + return hp; } /** @@ -119,15 +82,11 @@ public: * @param zta ZeroTier address of peer * @return Peer or NULL if not found */ - inline SharedPtr get(const Address &zta) + ZT_ALWAYS_INLINE SharedPtr get(const Address &zta) { - if (zta == _myIdentity.address()) - return SharedPtr(); Mutex::Lock l1(_peers_l); const SharedPtr *const ap = _peers.get(zta); - if (ap) - return *ap; - return SharedPtr(); + return (ap) ? *ap : SharedPtr(); } /** @@ -135,7 +94,7 @@ public: * @param zta ZeroTier address of peer * @return Identity or NULL identity if not found */ - inline Identity getIdentity(void *tPtr,const Address &zta) + ZT_ALWAYS_INLINE Identity getIdentity(void *tPtr,const Address &zta) { if (zta == _myIdentity.address()) { return _myIdentity; @@ -155,7 +114,7 @@ public: * @param r Remote address * @return Pointer to canonicalized Path object */ - inline SharedPtr getPath(const int64_t l,const InetAddress &r) + ZT_ALWAYS_INLINE SharedPtr getPath(const int64_t l,const InetAddress &r) { Mutex::Lock _l(_paths_l); SharedPtr &p = _paths[Path::HashKey(l,r)]; @@ -168,45 +127,17 @@ public: * @param id Identity to check * @return True if this identity corresponds to a root */ - inline bool isRoot(const Identity &id) const + ZT_ALWAYS_INLINE bool isRoot(const Identity &id) const { - Mutex::Lock l(_roots_l); - return _rootIdentities.contains(id); - } - - /** - * Do periodic tasks such as database cleanup - */ - inline void doPeriodicTasks(int64_t now) - { - { - Mutex::Lock _l1(_peers_l); - Hashtable< Address,SharedPtr >::Iterator i(_peers); - Address *a = (Address *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { - if (!(*p)->alive(now)) { - _peers.erase(*a); - } - } - } - { - Mutex::Lock _l(_paths_l); - Hashtable< Path::HashKey,SharedPtr >::Iterator i(_paths); - Path::HashKey *k = (Path::HashKey *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(k,p)) { - if (p->references() <= 1) - _paths.erase(*k); - } - } + Mutex::Lock l(_peers_l); + return (_roots.count(id) > 0); } /** * @param now Current time * @return Number of peers with active direct paths */ - inline unsigned long countActive(int64_t now) const + ZT_ALWAYS_INLINE unsigned long countActive(const int64_t now) const { unsigned long cnt = 0; Mutex::Lock _l(_peers_l); @@ -214,8 +145,7 @@ public: Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { - const SharedPtr pp((*p)->getAppropriatePath(now,false)); - if (pp) + if ((*p)->getAppropriatePath(now,false)) ++cnt; } return cnt; @@ -243,161 +173,6 @@ public: } } - /** - * Apply a function or function object to all roots - * - * This locks the root list during execution but other operations - * are fine. - * - * @param f Function to apply f(peer,IPs) - * @tparam F function or function object type - */ - template - inline void eachRoot(F f) - { - Mutex::Lock l(_roots_l); - Hashtable< Str,Locator >::Iterator i(_roots); - Str *k = (Str *)0; - Locator *v = (Locator *)0; - while (i.next(k,v)) { - if (*v) { - for(std::vector::const_iterator id(v->virt().begin());id!=v->virt().end();++id) { - const SharedPtr *ap; - { - Mutex::Lock l2(_peers_l); - ap = _peers.get(id->address()); - } - if (ap) { - if (!f(*ap,v->phy())) - return; - } else { - SharedPtr p(new Peer(RR,_myIdentity,*id)); - { - Mutex::Lock l2(_peers_l); - _peers.set(id->address(),p); - } - if (!f(p,v->phy())) - return; - } - } - } - } - } - - /** - * @return Current best root (updated automatically each second) - */ - inline SharedPtr root(const int64_t now) - { - Mutex::Lock l(_bestRoot_l); - if ((!_bestRoot)||((now - _lastUpdatedBestRoot) > 1000)) { - _lastUpdatedBestRoot = now; - _RootRankingFunction rrf; - rrf.now = now; - eachRoot(rrf); - _bestRoot = rrf.bestRoot; - } - return _bestRoot; - } - - /** - * Iterate through all root names - * - * @param f Function of (Str,Locator) - */ - template - inline void eachRootName(F f) const - { - Mutex::Lock l(_roots_l); - Str *k = (Str *)0; - Locator *v = (Locator *)0; - Hashtable< Str,Locator >::Iterator i(const_cast(this)->_roots); - while (i.next(k,v)) { - if (!f(*k,*v)) - break; - } - } - - /** - * Set or update dynamic root if new locator is newer - * - * This does not check signatures or internal validity of the locator. - * - * @param name DNS name used to retrive root or simply the address for static roots - * @param latestLocator Latest locator - * @return True if locator is newer or if a new entry was created - */ - inline bool setRoot(const Str &name,const SharedPtr &latestLocator) - { - Mutex::Lock l(_roots_l); - if (latestLocator) { - SharedPtr &ll = _roots[name]; - if ((ll)&&(ll->timestamp() < latestLocator->timestamp())) { - ll = latestLocator; - _updateRoots(); - _rootsModified = true; - return true; - } - } else if (!_roots.contains(name)) { - _roots.set(name,SharedPtr()); // no locator known yet, but add name to name list to trigger DNS refreshing - _rootsModified = true; - return true; - } - return false; - } - - /** - * Remove a dynamic root entry - */ - inline void removeRoot(const Str &name) - { - Mutex::Lock l(_roots_l); - if (_roots.erase(name)) { - _updateRoots(); - _rootsModified = true; - } - } - - /** - * @param Current time - * @return ZT_RootList as returned by the external CAPI - */ - inline ZT_RootList *apiRoots(const int64_t now) const - { - ScopedPtr< Buffer<65536> > lbuf(new Buffer<65536>()); - Mutex::Lock l2(_roots_l); - ZT_RootList *rl = (ZT_RootList *)malloc(sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size()) + (256 * _roots.size()) + (65536 * _roots.size())); - if (!rl) - return nullptr; - char *nptr = ((char *)rl) + sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size()); - uint8_t *lptr = ((uint8_t *)nptr) + (256 * _roots.size()); - - unsigned int c = 0; - Str *k = (Str *)0; - SharedPtr *v = (SharedPtr *)0; - Hashtable< Str,SharedPtr >::Iterator i(const_cast(this)->_roots); - while (i.next(k,v)) { - Utils::scopy(nptr,256,k->c_str()); - rl->roots[c].name = nptr; - nptr += 256; - if (*v) { - lbuf->clear(); - (*v)->serialize(*lbuf); - memcpy(lptr,lbuf->unsafeData(),lbuf->size()); - rl->roots[c].locator = lptr; - rl->roots[c].locatorSize = lbuf->size(); - } else { - rl->roots[c].locator = nullptr; - rl->roots[c].locatorSize = 0; - } - lptr += 65536; - ++c; - } - - rl->count = c; - return rl; - } - /** * Get the best relay to a given address, which may or may not be a root * @@ -405,10 +180,12 @@ public: * @param toAddr Destination address * @return Best current relay or NULL if none */ - inline SharedPtr findRelayTo(const int64_t now,const Address &toAddr) + ZT_ALWAYS_INLINE SharedPtr findRelayTo(const int64_t now,const Address &toAddr) { - // TODO: in the future this will check 'mesh-like' relays and if enabled consult LF for other roots (for if this is a root) - return root(now); + Mutex::Lock l(_peers_l); + if (_rootPeers.empty()) + return SharedPtr(); + return _rootPeers[0]; } /** @@ -422,9 +199,8 @@ public: Hashtable< Address,SharedPtr >::Iterator i(*(const_cast > *>(&_peers))); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { + while (i.next(a,p)) allPeers.push_back(*p); - } } /** @@ -436,7 +212,7 @@ public: * @param mtu Variable set to MTU * @param trustedPathId Variable set to trusted path ID */ - inline void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId) + ZT_ALWAYS_INLINE void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId) { for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i::iterator,bool > ir(_roots.insert(id)); + if (ir.second) { + SharedPtr &p = _peers[id.address()]; + if (!p) + p.set(new Peer(RR,_myIdentity,id)); + _rootPeers.push_back(p); + } + } + + /** + * Remove a root server's identity from the root server set + * + * @param id Root server identity + * @return True if root found and removed, false if not found + */ + inline bool removeRoot(const Identity &id) + { + Mutex::Lock l1(_peers_l); + std::set::iterator r(_roots.find(id)); + if (r != _roots.end()) { + for(std::vector< SharedPtr >::iterator p(_rootPeers.begin());p!=_rootPeers.end();++p) { + if ((*p)->identity() == id) { + _rootPeers.erase(p); + break; + } + } + _roots.erase(r); + return true; + } + return false; + } + + /** + * Do periodic tasks such as database cleanup + */ + inline void doPeriodicTasks(const int64_t now) + { + { + Mutex::Lock l1(_peers_l); + std::sort(_rootPeers.begin(),_rootPeers.end(),_RootSortComparisonOperator(now)); + Hashtable< Address,SharedPtr >::Iterator i(_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) { + if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) + _peers.erase(*a); + } + } + { + Mutex::Lock l1(_paths_l); + Hashtable< Path::HashKey,SharedPtr >::Iterator i(_paths); + Path::HashKey *k = (Path::HashKey *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(k,p)) { + if (p->references() <= 1) + _paths.erase(*k); + } + } + } + private: + struct _RootSortComparisonOperator + { + ZT_ALWAYS_INLINE _RootSortComparisonOperator(const int64_t now) : _now(now) {} + ZT_ALWAYS_INLINE bool operator()(const SharedPtr &a,const SharedPtr &b) + { + const int64_t now = _now; + if (a->alive(now)) { + if (b->alive(now)) + return (a->latency(now) < b->latency(now)); + return true; + } + return false; + } + const int64_t _now; + }; + const RuntimeEnvironment *const RR; const Identity _myIdentity; - std::pair _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; + Mutex _peers_l; + Mutex _paths_l; + + std::pair< InetAddress,ZT_PhysicalPathConfiguration > _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; unsigned int _numConfiguredPhysicalPaths; Hashtable< Address,SharedPtr > _peers; Hashtable< Path::HashKey,SharedPtr > _paths; - - Hashtable< Str,SharedPtr > _roots; - Hashtable< Identity,bool > _rootIdentities; - bool _rootsModified; - - int64_t _lastUpdatedBestRoot; - SharedPtr _bestRoot; - - Mutex _peers_l; - Mutex _paths_l; - Mutex _roots_l; - Mutex _bestRoot_l; + std::set< Identity > _roots; // locked by _peers_l + std::vector< SharedPtr > _rootPeers; // locked by _peers_l }; } // namespace ZeroTier diff --git a/node/Utils.cpp b/node/Utils.cpp index 736526cc8..fa29968a5 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -367,83 +367,11 @@ int b32d(const char *encoded,uint8_t *result,int bufSize) return count; } -unsigned int b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen) +static uint64_t _secureRandom64() { - static const char base64en[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; - unsigned int i = 0,j = 0; - uint8_t l = 0; - int s = 0; - for (;i= outlen) return 0; - out[j++] = base64en[(c >> 2) & 0x3f]; - break; - case 1: - s = 2; - if (j >= outlen) return 0; - out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xf)]; - break; - case 2: - s = 0; - if (j >= outlen) return 0; - out[j++] = base64en[((l & 0xf) << 2) | ((c >> 6) & 0x3)]; - if (j >= outlen) return 0; - out[j++] = base64en[c & 0x3f]; - break; - } - l = c; - } - switch (s) { - case 1: - if (j >= outlen) return 0; - out[j++] = base64en[(l & 0x3) << 4]; - //out[j++] = '='; - //out[j++] = '='; - break; - case 2: - if (j >= outlen) return 0; - out[j++] = base64en[(l & 0xf) << 2]; - //out[j++] = '='; - break; - } - if (j >= outlen) return 0; - out[j] = 0; - return j; -} - -unsigned int b64d(const char *in,unsigned char *out,unsigned int outlen) -{ - static const uint8_t base64de[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,255,255,255,255,255 }; - unsigned int i = 0; - unsigned int j = 0; - while ((in[i] != '=')&&(in[i] != 0)) { - if (j >= outlen) - break; - uint8_t c = base64de[(unsigned char)in[i]]; - if (c != 255) { - switch (i & 0x3) { - case 0: - out[j] = (c << 2) & 0xff; - break; - case 1: - out[j++] |= (c >> 4) & 0x3; - out[j] = (c & 0xf) << 4; - break; - case 2: - out[j++] |= (c >> 2) & 0xf; - out[j] = (c & 0x3) << 6; - break; - case 3: - out[j++] |= c; - break; - } - } - ++i; - } - return j; + uint64_t tmp = 0; + getSecureRandom(&tmp,sizeof(tmp)); + return tmp; } #define ROL64(x,k) (((x) << (k)) | ((x) >> (64 - (k)))) @@ -451,10 +379,10 @@ uint64_t random() { // https://en.wikipedia.org/wiki/Xorshift#xoshiro256** static Mutex l; - static uint64_t s0 = Utils::getSecureRandom64(); - static uint64_t s1 = Utils::getSecureRandom64(); - static uint64_t s2 = Utils::getSecureRandom64(); - static uint64_t s3 = Utils::getSecureRandom64(); + static uint64_t s0 = _secureRandom64(); + static uint64_t s1 = _secureRandom64(); + static uint64_t s2 = _secureRandom64(); + static uint64_t s3 = _secureRandom64(); l.lock(); const uint64_t result = ROL64(s1 * 5,7) * 9; diff --git a/node/Utils.hpp b/node/Utils.hpp index 47f4b1051..130c32348 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -140,21 +140,25 @@ unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen void getSecureRandom(void *buf,unsigned int bytes); /** - * Get a 64-bit unsigned secure random number + * Encode string to base32 + * + * @param data Binary data to encode + * @param length Length of data in bytes + * @param result Result buffer + * @param bufSize Size of result buffer + * @return Number of bytes written */ -static ZT_ALWAYS_INLINE uint64_t getSecureRandom64() -{ - uint64_t x; - getSecureRandom(&x,sizeof(x)); - return x; -} - int b32e(const uint8_t *data,int length,char *result,int bufSize); -int b32d(const char *encoded, uint8_t *result, int bufSize); -static ZT_ALWAYS_INLINE unsigned int b64MaxEncodedSize(const unsigned int s) { return ((((s + 2) / 3) * 4) + 1); } -unsigned int b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen); -unsigned int b64d(const char *in,uint8_t *out,unsigned int outlen); +/** + * Decode base32 string + * + * @param encoded C-string in base32 format (non-base32 characters are ignored) + * @param result Result buffer + * @param bufSize Size of result buffer + * @return Number of bytes written or -1 on error + */ +int b32d(const char *encoded, uint8_t *result, int bufSize); /** * Get a non-cryptographic random integer @@ -239,18 +243,34 @@ static ZT_ALWAYS_INLINE long long hexStrTo64(const char *s) */ bool scopy(char *dest,unsigned int len,const char *src); +/** + * Calculate a non-cryptographic hash of a byte string + * + * @param key Key to hash + * @param len Length in bytes + * @return Non-cryptographic hash suitable for use in a hash table + */ +static unsigned long ZT_ALWAYS_INLINE hashString(const void *restrict key,const unsigned int len) +{ + const uint8_t *p = reinterpret_cast(key); + unsigned long h = 0; + for (unsigned int i=0;i> 6); + } + h += (h << 3); + h ^= (h >> 11); + h += (h << 15); + return h; +} + #ifdef __GNUC__ static ZT_ALWAYS_INLINE unsigned int countBits(const uint8_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } static ZT_ALWAYS_INLINE unsigned int countBits(const uint16_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } static ZT_ALWAYS_INLINE unsigned int countBits(const uint32_t v) { return (unsigned int)__builtin_popcountl((unsigned long)v); } static ZT_ALWAYS_INLINE unsigned int countBits(const uint64_t v) { return (unsigned int)__builtin_popcountll((unsigned long long)v); } #else -/** - * Count the number of bits set in an integer - * - * @param v Unsigned integer - * @return Number of bits set in this integer (0-bits in integer) - */ template static ZT_ALWAYS_INLINE unsigned int countBits(T v) {