From 1b2cac0cc59790620262f447588274440c7fbefa Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 09:38:33 -0700 Subject: Trim some cruft that is not used and probably never would be. --- node/Node.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index d5cc50b9..d5ca147c 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -82,9 +82,6 @@ Node::Node( _lastPingCheck(0), _lastHousekeepingRun(0) { - _newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR; - _newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR; - _newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION; _online = false; // Use Salsa20 alone as a high-quality non-crypto PRNG @@ -540,16 +537,6 @@ std::string Node::dataStoreGet(const char *name) return r; } -void Node::postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev) -{ - if (Utils::compareVersion(major,minor,rev,_newestVersionSeen[0],_newestVersionSeen[1],_newestVersionSeen[2]) > 0) { - _newestVersionSeen[0] = major; - _newestVersionSeen[1] = minor; - _newestVersionSeen[2] = rev; - this->postEvent(ZT_EVENT_SAW_MORE_RECENT_VERSION,(const void *)_newestVersionSeen); - } -} - #ifdef ZT_TRACE void Node::postTrace(const char *module,unsigned int line,const char *fmt,...) { @@ -659,8 +646,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket( } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; } catch ( ... ) { - reinterpret_cast(node)->postEvent(ZT_EVENT_INVALID_PACKET,(const void *)remoteAddress); - return ZT_RESULT_OK; + return ZT_RESULT_OK; // "OK" since invalid packets are simply dropped, but the system is still up } } -- cgit v1.2.3 From 9347d6c866f6cae357448762f064a481fb765c00 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 7 Oct 2015 18:04:40 -0700 Subject: Make it so ZeroTierOne.h can be used with a C compiler again. --- include/ZeroTierOne.h | 4 ++-- node/Node.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 990f682d..175cedc5 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1255,7 +1255,7 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); * @param trust How much do you trust the local network under this interface? * @return Boolean: non-zero if address was accepted and added */ -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust); +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust); /** * Clear local interface addresses @@ -1296,7 +1296,7 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance); * @param reportCallback Function to call each time a report is received * @return OK or error if, for example, test is too big for a packet or support isn't compiled in */ -ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); +enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *, ZT_CircuitTest *,const ZT_CircuitTestReport *)); /** * Stop listening for results to a given circuit test diff --git a/node/Node.cpp b/node/Node.cpp index d5ca147c..7f469b97 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -779,7 +779,7 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance) } catch ( ... ) {} } -ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) +enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) { try { return reinterpret_cast(node)->circuitTestBegin(test,reportCallback); @@ -795,7 +795,7 @@ void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test) } catch ( ... ) {} } -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust) +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust) { try { return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust); -- cgit v1.2.3 From 0c498556d5b11c101d2b18cf85cff2d53aa97d58 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 09:39:27 -0700 Subject: Unroll Salsa20 fully for a little more speed (non-SSE now almost as fast as SSE) --- node/Identity.cpp | 9 +- node/IncomingPacket.cpp | 8 +- node/Node.cpp | 6 +- node/Packet.cpp | 12 +- node/Salsa20.cpp | 1206 ++++++++++++++++++++++++++++++++++++++++++----- node/Salsa20.hpp | 40 +- selftest.cpp | 42 +- 7 files changed, 1138 insertions(+), 185 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Identity.cpp b/node/Identity.cpp index 8765da51..e5aaf13d 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -41,7 +41,6 @@ #define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17 #define ZT_IDENTITY_GEN_MEMORY 2097152 -#define ZT_IDENTITY_GEN_SALSA20_ROUNDS 20 namespace ZeroTier { @@ -55,8 +54,8 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub // ordinary Salsa20 is randomly seekable. This is good for a cipher // but is not what we want for sequential memory-harndess. memset(genmem,0,ZT_IDENTITY_GEN_MEMORY); - Salsa20 s20(digest,256,(char *)digest + 32,ZT_IDENTITY_GEN_SALSA20_ROUNDS); - s20.encrypt((char *)genmem,(char *)genmem,64); + Salsa20 s20(digest,256,(char *)digest + 32); + s20.encrypt20((char *)genmem,(char *)genmem,64); for(unsigned long i=64;i(candidate)); SHA512::hash(shabuf,candidate,16 + challengeLength); - s20.init(shabuf,256,&s20iv,12); + s20.init(shabuf,256,&s20iv); memset(salsabuf,0,sizeof(salsabuf)); - s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf)); + s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf)); SHA512::hash(shabuf,salsabuf,sizeof(salsabuf)); d = difficulty; @@ -1186,9 +1186,9 @@ bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficult memcpy(candidate + 16,challenge,challengeLength); SHA512::hash(shabuf,candidate,16 + challengeLength); - s20.init(shabuf,256,&s20iv,12); + s20.init(shabuf,256,&s20iv); memset(salsabuf,0,sizeof(salsabuf)); - s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf)); + s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf)); SHA512::hash(shabuf,salsabuf,sizeof(salsabuf)); d = difficulty; diff --git a/node/Node.cpp b/node/Node.cpp index 7f469b97..84452146 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -88,9 +88,9 @@ Node::Node( { char foo[32]; Utils::getSecureRandom(foo,32); - _prng.init(foo,256,foo,8); + _prng.init(foo,256,foo); memset(_prngStream,0,sizeof(_prngStream)); - _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream)); } std::string idtmp(dataStoreGet("identity.secret")); @@ -574,7 +574,7 @@ uint64_t Node::prng() { unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t))); if (!p) - _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream)); return _prngStream[p]; } diff --git a/node/Packet.cpp b/node/Packet.cpp index 2fb7d488..f11ae1b8 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -92,14 +92,14 @@ void Packet::armor(const void *key,bool encryptPayload) setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE); _salsa20MangleKey((const unsigned char *)key,mangledKey); - Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS); + Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/); // MAC key is always the first 32 bytes of the Salsa20 key stream // This is the same construction DJB's NaCl library uses - s20.encrypt(ZERO_KEY,macKey,sizeof(macKey)); + s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey)); if (encryptPayload) - s20.encrypt(payload,payload,payloadLen); + s20.encrypt12(payload,payload,payloadLen); Poly1305::compute(mac,payload,payloadLen,macKey); memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8); @@ -116,15 +116,15 @@ bool Packet::dearmor(const void *key) if ((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)||(cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)) { _salsa20MangleKey((const unsigned char *)key,mangledKey); - Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS); + Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/); - s20.encrypt(ZERO_KEY,macKey,sizeof(macKey)); + s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey)); Poly1305::compute(mac,payload,payloadLen,macKey); if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8)) return false; if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012) - s20.decrypt(payload,payload,payloadLen); + s20.decrypt12(payload,payload,payloadLen); return true; } else return false; // unrecognized cipher suite diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp index dec14faf..3aa19ac6 100644 --- a/node/Salsa20.cpp +++ b/node/Salsa20.cpp @@ -66,7 +66,7 @@ static const _s20sseconsts _S20SSECONSTANTS; namespace ZeroTier { -void Salsa20::init(const void *key,unsigned int kbits,const void *iv,unsigned int rounds) +void Salsa20::init(const void *key,unsigned int kbits,const void *iv) throw() { #ifdef ZT_SALSA20_SSE @@ -121,11 +121,9 @@ void Salsa20::init(const void *key,unsigned int kbits,const void *iv,unsigned in _state.i[15] = U8TO32_LITTLE(constants + 12); _state.i[0] = U8TO32_LITTLE(constants + 0); #endif - - _roundsDiv4 = rounds / 4; } -void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) +void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes) throw() { uint8_t tmp[64]; @@ -181,61 +179,149 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) __m128i X2s = X2; __m128i X3s = X3; - for (i=0;i<_roundsDiv4;++i) { - T = _mm_add_epi32(X0, X3); - X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); - T = _mm_add_epi32(X1, X0); - X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); - T = _mm_add_epi32(X2, X1); - X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); - T = _mm_add_epi32(X3, X2); - X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); - - X1 = _mm_shuffle_epi32(X1, 0x93); - X2 = _mm_shuffle_epi32(X2, 0x4E); - X3 = _mm_shuffle_epi32(X3, 0x39); - - T = _mm_add_epi32(X0, X1); - X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); - T = _mm_add_epi32(X3, X0); - X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); - T = _mm_add_epi32(X2, X3); - X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); - T = _mm_add_epi32(X1, X2); - X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); - - X1 = _mm_shuffle_epi32(X1, 0x39); - X2 = _mm_shuffle_epi32(X2, 0x4E); - X3 = _mm_shuffle_epi32(X3, 0x93); - - // -- - - T = _mm_add_epi32(X0, X3); - X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); - T = _mm_add_epi32(X1, X0); - X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); - T = _mm_add_epi32(X2, X1); - X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); - T = _mm_add_epi32(X3, X2); - X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); - - X1 = _mm_shuffle_epi32(X1, 0x93); - X2 = _mm_shuffle_epi32(X2, 0x4E); - X3 = _mm_shuffle_epi32(X3, 0x39); - - T = _mm_add_epi32(X0, X1); - X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); - T = _mm_add_epi32(X3, X0); - X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); - T = _mm_add_epi32(X2, X3); - X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); - T = _mm_add_epi32(X1, X2); - X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); - - X1 = _mm_shuffle_epi32(X1, 0x39); - X2 = _mm_shuffle_epi32(X2, 0x4E); - X3 = _mm_shuffle_epi32(X3, 0x93); - } + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); X0 = _mm_add_epi32(X0s,X0); X1 = _mm_add_epi32(X1s,X1); @@ -273,76 +359,942 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) x14 = j14; x15 = j15; - for(i=0;i<_roundsDiv4;++i) { - x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); - x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); - x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); - x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); - x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); - x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); - x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); - x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); - x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); - x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); - x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); - x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); - x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); - x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); - x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); - x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); - x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); - x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); - x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); - x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); - x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); - x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); - x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); - x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); - x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); - x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); - x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); - x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); - x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); - x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); - x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); - x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); - - // -- - - x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); - x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); - x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); - x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); - x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); - x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); - x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); - x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); - x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); - x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); - x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); - x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); - x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); - x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); - x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); - x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); - x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); - x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); - x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); - x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); - x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); - x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); - x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); - x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); - x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); - x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); - x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); - x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); - x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); - x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); - x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); - x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + U32TO8_LITTLE(c + 0,XOR(x0,U8TO32_LITTLE(m + 0))); + U32TO8_LITTLE(c + 4,XOR(x1,U8TO32_LITTLE(m + 4))); + U32TO8_LITTLE(c + 8,XOR(x2,U8TO32_LITTLE(m + 8))); + U32TO8_LITTLE(c + 12,XOR(x3,U8TO32_LITTLE(m + 12))); + U32TO8_LITTLE(c + 16,XOR(x4,U8TO32_LITTLE(m + 16))); + U32TO8_LITTLE(c + 20,XOR(x5,U8TO32_LITTLE(m + 20))); + U32TO8_LITTLE(c + 24,XOR(x6,U8TO32_LITTLE(m + 24))); + U32TO8_LITTLE(c + 28,XOR(x7,U8TO32_LITTLE(m + 28))); + U32TO8_LITTLE(c + 32,XOR(x8,U8TO32_LITTLE(m + 32))); + U32TO8_LITTLE(c + 36,XOR(x9,U8TO32_LITTLE(m + 36))); + U32TO8_LITTLE(c + 40,XOR(x10,U8TO32_LITTLE(m + 40))); + U32TO8_LITTLE(c + 44,XOR(x11,U8TO32_LITTLE(m + 44))); + U32TO8_LITTLE(c + 48,XOR(x12,U8TO32_LITTLE(m + 48))); + U32TO8_LITTLE(c + 52,XOR(x13,U8TO32_LITTLE(m + 52))); + U32TO8_LITTLE(c + 56,XOR(x14,U8TO32_LITTLE(m + 56))); + U32TO8_LITTLE(c + 60,XOR(x15,U8TO32_LITTLE(m + 60))); + + if (!(++j8)) { + ++j9; + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } +#endif + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) + ctarget[i] = c[i]; + } + +#ifndef ZT_SALSA20_SSE + _state.i[8] = j8; + _state.i[9] = j9; +#endif + + return; + } + + bytes -= 64; + c += 64; + m += 64; + } +} + +void Salsa20::encrypt20(const void *in,void *out,unsigned int bytes) + throw() +{ + uint8_t tmp[64]; + const uint8_t *m = (const uint8_t *)in; + uint8_t *c = (uint8_t *)out; + uint8_t *ctarget = c; + unsigned int i; + +#ifndef ZT_SALSA20_SSE + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; +#endif + + if (!bytes) + return; + +#ifndef ZT_SALSA20_SSE + j0 = _state.i[0]; + j1 = _state.i[1]; + j2 = _state.i[2]; + j3 = _state.i[3]; + j4 = _state.i[4]; + j5 = _state.i[5]; + j6 = _state.i[6]; + j7 = _state.i[7]; + j8 = _state.i[8]; + j9 = _state.i[9]; + j10 = _state.i[10]; + j11 = _state.i[11]; + j12 = _state.i[12]; + j13 = _state.i[13]; + j14 = _state.i[14]; + j15 = _state.i[15]; +#endif + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) + tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; } +#ifdef ZT_SALSA20_SSE + __m128i X0 = _mm_loadu_si128((const __m128i *)&(_state.v[0])); + __m128i X1 = _mm_loadu_si128((const __m128i *)&(_state.v[1])); + __m128i X2 = _mm_loadu_si128((const __m128i *)&(_state.v[2])); + __m128i X3 = _mm_loadu_si128((const __m128i *)&(_state.v[3])); + __m128i T; + __m128i X0s = X0; + __m128i X1s = X1; + __m128i X2s = X2; + __m128i X3s = X3; + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + // 2X round ------------------------------------------------------------- + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(_mm_xor_si128(X3, _mm_slli_epi32(T, 7)), _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(_mm_xor_si128(X2, _mm_slli_epi32(T, 9)), _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(_mm_xor_si128(X1, _mm_slli_epi32(T, 13)), _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(_mm_xor_si128(X0, _mm_slli_epi32(T, 18)), _mm_srli_epi32(T, 14)); + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + + X0 = _mm_add_epi32(X0s,X0); + X1 = _mm_add_epi32(X1s,X1); + X2 = _mm_add_epi32(X2s,X2); + X3 = _mm_add_epi32(X3s,X3); + + __m128i k02 = _mm_shuffle_epi32(_mm_or_si128(_mm_slli_epi64(X0, 32), _mm_srli_epi64(X3, 32)), _MM_SHUFFLE(0, 1, 2, 3)); + __m128i k13 = _mm_shuffle_epi32(_mm_or_si128(_mm_slli_epi64(X1, 32), _mm_srli_epi64(X0, 32)), _MM_SHUFFLE(0, 1, 2, 3)); + __m128i k20 = _mm_or_si128(_mm_and_si128(X2, _S20SSECONSTANTS.maskLo32), _mm_and_si128(X1, _S20SSECONSTANTS.maskHi32)); + __m128i k31 = _mm_or_si128(_mm_and_si128(X3, _S20SSECONSTANTS.maskLo32), _mm_and_si128(X2, _S20SSECONSTANTS.maskHi32)); + _mm_storeu_ps(reinterpret_cast(c),_mm_castsi128_ps(_mm_xor_si128(_mm_unpackhi_epi64(k02,k20),_mm_castps_si128(_mm_loadu_ps(reinterpret_cast(m)))))); + _mm_storeu_ps(reinterpret_cast(c) + 4,_mm_castsi128_ps(_mm_xor_si128(_mm_unpackhi_epi64(k13,k31),_mm_castps_si128(_mm_loadu_ps(reinterpret_cast(m) + 4))))); + _mm_storeu_ps(reinterpret_cast(c) + 8,_mm_castsi128_ps(_mm_xor_si128(_mm_unpacklo_epi64(k20,k02),_mm_castps_si128(_mm_loadu_ps(reinterpret_cast(m) + 8))))); + _mm_storeu_ps(reinterpret_cast(c) + 12,_mm_castsi128_ps(_mm_xor_si128(_mm_unpacklo_epi64(k31,k13),_mm_castps_si128(_mm_loadu_ps(reinterpret_cast(m) + 12))))); + + if (!(++_state.i[8])) { + ++_state.i[5]; // state reordered for SSE + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } +#else + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + + // 2X round ------------------------------------------------------------- + x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7)); + x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9)); + x12 = XOR(x12,ROTATE(PLUS( x8, x4),13)); + x0 = XOR( x0,ROTATE(PLUS(x12, x8),18)); + x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7)); + x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9)); + x1 = XOR( x1,ROTATE(PLUS(x13, x9),13)); + x5 = XOR( x5,ROTATE(PLUS( x1,x13),18)); + x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7)); + x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9)); + x6 = XOR( x6,ROTATE(PLUS( x2,x14),13)); + x10 = XOR(x10,ROTATE(PLUS( x6, x2),18)); + x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7)); + x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9)); + x11 = XOR(x11,ROTATE(PLUS( x7, x3),13)); + x15 = XOR(x15,ROTATE(PLUS(x11, x7),18)); + x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7)); + x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9)); + x3 = XOR( x3,ROTATE(PLUS( x2, x1),13)); + x0 = XOR( x0,ROTATE(PLUS( x3, x2),18)); + x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7)); + x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9)); + x4 = XOR( x4,ROTATE(PLUS( x7, x6),13)); + x5 = XOR( x5,ROTATE(PLUS( x4, x7),18)); + x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7)); + x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9)); + x9 = XOR( x9,ROTATE(PLUS( x8,x11),13)); + x10 = XOR(x10,ROTATE(PLUS( x9, x8),18)); + x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7)); + x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9)); + x14 = XOR(x14,ROTATE(PLUS(x13,x12),13)); + x15 = XOR(x15,ROTATE(PLUS(x14,x13),18)); + x0 = PLUS(x0,j0); x1 = PLUS(x1,j1); x2 = PLUS(x2,j2); diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index 84baf3da..a2082bea 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -35,12 +35,11 @@ public: * @param key Key bits * @param kbits Number of key bits: 128 or 256 (recommended) * @param iv 64-bit initialization vector - * @param rounds Number of rounds: 8, 12, or 20 */ - Salsa20(const void *key,unsigned int kbits,const void *iv,unsigned int rounds) + Salsa20(const void *key,unsigned int kbits,const void *iv) throw() { - init(key,kbits,iv,rounds); + init(key,kbits,iv); } /** @@ -49,21 +48,43 @@ public: * @param key Key bits * @param kbits Number of key bits: 128 or 256 (recommended) * @param iv 64-bit initialization vector - * @param rounds Number of rounds: 8, 12, or 20 */ - void init(const void *key,unsigned int kbits,const void *iv,unsigned int rounds) + void init(const void *key,unsigned int kbits,const void *iv) throw(); /** - * Encrypt data + * Encrypt data using Salsa20/12 * * @param in Input data * @param out Output buffer * @param bytes Length of data */ - void encrypt(const void *in,void *out,unsigned int bytes) + void encrypt12(const void *in,void *out,unsigned int bytes) throw(); + /** + * Encrypt data using Salsa20/20 + * + * @param in Input data + * @param out Output buffer + * @param bytes Length of data + */ + void encrypt20(const void *in,void *out,unsigned int bytes) + throw(); + + /** + * Decrypt data + * + * @param in Input data + * @param out Output buffer + * @param bytes Length of data + */ + inline void decrypt12(const void *in,void *out,unsigned int bytes) + throw() + { + encrypt12(in,out,bytes); + } + /** * Decrypt data * @@ -71,10 +92,10 @@ public: * @param out Output buffer * @param bytes Length of data */ - inline void decrypt(const void *in,void *out,unsigned int bytes) + inline void decrypt20(const void *in,void *out,unsigned int bytes) throw() { - encrypt(in,out,bytes); + encrypt20(in,out,bytes); } private: @@ -84,7 +105,6 @@ private: #endif // ZT_SALSA20_SSE uint32_t i[16]; } _state; - unsigned int _roundsDiv4; }; } // namespace ZeroTier diff --git a/selftest.cpp b/selftest.cpp index 090839ee..4ba76c0b 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -162,27 +162,27 @@ static int testCrypto() memset(buf2,0,sizeof(buf2)); memset(buf3,0,sizeof(buf3)); Salsa20 s20; - s20.init("12345678123456781234567812345678",256,"12345678",20); - s20.encrypt(buf1,buf2,sizeof(buf1)); - s20.init("12345678123456781234567812345678",256,"12345678",20); - s20.decrypt(buf2,buf3,sizeof(buf2)); + s20.init("12345678123456781234567812345678",256,"12345678"); + s20.encrypt20(buf1,buf2,sizeof(buf1)); + s20.init("12345678123456781234567812345678",256,"12345678"); + s20.decrypt20(buf2,buf3,sizeof(buf2)); if (memcmp(buf1,buf3,sizeof(buf1))) { std::cout << "FAIL (encrypt/decrypt test)" << std::endl; return -1; } } - Salsa20 s20(s20TV0Key,256,s20TV0Iv,20); + Salsa20 s20(s20TV0Key,256,s20TV0Iv); memset(buf1,0,sizeof(buf1)); memset(buf2,0,sizeof(buf2)); - s20.encrypt(buf1,buf2,64); + s20.encrypt20(buf1,buf2,64); if (memcmp(buf2,s20TV0Ks,64)) { std::cout << "FAIL (test vector 0)" << std::endl; return -1; } - s20.init(s2012TV0Key,256,s2012TV0Iv,12); + s20.init(s2012TV0Key,256,s2012TV0Iv); memset(buf1,0,sizeof(buf1)); memset(buf2,0,sizeof(buf2)); - s20.encrypt(buf1,buf2,64); + s20.encrypt12(buf1,buf2,64); if (memcmp(buf2,s2012TV0Ks,64)) { std::cout << "FAIL (test vector 1)" << std::endl; return -1; @@ -195,34 +195,16 @@ static int testCrypto() std::cout << "[crypto] Salsa20 SSE: DISABLED" << std::endl; #endif - std::cout << "[crypto] Benchmarking Salsa20/8... "; std::cout.flush(); - { - unsigned char *bb = (unsigned char *)::malloc(1234567); - for(unsigned int i=0;i<1234567;++i) - bb[i] = (unsigned char)i; - Salsa20 s20(s20TV0Key,256,s20TV0Iv,8); - double bytes = 0.0; - uint64_t start = OSUtils::now(); - for(unsigned int i=0;i<200;++i) { - s20.encrypt(bb,bb,1234567); - bytes += 1234567.0; - } - uint64_t end = OSUtils::now(); - SHA512::hash(buf1,bb,1234567); - std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl; - ::free((void *)bb); - } - std::cout << "[crypto] Benchmarking Salsa20/12... "; std::cout.flush(); { unsigned char *bb = (unsigned char *)::malloc(1234567); for(unsigned int i=0;i<1234567;++i) bb[i] = (unsigned char)i; - Salsa20 s20(s20TV0Key,256,s20TV0Iv,12); + Salsa20 s20(s20TV0Key,256,s20TV0Iv); double bytes = 0.0; uint64_t start = OSUtils::now(); for(unsigned int i=0;i<200;++i) { - s20.encrypt(bb,bb,1234567); + s20.encrypt12(bb,bb,1234567); bytes += 1234567.0; } uint64_t end = OSUtils::now(); @@ -236,11 +218,11 @@ static int testCrypto() unsigned char *bb = (unsigned char *)::malloc(1234567); for(unsigned int i=0;i<1234567;++i) bb[i] = (unsigned char)i; - Salsa20 s20(s20TV0Key,256,s20TV0Iv,20); + Salsa20 s20(s20TV0Key,256,s20TV0Iv); double bytes = 0.0; uint64_t start = OSUtils::now(); for(unsigned int i=0;i<200;++i) { - s20.encrypt(bb,bb,1234567); + s20.encrypt20(bb,bb,1234567); bytes += 1234567.0; } uint64_t end = OSUtils::now(); -- cgit v1.2.3 From a95fa379cca0ddbce98d476b143c3606f3ae7bce Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 14:51:38 -0700 Subject: Circuit tests basically work but need some tweaks, and fix some issues found with valgrind. --- controller/SqliteNetworkController.cpp | 2 - controller/SqliteNetworkController.hpp | 2 - examples/api/circuit-test-pingpong.json | 13 ++++++ examples/docker/main.sh | 2 +- node/InetAddress.hpp | 72 ++++++++++++++++++++------------- node/Node.cpp | 2 +- 6 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 examples/api/circuit-test-pingpong.json (limited to 'node/Node.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 40fafd79..d87e5624 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -258,8 +258,6 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sCreateMember); sqlite3_finalize(_sGetNodeIdentity); sqlite3_finalize(_sCreateOrReplaceNode); - sqlite3_finalize(_sUpdateNode); - sqlite3_finalize(_sUpdateNode2); sqlite3_finalize(_sGetEtherTypesFromRuleTable); sqlite3_finalize(_sGetActiveBridges); sqlite3_finalize(_sGetIpAssignmentsForNode); diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 7a01487c..a3d5dfc7 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -155,8 +155,6 @@ private: sqlite3_stmt *_sCreateMember; sqlite3_stmt *_sGetNodeIdentity; sqlite3_stmt *_sCreateOrReplaceNode; - sqlite3_stmt *_sUpdateNode; - sqlite3_stmt *_sUpdateNode2; sqlite3_stmt *_sGetEtherTypesFromRuleTable; sqlite3_stmt *_sGetActiveBridges; sqlite3_stmt *_sGetIpAssignmentsForNode; diff --git a/examples/api/circuit-test-pingpong.json b/examples/api/circuit-test-pingpong.json new file mode 100644 index 00000000..8fcc5d94 --- /dev/null +++ b/examples/api/circuit-test-pingpong.json @@ -0,0 +1,13 @@ +{ + "hops": [ + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ] + ], + "reportAtEveryHop": true +} diff --git a/examples/docker/main.sh b/examples/docker/main.sh index e9febb13..53fb6540 100644 --- a/examples/docker/main.sh +++ b/examples/docker/main.sh @@ -20,6 +20,6 @@ else fi rm -f /var/lib/zerotier-one/identity.* -echo "$ZEROTIER_IDENTITY_SECRET" >identity.secret +echo "$ZEROTIER_IDENTITY_SECRET" >/var/lib/zerotier-one/identity.secret /var/lib/zerotier-one/zerotier-one diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 6970e92d..50db272a 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -100,74 +100,88 @@ struct InetAddress : public sockaddr_storage inline InetAddress &operator=(const InetAddress &a) throw() { - memcpy(this,&a,sizeof(InetAddress)); + if (&a != this) + memcpy(this,&a,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const InetAddress *a) throw() { - memcpy(this,a,sizeof(InetAddress)); + if (a != this) + memcpy(this,a,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_storage &ss) throw() { - memcpy(this,&ss,sizeof(InetAddress)); + if (reinterpret_cast(&ss) != this) + memcpy(this,&ss,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_storage *ss) throw() { - memcpy(this,ss,sizeof(InetAddress)); + if (reinterpret_cast(ss) != this) + memcpy(this,ss,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_in &sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in)); + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,&sa,sizeof(struct sockaddr_in)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in *sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in)); + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,sa,sizeof(struct sockaddr_in)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in6 &sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in6)); + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,&sa,sizeof(struct sockaddr_in6)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in6 *sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in6)); + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,sa,sizeof(struct sockaddr_in6)); + } return *this; } inline InetAddress &operator=(const struct sockaddr &sa) throw() { - memset(this,0,sizeof(InetAddress)); - switch(sa.sa_family) { - case AF_INET: - memcpy(this,&sa,sizeof(struct sockaddr_in)); - break; - case AF_INET6: - memcpy(this,&sa,sizeof(struct sockaddr_in6)); - break; + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + switch(sa.sa_family) { + case AF_INET: + memcpy(this,&sa,sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(this,&sa,sizeof(struct sockaddr_in6)); + break; + } } return *this; } @@ -175,14 +189,16 @@ struct InetAddress : public sockaddr_storage inline InetAddress &operator=(const struct sockaddr *sa) throw() { - memset(this,0,sizeof(InetAddress)); - switch(sa->sa_family) { - case AF_INET: - memcpy(this,sa,sizeof(struct sockaddr_in)); - break; - case AF_INET6: - memcpy(this,sa,sizeof(struct sockaddr_in6)); - break; + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + switch(sa->sa_family) { + case AF_INET: + memcpy(this,sa,sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(this,sa,sizeof(struct sockaddr_in6)); + break; + } } return *this; } diff --git a/node/Node.cpp b/node/Node.cpp index 84452146..1eb21914 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -491,7 +491,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback) for(unsigned int a=0;ahops[0].breadth;++a) { outp.newInitializationVector(); outp.setDestination(Address(test->hops[0].addresses[a])); - RR->sw->send(outp,true,test->credentialNetworkId); + RR->sw->send(outp,true,0); } } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet -- cgit v1.2.3 From cae58f43f1b18017b90499811772d107ea2f65b9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 08:49:36 -0700 Subject: More World stuff, and mkworld. --- include/ZeroTierOne.h | 4 +- make-mac.mk | 6 +- mkworld.cpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.cpp | 1 - node/Node.cpp | 24 +------- node/Node.hpp | 3 +- node/Packet.hpp | 6 +- node/Topology.cpp | 78 +++++++++++++++--------- node/Topology.hpp | 22 ++++++- node/World.hpp | 60 ++++++++++++------- one.cpp | 16 +---- service/OneService.cpp | 9 +-- service/OneService.hpp | 4 +- 13 files changed, 281 insertions(+), 105 deletions(-) create mode 100644 mkworld.cpp (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 80091f62..f69ab54c 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1023,7 +1023,6 @@ typedef int (*ZT_WirePacketSendFunction)( * @param dataStorePutFunction Function called to put objects in persistent storage * @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change * @param eventCallback Function to receive status updates and non-fatal error notices - * @param overrideRootTopology Alternative root server topology or NULL for default (mostly for test/debug use) * @return OK (0) or error code if a fatal error condition has occurred */ enum ZT_ResultCode ZT_Node_new( @@ -1035,8 +1034,7 @@ enum ZT_ResultCode ZT_Node_new( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_EventCallback eventCallback, - const char *overrideRootTopology); + ZT_EventCallback eventCallback); /** * Delete a node and free all resources it consumes diff --git a/make-mac.mk b/make-mac.mk index 6daa6aa0..9fb613d8 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -79,6 +79,10 @@ selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) $(STRIP) zerotier-selftest +mkworld: $(OBJS) + rm -f mkworld + $(CXX) $(CXXFLAGS) -o mkworld mkworld.cpp $(OBJS) $(LIBS) + # Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html mac-dist-pkg: FORCE packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj" @@ -93,7 +97,7 @@ official: FORCE make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg clean: - rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* + rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* mkworld # For those building from source -- installs signed binary tap driver in system ZT home install-mac-tap: FORCE diff --git a/mkworld.cpp b/mkworld.cpp new file mode 100644 index 00000000..2b41d735 --- /dev/null +++ b/mkworld.cpp @@ -0,0 +1,153 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +/* + * This utility makes the World from the configuration specified below. + * It probably won't be much use to anyone outside ZeroTier, Inc. except + * for testing and experimentation purposes. + * + * If you want to make your own World you must edit this file. + * + * When run, it expects two files in the current directory: + * + * previous.c25519 - key pair to sign this world (key from previous world) + * current.c25519 - key pair whose public key should be embedded in this world + * + * If these files do not exist, they are both created with the same key pair + * and a self-signed initial World is born. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "node/Constants.hpp" +#include "node/World.hpp" +#include "node/C25519.hpp" +#include "node/Identity.hpp" +#include "node/InetAddress.hpp" +#include "osdep/OSUtils.hpp" + +using namespace ZeroTier; + +class WorldMaker : public World +{ +public: + static inline World make(uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector &roots,const C25519::Pair &signWith) + { + WorldMaker w; + w._id = id; + w._ts = ts; + w._updateSigningKey = sk; + w._roots = roots; + + Buffer tmp; + w.serialize(tmp,true); + w._signature = C25519::sign(signWith,tmp.data(),tmp.size()); + + return w; + } +}; + +int main(int argc,char **argv) +{ + std::string previous,current; + if ((!OSUtils::readFile("previous.c25519",previous))||(!OSUtils::readFile("current.c25519",current))) { + C25519::Pair np(C25519::generate()); + previous = std::string(); + previous.append((const char *)np.pub.data,ZT_C25519_PUBLIC_KEY_LEN); + previous.append((const char *)np.priv.data,ZT_C25519_PRIVATE_KEY_LEN); + current = previous; + OSUtils::writeFile("previous.c25519",previous); + OSUtils::writeFile("current.c25519",current); + fprintf(stderr,"INFO: created initial world keys: previous.c25519, current.c25519"ZT_EOL_S); + } + + if ((previous.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))||(current.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))) { + fprintf(stderr,"FATAL: previous.c25519 or current.c25519 empty or invalid"ZT_EOL_S); + return 1; + } + C25519::Pair previousKP; + memcpy(previousKP.pub.data,previous.data(),ZT_C25519_PUBLIC_KEY_LEN); + memcpy(previousKP.priv.data,previous.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); + C25519::Pair currentKP; + memcpy(currentKP.pub.data,current.data(),ZT_C25519_PUBLIC_KEY_LEN); + memcpy(currentKP.priv.data,current.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); + + // EDIT BELOW HERE --------------------------------------------------------- + + std::vector roots; + + // old US-SFO + roots.push_back(World::Root()); + roots.back().identity = Identity("7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa"); + roots.back().stableEndpoints.push_back(InetAddress("198.199.97.220/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old EU-PARIS + roots.push_back(World::Root()); + roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c"); + roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old US-NYC + roots.push_back(World::Root()); + roots.back().identity = Identity("8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5"); + roots.back().stableEndpoints.push_back(InetAddress("162.243.77.111/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old AP-SNG + roots.push_back(World::Root()); + roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e"); + roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + std::sort(roots.begin(),roots.end()); + + const uint64_t id = ZT_WORLD_ID_EARTH; + const uint64_t ts = OSUtils::now(); + + // END WORLD SETUP --------------------------------------------------------- + + fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu"ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts); + + World nw = WorldMaker::make(id,ts,currentKP.pub,roots,previousKP); + + Buffer outtmp; + nw.serialize(outtmp,false); + fwrite(outtmp.data(),outtmp.size(),1,stdout); + fflush(stdout); + + fprintf(stderr,"INFO: wrote %u bytes to stdout"ZT_EOL_S,outtmp.size()); + + return 0; +} diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 9fcc2e49..39abe720 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -33,7 +33,6 @@ #include "../include/ZeroTierOne.h" #include "Constants.hpp" -#include "Defaults.hpp" #include "RuntimeEnvironment.hpp" #include "IncomingPacket.hpp" #include "Topology.hpp" diff --git a/node/Node.cpp b/node/Node.cpp index 1eb21914..7496b045 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -46,7 +46,6 @@ #include "Address.hpp" #include "Identity.hpp" #include "SelfAwareness.hpp" -#include "Defaults.hpp" const struct sockaddr_storage ZT_SOCKADDR_NULL = {0}; @@ -64,8 +63,7 @@ Node::Node( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_EventCallback eventCallback, - const char *overrideRootTopology) : + ZT_EventCallback eventCallback) : _RR(this), RR(&_RR), _uPtr(uptr), @@ -125,21 +123,6 @@ Node::Node( throw; } - Dictionary rt; - if (overrideRootTopology) { - rt.fromString(std::string(overrideRootTopology)); - } else { - std::string rttmp(dataStoreGet("root-topology")); - if (rttmp.length() > 0) { - rt.fromString(rttmp); - if (!Topology::authenticateRootTopology(rt)) - rt.clear(); - } - if ((!rt.size())||(!rt.contains("rootservers"))) - rt.fromString(ZT_DEFAULTS.defaultRootTopology); - } - RR->topology->setRootServers(Dictionary(rt.get("rootservers",""))); - postEvent(ZT_EVENT_UP); } @@ -609,12 +592,11 @@ enum ZT_ResultCode ZT_Node_new( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_EventCallback eventCallback, - const char *overrideRootTopology) + ZT_EventCallback eventCallback) { *node = (ZT_Node *)0; try { - *node = reinterpret_cast(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback,overrideRootTopology)); + *node = reinterpret_cast(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback)); return ZT_RESULT_OK; } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; diff --git a/node/Node.hpp b/node/Node.hpp index 0ae176c0..c7038ed4 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -71,8 +71,7 @@ public: ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_EventCallback eventCallback, - const char *overrideRootTopology); + ZT_EventCallback eventCallback); ~Node(); diff --git a/node/Packet.hpp b/node/Packet.hpp index 958d0f3e..939d84a5 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -540,6 +540,8 @@ public: * <[...] binary serialized identity (see Identity)> * <[1] destination address type> * [<[...] destination address>] + * <[8] 64-bit world ID of current world> + * <[8] 64-bit timestamp of current world> * * This is the only message that ever must be sent in the clear, since it * is used to push an identity to a new peer. @@ -564,8 +566,8 @@ public: * <[2] software revision (of responder)> * <[1] destination address type (for this OK, not copied from HELLO)> * [<[...] destination address>] - * <[8] 64-bit world ID of current world> - * <[8] 64-bit timestamp of current world> + * <[8] 64-bit world ID of current world (of responder)> + * <[8] 64-bit timestamp of current world (of responder)> * * ERROR has no payload. */ diff --git a/node/Topology.cpp b/node/Topology.cpp index 5aedae86..0cf4cfe8 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -42,23 +42,6 @@ Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), _amRoot(false) { - try { - std::string dsWorld(RR->node->dataStoreGet("world")); - Buffer dswtmp(dsWorld.data(),dsWorld.length()); - _world.deserialize(dswtmp,0); - } catch ( ... ) { - _world = World(); // set to null if cached world is invalid - } - { - World defaultWorld; - Buffer wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH); - defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top - if (_world.verifyUpdate(defaultWorld)) { - _world = defaultWorld; - RR->node->dataStorePut("world",ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH,false); - } - } - std::string alls(RR->node->dataStoreGet("peers.save")); const uint8_t *all = reinterpret_cast(alls.data()); RR->node->dataStoreDelete("peers.save"); @@ -97,19 +80,24 @@ Topology::Topology(const RuntimeEnvironment *renv) : clean(RR->node->now()); - for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { - if (r->identity == RR->identity) - _amRoot = true; - _rootAddresses.push_back(r->identity.address()); - SharedPtr *rp = _peers.get(r->identity.address()); - if (rp) { - _rootPeers.push_back(*rp); - } else if (r->identity.address() != RR->identity.address()) { - SharedPtr newrp(new Peer(RR->identity,r->identity)); - _peers.set(r->identity.address(),newrp); - _rootPeers.push_back(newrp); - } + std::string dsWorld(RR->node->dataStoreGet("world")); + World cachedWorld; + try { + Buffer dswtmp(dsWorld.data(),dsWorld.length()); + cachedWorld.deserialize(dswtmp,0); + } catch ( ... ) { + cachedWorld = World(); // clear if cached world is invalid } + World defaultWorld; + { + Buffer wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH); + defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top + } + if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) { + _setWorld(defaultWorld); + if (dsWorld.length() > 0) + RR->node->dataStoreDelete("world"); + } else _setWorld(cachedWorld); } Topology::~Topology() @@ -283,6 +271,16 @@ keep_searching_for_roots: return bestRoot; } +bool Topology::worldUpdateIfValid(const World &newWorld) +{ + Mutex::Lock _l(_lock); + if (_world.shouldBeReplacedBy(newWorld,true)) { + _setWorld(newWorld); + return true; + } + return false; +} + void Topology::clean(uint64_t now) { Mutex::Lock _l(_lock); @@ -320,4 +318,26 @@ void Topology::_saveIdentity(const Identity &id) } } +void Topology::_setWorld(const World &newWorld) +{ + // assumed _lock is locked (or in constructor) + _world = newWorld; + _amRoot = false; + _rootAddresses.clear(); + _rootPeers.clear(); + for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { + if (r->identity == RR->identity) + _amRoot = true; + _rootAddresses.push_back(r->identity.address()); + SharedPtr *rp = _peers.get(r->identity.address()); + if (rp) { + _rootPeers.push_back(*rp); + } else if (r->identity.address() != RR->identity.address()) { + SharedPtr newrp(new Peer(RR->identity,r->identity)); + _peers.set(r->identity.address(),newrp); + _rootPeers.push_back(newrp); + } + } +} + } // namespace ZeroTier diff --git a/node/Topology.hpp b/node/Topology.hpp index ed8f3d86..3abc27e4 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -31,10 +31,10 @@ #include #include -#include #include #include #include +#include #include "Constants.hpp" @@ -146,6 +146,23 @@ public: return _world; } + /** + * @return Pair containing world ID and world timestamp (faster than world().id() etc.) + */ + inline std::pair worldIdentification() const + { + Mutex::Lock _l(_lock); + return std::pair(_world.id(),_world.timestamp()); + } + + /** + * Validate new world and update if newer and signature is okay + * + * @param newWorld Potential new world definition revision + * @return True if an update actually occurred + */ + bool worldUpdateIfValid(const World &newWorld); + /** * Clean and flush database */ @@ -176,7 +193,7 @@ public: } /** - * @return All currently active peers by address + * @return All currently active peers by address (unsorted) */ inline std::vector< std::pair< Address,SharedPtr > > allPeers() const { @@ -187,6 +204,7 @@ public: private: Identity _getIdentity(const Address &zta); void _saveIdentity(const Identity &id); + void _setWorld(const World &newWorld); const RuntimeEnvironment *RR; diff --git a/node/World.hpp b/node/World.hpp index 0d26021f..7ccd2c53 100644 --- a/node/World.hpp +++ b/node/World.hpp @@ -55,7 +55,7 @@ /** * The (more than) maximum length of a serialized World */ -#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 64) +#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128) /** * World ID indicating null / empty World object @@ -68,15 +68,11 @@ #define ZT_WORLD_ID_TESTNET 1 /** - * World ID for Earth -- its approximate distance from the sun in kilometers + * World ID for Earth * * This is the ID for the ZeroTier World used on planet Earth. It is unrelated - * to the public network 8056c2e21c000001 of the same name. - * - * It's advisable to create a new World for network regions spaced more than - * 2-3 light seconds, since RTT times in excess of 5s are problematic for some - * protocols. Earth could therefore include its low and high orbits, the Moon, - * and nearby Lagrange points. + * to the public network 8056c2e21c000001 of the same name. It was chosen + * from Earth's approximate distance from the sun in kilometers. */ #define ZT_WORLD_ID_EARTH 149604618 @@ -90,9 +86,24 @@ namespace ZeroTier { /** * A world definition (formerly known as a root topology) * - * A world consists of a set of root servers and a signature scheme enabling - * it to be updated going forward. It defines a single ZeroTier VL1 network - * area within which any device can reach any other. + * Think of a World as a single data center. Within this data center a set + * of distributed fault tolerant root servers provide stable anchor points + * for a peer to peer network that provides VLAN service. Updates to a world + * definition can be published by signing them with the previous revision's + * signing key, and should be very infrequent. + * + * The maximum data center size is approximately 2.5 cubic light seconds, + * since many protocols have issues with >5s RTT latencies. + * + * ZeroTier operates a World for Earth capable of encompassing the planet, its + * orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A + * world ID for Mars and nearby space is defined but not yet used, and a test + * world ID is provided for testing purposes. + * + * If you absolutely must run your own "unofficial" ZeroTier network, please + * define your world IDs above 0xffffffff (4294967295). Code to make a World + * is in mkworld.cpp in the parent directory and must be edited to change + * settings. */ class World { @@ -130,24 +141,28 @@ public: inline uint64_t timestamp() const throw() { return _ts; } /** - * Verify a world update + * Check whether a world update should replace this one * * A new world update is valid if it is for the same world ID, is newer, * and is signed by the current world's signing key. If this world object * is null, it can always be updated. * * @param update Candidate update + * @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip) * @return True if update is newer than current and is properly signed */ - inline bool verifyUpdate(const World &update) + inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck) { if (_id == ZT_WORLD_ID_NULL) return true; - if ((update._id != _id)||(update._ts <= _ts)) - return false; - Buffer tmp; - update.serialize(tmp); - return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature); + if ((_id == update._id)&&(_ts < update._ts)) { + if (fullSignatureCheck) { + Buffer tmp; + update.serialize(tmp,true); + return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature); + } else return true; + } + return false; } /** @@ -156,13 +171,16 @@ public: inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); } template - inline void serialize(Buffer &b) const + inline void serialize(Buffer &b,bool forSign = false) const { + if (forSign) + b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); b.append((uint8_t)0x01); // version -- only one valid value for now b.append((uint64_t)_id); b.append((uint64_t)_ts); b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN); - b.append(_signature.data,ZT_C25519_SIGNATURE_LEN); + if (!forSign) + b.append(_signature.data,ZT_C25519_SIGNATURE_LEN); b.append((uint8_t)_roots.size()); for(std::vector::const_iterator r(_roots.begin());r!=_roots.end();++r) { r->identity.serialize(b); @@ -170,6 +188,8 @@ public: for(std::vector::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep) ep->serialize(b); } + if (forSign) + b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL); } template diff --git a/one.cpp b/one.cpp index a4d5190c..c8661fda 100644 --- a/one.cpp +++ b/one.cpp @@ -911,7 +911,6 @@ static void printHelp(const char *cn,FILE *out) fprintf(out," -v - Show version"ZT_EOL_S); fprintf(out," -U - Run as unprivileged user (skip privilege check)"ZT_EOL_S); fprintf(out," -p - Port for UDP and TCP/HTTP (default: 9993, 0 for random)"ZT_EOL_S); - //fprintf(out," -T - Override root topology, do not authenticate or update"ZT_EOL_S); #ifdef __UNIX_LIKE__ fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S); @@ -974,7 +973,6 @@ int main(int argc,char **argv) if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI"))) return cli(argc,argv); - std::string overrideRootTopology; std::string homeDir; unsigned int port = ZT_DEFAULT_PORT; bool skipRootCheck = false; @@ -1001,18 +999,6 @@ int main(int argc,char **argv) skipRootCheck = true; break; - case 'T': // Override root topology - if (argv[i][2]) { - if (!OSUtils::readFile(argv[i] + 2,overrideRootTopology)) { - fprintf(stderr,"%s: cannot read root topology from %s"ZT_EOL_S,argv[0],argv[i] + 2); - return 1; - } - } else { - printHelp(argv[0],stdout); - return 1; - } - break; - case 'v': // Display version printf("%d.%d.%d"ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); return 0; @@ -1169,7 +1155,7 @@ int main(int argc,char **argv) try { for(;;) { - zt1Service = OneService::newInstance(homeDir.c_str(),port,(overrideRootTopology.length() > 0) ? overrideRootTopology.c_str() : (const char *)0); + zt1Service = OneService::newInstance(homeDir.c_str(),port); switch(zt1Service->run()) { case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done case OneService::ONE_NORMAL_TERMINATION: diff --git a/service/OneService.cpp b/service/OneService.cpp index 071a2cbc..6b28c41e 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -418,14 +418,13 @@ struct TcpConnection class OneServiceImpl : public OneService { public: - OneServiceImpl(const char *hp,unsigned int port,const char *overrideRootTopology) : + OneServiceImpl(const char *hp,unsigned int port) : _homePath((hp) ? hp : "."), _tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY), #ifdef ZT_ENABLE_NETWORK_CONTROLLER _controller((SqliteNetworkController *)0), #endif _phy(this,false,true), - _overrideRootTopology((overrideRootTopology) ? overrideRootTopology : ""), _node((Node *)0), _controlPlane((ControlPlane *)0), _lastDirectReceiveFromGlobal(0), @@ -550,8 +549,7 @@ public: SnodeWirePacketSendFunction, SnodeVirtualNetworkFrameFunction, SnodeVirtualNetworkConfigFunction, - SnodeEventCallback, - ((_overrideRootTopology.length() > 0) ? _overrideRootTopology.c_str() : (const char *)0)); + SnodeEventCallback); #ifdef ZT_ENABLE_NETWORK_CONTROLLER _controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str()); @@ -1329,7 +1327,6 @@ private: SqliteNetworkController *_controller; #endif Phy _phy; - std::string _overrideRootTopology; Node *_node; InetAddress _v4LocalAddress,_v6LocalAddress; PhySocket *_v4UdpSocket; @@ -1526,7 +1523,7 @@ std::string OneService::autoUpdateUrl() return std::string(); } -OneService *OneService::newInstance(const char *hp,unsigned int port,const char *overrideRootTopology) { return new OneServiceImpl(hp,port,overrideRootTopology); } +OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } OneService::~OneService() {} } // namespace ZeroTier diff --git a/service/OneService.hpp b/service/OneService.hpp index 70d024bc..2f76ebaa 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -95,12 +95,10 @@ public: * * @param hp Home path * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port) - * @param overrideRootTopology String-serialized root topology (for testing, default: NULL) */ static OneService *newInstance( const char *hp, - unsigned int port, - const char *overrideRootTopology = (const char *)0); + unsigned int port); virtual ~OneService(); -- cgit v1.2.3 From 5d2f523e81a56a33405d2b98ccef9d384e269f34 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:10:44 -0700 Subject: World stuff... --- .gitignore | 3 +- include/ZeroTierOne.h | 5 -- make-mac.mk | 4 -- mkworld.cpp | 170 ----------------------------------------------- node/IncomingPacket.cpp | 34 ++++++++-- node/Node.cpp | 39 ++++++----- node/Packet.hpp | 35 +--------- node/Peer.cpp | 71 +++----------------- node/Peer.hpp | 25 ++----- node/RemotePath.hpp | 26 ++------ node/Topology.cpp | 11 ++- node/Topology.hpp | 15 +++-- service/ControlPlane.cpp | 2 - world/2015-10-13.bin | Bin 0 -> 494 bytes world/2015-10-13.out | 6 ++ world/mkworld.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 271 insertions(+), 345 deletions(-) delete mode 100644 mkworld.cpp create mode 100644 world/2015-10-13.bin create mode 100644 world/2015-10-13.out create mode 100644 world/mkworld.cpp (limited to 'node/Node.cpp') diff --git a/.gitignore b/.gitignore index 1cb16da5..2dbec1e5 100755 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ Thumbs.db /ZeroTierOneInstaller-* /examples/docker/zerotier-one /examples/docker/test-*.env -/mkworld +/world/mkworld +/world/*.c25519 # Miscellaneous file types that we don't want to check in *.log diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index f69ab54c..38db3222 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -582,11 +582,6 @@ typedef struct */ uint64_t lastReceive; - /** - * Is path fixed? (i.e. not learned, static) - */ - int fixed; - /** * Is path active? */ diff --git a/make-mac.mk b/make-mac.mk index 9fb613d8..174216fb 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -79,10 +79,6 @@ selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) $(STRIP) zerotier-selftest -mkworld: $(OBJS) - rm -f mkworld - $(CXX) $(CXXFLAGS) -o mkworld mkworld.cpp $(OBJS) $(LIBS) - # Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html mac-dist-pkg: FORCE packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj" diff --git a/mkworld.cpp b/mkworld.cpp deleted file mode 100644 index baf693fc..00000000 --- a/mkworld.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -/* - * This utility makes the World from the configuration specified below. - * It probably won't be much use to anyone outside ZeroTier, Inc. except - * for testing and experimentation purposes. - * - * If you want to make your own World you must edit this file. - * - * When run, it expects two files in the current directory: - * - * previous.c25519 - key pair to sign this world (key from previous world) - * current.c25519 - key pair whose public key should be embedded in this world - * - * If these files do not exist, they are both created with the same key pair - * and a self-signed initial World is born. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "node/Constants.hpp" -#include "node/World.hpp" -#include "node/C25519.hpp" -#include "node/Identity.hpp" -#include "node/InetAddress.hpp" -#include "osdep/OSUtils.hpp" - -using namespace ZeroTier; - -class WorldMaker : public World -{ -public: - static inline World make(uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector &roots,const C25519::Pair &signWith) - { - WorldMaker w; - w._id = id; - w._ts = ts; - w._updateSigningKey = sk; - w._roots = roots; - - Buffer tmp; - w.serialize(tmp,true); - w._signature = C25519::sign(signWith,tmp.data(),tmp.size()); - - return w; - } -}; - -int main(int argc,char **argv) -{ - std::string previous,current; - if ((!OSUtils::readFile("previous.c25519",previous))||(!OSUtils::readFile("current.c25519",current))) { - C25519::Pair np(C25519::generate()); - previous = std::string(); - previous.append((const char *)np.pub.data,ZT_C25519_PUBLIC_KEY_LEN); - previous.append((const char *)np.priv.data,ZT_C25519_PRIVATE_KEY_LEN); - current = previous; - OSUtils::writeFile("previous.c25519",previous); - OSUtils::writeFile("current.c25519",current); - fprintf(stderr,"INFO: created initial world keys: previous.c25519, current.c25519"ZT_EOL_S); - } - - if ((previous.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))||(current.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))) { - fprintf(stderr,"FATAL: previous.c25519 or current.c25519 empty or invalid"ZT_EOL_S); - return 1; - } - C25519::Pair previousKP; - memcpy(previousKP.pub.data,previous.data(),ZT_C25519_PUBLIC_KEY_LEN); - memcpy(previousKP.priv.data,previous.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); - C25519::Pair currentKP; - memcpy(currentKP.pub.data,current.data(),ZT_C25519_PUBLIC_KEY_LEN); - memcpy(currentKP.priv.data,current.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); - - // EDIT BELOW HERE --------------------------------------------------------- - - std::vector roots; - - // old US-SFO - roots.push_back(World::Root()); - roots.back().identity = Identity("7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa"); - roots.back().stableEndpoints.push_back(InetAddress("198.199.97.220/9993")); - std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); - - // old EU-PARIS - roots.push_back(World::Root()); - roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c"); - roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); - std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); - - // old US-NYC - roots.push_back(World::Root()); - roots.back().identity = Identity("8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5"); - roots.back().stableEndpoints.push_back(InetAddress("162.243.77.111/9993")); - std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); - - // old AP-SNG - roots.push_back(World::Root()); - roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e"); - roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); - std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); - - std::sort(roots.begin(),roots.end()); - - const uint64_t id = ZT_WORLD_ID_EARTH; - const uint64_t ts = OSUtils::now(); - - // END WORLD SETUP --------------------------------------------------------- - - fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu"ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts); - - World nw = WorldMaker::make(id,ts,currentKP.pub,roots,previousKP); - - Buffer outtmp; - nw.serialize(outtmp,false); - World testw; - testw.deserialize(outtmp,0); - if (testw != nw) { - fprintf(stderr,"FATAL: serialization test failed!"ZT_EOL_S); - return 1; - } - fwrite(outtmp.data(),outtmp.size(),1,stdout); - fflush(stdout); - - fprintf(stderr,"INFO: wrote %u bytes to stdout"ZT_EOL_S,outtmp.size()); - - fprintf(stderr,ZT_EOL_S); - fprintf(stderr,"#define ZT_DEFAULT_WORLD_LENGTH %u"ZT_EOL_S,outtmp.size()); - fprintf(stderr,"static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {"); - for(unsigned int i=0;i 0) - fprintf(stderr,","); - fprintf(stderr,"0x%.2x",(unsigned int)d[i]); - } - fprintf(stderr,"};"ZT_EOL_S); - - return 0; -} diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 39abe720..3c6268ed 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -42,6 +42,7 @@ #include "SelfAwareness.hpp" #include "Salsa20.hpp" #include "SHA512.hpp" +#include "World.hpp" namespace ZeroTier { @@ -199,10 +200,18 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) const uint64_t timestamp = at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); Identity id; - const unsigned int destAddrPtr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); InetAddress destAddr; - if (destAddrPtr < size()) // ZeroTier One < 1.0.3 did not include this field - destAddr.deserialize(*this,destAddrPtr); + uint64_t worldId = ZT_WORLD_ID_NULL; + uint64_t worldTimestamp = 0; + { + unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); + if (ptr < size()) // ZeroTier One < 1.0.3 did not include physical destination address info + ptr += destAddr.deserialize(*this,ptr); + if ((ptr + 16) <= size()) { // older versions also did not include World IDs or timestamps + worldId = at(ptr); ptr += 8; + worldTimestamp = at(ptr); + } + } if (protoVersion < ZT_PROTO_VERSION_MIN) { TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); @@ -286,8 +295,23 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); _remoteAddress.serialize(outp); - outp.armor(peer->key(),true); - RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + + if ((worldId != ZT_WORLD_ID_NULL)&&(worldId == RR->topology->worldId())) { + if (RR->topology->worldTimestamp() > worldTimestamp) { + World w(RR->topology->world()); + const unsigned int sizeAt = outp.size(); + outp.addSize(2); // make room for 16-bit size field + w.serialize(outp,false); + outp.setAt(sizeAt,(uint16_t)(outp.size() - sizeAt)); + } else { + outp.append((uint16_t)0); // no world update needed + } + + outp.armor(peer->key(),true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + } else { + TRACE("dropped HELLO from %s(%s): world ID mismatch: peer is %llu and we are %llu",source().toString().c_str(),_remoteAddress.toString().c_str(),worldId,RR->topology->worldId()); + } } catch ( ... ) { TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } diff --git a/node/Node.cpp b/node/Node.cpp index 7496b045..5468f102 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -177,37 +177,47 @@ public: RR(renv), _now(now), _relays(relays), - _rootAddresses(RR->topology->rootAddresses()) + _world(RR->topology->world()) { } - uint64_t lastReceiveFromUpstream; + uint64_t lastReceiveFromUpstream; // tracks last time we got a packet from an 'upstream' peer like a root or a relay inline void operator()(Topology &t,const SharedPtr &p) { - bool isRelay = false; - for(std::vector< std::pair >::const_iterator r(_relays.begin());r!=_relays.end();++r) { - if (r->first == p->address()) { - isRelay = true; + bool upstream = false; + InetAddress stableEndpoint; + for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { + if (r->identity.address() == p->address()) { + if (r->stableEndpoints.size() > 0) + stableEndpoint = r->stableEndpoints[(unsigned long)RR->node->prng() % r->stableEndpoints.size()]; + upstream = true; break; } } - if ((isRelay)||(std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end())) { - p->doPingAndKeepalive(RR,_now); - if (p->lastReceive() > lastReceiveFromUpstream) - lastReceiveFromUpstream = p->lastReceive(); - } else { - if (p->alive(_now)) - p->doPingAndKeepalive(RR,_now); + if (!upstream) { + for(std::vector< std::pair >::const_iterator r(_relays.begin());r!=_relays.end();++r) { + if (r->first == p->address()) { + stableEndpoint = r->second; + upstream = true; + break; + } + } } + + if ((!p->doPingAndKeepalive(RR,_now))&&(stableEndpoint)) + p->attemptToContactAt(RR,InetAddress(),stableEndpoint,_now); + + if (upstream) + lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); } private: const RuntimeEnvironment *RR; uint64_t _now; const std::vector< std::pair > &_relays; - std::vector
_rootAddresses; + World _world; }; ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline) @@ -376,7 +386,6 @@ ZT_PeerList *Node::peers() const memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage)); p->paths[p->pathCount].lastSend = path->lastSend(); p->paths[p->pathCount].lastReceive = path->lastReceived(); - p->paths[p->pathCount].fixed = path->fixed() ? 1 : 0; p->paths[p->pathCount].active = path->active(_now) ? 1 : 0; p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0; ++p->pathCount; diff --git a/node/Packet.hpp b/node/Packet.hpp index 939d84a5..810f5d67 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -566,8 +566,8 @@ public: * <[2] software revision (of responder)> * <[1] destination address type (for this OK, not copied from HELLO)> * [<[...] destination address>] - * <[8] 64-bit world ID of current world (of responder)> - * <[8] 64-bit timestamp of current world (of responder)> + * <[2] 16-bit length of world update or 0 if none> + * [[...] world update] * * ERROR has no payload. */ @@ -1098,36 +1098,7 @@ public: * * ERROR has no payload. */ - VERB_REQUEST_PROOF_OF_WORK = 19, - - /** - * Generic binary object access: - * <[8] 64-bit request ID> - * <[4] 32-bit index in blob to retrieve> - * <[2] 16-bit max length of block to retrieve> - * <[2] 16-bit length of blob identifier> - * <[...] blob identifier> - * - * This is used as a generic remote object retrieval mechanism. It returns - * OK if the object is accessible, INVALID_REQUEST if the index is beyond - * the size of the blob or another element is invalid, and OBJ_NOT_FOUND - * if no blob with the given identifier is available. - * - * Blob identifiers follow a de facto path-like schema, with the following - * names reserved: - * world - Current world definition (see World.hpp) - * updates.d/ - Software updates (not used yet, but reserved) - * - * OK payload: - * <[8] 64-bit request ID> - * <[4] 32-bit total length of blob> - * <[4] 32-bit index of this data in blob> - * <[...] data> - * - * ERROR payload: - * <[8] 64-bit request ID> - */ - VERB_GET_OBJECT = 20 + VERB_REQUEST_PROOF_OF_WORK = 19 }; /** diff --git a/node/Peer.cpp b/node/Peer.cpp index 111c849e..697ba75d 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -108,17 +108,16 @@ void Peer::received( // Add new path slot = &(_paths[np++]); } else { - // Replace oldest non-fixed path uint64_t slotLRmin = 0xffffffffffffffffULL; for(unsigned int p=0;preceived(now); _numPaths = np; pathIsConfirmed = true; @@ -172,12 +171,15 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo outp.append(now); RR->identity.serialize(outp,false); atAddress.serialize(outp); + outp.append((uint64_t)RR->topology->worldId()); + outp.append((uint64_t)RR->topology->worldTimestamp()); + outp.armor(_key,false); // HELLO is sent in the clear RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size()); } -void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) +RemotePath *Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) { Mutex::Lock _l(_lock); RemotePath *const bestPath = _getBestPath(now); @@ -193,6 +195,7 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) bestPath->sent(now); } } + return bestPath; } void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) @@ -269,59 +272,6 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ } } -void Peer::addPath(const RemotePath &newp,uint64_t now) -{ - Mutex::Lock _l(_lock); - - unsigned int np = _numPaths; - - for(unsigned int p=0;p dswtmp; + newWorld.serialize(dswtmp,false); + RR->node->dataStorePut("world",dswtmp.data(),dswtmp.size(),false); + } catch ( ... ) { + RR->node->dataStoreDelete("world"); + } return true; } return false; diff --git a/node/Topology.hpp b/node/Topology.hpp index 3abc27e4..6f0170f0 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -147,12 +147,19 @@ public: } /** - * @return Pair containing world ID and world timestamp (faster than world().id() etc.) + * @return Current world ID */ - inline std::pair worldIdentification() const + inline uint64_t worldId() const { - Mutex::Lock _l(_lock); - return std::pair(_world.id(),_world.timestamp()); + return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock + } + + /** + * @return Current world timestamp + */ + inline uint64_t worldTimestamp() const + { + return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock } /** diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 6e731bdc..dd755a30 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -182,14 +182,12 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath * "%s\t\"address\": \"%s\",\n" "%s\t\"lastSend\": %llu,\n" "%s\t\"lastReceive\": %llu,\n" - "%s\t\"fixed\": %s,\n" "%s\t\"active\": %s,\n" "%s\t\"preferred\": %s\n" "%s}", prefix,_jsonEscape(reinterpret_cast(&(pp[i].address))->toString()).c_str(), prefix,pp[i].lastSend, prefix,pp[i].lastReceive, - prefix,(pp[i].fixed == 0) ? "false" : "true", prefix,(pp[i].active == 0) ? "false" : "true", prefix,(pp[i].preferred == 0) ? "false" : "true", prefix); diff --git a/world/2015-10-13.bin b/world/2015-10-13.bin new file mode 100644 index 00000000..433a7763 Binary files /dev/null and b/world/2015-10-13.bin differ diff --git a/world/2015-10-13.out b/world/2015-10-13.out new file mode 100644 index 00000000..754e0692 --- /dev/null +++ b/world/2015-10-13.out @@ -0,0 +1,6 @@ +INFO: created initial world keys: previous.c25519, current.c25519 +INFO: generating and signing id==149604618 ts==1442567945403 +INFO: wrote 494 bytes to stdout + +#define ZT_DEFAULT_WORLD_LENGTH 494 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x4f,0xdf,0xbf,0xfc,0xbb,0x6c,0x7e,0x15,0x67,0x85,0x1b,0xb4,0x65,0x04,0x01,0xaf,0x56,0xbf,0xe7,0x63,0x9d,0x77,0xef,0xa4,0x1e,0x61,0x53,0x88,0xcb,0x8d,0x78,0xe5,0x47,0x38,0x98,0x5a,0x6c,0x8a,0xdd,0xe6,0x9c,0x65,0xdf,0x1a,0x80,0x63,0xce,0x2e,0x4d,0x48,0x24,0x3d,0x68,0x87,0x96,0x13,0x89,0xba,0x25,0x6f,0xc9,0xb0,0x9f,0x20,0xc5,0x4c,0x51,0x7b,0x30,0xb7,0x5f,0xba,0xca,0xa4,0xc5,0x48,0xa3,0x15,0xab,0x2f,0x1d,0x64,0xe8,0x04,0x42,0xb3,0x1c,0x51,0x8b,0x2a,0x04,0x01,0xf8,0xe1,0x81,0xaf,0x60,0x2f,0x70,0x3e,0xcd,0x0b,0x21,0x38,0x19,0x62,0x02,0xbd,0x0e,0x33,0x1d,0x0a,0x7b,0xf1,0xec,0xad,0xef,0x54,0xb3,0x7b,0x17,0x84,0xaa,0xda,0x0a,0x85,0x5d,0x0b,0x1c,0x05,0x83,0xb9,0x0e,0x3e,0xe3,0xb4,0xd1,0x8b,0x5b,0x64,0xf7,0xcf,0xe1,0xff,0x5d,0xc2,0x2a,0xcf,0x60,0x7b,0x09,0xb4,0xa3,0x86,0x3c,0x5a,0x7e,0x31,0xa0,0xc7,0xb4,0x86,0xe3,0x41,0x33,0x04,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x01,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x01,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09}; diff --git a/world/mkworld.cpp b/world/mkworld.cpp new file mode 100644 index 00000000..baf693fc --- /dev/null +++ b/world/mkworld.cpp @@ -0,0 +1,170 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +/* + * This utility makes the World from the configuration specified below. + * It probably won't be much use to anyone outside ZeroTier, Inc. except + * for testing and experimentation purposes. + * + * If you want to make your own World you must edit this file. + * + * When run, it expects two files in the current directory: + * + * previous.c25519 - key pair to sign this world (key from previous world) + * current.c25519 - key pair whose public key should be embedded in this world + * + * If these files do not exist, they are both created with the same key pair + * and a self-signed initial World is born. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "node/Constants.hpp" +#include "node/World.hpp" +#include "node/C25519.hpp" +#include "node/Identity.hpp" +#include "node/InetAddress.hpp" +#include "osdep/OSUtils.hpp" + +using namespace ZeroTier; + +class WorldMaker : public World +{ +public: + static inline World make(uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector &roots,const C25519::Pair &signWith) + { + WorldMaker w; + w._id = id; + w._ts = ts; + w._updateSigningKey = sk; + w._roots = roots; + + Buffer tmp; + w.serialize(tmp,true); + w._signature = C25519::sign(signWith,tmp.data(),tmp.size()); + + return w; + } +}; + +int main(int argc,char **argv) +{ + std::string previous,current; + if ((!OSUtils::readFile("previous.c25519",previous))||(!OSUtils::readFile("current.c25519",current))) { + C25519::Pair np(C25519::generate()); + previous = std::string(); + previous.append((const char *)np.pub.data,ZT_C25519_PUBLIC_KEY_LEN); + previous.append((const char *)np.priv.data,ZT_C25519_PRIVATE_KEY_LEN); + current = previous; + OSUtils::writeFile("previous.c25519",previous); + OSUtils::writeFile("current.c25519",current); + fprintf(stderr,"INFO: created initial world keys: previous.c25519, current.c25519"ZT_EOL_S); + } + + if ((previous.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))||(current.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))) { + fprintf(stderr,"FATAL: previous.c25519 or current.c25519 empty or invalid"ZT_EOL_S); + return 1; + } + C25519::Pair previousKP; + memcpy(previousKP.pub.data,previous.data(),ZT_C25519_PUBLIC_KEY_LEN); + memcpy(previousKP.priv.data,previous.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); + C25519::Pair currentKP; + memcpy(currentKP.pub.data,current.data(),ZT_C25519_PUBLIC_KEY_LEN); + memcpy(currentKP.priv.data,current.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN); + + // EDIT BELOW HERE --------------------------------------------------------- + + std::vector roots; + + // old US-SFO + roots.push_back(World::Root()); + roots.back().identity = Identity("7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa"); + roots.back().stableEndpoints.push_back(InetAddress("198.199.97.220/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old EU-PARIS + roots.push_back(World::Root()); + roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c"); + roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old US-NYC + roots.push_back(World::Root()); + roots.back().identity = Identity("8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5"); + roots.back().stableEndpoints.push_back(InetAddress("162.243.77.111/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + // old AP-SNG + roots.push_back(World::Root()); + roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e"); + roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + + std::sort(roots.begin(),roots.end()); + + const uint64_t id = ZT_WORLD_ID_EARTH; + const uint64_t ts = OSUtils::now(); + + // END WORLD SETUP --------------------------------------------------------- + + fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu"ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts); + + World nw = WorldMaker::make(id,ts,currentKP.pub,roots,previousKP); + + Buffer outtmp; + nw.serialize(outtmp,false); + World testw; + testw.deserialize(outtmp,0); + if (testw != nw) { + fprintf(stderr,"FATAL: serialization test failed!"ZT_EOL_S); + return 1; + } + fwrite(outtmp.data(),outtmp.size(),1,stdout); + fflush(stdout); + + fprintf(stderr,"INFO: wrote %u bytes to stdout"ZT_EOL_S,outtmp.size()); + + fprintf(stderr,ZT_EOL_S); + fprintf(stderr,"#define ZT_DEFAULT_WORLD_LENGTH %u"ZT_EOL_S,outtmp.size()); + fprintf(stderr,"static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {"); + for(unsigned int i=0;i 0) + fprintf(stderr,","); + fprintf(stderr,"0x%.2x",(unsigned int)d[i]); + } + fprintf(stderr,"};"ZT_EOL_S); + + return 0; +} -- cgit v1.2.3 From 70d8e3ad94076c51b05b4165cbd68a91806dbde7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:31:38 -0700 Subject: Expose world ID and world timestamp in ZT_NodeStatus --- include/ZeroTierOne.h | 10 ++++++++++ node/Node.cpp | 2 ++ 2 files changed, 12 insertions(+) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 38db3222..42c904eb 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -275,6 +275,16 @@ typedef struct */ uint64_t address; + /** + * Current world ID + */ + uint64_t worldId; + + /** + * Current world revision/timestamp + */ + uint64_t worldTimestamp; + /** * Public identity in string-serialized form (safe to send to others) * diff --git a/node/Node.cpp b/node/Node.cpp index 5468f102..be37b7c7 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -345,6 +345,8 @@ uint64_t Node::address() const void Node::status(ZT_NodeStatus *status) const { status->address = RR->identity.address().toInt(); + status->worldId = RR->topology->worldId(); + status->worldTimestamp = RR->topology->worldTimestamp(); status->publicIdentity = RR->publicIdentityStr.c_str(); status->secretIdentity = RR->secretIdentityStr.c_str(); status->online = _online ? 1 : 0; -- cgit v1.2.3 From 489e1a5b8339403288a4a88b91ae01cb0092ddca Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 13:51:54 -0700 Subject: Don't keep connections up longer than the alive timeout (unless they are relays or roots) --- node/Node.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index be37b7c7..d72bc73f 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -206,8 +206,10 @@ public: } } - if ((!p->doPingAndKeepalive(RR,_now))&&(stableEndpoint)) - p->attemptToContactAt(RR,InetAddress(),stableEndpoint,_now); + if ((p->alive(_now))||(upstream)) { + if ((!p->doPingAndKeepalive(RR,_now))&&(stableEndpoint)) + p->attemptToContactAt(RR,InetAddress(),stableEndpoint,_now); + } if (upstream) lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); -- cgit v1.2.3 From 2229e91b57676c1218b550749a2108372e0f37ad Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:10:12 -0700 Subject: IPv6 support fixes. --- node/Node.cpp | 39 +++++++++++++++++++++++++++++---------- node/Peer.cpp | 47 +++++++++++++++++++++++++++++++++++------------ node/Peer.hpp | 8 +++++--- 3 files changed, 69 insertions(+), 25 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index d72bc73f..5aa4b7d3 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -186,33 +186,52 @@ public: inline void operator()(Topology &t,const SharedPtr &p) { bool upstream = false; - InetAddress stableEndpoint; + InetAddress stableEndpoint4,stableEndpoint6; + + // If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive. for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { if (r->identity.address() == p->address()) { - if (r->stableEndpoints.size() > 0) - stableEndpoint = r->stableEndpoints[(unsigned long)RR->node->prng() % r->stableEndpoints.size()]; upstream = true; + for(unsigned long k=0,ptr=RR->node->prng();kstableEndpoints.size();++k) { + const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()]; + if (!stableEndpoint4) { + if (addr.ss_family == AF_INET) + stableEndpoint4 = addr; + } else if (!stableEndpoint6) { + if (addr.ss_family == AF_INET6) + stableEndpoint6 = addr; + } else break; // have both! + } break; } } + // If this is a network preferred relay, also always ping and if a stable endpoint is specified use that if not alive if (!upstream) { for(std::vector< std::pair >::const_iterator r(_relays.begin());r!=_relays.end();++r) { if (r->first == p->address()) { - stableEndpoint = r->second; + if (r->second.ss_family == AF_INET) + stableEndpoint4 = r->second; + else if (r->second.ss_family == AF_INET6) + stableEndpoint6 = r->second; upstream = true; break; } } } - if ((p->alive(_now))||(upstream)) { - if ((!p->doPingAndKeepalive(RR,_now))&&(stableEndpoint)) - p->attemptToContactAt(RR,InetAddress(),stableEndpoint,_now); - } - - if (upstream) + if (upstream) { + // "Upstream" devices are roots and relays and get special treatment -- they stay alive + // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them. + if ((!p->doPingAndKeepalive(RR,_now,AF_INET))&&(stableEndpoint4)) + p->attemptToContactAt(RR,InetAddress(),stableEndpoint4,_now); + if ((!p->doPingAndKeepalive(RR,_now,AF_INET6))&&(stableEndpoint6)) + p->attemptToContactAt(RR,InetAddress(),stableEndpoint6,_now); lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); + } else if (p->alive(_now)) { + // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently + p->doPingAndKeepalive(RR,_now,0); + } } private: diff --git a/node/Peer.cpp b/node/Peer.cpp index 697ba75d..becc77f9 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -179,23 +179,31 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size()); } -RemotePath *Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) +bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily) { + RemotePath *p = (RemotePath *)0; + Mutex::Lock _l(_lock); - RemotePath *const bestPath = _getBestPath(now); - if (bestPath) { - if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { - TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),bestPath->address().toString().c_str(),now - bestPath->lastSend(),now - bestPath->lastReceived()); - attemptToContactAt(RR,bestPath->localAddress(),bestPath->address(),now); - bestPath->sent(now); - } else if (((now - bestPath->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!bestPath->reliable())) { - TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),bestPath->address().toString().c_str(),now - bestPath->lastSend(),now - bestPath->lastReceived()); + if (inetAddressFamily != 0) { + p = _getBestPath(now,inetAddressFamily); + } else { + p = _getBestPath(now); + } + + if (p) { + if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { + TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived()); + attemptToContactAt(RR,p->localAddress(),p->address(),now); + p->sent(now); + } else if (((now - p->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!p->reliable())) { + TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived()); _natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads - RR->node->putPacket(bestPath->localAddress(),bestPath->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); - bestPath->sent(now); + RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); + p->sent(now); } + return true; } - return bestPath; + return false; } void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) @@ -465,4 +473,19 @@ RemotePath *Peer::_getBestPath(const uint64_t now) return (RemotePath *)0; } +RemotePath *Peer::_getBestPath(const uint64_t now,int inetAddressFamily) +{ + // assumes _lock is locked + if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL) + _sortPaths(now); + for(int k=0;k<2;++k) { // try once, and if it fails sort and try one more time + for(unsigned int i=0;i<_numPaths;++i) { + if ((_paths[i].active(now))&&((int)_paths[i].address().ss_family == inetAddressFamily)) + return &(_paths[i]); + } + _sortPaths(now); + } + return (RemotePath *)0; +} + } // namespace ZeroTier diff --git a/node/Peer.hpp b/node/Peer.hpp index 0139f386..4fb399c5 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -130,7 +130,7 @@ public: Packet::Verb inReVerb = Packet::VERB_NOP); /** - * Get the best direct path to this peer + * Get the current best direct path to this peer * * @param now Current time * @return Best path or NULL if there are no active direct paths @@ -178,9 +178,10 @@ public: * * @param RR Runtime environment * @param now Current time - * @return Current best path or NULL if no active paths + * @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family + * @return True if at least one direct path seems alive */ - RemotePath *doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now); + bool doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily); /** * Push direct paths if we haven't done so in [rate limit] milliseconds @@ -559,6 +560,7 @@ public: private: void _sortPaths(const uint64_t now); RemotePath *_getBestPath(const uint64_t now); + RemotePath *_getBestPath(const uint64_t now,int inetAddressFamily); unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized -- cgit v1.2.3 From 584072fa6a86b7cca0a708d7dd4c302aa444f2b6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 14:04:36 -0700 Subject: Fix for V4/V6 stable addressing. --- node/Node.cpp | 32 ++++++++++++++++++++++++++------ node/Peer.cpp | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 5aa4b7d3..26d5513e 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -197,10 +197,11 @@ public: if (!stableEndpoint4) { if (addr.ss_family == AF_INET) stableEndpoint4 = addr; - } else if (!stableEndpoint6) { + } + if (!stableEndpoint6) { if (addr.ss_family == AF_INET6) stableEndpoint6 = addr; - } else break; // have both! + } } break; } @@ -223,10 +224,29 @@ public: if (upstream) { // "Upstream" devices are roots and relays and get special treatment -- they stay alive // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them. - if ((!p->doPingAndKeepalive(RR,_now,AF_INET))&&(stableEndpoint4)) - p->attemptToContactAt(RR,InetAddress(),stableEndpoint4,_now); - if ((!p->doPingAndKeepalive(RR,_now,AF_INET6))&&(stableEndpoint6)) - p->attemptToContactAt(RR,InetAddress(),stableEndpoint6,_now); + bool needToContactIndirect = true; + if (!p->doPingAndKeepalive(RR,_now,AF_INET)) { + if (stableEndpoint4) { + needToContactIndirect = false; + p->attemptToContactAt(RR,InetAddress(),stableEndpoint4,_now); + } + } else needToContactIndirect = false; + if (!p->doPingAndKeepalive(RR,_now,AF_INET6)) { + if (stableEndpoint6) { + needToContactIndirect = false; + p->attemptToContactAt(RR,InetAddress(),stableEndpoint6,_now); + } + } else needToContactIndirect = false; + + if (needToContactIndirect) { + // If this is an upstream and we have no stable endpoint for either IPv4 or IPv6, + // send a NOP indirectly if possible to see if we can get to this peer in any + // way whatsoever. This will e.g. find network preferred relays that lack + // stable endpoints by using root servers. + Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP); + RR->sw->send(outp,true,0); + } + lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); } else if (p->alive(_now)) { // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently diff --git a/node/Peer.cpp b/node/Peer.cpp index fdc8dca7..a9d2f671 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -205,6 +205,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet } return true; } + return false; } -- cgit v1.2.3 From 57e29857cf79019af03f6a3dfe0bf6fd36e2fab2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 15:27:53 -0700 Subject: Cluster work -- integrating with the rest of the code. --- include/ZeroTierOne.h | 121 ++++++++++++++++++++++++++- make-mac.mk | 2 +- node/Cluster.cpp | 193 +++++++++++++++++++++++++++++++++++++++----- node/Cluster.hpp | 118 ++++++++++++--------------- node/IncomingPacket.cpp | 14 +++- node/Node.cpp | 155 +++++++++++++++++++++++++++++++++-- node/Node.hpp | 14 ++++ node/Peer.cpp | 6 ++ node/RuntimeEnvironment.hpp | 24 ++++-- 9 files changed, 539 insertions(+), 108 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 42c904eb..135c8e11 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -128,6 +128,16 @@ extern "C" { */ #define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 256 +/** + * Maximum number of cluster members (and max member ID plus one) + */ +#define ZT_CLUSTER_MAX_MEMBERS 256 + +/** + * Maximum allowed cluster message length in bytes + */ +#define ZT_CLUSTER_MAX_MESSAGE_LENGTH 65535 + /** * A null/empty sockaddr (all zero) to signify an unspecified socket address */ @@ -174,7 +184,17 @@ enum ZT_ResultCode /** * Network ID not valid */ - ZT_RESULT_ERROR_NETWORK_NOT_FOUND = 1000 + ZT_RESULT_ERROR_NETWORK_NOT_FOUND = 1000, + + /** + * The requested operation is not supported on this version or build + */ + ZT_RESULT_ERROR_UNSUPPORTED_OPERATION = 1001, + + /** + * The requestion operation was given a bad parameter or was called in an invalid state + */ + ZT_RESULT_ERROR_BAD_PARAMETER = 1002 }; /** @@ -1320,6 +1340,105 @@ enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,v */ void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test); +/** + * Initialize cluster operation + * + * This initializes the internal structures and state for cluster operation. + * It takes two function pointers. The first is to a function that can be + * used to send data to cluster peers (mechanism is not defined by Node), + * and the second is to a function that can be used to get the location of + * a physical address in X,Y,Z coordinate space (e.g. as cartesian coordinates + * projected from the center of the Earth). + * + * Send function takes an arbitrary pointer followed by the cluster member ID + * to send data to, a pointer to the data, and the length of the data. The + * maximum message length is ZT_CLUSTER_MAX_MESSAGE_LENGTH (65535). Messages + * must be delivered whole and may be dropped or transposed, though high + * failure rates are undesirable and can cause problems. Validity checking or + * CRC is also not required since the Node validates the authenticity of + * cluster messages using cryptogrphic methods and will silently drop invalid + * messages. + * + * Address to location function is optional and if NULL geo-handoff is not + * enabled (in this case x, y, and z in clusterInit are also unused). It + * takes an arbitrary pointer followed by a physical address and three result + * parameters for x, y, and z. It returns zero on failure or nonzero if these + * three coordinates have been set. Coordinate space is arbitrary and can be + * e.g. coordinates on Earth relative to Earth's center. These can be obtained + * from latitutde and longitude with versions of the Haversine formula. + * + * See: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates + * + * Neither the send nor the address to location function should block. If the + * address to location function does not have a location for an address, it + * should return zero and then look up the address for future use since it + * will be called again in (typically) 1-3 minutes. + * + * Note that both functions can be called from any thread from which the + * various Node functions are called, and so must be thread safe if multiple + * threads are being used. + * + * @param node Node instance + * @param myId My cluster member ID (less than or equal to ZT_CLUSTER_MAX_MEMBERS) + * @param zeroTierPhysicalEndpoints Preferred physical address(es) for ZeroTier clients to contact this cluster member (for peer redirect) + * @param numZeroTierPhysicalEndpoints Number of physical endpoints in zeroTierPhysicalEndpoints[] (max allowed: 255) + * @param x My cluster member's X location + * @param y My cluster member's Y location + * @param z My cluster member's Z location + * @param sendFunction Function to be called to send data to other cluster members + * @param sendFunctionArg First argument to sendFunction() + * @param addressToLocationFunction Function to be called to get the location of a physical address or NULL to disable geo-handoff + * @param addressToLocationFunctionArg First argument to addressToLocationFunction() + * @return OK or UNSUPPORTED_OPERATION if this Node was not built with cluster support + */ +enum ZT_ResultCode ZT_Node_clusterInit( + ZT_Node *node, + unsigned int myId, + const struct sockaddr_storage *zeroTierPhysicalEndpoints, + unsigned int numZeroTierPhysicalEndpoints, + int x, + int y, + int z, + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg); + +/** + * Add a member to this cluster + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS) + * @return OK or error if clustering is disabled, ID invalid, etc. + */ +enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId); + +/** + * Remove a member from this cluster + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param memberId Member ID to remove (nothing happens if not present) + */ +void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId); + +/** + * Handle an incoming cluster state message + * + * The message itself contains cluster member IDs, and invalid or badly + * addressed messages will be silently discarded. + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param msg Cluster message + * @param len Length of cluster message + */ +void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len); + /** * Get ZeroTier One version * diff --git a/make-mac.mk b/make-mac.mk index 174216fb..e53212c0 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -6,7 +6,7 @@ ifeq ($(origin CXX),default) endif INCLUDES= -DEFS= +DEFS=-DZT_ENABLE_CLUSTER LIBS= ARCH_FLAGS=-arch x86_64 diff --git a/node/Cluster.cpp b/node/Cluster.cpp index bfa39d22..5b76d1f0 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -31,10 +31,13 @@ #include #include #include +#include #include #include +#include "../version.h" + #include "Cluster.hpp" #include "RuntimeEnvironment.hpp" #include "MulticastGroup.hpp" @@ -42,22 +45,44 @@ #include "Salsa20.hpp" #include "Poly1305.hpp" #include "Packet.hpp" +#include "Identity.hpp" #include "Peer.hpp" #include "Switch.hpp" #include "Node.hpp" namespace ZeroTier { -Cluster::Cluster(const RuntimeEnvironment *renv,uint16_t id,DistanceAlgorithm da,int32_t x,int32_t y,int32_t z,void (*sendFunction)(void *,uint16_t,const void *,unsigned int),void *arg) : +static inline double _dist3d(int x1,int y1,int z1,int x2,int y2,int z2) + throw() +{ + double dx = ((double)x2 - (double)x1); + double dy = ((double)y2 - (double)y1); + double dz = ((double)z2 - (double)z1); + return sqrt((dx * dx) + (dy * dy) + (dz * dz)); +} + +Cluster::Cluster( + const RuntimeEnvironment *renv, + uint16_t id, + const std::vector &zeroTierPhysicalEndpoints, + int32_t x, + int32_t y, + int32_t z, + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg) : RR(renv), _sendFunction(sendFunction), - _arg(arg), + _sendFunctionArg(sendFunctionArg), + _addressToLocationFunction(addressToLocationFunction), + _addressToLocationFunctionArg(addressToLocationFunctionArg), _x(x), _y(y), _z(z), - _da(da), _id(id), - _members(new _Member[65536]) + _zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints), + _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -114,16 +139,20 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) s20.decrypt12(reinterpret_cast(msg) + 24,const_cast(dmsg.data()),dmsg.size()); } - if (dmsg.size() < 2) + if (dmsg.size() < 4) return; const uint16_t fromMemberId = dmsg.at(0); unsigned int ptr = 2; + if (fromMemberId == _id) + return; + const uint16_t toMemberId = dmsg.at(ptr); + ptr += 2; + if (toMemberId != _id) + return; _Member &m = _members[fromMemberId]; Mutex::Lock mlck(m.lock); - m.lastReceivedFrom = RR->node->now(); - try { while (ptr < dmsg.size()) { const unsigned int mlen = dmsg.at(ptr); ptr += 2; @@ -143,11 +172,13 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) ptr += 8; // skip local clock, not used m.load = dmsg.at(ptr); ptr += 8; ptr += 8; // skip flags, unused - m.physicalAddressCount = dmsg[ptr++]; - if (m.physicalAddressCount > ZT_CLUSTER_MEMBER_MAX_PHYSICAL_ADDRS) - m.physicalAddressCount = ZT_CLUSTER_MEMBER_MAX_PHYSICAL_ADDRS; - for(unsigned int i=0;inode->now(); } break; @@ -298,7 +329,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } } -void Cluster::replicateHavePeer(const Address &peerAddress) +void Cluster::replicateHavePeer(const Identity &peerId) { } @@ -312,23 +343,59 @@ void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembers void Cluster::doPeriodicTasks() { - // Go ahead and flush whenever possible right now + const uint64_t now = RR->node->now(); + { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { Mutex::Lock _l2(_members[*mid].lock); - _flush(*mid); + + if ((now - _members[*mid].lastAnnouncedAliveTo) >= ((ZT_CLUSTER_TIMEOUT / 2) - 1000)) { + Buffer<2048> alive; + alive.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR); + alive.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR); + alive.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + alive.append((uint8_t)ZT_PROTO_VERSION); + if (_addressToLocationFunction) { + alive.append((int32_t)_x); + alive.append((int32_t)_y); + alive.append((int32_t)_z); + } else { + alive.append((int32_t)0); + alive.append((int32_t)0); + alive.append((int32_t)0); + } + alive.append((uint64_t)now); + alive.append((uint64_t)0); // TODO: compute and send load average + alive.append((uint64_t)0); // unused/reserved flags + alive.append((uint8_t)_zeroTierPhysicalEndpoints.size()); + for(std::vector::const_iterator pe(_zeroTierPhysicalEndpoints.begin());pe!=_zeroTierPhysicalEndpoints.end();++pe) + pe->serialize(alive); + _send(*mid,alive.data(),alive.size()); + _members[*mid].lastAnnouncedAliveTo = now; + } + + _flush(*mid); // does nothing if nothing to flush } } } void Cluster::addMember(uint16_t memberId) { + if (memberId >= ZT_CLUSTER_MAX_MEMBERS) + return; + Mutex::Lock _l2(_members[memberId].lock); - Mutex::Lock _l(_memberIds_m); - _memberIds.push_back(memberId); - std::sort(_memberIds.begin(),_memberIds.end()); + { + Mutex::Lock _l(_memberIds_m); + if (std::find(_memberIds.begin(),_memberIds.end(),memberId) != _memberIds.end()) + return; + _memberIds.push_back(memberId); + std::sort(_memberIds.begin(),_memberIds.end()); + } + + _members[memberId].clear(); // Generate this member's message key from the master and its ID uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -346,6 +413,89 @@ void Cluster::addMember(uint16_t memberId) _members[memberId].q.append(iv,16); _members[memberId].q.addSize(8); // room for MAC _members[memberId].q.append((uint16_t)_id); + _members[memberId].q.append((uint16_t)memberId); +} + +void Cluster::removeMember(uint16_t memberId) +{ + Mutex::Lock _l(_memberIds_m); + std::vector newMemberIds; + for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { + if (*mid != memberId) + newMemberIds.push_back(*mid); + } + _memberIds = newMemberIds; +} + +bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPhysicalAddress,bool offload) +{ + if (!peerPhysicalAddress) // sanity check + return false; + if (_addressToLocationFunction) { + // Pick based on location if it can be determined + int px = 0,py = 0,pz = 0; + if (_addressToLocationFunction(_addressToLocationFunctionArg,reinterpret_cast(&peerPhysicalAddress),&px,&py,&pz) == 0) { + // No geo-info so no change + return false; + } + + // Find member closest to this peer + const uint64_t now = RR->node->now(); + std::vector best; // initial "best" is for peer to stay put + const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); + double bestDistance = (offload ? 2147483648.0 : currentDistance); + unsigned int bestMember = _id; + { + Mutex::Lock _l(_memberIds_m); + for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { + _Member &m = _members[*mid]; + Mutex::Lock _ml(m.lock); + + // Consider member if it's alive and has sent us a location and one or more physical endpoints to send peers to + if ( ((now - m.lastReceivedAliveAnnouncement) < ZT_CLUSTER_TIMEOUT) && ((m.x != 0)||(m.y != 0)||(m.z != 0)) && (m.zeroTierPhysicalEndpoints.size() > 0) ) { + double mdist = _dist3d(m.x,m.y,m.z,px,py,pz); + if (mdist < bestDistance) { + bestMember = *mid; + best = m.zeroTierPhysicalEndpoints; + } + } + } + } + + if (best.size() > 0) { + TRACE("peer %s is at [%d,%d,%d], distance to us is %f, sending to %u instead for better distance %f",peer->address().toString().c_str(),px,py,pz,currentDistance,bestMember,bestDistance); + + /* if (peer->remoteVersionProtocol() >= 5) { + // If it's a newer peer send VERB_PUSH_DIRECT_PATHS which is more idiomatic + } else { */ + // Otherwise send VERB_RENDEZVOUS for ourselves, which will trick peers into trying other endpoints for us even if they're too old for PUSH_DIRECT_PATHS + for(std::vector::const_iterator a(best.begin());a!=best.end();++a) { + if ((a->ss_family == AF_INET)||(a->ss_family == AF_INET6)) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); // no flags + RR->identity.address().appendTo(outp); // HACK: rendezvous with ourselves! with really old peers this will only work if I'm a root server! + outp.append((uint16_t)a->port()); + if (a->ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append(a->rawIpData(),4); + } else { + outp.append((uint8_t)16); + outp.append(a->rawIpData(),16); + } + RR->sw->send(outp,true,0); + } + } + //} + + return true; + } else { + TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peer->address().toString().c_str(),px,py,pz,currentDistance); + return false; + } + } else { + // TODO: pick based on load if no location info? + return false; + } } void Cluster::_send(uint16_t memberId,const void *msg,unsigned int len) @@ -366,7 +516,7 @@ void Cluster::_flush(uint16_t memberId) { _Member &m = _members[memberId]; // assumes m.lock is locked! - if (m.q.size() > 26) { // 16-byte IV + 8-byte MAC + 2-byte cluster member ID (latter two bytes are inside crypto envelope) + if (m.q.size() > (24 + 2 + 2)) { // 16-byte IV + 8-byte MAC + 2 byte from-member-ID + 2 byte to-member-ID // Create key from member's key and IV char keytmp[32]; memcpy(keytmp,m.key,32); @@ -389,7 +539,7 @@ void Cluster::_flush(uint16_t memberId) memcpy(m.q.field(16,8),mac,8); // Send! - _sendFunction(_arg,memberId,m.q.data(),m.q.size()); + _sendFunction(_sendFunctionArg,memberId,m.q.data(),m.q.size()); // Prepare for more m.q.clear(); @@ -397,7 +547,8 @@ void Cluster::_flush(uint16_t memberId) Utils::getSecureRandom(iv,16); m.q.append(iv,16); m.q.addSize(8); // room for MAC - m.q.append((uint16_t)_id); + m.q.append((uint16_t)_id); // from member ID + m.q.append((uint16_t)memberId); // to member ID } } diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 016730e3..f253e6f6 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -34,43 +34,32 @@ #include #include "Constants.hpp" +#include "../include/ZeroTierOne.h" #include "Address.hpp" #include "InetAddress.hpp" #include "SHA512.hpp" #include "Utils.hpp" #include "Buffer.hpp" #include "Mutex.hpp" +#include "SharedPtr.hpp" /** * Timeout for cluster members being considered "alive" */ -#define ZT_CLUSTER_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT +#define ZT_CLUSTER_TIMEOUT 30000 /** - * Maximum cluster message length in bytes - * - * Cluster nodes speak via TCP, with data encapsulated into individually - * encrypted and authenticated messages. The maximum message size is - * 65535 (0xffff) since the TCP stream uses 16-bit message size headers - * (and this is a reasonable chunk size anyway). - */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH 65535 - -/** - * Maximum number of physical addresses we will cache for a cluster member - */ -#define ZT_CLUSTER_MEMBER_MAX_PHYSICAL_ADDRS 8 - -/** - * How frequently should doPeriodicTasks() be ideally called? (ms) + * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_DEADLINE 10 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 50 namespace ZeroTier { class RuntimeEnvironment; class CertificateOfMembership; class MulticastGroup; +class Peer; +class Identity; /** * Multi-homing cluster state replication and packet relaying @@ -95,22 +84,6 @@ class MulticastGroup; class Cluster { public: - /** - * Which distance algorithm is this cluster using? - */ - enum DistanceAlgorithm - { - /** - * Simple linear distance in three dimensions - */ - DISTANCE_SIMPLE = 0, - - /** - * Haversine formula using X,Y as lat,long and ignoring Z - */ - DISTANCE_HAVERSINE = 1 - }; - /** * State message types */ @@ -184,25 +157,18 @@ public: /** * Construct a new cluster - * - * @param renv Runtime environment - * @param id This member's ID in the cluster - * @param da Distance algorithm this cluster uses to compute distance and hand off peers - * @param x My X - * @param y My Y - * @param z My Z - * @param sendFunction Function to call to send messages to other cluster members - * @param arg First argument to sendFunction */ Cluster( const RuntimeEnvironment *renv, uint16_t id, - DistanceAlgorithm da, + const std::vector &zeroTierPhysicalEndpoints, int32_t x, int32_t y, int32_t z, - void (*sendFunction)(void *,uint16_t,const void *,unsigned int), - void *arg); + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg); ~Cluster(); @@ -222,9 +188,9 @@ public: /** * Advertise to the cluster that we have this peer * - * @param peerAddress Peer address that we have + * @param peerId Identity of peer that we have */ - void replicateHavePeer(const Address &peerAddress); + void replicateHavePeer(const Identity &peerId); /** * Advertise a multicast LIKE to the cluster @@ -243,7 +209,7 @@ public: void replicateCertificateOfNetworkMembership(const CertificateOfMembership &com); /** - * Call every ~ZT_CLUSTER_PERIODIC_TASK_DEADLINE milliseconds. + * Call every ~ZT_CLUSTER_PERIODIC_TASK_PERIOD milliseconds. */ void doPeriodicTasks(); @@ -254,6 +220,23 @@ public: */ void addMember(uint16_t memberId); + /** + * Remove a member ID from this cluster + * + * @param memberId Member ID to remove + */ + void removeMember(uint16_t memberId); + + /** + * Redirect this peer to a better cluster member if needed + * + * @param peer Peer to (possibly) redirect + * @param peerPhysicalAddress Physical address of peer's current best path (where packet was most recently received or getBestPath()->address()) + * @param offload Always redirect if possible -- can be used to offload peers during shutdown + * @return True if peer was redirected + */ + bool redirectPeer(const SharedPtr &peer,const InetAddress &peerPhysicalAddress,bool offload); + private: void _send(uint16_t memberId,const void *msg,unsigned int len); void _flush(uint16_t memberId); @@ -262,44 +245,45 @@ private: uint16_t _masterSecret[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; const RuntimeEnvironment *RR; - void (*_sendFunction)(void *,uint16_t,const void *,unsigned int); - void *_arg; + void (*_sendFunction)(void *,unsigned int,const void *,unsigned int); + void *_sendFunctionArg; + int (*_addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *); + void *_addressToLocationFunctionArg; const int32_t _x; const int32_t _y; const int32_t _z; - const DistanceAlgorithm _da; const uint16_t _id; + const std::vector _zeroTierPhysicalEndpoints; struct _Member { unsigned char key[ZT_PEER_SECRET_KEY_LENGTH]; - uint64_t lastReceivedFrom; uint64_t lastReceivedAliveAnnouncement; - uint64_t lastSentTo; uint64_t lastAnnouncedAliveTo; uint64_t load; int32_t x,y,z; - InetAddress physicalAddresses[ZT_CLUSTER_MEMBER_MAX_PHYSICAL_ADDRS]; - unsigned int physicalAddressCount; + std::vector zeroTierPhysicalEndpoints; Buffer q; Mutex lock; - _Member() : - lastReceivedFrom(0), - lastReceivedAliveAnnouncement(0), - lastSentTo(0), - lastAnnouncedAliveTo(0), - load(0), - x(0), - y(0), - z(0), - physicalAddressCount(0) {} - + inline void clear() + { + lastReceivedAliveAnnouncement = 0; + lastAnnouncedAliveTo = 0; + load = 0; + x = 0; + y = 0; + z = 0; + zeroTierPhysicalEndpoints.clear(); + q.clear(); + } + + _Member() { this->clear(); } ~_Member() { Utils::burn(key,sizeof(key)); } }; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 19747bbd..a4d45068 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -871,6 +871,8 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha } peer->setLastDirectPathPushReceived(now); + const RemotePath *currentBest = peer->getBestPath(); + unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; unsigned int v4Count = 0,v6Count = 0; @@ -889,16 +891,20 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha InetAddress a(field(ptr,4),4,at(ptr + 4)); if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - if (v4Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + if (v4Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) { + if ((!currentBest)||(currentBest->address() != a)) + peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + } } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - if (v6Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + if (v6Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) { + if ((!currentBest)||(currentBest->address() != a)) + peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + } } } break; } diff --git a/node/Node.cpp b/node/Node.cpp index 26d5513e..6eea3d3d 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -46,6 +46,7 @@ #include "Address.hpp" #include "Identity.hpp" #include "SelfAwareness.hpp" +#include "Cluster.hpp" const struct sockaddr_storage ZT_SOCKADDR_NULL = {0}; @@ -135,6 +136,9 @@ Node::~Node() delete RR->antiRec; delete RR->mc; delete RR->sw; +#ifdef ZT_ENABLE_CLUSTER + delete RR->cluster; +#endif } ZT_ResultCode Node::processWirePacket( @@ -329,7 +333,18 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB } try { - *nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY); +#ifdef ZT_ENABLE_CLUSTER + // If clustering is enabled we have to call cluster->doPeriodicTasks() very often, so we override normal timer deadline behavior + if (RR->cluster) { + RR->sw->doTimerTasks(now); + RR->cluster->doPeriodicTasks(); + *nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate + } else { +#endif + *nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY); +#ifdef ZT_ENABLE_CLUSTER + } +#endif } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } @@ -554,6 +569,62 @@ void Node::circuitTestEnd(ZT_CircuitTest *test) } } +ZT_ResultCode Node::clusterInit( + unsigned int myId, + const struct sockaddr_storage *zeroTierPhysicalEndpoints, + unsigned int numZeroTierPhysicalEndpoints, + int x, + int y, + int z, + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg) +{ +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + return ZT_RESULT_ERROR_BAD_PARAMETER; + + std::vector eps; + for(unsigned int i=0;icluster = new Cluster(RR,myId,eps,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg); + + return ZT_RESULT_OK; +#else + return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION; +#endif +} + +ZT_ResultCode Node::clusterAddMember(unsigned int memberId) +{ +#ifdef ZT_ENABLE_CLUSTER + if (!RR->cluster) + return ZT_RESULT_ERROR_BAD_PARAMETER; + RR->cluster->addMember((uint16_t)memberId); + return ZT_RESULT_OK; +#else + return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION; +#endif +} + +void Node::clusterRemoveMember(unsigned int memberId) +{ +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->removeMember((uint16_t)memberId); +#endif +} + +void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len) +{ +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->handleIncomingStateMessage(msg,len); +#endif +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -806,6 +877,22 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr) } catch ( ... ) {} } +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust) +{ + try { + return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust); + } catch ( ... ) { + return 0; + } +} + +void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node) +{ + try { + reinterpret_cast(node)->clearLocalInterfaceAddresses(); + } catch ( ... ) {} +} + void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance) { try { @@ -829,19 +916,75 @@ void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test) } catch ( ... ) {} } -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust) +enum ZT_ResultCode ZT_Node_clusterInit( + ZT_Node *node, + unsigned int myId, + const struct sockaddr_storage *zeroTierPhysicalEndpoints, + unsigned int numZeroTierPhysicalEndpoints, + int x, + int y, + int z, + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg) { try { - return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust); + return reinterpret_cast(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg); } catch ( ... ) { - return 0; + return ZT_RESULT_FATAL_ERROR_INTERNAL; } } -void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node) +/** + * Add a member to this cluster + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS) + * @return OK or error if clustering is disabled, ID invalid, etc. + */ +enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) { try { - reinterpret_cast(node)->clearLocalInterfaceAddresses(); + return reinterpret_cast(node)->clusterAddMember(memberId); + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; + } +} + +/** + * Remove a member from this cluster + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param memberId Member ID to remove (nothing happens if not present) + */ +void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) +{ + try { + reinterpret_cast(node)->clusterRemoveMember(memberId); + } catch ( ... ) {} +} + +/** + * Handle an incoming cluster state message + * + * The message itself contains cluster member IDs, and invalid or badly + * addressed messages will be silently discarded. + * + * Calling this without having called clusterInit() will do nothing. + * + * @param node Node instance + * @param msg Cluster message + * @param len Length of cluster message + */ +void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len) +{ + try { + reinterpret_cast(node)->clusterHandleIncomingMessage(msg,len); } catch ( ... ) {} } diff --git a/node/Node.hpp b/node/Node.hpp index c7038ed4..b8bd4dc5 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -110,6 +110,20 @@ public: void setNetconfMaster(void *networkControllerInstance); ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); void circuitTestEnd(ZT_CircuitTest *test); + ZT_ResultCode clusterInit( + unsigned int myId, + const struct sockaddr_storage *zeroTierPhysicalEndpoints, + unsigned int numZeroTierPhysicalEndpoints, + int x, + int y, + int z, + void (*sendFunction)(void *,unsigned int,const void *,unsigned int), + void *sendFunctionArg, + int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), + void *addressToLocationFunctionArg); + ZT_ResultCode clusterAddMember(unsigned int memberId); + void clusterRemoveMember(unsigned int memberId); + void clusterHandleIncomingMessage(const void *msg,unsigned int len); // Internal functions ------------------------------------------------------ diff --git a/node/Peer.cpp b/node/Peer.cpp index 6f566be4..0ba379c6 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -213,6 +213,12 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) { +#ifdef ZT_ENABLE_CLUSTER + // Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection + if (RR->cluster) + return; +#endif + Mutex::Lock _l(_lock); if (((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index e5d1f446..2ec88f72 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -43,6 +43,7 @@ class Multicaster; class AntiRecursion; class NetworkController; class SelfAwareness; +class Cluster; /** * Holds global state for an instance of ZeroTier::Node @@ -51,14 +52,17 @@ class RuntimeEnvironment { public: RuntimeEnvironment(Node *n) : - node(n), - identity(), - localNetworkController((NetworkController *)0), - sw((Switch *)0), - mc((Multicaster *)0), - antiRec((AntiRecursion *)0), - topology((Topology *)0), - sa((SelfAwareness *)0) + node(n) + ,identity() + ,localNetworkController((NetworkController *)0) + ,sw((Switch *)0) + ,mc((Multicaster *)0) + ,antiRec((AntiRecursion *)0) + ,topology((Topology *)0) + ,sa((SelfAwareness *)0) +#ifdef ZT_ENABLE_CLUSTER + ,cluster((Cluster *)0) +#endif { } @@ -86,6 +90,10 @@ public: AntiRecursion *antiRec; Topology *topology; SelfAwareness *sa; + +#ifdef ZT_ENABLE_CLUSTER + Cluster *cluster; +#endif }; } // namespace ZeroTier -- cgit v1.2.3 From e9648a6cdf5bd1d9f9e08c7cfef50265114c09d3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 14:05:12 -0700 Subject: Clarify logic in pinging, and prevent roots from pinging "down." --- node/Node.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 6eea3d3d..c54d783f 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -211,8 +211,15 @@ public: } } - // If this is a network preferred relay, also always ping and if a stable endpoint is specified use that if not alive if (!upstream) { + // If I am a root server, only ping other root servers -- roots don't ping "down" + // since that would just be a waste of bandwidth and could potentially cause route + // flapping in Cluster mode. + if (RR->topology->amRoot()) + return; + + // Check for network preferred relays, also considered 'upstream' and thus always + // pinged to keep links up. If they have stable addresses we will try them there. for(std::vector< std::pair >::const_iterator r(_relays.begin());r!=_relays.end();++r) { if (r->first == p->address()) { if (r->second.ss_family == AF_INET) @@ -229,18 +236,22 @@ public: // "Upstream" devices are roots and relays and get special treatment -- they stay alive // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them. bool needToContactIndirect = true; - if (!p->doPingAndKeepalive(RR,_now,AF_INET)) { + if (p->doPingAndKeepalive(RR,_now,AF_INET)) { + needToContactIndirect = false; + } else { if (stableEndpoint4) { needToContactIndirect = false; p->attemptToContactAt(RR,InetAddress(),stableEndpoint4,_now); } - } else needToContactIndirect = false; - if (!p->doPingAndKeepalive(RR,_now,AF_INET6)) { + } + if (p->doPingAndKeepalive(RR,_now,AF_INET6)) { + needToContactIndirect = false; + } else { if (stableEndpoint6) { needToContactIndirect = false; p->attemptToContactAt(RR,InetAddress(),stableEndpoint6,_now); } - } else needToContactIndirect = false; + } if (needToContactIndirect) { // If this is an upstream and we have no stable endpoint for either IPv4 or IPv6, -- cgit v1.2.3 From 5ff7733f84302bbd0ce0a578b3040122d1140f0f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 12:49:17 -0700 Subject: More plumbing of cluster status. --- include/ZeroTierOne.h | 11 +++++++++++ node/Node.cpp | 48 +++++++++++++++++++----------------------------- node/Node.hpp | 1 + 3 files changed, 31 insertions(+), 29 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 86bffcdf..68c1e4d7 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1516,6 +1516,17 @@ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId); */ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len); +/** + * Get the current status of the cluster from this node's point of view + * + * Calling this without clusterInit() or without cluster support will just + * zero out the structure and show a cluster size of zero. + * + * @param node Node instance + * @param cs Cluster status structure to fill with data + */ +void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); + /** * Get ZeroTier One version * diff --git a/node/Node.cpp b/node/Node.cpp index c54d783f..2b298903 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -636,6 +636,18 @@ void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len) #endif } +void Node::clusterStatus(ZT_ClusterStatus *cs) +{ + if (!cs) + return; +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->status(*cs); + else +#endif + memset(cs,0,sizeof(ZT_ClusterStatus)); +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -947,15 +959,6 @@ enum ZT_ResultCode ZT_Node_clusterInit( } } -/** - * Add a member to this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS) - * @return OK or error if clustering is disabled, ID invalid, etc. - */ enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) { try { @@ -965,14 +968,6 @@ enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) } } -/** - * Remove a member from this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID to remove (nothing happens if not present) - */ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) { try { @@ -980,18 +975,6 @@ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) } catch ( ... ) {} } -/** - * Handle an incoming cluster state message - * - * The message itself contains cluster member IDs, and invalid or badly - * addressed messages will be silently discarded. - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param msg Cluster message - * @param len Length of cluster message - */ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len) { try { @@ -999,6 +982,13 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned } catch ( ... ) {} } +void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs) +{ + try { + reinterpret_cast(node)->clusterStatus(cs); + } catch ( ... ) {} +} + void ZT_version(int *major,int *minor,int *revision,unsigned long *featureFlags) { if (major) *major = ZEROTIER_ONE_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index b8bd4dc5..4094a79e 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -124,6 +124,7 @@ public: ZT_ResultCode clusterAddMember(unsigned int memberId); void clusterRemoveMember(unsigned int memberId); void clusterHandleIncomingMessage(const void *msg,unsigned int len); + void clusterStatus(ZT_ClusterStatus *cs); // Internal functions ------------------------------------------------------ -- cgit v1.2.3 From 0ffbd05c0e14088ddb7eaecba7f19541651e859b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 12:21:57 -0700 Subject: --wtf; prevent roots from TCP fallback --- node/Node.cpp | 4 ++-- service/OneService.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 2b298903..87871e20 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -321,8 +321,8 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc); // Update online status, post status change as event - bool oldOnline = _online; - _online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT); + const bool oldOnline = _online; + _online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot())); if (oldOnline != _online) postEvent(_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE); } catch ( ... ) { diff --git a/service/OneService.cpp b/service/OneService.cpp index 729812ed..7a473b67 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -867,6 +867,7 @@ public: { #ifdef ZT_ENABLE_CLUSTER if (sock == _clusterMessageSocket) { + _lastDirectReceiveFromGlobal = OSUtils::now(); _node->clusterHandleIncomingMessage(data,len); return; } @@ -1030,7 +1031,7 @@ public: if (from) { ZT_ResultCode rc = _node->processWirePacket( OSUtils::now(), - 0, + &ZT_SOCKADDR_NULL, reinterpret_cast(&from), data, plen, -- cgit v1.2.3 From 16bc3e03982286232e1df2da17d8b2fc3c5a5c55 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 15:00:16 -0700 Subject: Factor out RemotePath subclass of Path -- no longer needed, just cruft. --- include/ZeroTierOne.h | 3 +- node/Cluster.cpp | 1 + node/IncomingPacket.cpp | 5 +- node/Multicaster.cpp | 1 + node/Network.cpp | 1 + node/Node.cpp | 14 ++--- node/Node.hpp | 6 +- node/Path.cpp | 45 ++++++++++++++ node/Path.hpp | 117 +++++++++++++++++++++++++++++++++-- node/Peer.cpp | 32 +++++----- node/Peer.hpp | 24 ++++---- node/RemotePath.hpp | 161 ------------------------------------------------ node/SelfAwareness.cpp | 2 +- node/Switch.cpp | 2 +- objects.mk | 1 + service/OneService.cpp | 4 +- 16 files changed, 208 insertions(+), 211 deletions(-) create mode 100644 node/Path.cpp delete mode 100644 node/RemotePath.hpp (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 3457634b..01c8bcde 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1356,11 +1356,10 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); * reject bad, empty, and unusable addresses. * * @param addr Local interface address - * @param metric Local interface metric * @param trust How much do you trust the local network under this interface? * @return Boolean: non-zero if address was accepted and added */ -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust); +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,enum ZT_LocalInterfaceAddressTrust trust); /** * Clear local interface addresses diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 73ff5846..07ca0ba1 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -48,6 +48,7 @@ #include "Topology.hpp" #include "Packet.hpp" #include "Switch.hpp" +#include "Node.hpp" namespace ZeroTier { diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 2514cd64..7015535a 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -44,6 +44,7 @@ #include "SHA512.hpp" #include "World.hpp" #include "Cluster.hpp" +#include "Node.hpp" namespace ZeroTier { @@ -888,7 +889,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); - const RemotePath *currentBest = peer->getBestPath(now); + const Path *currentBest = peer->getBestPath(now); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; @@ -1036,7 +1037,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt remainingHopsPtr += ZT_ADDRESS_LENGTH; SharedPtr nhp(RR->topology->getPeer(nextHop[h])); if (nhp) { - RemotePath *const rp = nhp->getBestPath(now); + Path *const rp = nhp->getBestPath(now); if (rp) nextHopBestPathAddress[h] = rp->address(); } diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 6e6cd628..e43d7d88 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -37,6 +37,7 @@ #include "Peer.hpp" #include "C25519.hpp" #include "CertificateOfMembership.hpp" +#include "Node.hpp" namespace ZeroTier { diff --git a/node/Network.cpp b/node/Network.cpp index cd30e386..afbe1074 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -37,6 +37,7 @@ #include "Packet.hpp" #include "Buffer.hpp" #include "NetworkController.hpp" +#include "Node.hpp" #include "../version.h" diff --git a/node/Node.cpp b/node/Node.cpp index 87871e20..82cda66d 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -448,10 +448,10 @@ ZT_PeerList *Node::peers() const p->latency = pi->second->latency(); p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF; - std::vector paths(pi->second->paths()); - RemotePath *bestPath = pi->second->getBestPath(_now); + std::vector paths(pi->second->paths()); + Path *bestPath = pi->second->getBestPath(_now); p->pathCount = 0; - for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { + for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage)); p->paths[p->pathCount].lastSend = path->lastSend(); p->paths[p->pathCount].lastReceive = path->lastReceived(); @@ -499,11 +499,11 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust) +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,ZT_LocalInterfaceAddressTrust trust) { if (Path::isAddressValidForPath(*(reinterpret_cast(addr)))) { Mutex::Lock _l(_directPaths_m); - _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust)); + _directPaths.push_back(*(reinterpret_cast(addr))); std::sort(_directPaths.begin(),_directPaths.end()); _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); return 1; @@ -900,10 +900,10 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr) } catch ( ... ) {} } -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust) +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,enum ZT_LocalInterfaceAddressTrust trust) { try { - return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust); + return reinterpret_cast(node)->addLocalInterfaceAddress(addr,trust); } catch ( ... ) { return 0; } diff --git a/node/Node.hpp b/node/Node.hpp index 4094a79e..48c5ead8 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -105,7 +105,7 @@ public: ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr,ZT_LocalInterfaceAddressTrust trust); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); @@ -207,7 +207,7 @@ public: /** * @return Potential direct paths to me a.k.a. local interface addresses */ - inline std::vector directPaths() const + inline std::vector directPaths() const { Mutex::Lock _l(_directPaths_m); return _directPaths; @@ -285,7 +285,7 @@ private: std::vector< ZT_CircuitTest * > _circuitTests; Mutex _circuitTests_m; - std::vector _directPaths; + std::vector _directPaths; Mutex _directPaths_m; Mutex _backgroundTasksLock; diff --git a/node/Path.cpp b/node/Path.cpp new file mode 100644 index 00000000..e2475751 --- /dev/null +++ b/node/Path.cpp @@ -0,0 +1,45 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include "Path.hpp" +#include "AntiRecursion.hpp" +#include "RuntimeEnvironment.hpp" +#include "Node.hpp" + +namespace ZeroTier { + +bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) +{ + if (RR->node->putPacket(_localAddress,address(),data,len)) { + sent(now); + RR->antiRec->logOutgoingZT(data,len); + return true; + } + return false; +} + +} // namespace ZeroTier diff --git a/node/Path.hpp b/node/Path.hpp index 6fa3c52e..99f6590b 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -28,12 +28,19 @@ #ifndef ZT_PATH_HPP #define ZT_PATH_HPP +#include +#include + +#include +#include + #include "Constants.hpp" #include "InetAddress.hpp" -#include "Utils.hpp" namespace ZeroTier { +class RuntimeEnvironment; + /** * Base class for paths * @@ -67,19 +74,87 @@ public: }; Path() : + _lastSend(0), + _lastReceived(0), _addr(), + _localAddress(), _ipScope(InetAddress::IP_SCOPE_NONE), - _trust(TRUST_NORMAL) + _trust(TRUST_NORMAL), + _flags(0) { } - Path(const InetAddress &addr,int metric,Trust trust) : + Path(const InetAddress &localAddress,const InetAddress &addr,Trust trust) : + _lastSend(0), + _lastReceived(0), _addr(addr), + _localAddress(localAddress), _ipScope(addr.ipScope()), - _trust(trust) + _trust(trust), + _flags(0) + { + } + + /** + * Called when a packet is sent to this remote path + * + * This is called automatically by Path::send(). + * + * @param t Time of send + */ + inline void sent(uint64_t t) + throw() { + _lastSend = t; } + /** + * Called when a packet is received from this remote path + * + * @param t Time of receive + */ + inline void received(uint64_t t) + throw() + { + _lastReceived = t; + } + + /** + * @param now Current time + * @return True if this path appears active + */ + inline bool active(uint64_t now) const + throw() + { + return ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT); + } + + /** + * Send a packet via this path + * + * @param RR Runtime environment + * @param data Packet data + * @param len Packet length + * @param now Current time + * @return True if transport reported success + */ + bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now); + + /** + * @return Address of local side of this path or NULL if unspecified + */ + inline const InetAddress &localAddress() const throw() { return _localAddress; } + + /** + * @return Time of last send to this path + */ + inline uint64_t lastSend() const throw() { return _lastSend; } + + /** + * @return Time of last receive from this path + */ + inline uint64_t lastReceived() const throw() { return _lastReceived; } + /** * @return Physical address */ @@ -157,10 +232,42 @@ public: return false; } -protected: + template + inline void serialize(Buffer &b) const + { + b.append((uint8_t)0); // version + b.append((uint64_t)_lastSend); + b.append((uint64_t)_lastReceived); + _addr.serialize(b); + _localAddress.serialize(b); + b.append((uint8_t)_trust); + b.append((uint16_t)_flags); + } + + template + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + { + unsigned int p = startAt; + if (b[p++] != 0) + throw std::invalid_argument("invalid serialized Path"); + _lastSend = b.template at(p); p += 8; + _lastReceived = b.template at(p); p += 8; + p += _addr.deserialize(b,p); + p += _localAddress.deserialize(b,p); + _ipScope = _addr.ipScope(); + _trust = (Path::Trust)b[p++]; + _flags = b.template at(p); p += 2; + return (p - startAt); + } + +private: + uint64_t _lastSend; + uint64_t _lastReceived; InetAddress _addr; + InetAddress _localAddress; InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often Trust _trust; + uint16_t _flags; }; } // namespace ZeroTier diff --git a/node/Peer.cpp b/node/Peer.cpp index 009e2be5..ebdb6026 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -169,7 +169,7 @@ void Peer::received( if (!pathIsConfirmed) { if (verb == Packet::VERB_OK) { - RemotePath *slot = (RemotePath *)0; + Path *slot = (Path *)0; if (np < ZT_MAX_PEER_NETWORK_PATHS) { slot = &(_paths[np++]); } else { @@ -182,7 +182,7 @@ void Peer::received( } } if (slot) { - *slot = RemotePath(localAddr,remoteAddr); + *slot = Path(localAddr,remoteAddr,Path::TRUST_NORMAL); slot->received(now); _numPaths = np; pathIsConfirmed = true; @@ -240,7 +240,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily) { - RemotePath *p = (RemotePath *)0; + Path *p = (Path *)0; Mutex::Lock _l(_lock); if (inetAddressFamily != 0) { @@ -268,7 +268,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet return false; } -void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) +void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force) { #ifdef ZT_ENABLE_CLUSTER // Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection @@ -281,7 +281,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ if (((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { _lastDirectPathPushSent = now; - std::vector dps(RR->node->directPaths()); + std::vector dps(RR->node->directPaths()); if (dps.empty()) return; @@ -291,13 +291,13 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ for(std::vector::const_iterator p(dps.begin());p!=dps.end();++p) { if (ps.length() > 0) ps.push_back(','); - ps.append(p->address().toString()); + ps.append(p->toString()); } TRACE("pushing %u direct paths to %s: %s",(unsigned int)dps.size(),_id.address().toString().c_str(),ps.c_str()); } #endif - std::vector::const_iterator p(dps.begin()); + std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); outp.addSize(2); // leave room for count @@ -305,7 +305,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ unsigned int count = 0; while ((p != dps.end())&&((outp.size() + 24) < ZT_PROTO_MAX_PACKET_LENGTH)) { uint8_t addressType = 4; - switch(p->address().ss_family) { + switch(p->ss_family) { case AF_INET: break; case AF_INET6: @@ -317,6 +317,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ } uint8_t flags = 0; + /* TODO: path trust is not implemented yet switch(p->trust()) { default: break; @@ -327,13 +328,14 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ flags |= (0x04 | 0x08); // no encryption, no authentication (redundant but go ahead and set both) break; } + */ outp.append(flags); outp.append((uint16_t)0); // no extensions outp.append(addressType); outp.append((uint8_t)((addressType == 4) ? 6 : 18)); - outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); - outp.append((uint16_t)p->address().port()); + outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16)); + outp.append((uint16_t)p->port()); ++count; ++p; @@ -506,7 +508,7 @@ struct _SortPathsByQuality { uint64_t _now; _SortPathsByQuality(const uint64_t now) : _now(now) {} - inline bool operator()(const RemotePath &a,const RemotePath &b) const + inline bool operator()(const Path &a,const Path &b) const { const uint64_t qa = ( ((uint64_t)a.active(_now) << 63) | @@ -526,7 +528,7 @@ void Peer::_sortPaths(const uint64_t now) std::sort(&(_paths[0]),&(_paths[_numPaths]),_SortPathsByQuality(now)); } -RemotePath *Peer::_getBestPath(const uint64_t now) +Path *Peer::_getBestPath(const uint64_t now) { // assumes _lock is locked if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL) @@ -538,10 +540,10 @@ RemotePath *Peer::_getBestPath(const uint64_t now) if (_paths[0].active(now)) return &(_paths[0]); } - return (RemotePath *)0; + return (Path *)0; } -RemotePath *Peer::_getBestPath(const uint64_t now,int inetAddressFamily) +Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily) { // assumes _lock is locked if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL) @@ -553,7 +555,7 @@ RemotePath *Peer::_getBestPath(const uint64_t now,int inetAddressFamily) } _sortPaths(now); } - return (RemotePath *)0; + return (Path *)0; } } // namespace ZeroTier diff --git a/node/Peer.hpp b/node/Peer.hpp index 04b541af..aa75b3f4 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -41,7 +41,7 @@ #include "RuntimeEnvironment.hpp" #include "CertificateOfMembership.hpp" -#include "RemotePath.hpp" +#include "Path.hpp" #include "Address.hpp" #include "Utils.hpp" #include "Identity.hpp" @@ -135,7 +135,7 @@ public: * @param now Current time * @return Best path or NULL if there are no active direct paths */ - inline RemotePath *getBestPath(uint64_t now) + inline Path *getBestPath(uint64_t now) { Mutex::Lock _l(_lock); return _getBestPath(now); @@ -150,14 +150,14 @@ public: * @param now Current time * @return Path used on success or NULL on failure */ - inline RemotePath *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) + inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) { - RemotePath *bestPath = getBestPath(now); + Path *bestPath = getBestPath(now); if (bestPath) { if (bestPath->send(RR,data,len,now)) return bestPath; } - return (RemotePath *)0; + return (Path *)0; } /** @@ -191,14 +191,14 @@ public: * @param now Current time * @param force If true, push regardless of rate limit */ - void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force); + void pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force); /** * @return All known direct paths to this peer */ - inline std::vector paths() const + inline std::vector paths() const { - std::vector pp; + std::vector pp; Mutex::Lock _l(_lock); for(unsigned int p=0,np=_numPaths;p_paths[np->_numPaths++].deserialize(b,p); } else { // Skip any paths beyond max, but still read stream - RemotePath foo; + Path foo; p += foo.deserialize(b,p); } } @@ -557,8 +557,8 @@ public: private: void _sortPaths(const uint64_t now); - RemotePath *_getBestPath(const uint64_t now); - RemotePath *_getBestPath(const uint64_t now,int inetAddressFamily); + Path *_getBestPath(const uint64_t now); + Path *_getBestPath(const uint64_t now,int inetAddressFamily); unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized @@ -575,7 +575,7 @@ private: uint16_t _vMinor; uint16_t _vRevision; Identity _id; - RemotePath _paths[ZT_MAX_PEER_NETWORK_PATHS]; + Path _paths[ZT_MAX_PEER_NETWORK_PATHS]; unsigned int _numPaths; unsigned int _latency; diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp deleted file mode 100644 index 8b37621a..00000000 --- a/node/RemotePath.hpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_REMOTEPATH_HPP -#define ZT_REMOTEPATH_HPP - -#include -#include - -#include -#include - -#include "Path.hpp" -#include "Node.hpp" -#include "AntiRecursion.hpp" -#include "RuntimeEnvironment.hpp" - -namespace ZeroTier { - -/** - * Path to a remote peer - * - * This extends Path to include status information about path activity. - */ -class RemotePath : public Path -{ -public: - RemotePath() : - Path(), - _lastSend(0), - _lastReceived(0), - _localAddress(), - _flags(0) {} - - RemotePath(const InetAddress &localAddress,const InetAddress &addr) : - Path(addr,0,TRUST_NORMAL), - _lastSend(0), - _lastReceived(0), - _localAddress(localAddress), - _flags(0) {} - - inline const InetAddress &localAddress() const throw() { return _localAddress; } - - inline uint64_t lastSend() const throw() { return _lastSend; } - inline uint64_t lastReceived() const throw() { return _lastReceived; } - - /** - * Called when a packet is sent to this remote path - * - * This is called automatically by RemotePath::send(). - * - * @param t Time of send - */ - inline void sent(uint64_t t) - throw() - { - _lastSend = t; - } - - /** - * Called when a packet is received from this remote path - * - * @param t Time of receive - */ - inline void received(uint64_t t) - throw() - { - _lastReceived = t; - } - - /** - * @param now Current time - * @return True if this path appears active - */ - inline bool active(uint64_t now) const - throw() - { - return ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT); - } - - /** - * Send a packet via this path - * - * @param RR Runtime environment - * @param data Packet data - * @param len Packet length - * @param now Current time - * @return True if transport reported success - */ - inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) - { - if (RR->node->putPacket(_localAddress,address(),data,len)) { - sent(now); - RR->antiRec->logOutgoingZT(data,len); - return true; - } - return false; - } - - template - inline void serialize(Buffer &b) const - { - b.append((uint8_t)1); // version - _addr.serialize(b); - b.append((uint8_t)_trust); - b.append((uint64_t)_lastSend); - b.append((uint64_t)_lastReceived); - _localAddress.serialize(b); - b.append((uint16_t)_flags); - } - - template - inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) - { - unsigned int p = startAt; - if (b[p++] != 1) - throw std::invalid_argument("invalid serialized RemotePath"); - p += _addr.deserialize(b,p); - _ipScope = _addr.ipScope(); - _trust = (Path::Trust)b[p++]; - _lastSend = b.template at(p); p += 8; - _lastReceived = b.template at(p); p += 8; - p += _localAddress.deserialize(b,p); - _flags = b.template at(p); p += 2; - return (p - startAt); - } - -protected: - uint64_t _lastSend; - uint64_t _lastReceived; - InetAddress _localAddress; - uint16_t _flags; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 81d19369..b4841544 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -125,7 +125,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // they are still considered alive so that we will re-establish direct links. SharedPtr r(RR->topology->getBestRoot()); if (r) { - RemotePath *rp = r->getBestPath(now); + Path *rp = r->getBestPath(now); if (rp) { for(std::vector< SharedPtr >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) { if ((*p)->alive(now)) { diff --git a/node/Switch.cpp b/node/Switch.cpp index 772eaf02..2f72f57a 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -736,7 +736,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) return false; // sanity check: unconfigured network? why are we trying to talk to it? } - RemotePath *viaPath = peer->getBestPath(now); + Path *viaPath = peer->getBestPath(now); SharedPtr relay; if (!viaPath) { // See if this network has a preferred relay (if packet has an associated network) diff --git a/objects.mk b/objects.mk index 6dd5ea30..540072d5 100644 --- a/objects.mk +++ b/objects.mk @@ -15,6 +15,7 @@ OBJS=\ node/Node.o \ node/OutboundMulticast.o \ node/Packet.o \ + node/Path.o \ node/Peer.o \ node/Poly1305.o \ node/Salsa20.o \ diff --git a/service/OneService.cpp b/service/OneService.cpp index 1765b5c4..4e3f24c7 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -731,7 +731,7 @@ public: #ifdef ZT_USE_MINIUPNPC std::vector upnpAddresses(_upnpClient->get()); for(std::vector::const_iterator ext(upnpAddresses.begin());ext!=upnpAddresses.end();++ext) - _node->addLocalInterfaceAddress(reinterpret_cast(&(*ext)),0,ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); + _node->addLocalInterfaceAddress(reinterpret_cast(&(*ext)),ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); #endif struct ifaddrs *ifatbl = (struct ifaddrs *)0; @@ -749,7 +749,7 @@ public: if (!isZT) { InetAddress ip(ifa->ifa_addr); ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); } } ifa = ifa->ifa_next; -- cgit v1.2.3 From a994573a436ec2835781d13ecd2307e18c67855b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 29 Oct 2015 09:42:15 -0700 Subject: Eliminate some more dead code. We may do path trust, but not like that. --- include/ZeroTierOne.h | 17 +---------------- node/Node.cpp | 6 +++--- node/Node.hpp | 2 +- node/Path.hpp | 16 +++------------- node/Peer.cpp | 2 +- service/OneService.cpp | 4 ++-- 6 files changed, 11 insertions(+), 36 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 01c8bcde..e9b38c52 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -424,15 +424,6 @@ enum ZT_VirtualNetworkConfigOperation ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4 }; -/** - * Local interface trust levels - */ -enum ZT_LocalInterfaceAddressTrust { - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 10, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20 -}; - /** * What trust hierarchy role does this peer have? */ @@ -1337,11 +1328,6 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); /** * Add a local interface address * - * Local interface addresses may be added if you want remote peers - * with whom you have a trust relatinship (e.g. common network membership) - * to receive information about these endpoints as potential endpoints for - * direct communication. - * * Take care that these are never ZeroTier interface addresses, otherwise * strange things might happen or they simply won't work. * @@ -1356,10 +1342,9 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); * reject bad, empty, and unusable addresses. * * @param addr Local interface address - * @param trust How much do you trust the local network under this interface? * @return Boolean: non-zero if address was accepted and added */ -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,enum ZT_LocalInterfaceAddressTrust trust); +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr); /** * Clear local interface addresses diff --git a/node/Node.cpp b/node/Node.cpp index 82cda66d..42180e99 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -499,7 +499,7 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,ZT_LocalInterfaceAddressTrust trust) +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr) { if (Path::isAddressValidForPath(*(reinterpret_cast(addr)))) { Mutex::Lock _l(_directPaths_m); @@ -900,10 +900,10 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr) } catch ( ... ) {} } -int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,enum ZT_LocalInterfaceAddressTrust trust) +int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr) { try { - return reinterpret_cast(node)->addLocalInterfaceAddress(addr,trust); + return reinterpret_cast(node)->addLocalInterfaceAddress(addr); } catch ( ... ) { return 0; } diff --git a/node/Node.hpp b/node/Node.hpp index 48c5ead8..9b85b832 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -105,7 +105,7 @@ public: ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - int addLocalInterfaceAddress(const struct sockaddr_storage *addr,ZT_LocalInterfaceAddressTrust trust); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); diff --git a/node/Path.hpp b/node/Path.hpp index 2b05b812..c0182990 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -79,18 +79,16 @@ public: _addr(), _localAddress(), _ipScope(InetAddress::IP_SCOPE_NONE), - _trust(TRUST_NORMAL), _flags(0) { } - Path(const InetAddress &localAddress,const InetAddress &addr,Trust trust) : + Path(const InetAddress &localAddress,const InetAddress &addr) : _lastSend(0), _lastReceived(0), _addr(addr), _localAddress(localAddress), _ipScope(addr.ipScope()), - _trust(trust), _flags(0) { } @@ -187,11 +185,6 @@ public: return ( ((int)_ipScope * 2) + ((_addr.ss_family == AF_INET6) ? 1 : 0) ); } - /** - * @return Path trust level - */ - inline Trust trust() const throw() { return _trust; } - /** * @return True if path is considered reliable (no NAT keepalives etc. are needed) */ @@ -243,12 +236,11 @@ public: template inline void serialize(Buffer &b) const { - b.append((uint8_t)0); // version + b.append((uint8_t)1); // version b.append((uint64_t)_lastSend); b.append((uint64_t)_lastReceived); _addr.serialize(b); _localAddress.serialize(b); - b.append((uint8_t)_trust); b.append((uint16_t)_flags); } @@ -256,14 +248,13 @@ public: inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { unsigned int p = startAt; - if (b[p++] != 0) + if (b[p++] != 1) throw std::invalid_argument("invalid serialized Path"); _lastSend = b.template at(p); p += 8; _lastReceived = b.template at(p); p += 8; p += _addr.deserialize(b,p); p += _localAddress.deserialize(b,p); _ipScope = _addr.ipScope(); - _trust = (Path::Trust)b[p++]; _flags = b.template at(p); p += 2; return (p - startAt); } @@ -274,7 +265,6 @@ private: InetAddress _addr; InetAddress _localAddress; InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often - Trust _trust; uint16_t _flags; }; diff --git a/node/Peer.cpp b/node/Peer.cpp index 976c7c44..9d0d78e5 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -179,7 +179,7 @@ void Peer::received( } } if (slot) { - *slot = Path(localAddr,remoteAddr,Path::TRUST_NORMAL); + *slot = Path(localAddr,remoteAddr); slot->received(now); _numPaths = np; pathIsConfirmed = true; diff --git a/service/OneService.cpp b/service/OneService.cpp index 4e3f24c7..8c8ff1ed 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -731,7 +731,7 @@ public: #ifdef ZT_USE_MINIUPNPC std::vector upnpAddresses(_upnpClient->get()); for(std::vector::const_iterator ext(upnpAddresses.begin());ext!=upnpAddresses.end();++ext) - _node->addLocalInterfaceAddress(reinterpret_cast(&(*ext)),ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); + _node->addLocalInterfaceAddress(reinterpret_cast(&(*ext))); #endif struct ifaddrs *ifatbl = (struct ifaddrs *)0; @@ -749,7 +749,7 @@ public: if (!isZT) { InetAddress ip(ifa->ifa_addr); ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip)); } } ifa = ifa->ifa_next; -- cgit v1.2.3 From 60ce886605c0298fc22dbce48beb106a96bd35e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 15:15:20 -0800 Subject: Tweak some timings for better reliability. --- node/Cluster.cpp | 6 +- node/Cluster.hpp | 8 +- node/Constants.hpp | 24 +---- node/Multicaster.cpp | 220 ++++++++++++++++++++++--------------------- node/Node.cpp | 13 +-- tests/http/big-test-kill.sh | 2 +- tests/http/big-test-ready.sh | 2 +- tests/http/big-test-start.sh | 4 +- 8 files changed, 131 insertions(+), 148 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Cluster.cpp b/node/Cluster.cpp index d0daae43..e9e31ede 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -85,7 +85,8 @@ Cluster::Cluster( _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]), _peerAffinities(65536), _lastCleanedPeerAffinities(0), - _lastCheckedPeersForAnnounce(0) + _lastCheckedPeersForAnnounce(0), + _lastFlushed(0) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -510,7 +511,8 @@ void Cluster::doPeriodicTasks() } // Flush outgoing packet send queue every doPeriodicTasks() - { + if ((now - _lastFlushed) >= ZT_CLUSTER_FLUSH_PERIOD) { + _lastFlushed = now; Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { Mutex::Lock _l2(_members[*mid].lock); diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 7d7a1ced..f1caa436 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -55,13 +55,18 @@ /** * How often should we announce that we have a peer? */ -#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD ((ZT_PEER_ACTIVITY_TIMEOUT / 2) - 1000) +#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD (ZT_PEER_DIRECT_PING_DELAY / 2) /** * Desired period between doPeriodicTasks() in milliseconds */ #define ZT_CLUSTER_PERIODIC_TASK_PERIOD 250 +/** + * How often to flush outgoing message queues (maximum interval) + */ +#define ZT_CLUSTER_FLUSH_PERIOD 500 + namespace ZeroTier { class RuntimeEnvironment; @@ -355,6 +360,7 @@ private: uint64_t _lastCleanedPeerAffinities; uint64_t _lastCheckedPeersForAnnounce; + uint64_t _lastFlushed; }; } // namespace ZeroTier diff --git a/node/Constants.hpp b/node/Constants.hpp index 1d5fa6f4..bb62484d 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -173,13 +173,8 @@ /** * Timeout for receipt of fragmented packets in ms - * - * Since there's no retransmits, this is just a really bad case scenario for - * transit time. It's short enough that a DOS attack from exhausing buffers is - * very unlikely, as the transfer rate would have to be fast enough to fill - * system memory in this time. */ -#define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 1000 +#define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 500 /** * Length of secret key in bytes -- 256-bit -- do not change @@ -194,7 +189,7 @@ /** * Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet */ -#define ZT_CORE_TIMER_TASK_GRANULARITY 1000 +#define ZT_CORE_TIMER_TASK_GRANULARITY 500 /** * How long to remember peer records in RAM if they haven't been used @@ -269,7 +264,7 @@ /** * Delay between ordinary case pings of direct links */ -#define ZT_PEER_DIRECT_PING_DELAY 120000 +#define ZT_PEER_DIRECT_PING_DELAY 60000 /** * Delay between requests for updated network autoconf information @@ -279,18 +274,7 @@ /** * Timeout for overall peer activity (measured from last receive) */ -#define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_DIRECT_PING_DELAY + (ZT_PING_CHECK_INVERVAL * 3)) - -/** - * Stop relaying via peers that have not responded to direct sends - * - * When we send something (including frames), we generally expect a response. - * Switching relays if no response in a short period of time causes more - * rapid failover if a root server goes down or becomes unreachable. In the - * mistaken case, little harm is done as it'll pick the next-fastest - * root server and will switch back eventually. - */ -#define ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD 10000 +#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 3) + (ZT_PING_CHECK_INVERVAL * 2)) /** * Minimum interval between attempts by relays to unite peers diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index e43d7d88..01e6b799 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -175,128 +175,130 @@ void Multicaster::send( unsigned long idxbuf[8194]; unsigned long *indexes = idxbuf; - Mutex::Lock _l(_groups_m); - MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)]; - - if (!gs.members.empty()) { - // Allocate a memory buffer if group is monstrous - if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long))) - indexes = new unsigned long[gs.members.size()]; - - // Generate a random permutation of member indexes - for(unsigned long i=0;i0;--i) { - unsigned long j = (unsigned long)RR->node->prng() % (i + 1); - unsigned long tmp = indexes[j]; - indexes[j] = indexes[i]; - indexes[i] = tmp; + try { + Mutex::Lock _l(_groups_m); + MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)]; + + if (!gs.members.empty()) { + // Allocate a memory buffer if group is monstrous + if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long))) + indexes = new unsigned long[gs.members.size()]; + + // Generate a random permutation of member indexes + for(unsigned long i=0;i0;--i) { + unsigned long j = (unsigned long)RR->node->prng() % (i + 1); + unsigned long tmp = indexes[j]; + indexes[j] = indexes[i]; + indexes[i] = tmp; + } } - } - if (gs.members.size() >= limit) { - // Skip queue if we already have enough members to complete the send operation - OutboundMulticast out; - - out.init( - RR, - now, - nwid, - com, - limit, - 1, // we'll still gather a little from peers to keep multicast list fresh - src, - mg, - etherType, - data, - len); - - unsigned int count = 0; - - for(std::vector
::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) { - if (*ast != RR->identity.address()) { - out.sendOnly(RR,*ast); - if (++count >= limit) - break; + if (gs.members.size() >= limit) { + // Skip queue if we already have enough members to complete the send operation + OutboundMulticast out; + + out.init( + RR, + now, + nwid, + com, + limit, + 1, // we'll still gather a little from peers to keep multicast list fresh + src, + mg, + etherType, + data, + len); + + unsigned int count = 0; + + for(std::vector
::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) { + if (*ast != RR->identity.address()) { + out.sendOnly(RR,*ast); // optimization: don't use dedup log if it's a one-pass send + if (++count >= limit) + break; + } } - } - unsigned long idx = 0; - while ((count < limit)&&(idx < gs.members.size())) { - Address ma(gs.members[indexes[idx++]].address); - if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) { - out.sendOnly(RR,ma); - ++count; + unsigned long idx = 0; + while ((count < limit)&&(idx < gs.members.size())) { + Address ma(gs.members[indexes[idx++]].address); + if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) { + out.sendOnly(RR,ma); // optimization: don't use dedup log if it's a one-pass send + ++count; + } } - } - } else { - unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1; - - if ((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY) { - gs.lastExplicitGather = now; - SharedPtr r(RR->topology->getBestRoot()); - if (r) { - TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str()); - - const CertificateOfMembership *com = (CertificateOfMembership *)0; - { - SharedPtr nw(RR->node->network(nwid)); - if (nw) { - SharedPtr nconf(nw->config2()); - if ((nconf)&&(nconf->com())&&(nconf->isPrivate())&&(r->needsOurNetworkMembershipCertificate(nwid,now,true))) - com = &(nconf->com()); + } else { + unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1; + + if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) { + gs.lastExplicitGather = now; + SharedPtr r(RR->topology->getBestRoot()); + if (r) { + TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str()); + + const CertificateOfMembership *com = (CertificateOfMembership *)0; + { + SharedPtr nw(RR->node->network(nwid)); + if (nw) { + SharedPtr nconf(nw->config2()); + if ((nconf)&&(nconf->com())&&(nconf->isPrivate())&&(r->needsOurNetworkMembershipCertificate(nwid,now,true))) + com = &(nconf->com()); + } } - } - Packet outp(r->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER); - outp.append(nwid); - outp.append((uint8_t)(com ? 0x01 : 0x00)); - mg.mac().appendTo(outp); - outp.append((uint32_t)mg.adi()); - outp.append((uint32_t)gatherLimit); - if (com) - com->serialize(outp); - outp.armor(r->key(),true); - r->send(RR,outp.data(),outp.size(),now); + Packet outp(r->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER); + outp.append(nwid); + outp.append((uint8_t)(com ? 0x01 : 0x00)); + mg.mac().appendTo(outp); + outp.append((uint32_t)mg.adi()); + outp.append((uint32_t)gatherLimit); + if (com) + com->serialize(outp); + outp.armor(r->key(),true); + r->send(RR,outp.data(),outp.size(),now); + } + gatherLimit = 0; } - gatherLimit = 0; - } - gs.txQueue.push_back(OutboundMulticast()); - OutboundMulticast &out = gs.txQueue.back(); - - out.init( - RR, - now, - nwid, - com, - limit, - gatherLimit, - src, - mg, - etherType, - data, - len); - - unsigned int count = 0; - - for(std::vector
::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) { - if (*ast != RR->identity.address()) { - out.sendAndLog(RR,*ast); - if (++count >= limit) - break; + gs.txQueue.push_back(OutboundMulticast()); + OutboundMulticast &out = gs.txQueue.back(); + + out.init( + RR, + now, + nwid, + com, + limit, + gatherLimit, + src, + mg, + etherType, + data, + len); + + unsigned int count = 0; + + for(std::vector
::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) { + if (*ast != RR->identity.address()) { + out.sendAndLog(RR,*ast); + if (++count >= limit) + break; + } } - } - unsigned long idx = 0; - while ((count < limit)&&(idx < gs.members.size())) { - Address ma(gs.members[indexes[idx++]].address); - if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) { - out.sendAndLog(RR,ma); - ++count; + unsigned long idx = 0; + while ((count < limit)&&(idx < gs.members.size())) { + Address ma(gs.members[indexes[idx++]].address); + if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) { + out.sendAndLog(RR,ma); + ++count; + } } } - } + } catch ( ... ) {} // this is a sanity check to catch any failures and make sure indexes[] still gets deleted // Free allocated memory buffer if any if (indexes != idxbuf) diff --git a/node/Node.cpp b/node/Node.cpp index 42180e99..74acc869 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -305,18 +305,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB for(std::vector< SharedPtr >::const_iterator n(needConfig.begin());n!=needConfig.end();++n) (*n)->requestConfiguration(); - // Attempt to contact network preferred relays that we don't have direct links to - std::sort(networkRelays.begin(),networkRelays.end()); - networkRelays.erase(std::unique(networkRelays.begin(),networkRelays.end()),networkRelays.end()); - for(std::vector< std::pair >::const_iterator nr(networkRelays.begin());nr!=networkRelays.end();++nr) { - if (nr->second) { - SharedPtr rp(RR->topology->getPeer(nr->first)); - if ((rp)&&(!rp->hasActiveDirectPath(now))) - rp->attemptToContactAt(RR,InetAddress(),nr->second,now); - } - } - - // Ping living or root server/relay peers + // Do pings and keepalives _PingPeersThatNeedPing pfunc(RR,now,networkRelays); RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc); diff --git a/tests/http/big-test-kill.sh b/tests/http/big-test-kill.sh index 4a764d1f..59f36788 100755 --- a/tests/http/big-test-kill.sh +++ b/tests/http/big-test-kill.sh @@ -13,6 +13,6 @@ CONTAINER_IMAGE=zerotier/http-test export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin -pssh -h big-test-hosts -i -t 128 -p 256 "docker ps -aq | xargs -r docker rm -f" +pssh -h big-test-hosts -i -t 0 -p 256 "docker ps -aq | xargs -r docker rm -f" exit 0 diff --git a/tests/http/big-test-ready.sh b/tests/http/big-test-ready.sh index 391ca2a1..aa540bba 100755 --- a/tests/http/big-test-ready.sh +++ b/tests/http/big-test-ready.sh @@ -25,6 +25,6 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin # docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE #done -pssh -h big-test-hosts -i -t 128 -p 256 "docker pull $CONTAINER_IMAGE" +pssh -h big-test-hosts -i -t 0 -p 256 "docker pull $CONTAINER_IMAGE" exit 0 diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh index a5e71ef1..43166c6e 100755 --- a/tests/http/big-test-start.sh +++ b/tests/http/big-test-start.sh @@ -1,7 +1,7 @@ #!/bin/bash # Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits -NUM_CONTAINERS=100 +NUM_CONTAINERS=25 CONTAINER_IMAGE=zerotier/http-test # @@ -25,6 +25,6 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin # docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE #done -pssh -h big-test-hosts -i -t 128 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.25; done" +pssh -h big-test-hosts -i -t 0 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.25; done" exit 0 -- cgit v1.2.3 From 7fbe2f7adf3575f3a21fc1ab3a5a2a036e18e6e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 15:38:53 -0800 Subject: Tweak some more timings for better reliability. --- node/Cluster.hpp | 2 +- node/Constants.hpp | 10 +++++----- node/Node.cpp | 2 +- node/Peer.hpp | 4 ++-- node/SelfAwareness.cpp | 2 +- node/Switch.cpp | 6 +++--- node/Topology.hpp | 9 ++++++--- tests/http/big-test-start.sh | 4 ++-- 8 files changed, 21 insertions(+), 18 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Cluster.hpp b/node/Cluster.hpp index f1caa436..ee220999 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -55,7 +55,7 @@ /** * How often should we announce that we have a peer? */ -#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD (ZT_PEER_DIRECT_PING_DELAY / 2) +#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD ZT_PEER_DIRECT_PING_DELAY /** * Desired period between doPeriodicTasks() in milliseconds diff --git a/node/Constants.hpp b/node/Constants.hpp index bb62484d..552688a6 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -267,14 +267,14 @@ #define ZT_PEER_DIRECT_PING_DELAY 60000 /** - * Delay between requests for updated network autoconf information + * Timeout for overall peer activity (measured from last receive) */ -#define ZT_NETWORK_AUTOCONF_DELAY 60000 +#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 4) + ZT_PING_CHECK_INVERVAL) /** - * Timeout for overall peer activity (measured from last receive) + * Delay between requests for updated network autoconf information */ -#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 3) + (ZT_PING_CHECK_INVERVAL * 2)) +#define ZT_NETWORK_AUTOCONF_DELAY 60000 /** * Minimum interval between attempts by relays to unite peers @@ -283,7 +283,7 @@ * a RENDEZVOUS message no more than this often. This instructs the peers * to attempt NAT-t and gives each the other's corresponding IP:port pair. */ -#define ZT_MIN_UNITE_INTERVAL 60000 +#define ZT_MIN_UNITE_INTERVAL 30000 /** * Delay between initial direct NAT-t packet and more aggressive techniques diff --git a/node/Node.cpp b/node/Node.cpp index 74acc869..82cb7ddb 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -263,7 +263,7 @@ public: } lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream); - } else if (p->alive(_now)) { + } else if (p->activelyTransferringFrames(_now)) { // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently p->doPingAndKeepalive(RR,_now,0); } diff --git a/node/Peer.hpp b/node/Peer.hpp index e5db3bde..ad4c6746 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -231,9 +231,9 @@ public: inline uint64_t lastAnnouncedTo() const throw() { return _lastAnnouncedTo; } /** - * @return True if peer has received an actual data frame within ZT_PEER_ACTIVITY_TIMEOUT milliseconds + * @return True if this peer is actively sending real network frames */ - inline uint64_t alive(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); } + inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); } /** * @return Current latency or 0 if unknown (max: 65535) diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index d8eca071..ce75eb03 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -128,7 +128,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // links to be re-established if possible, possibly using a root server or some // other relay. for(std::vector< SharedPtr >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) { - if ((*p)->alive(now)) { + if ((*p)->activelyTransferringFrames(now)) { Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP); RR->sw->send(outp,true,0); } diff --git a/node/Switch.cpp b/node/Switch.cpp index 2f72f57a..120ce7a4 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -442,8 +442,8 @@ unsigned long Switch::doTimerTasks(uint64_t now) Mutex::Lock _l(_contactQueue_m); for(std::list::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) { if (now >= qi->fireAtTime) { - if ((!qi->peer->alive(now))||(qi->peer->hasActiveDirectPath(now))) { - // Cancel attempt if we've already connected or peer is no longer "alive" + if (qi->peer->hasActiveDirectPath(now)) { + // Cancel if connection has succeeded _contactQueue.erase(qi++); continue; } else { @@ -539,7 +539,7 @@ unsigned long Switch::doTimerTasks(uint64_t now) _LastUniteKey *k = (_LastUniteKey *)0; uint64_t *v = (uint64_t *)0; while (i.next(k,v)) { - if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 16)) + if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 8)) _lastUniteAttempt.erase(*k); } } diff --git a/node/Topology.hpp b/node/Topology.hpp index 4c1a2ab3..a0c28b0f 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -81,6 +81,11 @@ public: /** * Get a peer only if it is presently in memory (no disk cache) * + * This also does not update the lastUsed() time for peers, which means + * that it won't prevent them from falling out of RAM. This is currently + * used in the Cluster code to update peer info without forcing all peers + * across the entire cluster to remain in memory cache. + * * @param zta ZeroTier address * @param now Current time */ @@ -88,10 +93,8 @@ public: { Mutex::Lock _l(_lock); const SharedPtr *const ap = _peers.get(zta); - if (ap) { - (*ap)->use(now); + if (ap) return *ap; - } return SharedPtr(); } diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh index 43166c6e..f300ac61 100755 --- a/tests/http/big-test-start.sh +++ b/tests/http/big-test-start.sh @@ -1,7 +1,7 @@ #!/bin/bash # Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits -NUM_CONTAINERS=25 +NUM_CONTAINERS=50 CONTAINER_IMAGE=zerotier/http-test # @@ -25,6 +25,6 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin # docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE #done -pssh -h big-test-hosts -i -t 0 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.25; done" +pssh -h big-test-hosts -o big-test-out -t 0 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.25; done" exit 0 -- cgit v1.2.3 From 8ef4edebbfeead53e5b2b454086e21e42e809aab Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 5 Nov 2015 12:22:58 -0800 Subject: Deferred decode for HELLO to prevent HELLOcalypse under high load of new peers. --- include/ZeroTierOne.h | 21 ++++++++ node/BinarySemaphore.hpp | 106 +++++++++++++++++++++++++++++++++++++++++ node/DeferredPackets.cpp | 95 +++++++++++++++++++++++++++++++++++++ node/DeferredPackets.hpp | 98 ++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.cpp | 113 ++++++++++++++++++++++++-------------------- node/IncomingPacket.hpp | 17 +++++-- node/Node.cpp | 38 ++++++++++++++- node/Node.hpp | 1 + node/RuntimeEnvironment.hpp | 7 +++ node/SharedPtr.hpp | 33 +++++++------ node/Switch.cpp | 8 ++-- objects.mk | 1 + 12 files changed, 464 insertions(+), 74 deletions(-) create mode 100644 node/BinarySemaphore.hpp create mode 100644 node/DeferredPackets.cpp create mode 100644 node/DeferredPackets.hpp (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index e9b38c52..fd7857d9 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1511,6 +1511,27 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned */ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); +/** + * Do things in the background until Node dies + * + * This function can be called from one or more background threads to process + * certain tasks in the background to improve foreground performance. It will + * not return until the Node is shut down. If threading is not enabled in + * this build it will return immediately and will do nothing. + * + * This is completely optional. If this is never called, all processing is + * done in the foreground in the various processXXXX() methods. + * + * This does NOT replace or eliminate the need to call the normal + * processBackgroundTasks() function in your main loop. This mechanism is + * used to offload the processing of expensive mssages onto background + * handler threads to prevent foreground performance degradation under + * high load. + * + * @param node Node instance + */ +void ZT_Node_backgroundThreadMain(ZT_Node *node); + /** * Get ZeroTier One version * diff --git a/node/BinarySemaphore.hpp b/node/BinarySemaphore.hpp new file mode 100644 index 00000000..97d0d1c4 --- /dev/null +++ b/node/BinarySemaphore.hpp @@ -0,0 +1,106 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_BINARYSEMAPHORE_HPP +#define ZT_BINARYSEMAPHORE_HPP + +#include +#include +#include + +#include "Constants.hpp" +#include "NonCopyable.hpp" + +#ifdef __WINDOWS__ + +#include + +namespace ZeroTier { + +class BinarySemaphore : NonCopyable +{ +public: + BinarySemaphore() throw() { _sem = CreateSemaphore(NULL,0,1,NULL); } + ~BinarySemaphore() { CloseHandle(_sem); } + inline void wait() { WaitForSingleObject(_sem,INFINITE); } + inline void post() { ReleaseSemaphore(_sem,1,NULL); } +private: + HANDLE _sem; +}; + +} // namespace ZeroTier + +#else // !__WINDOWS__ + +#include + +namespace ZeroTier { + +class BinarySemaphore : NonCopyable +{ +public: + BinarySemaphore() + { + pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0); + pthread_cond_init(&_cond,(const pthread_condattr_t *)0); + _f = false; + } + + ~BinarySemaphore() + { + pthread_cond_destroy(&_cond); + pthread_mutex_destroy(&_mh); + } + + inline void wait() + { + pthread_mutex_lock(const_cast (&_mh)); + while (!_f) + pthread_cond_wait(const_cast (&_cond),const_cast (&_mh)); + _f = false; + pthread_mutex_unlock(const_cast (&_mh)); + } + + inline void post() + { + pthread_mutex_lock(const_cast (&_mh)); + _f = true; + pthread_mutex_unlock(const_cast (&_mh)); + pthread_cond_signal(const_cast (&_cond)); + } + +private: + pthread_cond_t _cond; + pthread_mutex_t _mh; + volatile bool _f; +}; + +} // namespace ZeroTier + +#endif // !__WINDOWS__ + +#endif diff --git a/node/DeferredPackets.cpp b/node/DeferredPackets.cpp new file mode 100644 index 00000000..923e1339 --- /dev/null +++ b/node/DeferredPackets.cpp @@ -0,0 +1,95 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include "Constants.hpp" +#include "DeferredPackets.hpp" +#include "IncomingPacket.hpp" +#include "RuntimeEnvironment.hpp" +#include "Node.hpp" + +namespace ZeroTier { + +DeferredPackets::DeferredPackets(const RuntimeEnvironment *renv) : + RR(renv), + _readPtr(0), + _writePtr(0), + _die(false) +{ +} + +DeferredPackets::~DeferredPackets() +{ + _q_m.lock(); + _die = true; + _q_m.unlock(); + _q_s.post(); +} + +bool DeferredPackets::enqueue(IncomingPacket *pkt) +{ + _q_m.lock(); + const unsigned long p = _writePtr % ZT_DEFFEREDPACKETS_MAX; + if (_q[p]) { + _q_m.unlock(); + return false; + } else { + _q[p].setToUnsafe(pkt); + ++_writePtr; + _q_m.unlock(); + _q_s.post(); + return true; + } +} + +int DeferredPackets::process() +{ + SharedPtr pkt; + + _q_m.lock(); + if (_die) { + _q_m.unlock(); + _q_s.post(); + return -1; + } + while (_readPtr == _writePtr) { + _q_m.unlock(); + _q_s.wait(); + _q_m.lock(); + if (_die) { + _q_m.unlock(); + _q_s.post(); + return -1; + } + } + pkt.swap(_q[_readPtr++ % ZT_DEFFEREDPACKETS_MAX]); + _q_m.unlock(); + + pkt->tryDecode(RR,true); + return 1; +} + +} // namespace ZeroTier diff --git a/node/DeferredPackets.hpp b/node/DeferredPackets.hpp new file mode 100644 index 00000000..1ea65f3c --- /dev/null +++ b/node/DeferredPackets.hpp @@ -0,0 +1,98 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_DEFERREDPACKETS_HPP +#define ZT_DEFERREDPACKETS_HPP + +#include "Constants.hpp" +#include "SharedPtr.hpp" +#include "Mutex.hpp" +#include "DeferredPackets.hpp" +#include "BinarySemaphore.hpp" + +/** + * Maximum number of deferred packets + */ +#define ZT_DEFFEREDPACKETS_MAX 1024 + +namespace ZeroTier { + +class IncomingPacket; +class RuntimeEnvironment; + +/** + * Deferred packets + * + * IncomingPacket can defer its decoding this way by enqueueing itself here. + * When this is done, deferredDecode() is called later. This is done for + * operations that may be expensive to allow them to potentially be handled + * in the background or rate limited to maintain quality of service for more + * routine operations. + */ +class DeferredPackets +{ +public: + DeferredPackets(const RuntimeEnvironment *renv); + ~DeferredPackets(); + + /** + * Enqueue a packet + * + * Since packets enqueue themselves, they call it with 'this' and we wrap + * them in a SharedPtr<>. This is safe as SharedPtr<> is introspective and + * supports this. This should not be called from any other code outside + * IncomingPacket. + * + * @param pkt Packet to process later (possibly in the background) + * @return False if queue is full + */ + bool enqueue(IncomingPacket *pkt); + + /** + * Wait for and then process a deferred packet + * + * If we are shutting down (in destructor), this returns -1 and should + * not be called again. Otherwise it returns the number of packets + * processed. + * + * @return Number processed or -1 if shutting down + */ + int process(); + +private: + SharedPtr _q[ZT_DEFFEREDPACKETS_MAX]; + const RuntimeEnvironment *const RR; + unsigned long _readPtr; + unsigned long _writePtr; + bool _die; + Mutex _q_m; + BinarySemaphore _q_s; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 32229ba6..f2216235 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -46,21 +46,31 @@ #include "Cluster.hpp" #include "Node.hpp" #include "AntiRecursion.hpp" +#include "DeferredPackets.hpp" namespace ZeroTier { -bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) +bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred) { const Address sourceAddress(source()); try { if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) { - // Unencrypted HELLOs are handled here since they are used to - // populate our identity cache in the first place. _doHELLO() is special - // in that it contains its own authentication logic. - return _doHELLO(RR); + // Unencrypted HELLOs require some potentially expensive verification, so + // do this in the background if background processing is enabled. + DeferredPackets *const dp = RR->dp; // read volatile pointer + if ((dp)&&(!deferred)) { + dp->enqueue(this); + return true; // 'handled' via deferring to background thread(s) + } else { + // A null pointer for peer to _doHELLO() tells it to run its own + // special internal authentication logic. This is done for unencrypted + // HELLOs to learn new identities, etc. + SharedPtr tmp; + return _doHELLO(RR,tmp); + } } - SharedPtr peer = RR->topology->getPeer(sourceAddress); + SharedPtr peer(RR->topology->getPeer(sourceAddress)); if (peer) { if (!dearmor(peer->key())) { TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size()); @@ -79,7 +89,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) default: // ignore unknown verbs, but if they pass auth check they are "received" peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP); return true; - case Packet::VERB_HELLO: return _doHELLO(RR); + + case Packet::VERB_HELLO: return _doHELLO(RR,peer); case Packet::VERB_ERROR: return _doERROR(RR,peer); case Packet::VERB_OK: return _doOK(RR,peer); case Packet::VERB_WHOIS: return _doWHOIS(RR,peer); @@ -185,7 +196,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr return true; } -bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) +bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr &peer) { /* Note: this is the only packet ever sent in the clear, and it's also * the only packet that we authenticate via a different path. Authentication @@ -226,63 +237,65 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) return true; } - SharedPtr peer(RR->topology->getPeer(id.address())); - if (peer) { - // We already have an identity with this address -- check for collisions - - if (peer->identity() != id) { - // Identity is different from the one we already have -- address collision - - unsigned char key[ZT_PEER_SECRET_KEY_LENGTH]; - if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) { - if (dearmor(key)) { // ensure packet is authentic, otherwise drop - TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR); - outp.append((unsigned char)Packet::VERB_HELLO); - outp.append((uint64_t)pid); - outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION); - outp.armor(key,true); - RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + if (!peer) { + peer = RR->topology->getPeer(id.address()); + if (peer) { + // We already have an identity with this address -- check for collisions + + if (peer->identity() != id) { + // Identity is different from the one we already have -- address collision + + unsigned char key[ZT_PEER_SECRET_KEY_LENGTH]; + if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) { + if (dearmor(key)) { // ensure packet is authentic, otherwise drop + TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_HELLO); + outp.append((uint64_t)pid); + outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION); + outp.armor(key,true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + } else { + TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + } } else { - TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_remoteAddress.toString().c_str()); } + + return true; } else { - TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - } + // Identity is the same as the one we already have -- check packet integrity - return true; + if (!dearmor(peer->key())) { + TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // Continue at // VALID + } } else { - // Identity is the same as the one we already have -- check packet integrity + // We don't already have an identity with this address -- validate and learn it - if (!dearmor(peer->key())) { + // Check identity proof of work + if (!id.locallyValidate()) { + TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // Check packet integrity and authentication + SharedPtr newPeer(new Peer(RR->identity,id)); + if (!dearmor(newPeer->key())) { TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } + peer = RR->topology->addPeer(newPeer); // Continue at // VALID } - } else { - // We don't already have an identity with this address -- validate and learn it - - // Check identity proof of work - if (!id.locallyValidate()) { - TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - // Check packet integrity and authentication - SharedPtr newPeer(new Peer(RR->identity,id)); - if (!dearmor(newPeer->key())) { - TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - peer = RR->topology->addPeer(newPeer); - - // Continue at // VALID + // VALID -- if we made it here, packet passed identity and authenticity checks! } - // VALID -- if we made it here, packet passed identity and authenticity checks! - if (externalSurfaceAddress) RR->sa->iam(id.address(),_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now()); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index f5dd4b27..7fb7dbd3 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -93,14 +93,21 @@ public: * about whether the packet was valid. A rejection is 'complete.' * * Once true is returned, this must not be called again. The packet's state - * may no longer be valid. + * may no longer be valid. The only exception is deferred decoding. In this + * case true is returned to indicate to the normal decode path that it is + * finished with the packet. The packet will have added itself to the + * deferred queue and will expect tryDecode() to be called one more time + * with deferred set to true. + * + * Deferred decoding is performed by DeferredPackets.cpp and should not be + * done elsewhere. Under deferred decoding packets only get one shot and + * so the return value of tryDecode() is ignored. * * @param RR Runtime environment + * @param deferred If true, this is a deferred decode and the return is ignored * @return True if decoding and processing is complete, false if caller should try again - * @throws std::out_of_range Range error processing packet (should be discarded) - * @throws std::runtime_error Other error processing packet (should be discarded) */ - bool tryDecode(const RuntimeEnvironment *RR); + bool tryDecode(const RuntimeEnvironment *RR,bool deferred); /** * @return Time of packet receipt / start of decode @@ -132,7 +139,7 @@ private: // These are called internally to handle packet contents once it has // been authenticated, decrypted, decompressed, and classified. bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr &peer); - bool _doHELLO(const RuntimeEnvironment *RR); + bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr &peer); // can be called with NULL peer, while all others cannot bool _doOK(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr &peer); diff --git a/node/Node.cpp b/node/Node.cpp index 82cb7ddb..bcf5db1a 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -47,6 +47,7 @@ #include "Identity.hpp" #include "SelfAwareness.hpp" #include "Cluster.hpp" +#include "DeferredPackets.hpp" const struct sockaddr_storage ZT_SOCKADDR_NULL = {0}; @@ -130,7 +131,14 @@ Node::Node( Node::~Node() { Mutex::Lock _l(_networks_m); - _networks.clear(); // ensure that networks are destroyed before shutdown + Mutex::Lock _l2(RR->dpSetLock); + + _networks.clear(); // ensure that networks are destroyed before shutdow + + DeferredPackets *dp = RR->dp; + RR->dp = (DeferredPackets *)0; + delete dp; + delete RR->sa; delete RR->topology; delete RR->antiRec; @@ -637,6 +645,27 @@ void Node::clusterStatus(ZT_ClusterStatus *cs) memset(cs,0,sizeof(ZT_ClusterStatus)); } +void Node::backgroundThreadMain() +{ + RR->dpSetLock.lock(); + if (!RR->dp) { + try { + RR->dp = new DeferredPackets(RR); + } catch ( ... ) { // sanity check -- could only really happen if out of memory + RR->dpSetLock.unlock(); + return; + } + } + RR->dpSetLock.unlock(); + + for(;;) { + try { + if (RR->dp->process() < 0) + break; + } catch ( ... ) {} // sanity check -- should not throw + } +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -978,6 +1007,13 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs) } catch ( ... ) {} } +void ZT_Node_backgroundThreadMain(ZT_Node *node) +{ + try { + reinterpret_cast(node)->backgroundThreadMain(); + } catch ( ... ) {} +} + void ZT_version(int *major,int *minor,int *revision,unsigned long *featureFlags) { if (major) *major = ZEROTIER_ONE_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index 9b85b832..800c0a55 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -125,6 +125,7 @@ public: void clusterRemoveMember(unsigned int memberId); void clusterHandleIncomingMessage(const void *msg,unsigned int len); void clusterStatus(ZT_ClusterStatus *cs); + void backgroundThreadMain(); // Internal functions ------------------------------------------------------ diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 2ec88f72..18d9e8e5 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -32,6 +32,7 @@ #include "Constants.hpp" #include "Identity.hpp" +#include "Mutex.hpp" namespace ZeroTier { @@ -44,6 +45,7 @@ class AntiRecursion; class NetworkController; class SelfAwareness; class Cluster; +class DeferredPackets; /** * Holds global state for an instance of ZeroTier::Node @@ -55,6 +57,7 @@ public: node(n) ,identity() ,localNetworkController((NetworkController *)0) + ,dp((DeferredPackets *)0) ,sw((Switch *)0) ,mc((Multicaster *)0) ,antiRec((AntiRecursion *)0) @@ -77,6 +80,10 @@ public: // This is set externally to an instance of this base class NetworkController *localNetworkController; + // This is created if background threads call Node::backgroundThreadMain(). + DeferredPackets *volatile dp; // can be read without lock but not written + Mutex dpSetLock; + /* * Order matters a bit here. These are constructed in this order * and then deleted in the opposite order on Node exit. The order ensures diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp index 4ecfa818..289c499f 100644 --- a/node/SharedPtr.hpp +++ b/node/SharedPtr.hpp @@ -64,20 +64,6 @@ public: ++obj->__refCount; } - SharedPtr(T *obj,bool runAwayFromZombies) - throw() : - _ptr(obj) - { - // HACK: this is used in "handlers" to take ownership of naked pointers, - // an ugly pattern that really ought to be factored out. - if (runAwayFromZombies) { - if ((int)(++obj->__refCount) < 2) { - --obj->__refCount; - _ptr = (T *)0; - } - } else ++obj->__refCount; - } - SharedPtr(const SharedPtr &sp) throw() : _ptr(sp._getAndInc()) @@ -105,6 +91,25 @@ public: return *this; } + /** + * Set to a naked pointer and increment its reference count + * + * This assumes this SharedPtr is NULL and that ptr is not a 'zombie.' No + * checks are performed. + * + * @param ptr Naked pointer to assign + */ + inline void setToUnsafe(T *ptr) + { + ++ptr->__refCount; + _ptr = ptr; + } + + /** + * Swap with another pointer 'for free' without ref count overhead + * + * @param with Pointer to swap with + */ inline void swap(SharedPtr &with) throw() { diff --git a/node/Switch.cpp b/node/Switch.cpp index 97befbc6..c047a3d1 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -475,7 +475,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr &peer) { // finish processing any packets waiting on peer's public key / identity Mutex::Lock _l(_rxQueue_m); for(std::list< SharedPtr >::iterator rxi(_rxQueue.begin());rxi!=_rxQueue.end();) { - if ((*rxi)->tryDecode(RR)) + if ((*rxi)->tryDecode(RR,false)) _rxQueue.erase(rxi++); else ++rxi; } @@ -672,7 +672,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet packet->append(dq.frags[f - 1].payload(),dq.frags[f - 1].payloadLength()); _defragQueue.erase(pid); // dq no longer valid after this - if (!packet->tryDecode(RR)) { + if (!packet->tryDecode(RR,false)) { Mutex::Lock _l(_rxQueue_m); _rxQueue.push_back(packet); } @@ -746,7 +746,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr packet->append(dq.frags[f - 1].payload(),dq.frags[f - 1].payloadLength()); _defragQueue.erase(pid); // dq no longer valid after this - if (!packet->tryDecode(RR)) { + if (!packet->tryDecode(RR,false)) { Mutex::Lock _l(_rxQueue_m); _rxQueue.push_back(packet); } @@ -757,7 +757,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr } // else this is a duplicate head, ignore } else { // Packet is unfragmented, so just process it - if (!packet->tryDecode(RR)) { + if (!packet->tryDecode(RR,false)) { Mutex::Lock _l(_rxQueue_m); _rxQueue.push_back(packet); } diff --git a/objects.mk b/objects.mk index 540072d5..8daec8b5 100644 --- a/objects.mk +++ b/objects.mk @@ -5,6 +5,7 @@ OBJS=\ node/C25519.o \ node/CertificateOfMembership.o \ node/Cluster.o \ + node/DeferredPackets.o \ node/Dictionary.o \ node/Identity.o \ node/IncomingPacket.o \ -- cgit v1.2.3 From 5f39d5b7ea202ca39cef46779b5406e35e0dcb3e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 6 Nov 2015 14:37:17 -0800 Subject: Further pare down Cluster messaging and rename some stuff. --- node/Cluster.cpp | 31 ++++++++++--------------------- node/Cluster.hpp | 7 +------ node/IncomingPacket.cpp | 4 ++-- node/Node.cpp | 4 ++-- node/Peer.cpp | 10 +++++----- node/Peer.hpp | 2 +- node/Switch.cpp | 6 +++--- 7 files changed, 24 insertions(+), 40 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Cluster.cpp b/node/Cluster.cpp index e9c5856f..dc7ecab2 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -214,19 +214,10 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) case STATE_MESSAGE_HAVE_PEER: { const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; - InetAddress physicalAddress; - ptr += physicalAddress.deserialize(dmsg,ptr); - if (physicalAddress) { - SharedPtr myPeerRecord(RR->topology->getPeerNoCache(zeroTierAddress)); - if (myPeerRecord) - myPeerRecord->removePathByAddress(physicalAddress); - } - { - Mutex::Lock _l2(_peerAffinities_m); - _PA &pa = _peerAffinities[zeroTierAddress]; - pa.ts = RR->node->now(); - pa.mid = fromMemberId; - } + Mutex::Lock _l2(_peerAffinities_m); + _PA &pa = _peerAffinities[zeroTierAddress]; + pa.ts = RR->node->now(); + pa.mid = fromMemberId; TRACE("[%u] has %s @ %s",(unsigned int)fromMemberId,id.address().toString().c_str(),physicalAddress.toString().c_str()); } break; @@ -402,7 +393,7 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee return true; } -void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress) +void Cluster::replicateHavePeer(const Identity &peerId) { const uint64_t now = RR->node->now(); { @@ -420,14 +411,13 @@ void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physic } } - Buffer<1024> buf; - peerId.address().appendTo(buf); - physicalAddress.serialize(buf); + char buf[ZT_ADDRESS_LENGTH]; + peerId.address().copyTo(buf,ZT_ADDRESS_LENGTH); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { Mutex::Lock _l2(_members[*mid].lock); - _send(*mid,STATE_MESSAGE_HAVE_PEER,buf.data(),buf.size()); + _send(*mid,STATE_MESSAGE_HAVE_PEER,buf,ZT_ADDRESS_LENGTH); } } } @@ -472,9 +462,8 @@ struct _ClusterAnnouncePeers Cluster *const parent; inline void operator()(const Topology &t,const SharedPtr &peer) const { - Path *p = peer->getBestPath(now); - if (p) - parent->replicateHavePeer(peer->identity(),p->address()); + if (peer->hasActiveDirectPath(now)) + parent->replicateHavePeer(peer->identity()); } }; void Cluster::doPeriodicTasks() diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 22297506..73391114 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -125,10 +125,6 @@ public: /** * Cluster member has this peer: * <[5] ZeroTier address of peer> - * <[...] binary serialized peer remote physical address> - * - * Clusters send this message when they learn a path to a peer. The - * replicated physical address is the one learned. */ STATE_MESSAGE_HAVE_PEER = 2, @@ -237,9 +233,8 @@ public: * Advertise to the cluster that we have this peer * * @param peerId Identity of peer that we have - * @param physicalAddress Physical address of peer (from our POV) */ - void replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress); + void replicateHavePeer(const Identity &peerId); /** * Advertise a multicast LIKE to the cluster diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f2216235..26339b00 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -972,7 +972,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - peer->attemptToContactAt(RR,_localAddress,a,now); + peer->sendHELLO(RR,_localAddress,a,now); } else { TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } @@ -983,7 +983,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - peer->attemptToContactAt(RR,_localAddress,a,now); + peer->sendHELLO(RR,_localAddress,a,now); } else { TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } diff --git a/node/Node.cpp b/node/Node.cpp index bcf5db1a..6ba038eb 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -249,7 +249,7 @@ public: } else { if (stableEndpoint4) { needToContactIndirect = false; - p->attemptToContactAt(RR,InetAddress(),stableEndpoint4,_now); + p->sendHELLO(RR,InetAddress(),stableEndpoint4,_now); } } if (p->doPingAndKeepalive(RR,_now,AF_INET6)) { @@ -257,7 +257,7 @@ public: } else { if (stableEndpoint6) { needToContactIndirect = false; - p->attemptToContactAt(RR,InetAddress(),stableEndpoint6,_now); + p->sendHELLO(RR,InetAddress(),stableEndpoint6,_now); } } diff --git a/node/Peer.cpp b/node/Peer.cpp index 0b981c8e..d3394da6 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -188,7 +188,7 @@ void Peer::received( if ((now - _lastPathConfirmationSent) >= ZT_MIN_PATH_CONFIRMATION_INTERVAL) { _lastPathConfirmationSent = now; TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str()); - attemptToContactAt(RR,localAddr,remoteAddr,now); + sendHELLO(RR,localAddr,remoteAddr,now); } } @@ -198,7 +198,7 @@ void Peer::received( #ifdef ZT_ENABLE_CLUSTER if ((RR->cluster)&&(pathIsConfirmed)) - RR->cluster->replicateHavePeer(_id,remoteAddr); + RR->cluster->replicateHavePeer(_id); #endif if (needMulticastGroupAnnounce) { @@ -208,7 +208,7 @@ void Peer::received( } } -void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now) +void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now) { // _lock not required here since _id is immutable and nothing else is accessed @@ -242,7 +242,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet if (p) { if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { //TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived()); - attemptToContactAt(RR,p->localAddress(),p->address(),now); + sendHELLO(RR,p->localAddress(),p->address(),now); p->sent(now); } else if (((now - p->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!p->reliable())) { //TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived()); @@ -348,7 +348,7 @@ bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope sc unsigned int y = 0; while (x < np) { if (_paths[x].address().ipScope() == scope) { - attemptToContactAt(RR,_paths[x].localAddress(),_paths[x].address(),now); + sendHELLO(RR,_paths[x].localAddress(),_paths[x].address(),now); } else { _paths[y++] = _paths[x]; } diff --git a/node/Peer.hpp b/node/Peer.hpp index a70d9868..42708128 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -171,7 +171,7 @@ public: * @param atAddress Destination address * @param now Current time */ - void attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now); + void sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now); /** * Send pings or keepalives depending on configured timeouts diff --git a/node/Switch.cpp b/node/Switch.cpp index 8d3c12f5..4d6dacff 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -435,7 +435,7 @@ void Switch::rendezvous(const SharedPtr &peer,const InetAddress &localAddr { TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str()); const uint64_t now = RR->node->now(); - peer->attemptToContactAt(RR,localAddr,atAddr,now); + peer->sendHELLO(RR,localAddr,atAddr,now); { Mutex::Lock _l(_contactQueue_m); _contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr)); @@ -508,14 +508,14 @@ unsigned long Switch::doTimerTasks(uint64_t now) } else { if (qi->strategyIteration == 0) { // First strategy: send packet directly to destination - qi->peer->attemptToContactAt(RR,qi->localAddr,qi->inaddr,now); + qi->peer->sendHELLO(RR,qi->localAddr,qi->inaddr,now); } else if (qi->strategyIteration <= 4) { // Strategies 1-4: try escalating ports for symmetric NATs that remap sequentially InetAddress tmpaddr(qi->inaddr); int p = (int)qi->inaddr.port() + qi->strategyIteration; if (p < 0xffff) { tmpaddr.setPort((unsigned int)p); - qi->peer->attemptToContactAt(RR,qi->localAddr,tmpaddr,now); + qi->peer->sendHELLO(RR,qi->localAddr,tmpaddr,now); } else qi->strategyIteration = 5; } else { // All strategies tried, expire entry -- cgit v1.2.3 From 94f4316a0ecb56f7e34422a7dfed73efb2bbe1a3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 9 Nov 2015 14:54:05 -0800 Subject: Fix for possible high CPU usage on multicast queries. --- node/IncomingPacket.cpp | 13 ++++++------- node/Node.cpp | 22 ++++++---------------- node/RuntimeEnvironment.hpp | 11 ++++++----- tests/http/big-test-start.sh | 3 ++- 4 files changed, 20 insertions(+), 29 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index d655b856..cffa0b9a 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -57,9 +57,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred) if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) { // Unencrypted HELLOs require some potentially expensive verification, so // do this in the background if background processing is enabled. - DeferredPackets *const dp = RR->dp; // read volatile pointer - if ((dp)&&(!deferred)) { - dp->enqueue(this); + if ((RR->dpEnabled > 0)&&(!deferred)) { + RR->dp->enqueue(this); return true; // 'handled' via deferring to background thread(s) } else { // A null pointer for peer to _doHELLO() tells it to run its own @@ -405,12 +404,12 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p } break; case Packet::VERB_WHOIS: { - /* Right now only root servers are allowed to send OK(WHOIS) to prevent - * poisoning attacks. Further decentralization will require some other - * kind of trust mechanism. */ if (RR->topology->isRoot(peer->identity())) { const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); - if (id.locallyValidate()) + // Right now we can skip this since OK(WHOIS) is only accepted from + // roots. In the future it should be done if we query less trusted + // sources. + //if (id.locallyValidate()) RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr(new Peer(RR->identity,id)))); } } break; diff --git a/node/Node.cpp b/node/Node.cpp index 6ba038eb..31757b45 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -116,7 +116,9 @@ Node::Node( RR->antiRec = new AntiRecursion(); RR->topology = new Topology(RR); RR->sa = new SelfAwareness(RR); + RR->dp = new DeferredPackets(RR); } catch ( ... ) { + delete RR->dp; delete RR->sa; delete RR->topology; delete RR->antiRec; @@ -131,14 +133,11 @@ Node::Node( Node::~Node() { Mutex::Lock _l(_networks_m); - Mutex::Lock _l2(RR->dpSetLock); _networks.clear(); // ensure that networks are destroyed before shutdow - DeferredPackets *dp = RR->dp; - RR->dp = (DeferredPackets *)0; - delete dp; - + RR->dpEnabled = 0; + delete RR->dp; delete RR->sa; delete RR->topology; delete RR->antiRec; @@ -647,23 +646,14 @@ void Node::clusterStatus(ZT_ClusterStatus *cs) void Node::backgroundThreadMain() { - RR->dpSetLock.lock(); - if (!RR->dp) { - try { - RR->dp = new DeferredPackets(RR); - } catch ( ... ) { // sanity check -- could only really happen if out of memory - RR->dpSetLock.unlock(); - return; - } - } - RR->dpSetLock.unlock(); - + ++RR->dpEnabled; for(;;) { try { if (RR->dp->process() < 0) break; } catch ( ... ) {} // sanity check -- should not throw } + --RR->dpEnabled; } /****************************************************************************/ diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 18d9e8e5..10cc6ec0 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -57,12 +57,12 @@ public: node(n) ,identity() ,localNetworkController((NetworkController *)0) - ,dp((DeferredPackets *)0) ,sw((Switch *)0) ,mc((Multicaster *)0) ,antiRec((AntiRecursion *)0) ,topology((Topology *)0) ,sa((SelfAwareness *)0) + ,dp((DeferredPackets *)0) #ifdef ZT_ENABLE_CLUSTER ,cluster((Cluster *)0) #endif @@ -80,10 +80,6 @@ public: // This is set externally to an instance of this base class NetworkController *localNetworkController; - // This is created if background threads call Node::backgroundThreadMain(). - DeferredPackets *volatile dp; // can be read without lock but not written - Mutex dpSetLock; - /* * Order matters a bit here. These are constructed in this order * and then deleted in the opposite order on Node exit. The order ensures @@ -97,10 +93,15 @@ public: AntiRecursion *antiRec; Topology *topology; SelfAwareness *sa; + DeferredPackets *dp; #ifdef ZT_ENABLE_CLUSTER Cluster *cluster; #endif + + // This is set to >0 if background threads are waiting on deferred + // packets, otherwise 'dp' should not be used. + volatile int dpEnabled; }; } // namespace ZeroTier diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh index b2782ac2..1b462285 100755 --- a/tests/http/big-test-start.sh +++ b/tests/http/big-test-start.sh @@ -4,9 +4,10 @@ # 250 with a 16GB RAM VM like Amazon m4.xlarge seems good NUM_CONTAINERS=250 CONTAINER_IMAGE=zerotier/http-test +SCALE_UP_DELAY=4 export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin -pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo sysctl -w net.netfilter.nf_conntrack_max=262144 ; for ((n=0;n<$NUM_CONTAINERS;n++)); do sudo docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 10; done" +pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo sysctl -w net.netfilter.nf_conntrack_max=262144 ; for ((n=0;n<$NUM_CONTAINERS;n++)); do sudo docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep $SCALE_UP_DELAY; done" exit 0 -- cgit v1.2.3 From ccae473783f834c9062474f7bb347b698764c8be Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 12 Nov 2015 16:51:33 -0800 Subject: Eliminate Windows warnings. --- node/Node.cpp | 4 ++-- node/Topology.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 31757b45..4b449401 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -203,7 +203,7 @@ public: for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { if (r->identity.address() == p->address()) { upstream = true; - for(unsigned long k=0,ptr=RR->node->prng();kstableEndpoints.size();++k) { + for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) { const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()]; if (!stableEndpoint4) { if (addr.ss_family == AF_INET) @@ -536,7 +536,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback) outp.append((uint16_t)0); C25519::Signature sig(RR->identity.sign(reinterpret_cast(outp.data()) + ZT_PACKET_IDX_PAYLOAD,outp.size() - ZT_PACKET_IDX_PAYLOAD)); outp.append((uint16_t)sig.size()); - outp.append(sig.data,sig.size()); + outp.append(sig.data,(unsigned int)sig.size()); outp.append((uint16_t)0); // originator doesn't need an extra credential, since it's the originator for(unsigned int h=1;hhopCount;++h) { outp.append((uint8_t)0); diff --git a/node/Topology.cpp b/node/Topology.cpp index bea97ab9..5379ac1f 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -76,7 +76,7 @@ Topology::Topology(const RuntimeEnvironment *renv) : std::string dsWorld(RR->node->dataStoreGet("world")); World cachedWorld; try { - Buffer dswtmp(dsWorld.data(),dsWorld.length()); + Buffer dswtmp(dsWorld.data(),(unsigned int)dsWorld.length()); cachedWorld.deserialize(dswtmp,0); } catch ( ... ) { cachedWorld = World(); // clear if cached world is invalid -- cgit v1.2.3 From 0d9f33dc4f14b7339d1893e79772a8e49b4821eb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 13 Nov 2015 12:14:28 -0800 Subject: Fix: (1) Windows stack overflow due to buffer too large in peer deserialize, (2) clean up some other stuff seen during debugging and reduce the sizes of some buffers due to Windows small stack size, (3) remove a redundant try/catch. --- node/AntiRecursion.hpp | 7 ++- node/Cluster.cpp | 4 +- node/Identity.cpp | 2 +- node/Node.cpp | 33 +++++++------- node/Topology.cpp | 31 +++++-------- node/Topology.hpp | 16 ++++++- one.cpp | 60 +++++++++++-------------- osdep/OSUtils.cpp | 2 +- osdep/WindowsEthernetTap.cpp | 20 ++++----- selftest.cpp | 3 +- windows/ZeroTierOne/ZeroTierOne.vcxproj | 13 +++--- windows/ZeroTierOne/ZeroTierOne.vcxproj.filters | 9 ++-- 12 files changed, 103 insertions(+), 97 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp index 8629d19a..4d9df465 100644 --- a/node/AntiRecursion.hpp +++ b/node/AntiRecursion.hpp @@ -58,7 +58,12 @@ class AntiRecursion public: AntiRecursion() { - memset(_history,0,sizeof(_history)); + for(int i=0;itopology->countActive()); + alive.append((uint64_t)RR->topology->countActive(now)); alive.append((uint64_t)0); // unused/reserved flags alive.append((uint8_t)_zeroTierPhysicalEndpoints.size()); for(std::vector::const_iterator pe(_zeroTierPhysicalEndpoints.begin());pe!=_zeroTierPhysicalEndpoints.end();++pe) @@ -769,7 +769,7 @@ void Cluster::status(ZT_ClusterStatus &status) const s->y = _y; s->z = _z; s->load = 0; // TODO - s->peers = RR->topology->countActive(); + s->peers = RR->topology->countActive(now); for(std::vector::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) { if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check break; diff --git a/node/Identity.cpp b/node/Identity.cpp index e5aaf13d..4611f6a5 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -158,7 +158,7 @@ bool Identity::fromString(const char *str) return false; char *saveptr = (char *)0; - char tmp[4096]; + char tmp[1024]; if (!Utils::scopy(tmp,sizeof(tmp),str)) return false; diff --git a/node/Node.cpp b/node/Node.cpp index 4b449401..f077424b 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -93,21 +93,22 @@ Node::Node( _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream)); } - std::string idtmp(dataStoreGet("identity.secret")); - if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { - TRACE("identity.secret not found, generating..."); - RR->identity.generate(); - idtmp = RR->identity.toString(true); - if (!dataStorePut("identity.secret",idtmp,true)) - throw std::runtime_error("unable to write identity.secret"); - } - RR->publicIdentityStr = RR->identity.toString(false); - RR->secretIdentityStr = RR->identity.toString(true); - - idtmp = dataStoreGet("identity.public"); - if (idtmp != RR->publicIdentityStr) { - if (!dataStorePut("identity.public",RR->publicIdentityStr,false)) - throw std::runtime_error("unable to write identity.public"); + { + std::string idtmp(dataStoreGet("identity.secret")); + if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { + TRACE("identity.secret not found, generating..."); + RR->identity.generate(); + idtmp = RR->identity.toString(true); + if (!dataStorePut("identity.secret",idtmp,true)) + throw std::runtime_error("unable to write identity.secret"); + } + RR->publicIdentityStr = RR->identity.toString(false); + RR->secretIdentityStr = RR->identity.toString(true); + idtmp = dataStoreGet("identity.public"); + if (idtmp != RR->publicIdentityStr) { + if (!dataStorePut("identity.public",RR->publicIdentityStr,false)) + throw std::runtime_error("unable to write identity.public"); + } } try { @@ -662,7 +663,7 @@ void Node::backgroundThreadMain() std::string Node::dataStoreGet(const char *name) { - char buf[16384]; + char buf[1024]; std::string r; unsigned long olen = 0; do { diff --git a/node/Topology.cpp b/node/Topology.cpp index 5379ac1f..1617dd94 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -50,6 +50,7 @@ Topology::Topology(const RuntimeEnvironment *renv) : const uint8_t *all = reinterpret_cast(alls.data()); RR->node->dataStoreDelete("peers.save"); + Buffer *deserializeBuf = new Buffer(); unsigned int ptr = 0; while ((ptr + 4) < alls.size()) { try { @@ -60,7 +61,8 @@ Topology::Topology(const RuntimeEnvironment *renv) : (((unsigned int)all[ptr + 3]) & 0xff) ); unsigned int pos = 0; - SharedPtr p(Peer::deserializeNew(RR->identity,Buffer(all + ptr,reclen + 4),pos)); + deserializeBuf->copyFrom(all + ptr,reclen + 4); + SharedPtr p(Peer::deserializeNew(RR->identity,*deserializeBuf,pos)); ptr += pos; if (!p) break; // stop if invalid records @@ -70,16 +72,19 @@ Topology::Topology(const RuntimeEnvironment *renv) : break; // stop if invalid records } } + delete deserializeBuf; clean(RR->node->now()); std::string dsWorld(RR->node->dataStoreGet("world")); World cachedWorld; - try { - Buffer dswtmp(dsWorld.data(),(unsigned int)dsWorld.length()); - cachedWorld.deserialize(dswtmp,0); - } catch ( ... ) { - cachedWorld = World(); // clear if cached world is invalid + if (dsWorld.length() > 0) { + try { + Buffer dswtmp(dsWorld.data(),(unsigned int)dsWorld.length()); + cachedWorld.deserialize(dswtmp,0); + } catch ( ... ) { + cachedWorld = World(); // clear if cached world is invalid + } } World defaultWorld; { @@ -315,20 +320,6 @@ void Topology::clean(uint64_t now) } } -unsigned long Topology::countActive() const -{ - const uint64_t now = RR->node->now(); - unsigned long cnt = 0; - Mutex::Lock _l(_lock); - Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); - Address *a = (Address *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { - cnt += (unsigned long)((*p)->hasActiveDirectPath(now)); - } - return cnt; -} - Identity Topology::_getIdentity(const Address &zta) { char p[128]; diff --git a/node/Topology.hpp b/node/Topology.hpp index 68ad273f..a9a5f2f9 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -200,9 +200,21 @@ public: void clean(uint64_t now); /** + * @param now Current time * @return Number of peers with active direct paths */ - unsigned long countActive() const; + inline unsigned long countActive(uint64_t now) const + { + unsigned long cnt = 0; + Mutex::Lock _l(_lock); + Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) { + cnt += (unsigned long)((*p)->hasActiveDirectPath(now)); + } + return cnt; + } /** * Apply a function or function object to all peers @@ -253,7 +265,7 @@ private: Identity _getIdentity(const Address &zta); void _setWorld(const World &newWorld); - const RuntimeEnvironment *RR; + const RuntimeEnvironment *const RR; World _world; Hashtable< Address,SharedPtr > _peers; diff --git a/one.cpp b/one.cpp index c8661fda..11bcc11e 100644 --- a/one.cpp +++ b/one.cpp @@ -958,13 +958,15 @@ int main(int argc,char **argv) #endif // __UNIX_LIKE__ #ifdef __WINDOWS__ - WSADATA wsaData; - WSAStartup(MAKEWORD(2,2),&wsaData); + { + WSADATA wsaData; + WSAStartup(MAKEWORD(2,2),&wsaData); + } #ifdef ZT_WIN_RUN_IN_CONSOLE bool winRunFromCommandLine = true; #else - bool winRunFromCommandLine = false; + bool winRunFromCommandLine = true; #endif #endif // __WINDOWS__ @@ -1153,37 +1155,29 @@ int main(int argc,char **argv) unsigned int returnValue = 0; - try { - for(;;) { - zt1Service = OneService::newInstance(homeDir.c_str(),port); - switch(zt1Service->run()) { - case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done - case OneService::ONE_NORMAL_TERMINATION: - break; - case OneService::ONE_UNRECOVERABLE_ERROR: - fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str()); - returnValue = 1; - break; - case OneService::ONE_IDENTITY_COLLISION: { - delete zt1Service; - zt1Service = (OneService *)0; - std::string oldid; - OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); - if (oldid.length()) { - OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); - OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); - OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); - } - } continue; // restart! - } - break; // terminate loop -- normally we don't keep restarting + for(;;) { + zt1Service = OneService::newInstance(homeDir.c_str(),port); + switch(zt1Service->run()) { + case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done + case OneService::ONE_NORMAL_TERMINATION: + break; + case OneService::ONE_UNRECOVERABLE_ERROR: + fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str()); + returnValue = 1; + break; + case OneService::ONE_IDENTITY_COLLISION: { + delete zt1Service; + zt1Service = (OneService *)0; + std::string oldid; + OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); + if (oldid.length()) { + OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); + OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); + OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); + } + } continue; // restart! } - } catch (std::exception &exc) { - fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],exc.what()); - returnValue = 1; - } catch ( ... ) { - fprintf(stderr,"%s: fatal error: unknown exception"ZT_EOL_S,argv[0]); - returnValue = 1; + break; // terminate loop -- normally we don't keep restarting } delete zt1Service; diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index 45fbf100..728704e5 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -206,7 +206,7 @@ skip_add_inetaddr: bool OSUtils::readFile(const char *path,std::string &buf) { - char tmp[4096]; + char tmp[1024]; FILE *f = fopen(path,"rb"); if (f) { for(;;) { diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index f327009d..5b2bc154 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -213,7 +213,7 @@ std::string WindowsEthernetTap::addNewPersistentTapDevice(const char *pathToInf, Mutex::Lock _l(_systemDeviceManagementLock); GUID classGuid; - char className[4096]; + char className[1024]; if (!WINENV.SetupDiGetINFClassA(pathToInf,&classGuid,className,sizeof(className),(PDWORD)0)) { return std::string("SetupDiGetINFClassA() failed -- unable to read zttap driver INF file"); } @@ -269,9 +269,9 @@ std::string WindowsEthernetTap::addNewPersistentTapDevice(const char *pathToInf, std::string WindowsEthernetTap::destroyAllLegacyPersistentTapDevices() { - char subkeyName[4096]; - char subkeyClass[4096]; - char data[4096]; + char subkeyName[1024]; + char subkeyClass[1024]; + char data[1024]; std::set instanceIdPathsToRemove; { @@ -321,9 +321,9 @@ std::string WindowsEthernetTap::destroyAllLegacyPersistentTapDevices() std::string WindowsEthernetTap::destroyAllPersistentTapDevices() { - char subkeyName[4096]; - char subkeyClass[4096]; - char data[4096]; + char subkeyName[1024]; + char subkeyClass[1024]; + char data[1024]; std::set instanceIdPathsToRemove; { @@ -479,9 +479,9 @@ WindowsEthernetTap::WindowsEthernetTap( _initialized(false), _enabled(true) { - char subkeyName[4096]; - char subkeyClass[4096]; - char data[4096]; + char subkeyName[1024]; + char subkeyClass[1024]; + char data[1024]; char tag[24]; std::string mySubkeyName; diff --git a/selftest.cpp b/selftest.cpp index fa8df48b..87043ddd 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -860,8 +860,9 @@ struct TestPhyHandlers inline void phyOnUnixClose(PhySocket *sock,void **uptr) {} inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {} - inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} #endif // __UNIX_LIKE__ + + inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} }; static int testPhy() { diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index c3163608..82b70400 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -24,6 +24,7 @@ + @@ -44,18 +45,14 @@ - + + false + - - true - true - true - true - @@ -123,6 +120,7 @@ + @@ -232,6 +230,7 @@ NOMINMAX;ZT_TRACE;ZT_USE_MINIUPNPC;%(PreprocessorDefinitions) + false true diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index b9e00b37..5a2767e4 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -96,9 +96,6 @@ Source Files\osdep - - Source Files - Source Files\node @@ -192,6 +189,9 @@ Source Files\node + + Source Files\node + @@ -419,6 +419,9 @@ Header Files\node + + Header Files\node + -- cgit v1.2.3