From 2d0adb562d4d710fe8db87ad47a50e334f3fafbf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sun, 27 Sep 2015 11:37:39 -0700 Subject: [PATCH 001/195] Specify circuit test messages. --- node/Packet.hpp | 172 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 5 deletions(-) diff --git a/node/Packet.hpp b/node/Packet.hpp index fa377964c..2d439b116 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -553,10 +553,10 @@ public: * address that require re-establishing connectivity. * * Destination address types and formats (not all of these are used now): - * 0 - None -- no destination address data present - * 1 - Ethernet address -- format: <[6] Ethernet MAC> - * 4 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port> - * 6 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port> + * 0x00 - None -- no destination address data present + * 0x01 - Ethernet address -- format: <[6] Ethernet MAC> + * 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port> + * 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port> * * OK payload: * <[8] timestamp (echoed from original HELLO)> @@ -904,7 +904,169 @@ public: * * OK and ERROR are not generated. */ - VERB_PUSH_DIRECT_PATHS = 16 + VERB_PUSH_DIRECT_PATHS = 16, + + /* Source-routed circuit test message: + * <[5] address of originator of circuit test> + * <[2] 16-bit flags> + * <[8] 64-bit timestamp> + * <[8] 64-bit test ID (arbitrary, set by tester)> + * <[1] originator credential type (for authorizing test)> + * <[...] credential> + * <[2] 16-bit length of additional fields> + * <[...] additional fields> + * <[2] 16-bit length of signature of request> + * <[...] signature of request by originator> + * <[1] previous hop credential type> + * <[...] previous hop credential> + * <[...] next hop(s) in path> + * + * Flags: + * 0x01 - Report back to originator at each hop + * 0x02 - Report back to originator at last hop + * + * Originator credential types: + * 0x00 - No credentials included + * 0x01 - 64-bit network ID for which originator is controller + * + * Previous hop credential types: + * 0x00 - No credentials included + * 0x01 - Certificate of network membership + * + * Path record format: + * <[1] 8-bit flags> + * <[1] 8-bit breadth (number of next hops)> + * <[...] one or more ZeroTier addresses of next hops> + * + * Path record flags (in each path record): + * 0x80 - End of path (should be set on last entry) + * + * The circuit test allows a device to send a message that will traverse + * the network along a specified path, with each hop optionally reporting + * back to the tester via VERB_CIRCUIT_TEST_REPORT. + * + * Each circuit test packet includes a digital signature by the originator + * of the request, as well as a credential by which that originator claims + * authorization to perform the test. Currently this signature is ed25519, + * but in the future flags might be used to indicate an alternative + * algorithm. For example, the originator might be a network controller. + * In this case the test might be authorized if the recipient is a member + * of a network controlled by it, and if the previous hop(s) are also + * members. Each hop may include its certificate of network membership. + * + * Circuit test paths consist of a series of records. When a node receives + * an authorized circuit test, it: + * + * (1) Reports back to circuit tester as flags indicate + * (2) Reads and removes the next hop from the packet's path + * (3) Sends the packet along to next hop(s), if any. + * + * It is perfectly legal for a path to contain the same hop more than + * once. In fact, this can be a very useful test to determine if a hop + * can be reached bidirectionally and if so what that connectivity looks + * like. + * + * The breadth field in source-routed path records allows a hop to forward + * to more than one recipient, allowing the tester to specify different + * forms of graph traversal in a test. + * + * There is no hard limit to the number of hops in a test, but it is + * practically limited by the maximum size of a (possibly fragmented) + * ZeroTier packet. + * + * Support for circuit tests is optional. If they are not supported, the + * node should respond with an UNSUPPORTED_OPERATION error. If a circuit + * test request is not authorized, it may be ignored or reported as + * an INVALID_REQUEST. No OK messages are generated, but TEST_REPORT + * messages may be sent (see below). + * + * ERROR packet format: + * <[8] 64-bit timestamp (echoed from original> + * <[8] 64-bit test ID (echoed from original)> + */ + VERB_CIRCUIT_TEST = 17, + + /* Circuit test hop report: + * <[8] 64-bit timestamp (from original test)> + * <[8] 64-bit test ID (from original test)> + * <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)> + * <[1] 8-bit vendor ID (set to 0, currently unused)> + * <[1] 8-bit reporter protocol version> + * <[1] 8-bit reporter major version> + * <[1] 8-bit reporter minor version> + * <[2] 16-bit reporter revision> + * <[2] 16-bit reporter OS/platform> + * <[2] 16-bit reporter architecture> + * <[2] 16-bit error code (set to 0, currently unused)> + * <[8] 64-bit report flags> + * <[8] 64-bit source packet ID> + * <[1] 8-bit source packet hop count> + * <[1] 8-bit source address type> + * [<[...] source address>] + * <[2] 16-bit length of network information> + * <[...] network information> + * <[2] 16-bit length of additional fields> + * <[...] additional fields> + * <[2] 16-bit number of next hops to which something is being sent> + * <[...] next hop information> + * + * Circuit test report flags: + * (currently none, must be zero) + * + * Next hop information record format: + * <[5] ZeroTier address of next hop> + * <[1] 8-bit destination wire address type> + * <[...] destination wire address> + * + * See enums below for OS/platform and architecture. Source address format + * is the same as specified in HELLO. + * + * Circuit test reports can be sent by hops in a circuit test to report + * back results. They should include information about the sender as well + * as about the paths to which next hops are being sent. + * + * If a test report is received and no circuit test was sent, it should be + * ignored. This message generates no OK or ERROR response. + */ + VERB_CIRCUIT_TEST_REPORT = 18 + }; + + /** + * Platforms reported in circuit tests + */ + enum CircuitTestReportPlatform + { + CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED = 0, + CIRCUIT_TEST_REPORT_PLATFORM_LINUX = 1, + CIRCUIT_TEST_REPORT_PLATFORM_WINDOWS = 2, + CIRCUIT_TEST_REPORT_PLATFORM_MACOS = 3, + CIRCUIT_TEST_REPORT_PLATFORM_ANDROID = 4, + CIRCUIT_TEST_REPORT_PLATFORM_IOS = 5, + CIRCUIT_TEST_REPORT_PLATFORM_SOLARIS_SMARTOS = 6, + CIRCUIT_TEST_REPORT_PLATFORM_FREEBSD = 7, + CIRCUIT_TEST_REPORT_PLATFORM_NETBSD = 8, + CIRCUIT_TEST_REPORT_PLATFORM_OPENBSD = 9, + CIRCUIT_TEST_REPORT_PLATFORM_RISCOS = 10, + CIRCUIT_TEST_REPORT_PLATFORM_VXWORKS = 11, + CIRCUIT_TEST_REPORT_PLATFORM_FREERTOS = 12, + CIRCUIT_TEST_REPORT_PLATFORM_SYSBIOS = 13, + CIRCUIT_TEST_REPORT_PLATFORM_HURD = 14 + }; + + /** + * Architectures reported in circuit tests + */ + enum CircuitTestReportArchitecture + { + CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED = 0, + CIRCUIT_TEST_REPORT_ARCH_X86 = 1, + CIRCUIT_TEST_REPORT_ARCH_X64 = 2, + CIRCUIT_TEST_REPORT_ARCH_ARM32 = 3, + CIRCUIT_TEST_REPORT_ARCH_ARM64 = 4, + CIRCUIT_TEST_REPORT_ARCH_MIPS32 = 5, + CIRCUIT_TEST_REPORT_ARCH_MIPS64 = 6, + CIRCUIT_TEST_REPORT_ARCH_POWER32 = 7, + CIRCUIT_TEST_REPORT_ARCH_POWER64 = 8 }; /** From a7bd1eaa409a5532c30ac5990a32289496d7ed3e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 28 Sep 2015 15:28:30 -0700 Subject: [PATCH 002/195] Never assign v4 IPs ending in .255 even within range. --- controller/SqliteNetworkController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index d16c5996b..3aa843301 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1742,6 +1742,8 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c for(uint32_t k=ipRangeStart,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) { uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; ++ipTrialCounter; + if ((ip & 0x000000ff) == 0x000000ff) + continue; // don't allow addresses that end in .255 for(std::vector< std::pair >::const_iterator r(routedNetworks.begin());r!=routedNetworks.end();++r) { if ((ip & (0xffffffff << (32 - r->second))) == r->first) { From 1a4f16e0ed1b62ebf3bdbb539858a3874c689fa4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 13:59:05 -0700 Subject: [PATCH 003/195] More work on circuit testing... --- node/IncomingPacket.cpp | 63 +++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.hpp | 4 ++- node/Packet.hpp | 5 +++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c94ffe2e2..3866abf17 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -85,6 +85,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer); case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); + case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer); + case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer); } } else { RR->sw->requestWhois(source()); @@ -926,6 +928,67 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha return true; } +bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + try { + const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + SharedPtr originator(RR->topology->getPeer(originatorAddress)); + if (!originator) { + RR->sw->requestWhois(originatorAddress); + return false; + } + + const unsigned int flags = at(ZT_PACKET_IDX_PAYLOAD + 5); + const uint64_t timestamp = at(ZT_PACKET_IDX_PAYLOAD + 7); + const uint64_t testId = at(ZT_PACKET_IDX_PAYLOAD + 15); + + unsigned int vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); // variable length field length + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { + case 0x01: { // 64-bit network ID, originator must be controller + } break; + default: break; + } + + vlf += at(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused + + const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 28 + vlf); + if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,28 + vlf),28 + vlf,field(30 + vlf,signatureLength),signatureLength)) { + TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); + return true; + } + vlf += signatureLength; + + vlf += at(ZT_PACKET_IDX_PAYLOAD + 30 + vlf); + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]) { + case 0x01: { // network certificate of membership for previous hop + } break; + default: break; + } + + if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) { + const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf]; + Address nextHops[255]; + SharedPtr nextHopPeers[255]; + unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf; + for(unsigned int h=0;((h256 but be safe anyway + nextHops[h].setTo(field(hptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + hptr += ZT_ADDRESS_LENGTH; + nextHopPeers[h] = RR->topology->getPeer(nextHops[h]); + } + } + } catch (std::exception &exc) { + TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + return true; +} + void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid) { Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index d19eb5c61..06220c4bc 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -124,8 +124,10 @@ private: bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer); - // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join + // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid); uint64_t _receiveTime; diff --git a/node/Packet.hpp b/node/Packet.hpp index 2d439b116..2a1a39afb 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -911,12 +911,15 @@ public: * <[2] 16-bit flags> * <[8] 64-bit timestamp> * <[8] 64-bit test ID (arbitrary, set by tester)> + * <[2] 16-bit originator credential length> * <[1] originator credential type (for authorizing test)> * <[...] credential> * <[2] 16-bit length of additional fields> * <[...] additional fields> + * [ ... end of signed portion of request ... ] * <[2] 16-bit length of signature of request> * <[...] signature of request by originator> + * <[2] 16-bit previous hop credential length> * <[1] previous hop credential type> * <[...] previous hop credential> * <[...] next hop(s) in path> @@ -939,7 +942,7 @@ public: * <[...] one or more ZeroTier addresses of next hops> * * Path record flags (in each path record): - * 0x80 - End of path (should be set on last entry) + * (unused, must be zero) * * The circuit test allows a device to send a message that will traverse * the network along a specified path, with each hop optionally reporting From 789046ca575e0aabe621c312832a8ccadf102989 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 14:35:05 -0700 Subject: [PATCH 004/195] Speed up Salsa20 just a bit. --- node/Salsa20.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++-- node/Salsa20.hpp | 2 +- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp index de8f1569f..f8cf8591d 100644 --- a/node/Salsa20.cpp +++ b/node/Salsa20.cpp @@ -122,7 +122,7 @@ void Salsa20::init(const void *key,unsigned int kbits,const void *iv,unsigned in _state.i[0] = U8TO32_LITTLE(constants + 0); #endif - _roundsDiv2 = rounds / 2; + _roundsDiv4 = rounds / 4; } void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) @@ -180,7 +180,7 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) __m128i X2s = X2; __m128i X3s = X3; - for (i=0;i<_roundsDiv2;++i) { + for (i=0;i<_roundsDiv4;++i) { __m128i T = _mm_add_epi32(X0, X3); X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); @@ -214,6 +214,42 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) 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(X1, _mm_slli_epi32(T, 7)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _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(X3, _mm_slli_epi32(T, 7)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _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); @@ -260,7 +296,42 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) x14 = j14; x15 = j15; - for(i=0;i<_roundsDiv2;++i) { + 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)); diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index 3bb041ac4..84baf3da7 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -84,7 +84,7 @@ private: #endif // ZT_SALSA20_SSE uint32_t i[16]; } _state; - unsigned int _roundsDiv2; + unsigned int _roundsDiv4; }; } // namespace ZeroTier From 0d0039674fefbfc14eb41f3bdbec7bf3e6dba9a5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 14:48:07 -0700 Subject: [PATCH 005/195] Add new verb names, and fix some Mac compiler flags. --- make-mac.mk | 4 ++-- node/Packet.cpp | 6 ++++++ node/Packet.hpp | 11 ++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/make-mac.mk b/make-mac.mk index c0b3f89d1..6daa6aa0b 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -58,8 +58,8 @@ ifeq ($(ZT_DEBUG),1) # C25519 in particular is almost UNUSABLE in heavy testing without it. ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS) else - CFLAGS?=-O3 -fstack-protector - CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -fvectorize -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) + CFLAGS?=-Ofast -fstack-protector + CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) STRIP=strip endif diff --git a/node/Packet.cpp b/node/Packet.cpp index 2c73a087b..f69e4e79b 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -31,6 +31,8 @@ namespace ZeroTier { const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +#ifdef ZT_TRACE + const char *Packet::verbString(Verb v) throw() { @@ -52,6 +54,8 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY"; case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; + case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST"; + case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT"; } return "(unknown)"; } @@ -73,6 +77,8 @@ const char *Packet::errorString(ErrorCode e) return "(unknown)"; } +#endif // ZT_TRACE + void Packet::armor(const void *key,bool encryptPayload) { unsigned char mangledKey[32]; diff --git a/node/Packet.hpp b/node/Packet.hpp index 2a1a39afb..71fa6e569 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1105,19 +1105,12 @@ public: ERROR_UNWANTED_MULTICAST = 8 }; - /** - * @param v Verb - * @return String representation (e.g. HELLO, OK) - */ +#ifdef ZT_TRACE static const char *verbString(Verb v) throw(); - - /** - * @param e Error code - * @return String error name - */ static const char *errorString(ErrorCode e) throw(); +#endif template Packet(const Buffer &b) : From 11ff96ba1ddc07c3414590aa31a35e6353176213 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 15:20:08 -0700 Subject: [PATCH 006/195] Consider IPv6 paths reliable (no constant keepalives needed) --- node/Path.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Path.hpp b/node/Path.hpp index 3fa06b586..9d97d2df2 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -114,7 +114,7 @@ public: */ inline bool reliable() const throw() { - return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE)); + return ( (_addr.ss_family == AF_INET6) || ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE)) ); } /** From a3db7d0728c1bc5181b8a70e8c379632125ee376 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 11:11:52 -0700 Subject: [PATCH 007/195] Refactor: move network COMs out of Network and into Peer in prep for tightening up multicast lookup and other things. --- node/Constants.hpp | 7 ++ node/IncomingPacket.cpp | 25 ++--- node/Network.cpp | 132 ++---------------------- node/Network.hpp | 45 ++------- node/Node.hpp | 10 ++ node/OutboundMulticast.cpp | 6 +- node/Path.hpp | 8 -- node/Peer.cpp | 199 +++++++++++++++++++++++++++++++++---- node/Peer.hpp | 57 ++++++++++- node/Switch.cpp | 8 +- node/Topology.cpp | 7 +- 11 files changed, 289 insertions(+), 215 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index 4f783550c..27c43aa7d 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,6 +324,13 @@ */ #define ZT_DIRECT_PATH_PUSH_INTERVAL 300000 +/** + * How long (max) to remember network certificates of membership? + * + * This only applies to networks we don't belong to. + */ +#define ZT_PEER_NETWORK_COM_EXPIRATION 3600000 + /** * Sanity limit on maximum bridge routes * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 3866abf17..e892edc21 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -421,9 +421,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p // OK(MULTICAST_FRAME) includes certificate of membership update CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS); - SharedPtr network(RR->node->network(nwid)); - if ((network)&&(com.hasRequiredFields())) - network->validateAndAddMembershipCertificate(com); + peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com); } if ((flags & 0x02) != 0) { @@ -511,7 +509,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr const SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID))); if (network) { if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { - if (!network->isAllowed(peer->address())) { + if (!network->isAllowed(peer)) { TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; @@ -552,13 +550,11 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndAddMembershipCertificate(com)) - comFailed = true; // technically this check is redundant to isAllowed(), but do it anyway for thoroughness - } + if (!peer->validateAndSetNetworkMembershipCertificate(RR,network->id(),com)) + comFailed = true; } - if ((comFailed)||(!network->isAllowed(peer->address()))) { + if ((comFailed)||(!network->isAllowed(peer))) { TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; @@ -642,11 +638,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; while (ptr < size()) { ptr += com.deserialize(*this,ptr); - if (com.hasRequiredFields()) { - SharedPtr network(RR->node->network(com.networkId())); - if (network) - network->validateAndAddMembershipCertificate(com); - } + peer->validateAndSetNetworkMembershipCertificate(RR,com.networkId(),com); } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP); @@ -809,13 +801,12 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share if ((flags & 0x01) != 0) { CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM); - if (com.hasRequiredFields()) - network->validateAndAddMembershipCertificate(com); + peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com); } // Check membership after we've read any included COM, since // that cert might be what we needed. - if (!network->isAllowed(peer->address())) { + if (!network->isAllowed(peer)) { TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; diff --git a/node/Network.cpp b/node/Network.cpp index 2b24d5f93..9c8aabfa9 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -59,6 +59,9 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid) : Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id); Utils::snprintf(mcdbn,sizeof(mcdbn),"networks.d/%.16llx.mcerts",_id); + // These files are no longer used, so clean them. + RR->node->dataStoreDelete(mcdbn); + if (_id == ZT_TEST_NETWORK_ID) { applyConfiguration(NetworkConfig::createTestNetworkConfig(RR->identity.address())); @@ -79,24 +82,6 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid) : // Save a one-byte CR to persist membership while we request a real netconf RR->node->dataStorePut(confn,"\n",1,false); } - - try { - std::string mcdb(RR->node->dataStoreGet(mcdbn)); - if (mcdb.length() > 6) { - const char *p = mcdb.data(); - const char *e = p + mcdb.length(); - if (!memcmp("ZTMCD0",p,6)) { - p += 6; - while (p != e) { - CertificateOfMembership com; - com.deserialize2(p,e); - if (!com) - break; - _certInfo[com.issuedTo()].com = com; - } - } - } - } catch ( ... ) {} // ignore invalid MCDB, we'll re-learn from peers } if (!_portInitialized) { @@ -115,32 +100,10 @@ Network::~Network() char n[128]; if (_destroyed) { RR->node->configureVirtualNetworkPort(_id,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); RR->node->dataStoreDelete(n); - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.mcerts",_id); - RR->node->dataStoreDelete(n); } else { RR->node->configureVirtualNetworkPort(_id,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp); - - clean(); - - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.mcerts",_id); - - Mutex::Lock _l(_lock); - if ((!_config)||(_config->isPublic())||(_certInfo.empty())) { - RR->node->dataStoreDelete(n); - } else { - std::string buf("ZTMCD0"); - Hashtable< Address,_RemoteMemberCertificateInfo >::Iterator i(_certInfo); - Address *a = (Address *)0; - _RemoteMemberCertificateInfo *ci = (_RemoteMemberCertificateInfo *)0; - while (i.next(a,ci)) { - if (ci->com) - ci->com.serialize2(buf); - } - RR->node->dataStorePut(n,buf,true); - } } } @@ -281,70 +244,6 @@ void Network::requestConfiguration() RR->sw->send(outp,true,0); } -bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert) -{ - if (!cert) // sanity check - return false; - - Mutex::Lock _l(_lock); - - { - const _RemoteMemberCertificateInfo *ci = _certInfo.get(cert.issuedTo()); - if ((ci)&&(ci->com == cert)) - return true; // we already have it - } - - // Check signature, log and return if cert is invalid - if (cert.signedBy() != controller()) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return false; // invalid signer - } - - if (cert.signedBy() == RR->identity.address()) { - - // We are the controller: RR->identity.address() == controller() == cert.signedBy() - // So, verify that we signed th cert ourself - if (!cert.verify(RR->identity)) { - TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return false; // invalid signature - } - - } else { - - SharedPtr signer(RR->topology->getPeer(cert.signedBy())); - - if (!signer) { - // This would be rather odd, since this is our controller... could happen - // if we get packets before we've gotten config. - RR->sw->requestWhois(cert.signedBy()); - return false; // signer unknown - } - - if (!cert.verify(signer->identity())) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return false; // invalid signature - } - } - - // If we made it past authentication, add or update cert in our cert info store - _certInfo[cert.issuedTo()].com = cert; - - return true; -} - -bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now) -{ - Mutex::Lock _l(_lock); - if ((_config)&&(!_config->isPublic())&&(_config->com())) { - _RemoteMemberCertificateInfo &ci = _certInfo[to]; - if ((now - ci.lastPushed) > (ZT_NETWORK_AUTOCONF_DELAY / 2)) { - ci.lastPushed = now; - return true; - } - } - return false; -} - void Network::clean() { const uint64_t now = RR->node->now(); @@ -353,22 +252,6 @@ void Network::clean() if (_destroyed) return; - if ((_config)&&(_config->isPublic())) { - // Open (public) networks do not track certs or cert pushes at all. - _certInfo.clear(); - } else if (_config) { - // Clean obsolete entries from private network cert info table - Hashtable< Address,_RemoteMemberCertificateInfo >::Iterator i(_certInfo); - Address *a = (Address *)0; - _RemoteMemberCertificateInfo *ci = (_RemoteMemberCertificateInfo *)0; - const uint64_t forgetIfBefore = now - (ZT_PEER_ACTIVITY_TIMEOUT * 16); // arbitrary reasonable cutoff - while (i.next(a,ci)) { - if ((ci->lastPushed < forgetIfBefore)&&(!ci->com.agreesWith(_config->com()))) - _certInfo.erase(*a); - } - } - - // Clean learned multicast groups if we haven't heard from them in a while { Hashtable< MulticastGroup,uint64_t >::Iterator i(_multicastGroupsBehindMe); MulticastGroup *mg = (MulticastGroup *)0; @@ -494,7 +377,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const } else ec->assignedAddressCount = 0; } -bool Network::_isAllowed(const Address &peer) const +bool Network::_isAllowed(const SharedPtr &peer) const { // Assumes _lock is locked try { @@ -502,10 +385,7 @@ bool Network::_isAllowed(const Address &peer) const return false; if (_config->isPublic()) return true; - const _RemoteMemberCertificateInfo *ci = _certInfo.get(peer); - if (!ci) - return false; - return _config->com().agreesWith(ci->com); + return ((_config->com())&&(peer->networkMembershipCertificatesAgree(_id,_config->com()))); } catch (std::exception &exc) { TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what()); } catch ( ... ) { @@ -542,7 +422,7 @@ public: inline void operator()(Topology &t,const SharedPtr &p) { - if ( ( (p->hasActiveDirectPath(_now)) && ( (_network->_isAllowed(p->address())) || (p->address() == _network->controller()) ) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) { + if ( ( (p->hasActiveDirectPath(_now)) && ( (_network->_isAllowed(p)) || (p->address() == _network->controller()) ) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) { Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); for(std::vector::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) { diff --git a/node/Network.hpp b/node/Network.hpp index ad9f18de9..370776506 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -56,6 +56,7 @@ namespace ZeroTier { class RuntimeEnvironment; class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths; +class Peer; /** * A virtual LAN @@ -94,6 +95,12 @@ public: */ inline Address controller() throw() { return Address(_id >> 24); } + /** + * @param nwid Network ID + * @return Address of network's controller + */ + static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); } + /** * @return Multicast group memberships for this network's port (local, not learned via bridging) */ @@ -177,33 +184,10 @@ public: void requestConfiguration(); /** - * Add or update a membership certificate - * - * @param cert Certificate of membership - * @return True if certificate was accepted as valid - */ - bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); - - /** - * Check if we should push membership certificate to a peer, AND update last pushed - * - * If we haven't pushed a cert to this peer in a long enough time, this returns - * true and updates the last pushed time. Otherwise it returns false. - * - * This doesn't actually send anything, since COMs can hitch a ride with several - * different kinds of packets. - * - * @param to Destination peer - * @param now Current time - * @return True if we should include a COM with whatever we're currently sending - */ - bool peerNeedsOurMembershipCertificate(const Address &to,uint64_t now); - - /** - * @param peer Peer address to check + * @param peer Peer to check * @return True if peer is allowed to communicate on this network */ - inline bool isAllowed(const Address &peer) const + inline bool isAllowed(const SharedPtr &peer) const { Mutex::Lock _l(_lock); return _isAllowed(peer); @@ -347,16 +331,9 @@ public: inline bool operator>=(const Network &n) const throw() { return (_id >= n._id); } private: - struct _RemoteMemberCertificateInfo - { - _RemoteMemberCertificateInfo() : com(),lastPushed(0) {} - CertificateOfMembership com; // remote member's COM - uint64_t lastPushed; // when did we last push ours to them? - }; - ZT_VirtualNetworkStatus _status() const; void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked - bool _isAllowed(const Address &peer) const; + bool _isAllowed(const SharedPtr &peer) const; void _announceMulticastGroups(); std::vector _allMulticastGroups() const; @@ -370,8 +347,6 @@ private: Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge) Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges) - Hashtable< Address,_RemoteMemberCertificateInfo > _certInfo; - SharedPtr _config; // Most recent network configuration, which is an immutable value-object volatile uint64_t _lastConfigUpdate; diff --git a/node/Node.hpp b/node/Node.hpp index b81c19435..0f659f47c 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -168,6 +168,16 @@ public: return _network(nwid); } + inline bool belongsToNetwork(uint64_t nwid) const + { + Mutex::Lock _l(_networks_m); + for(std::vector< std::pair< uint64_t, SharedPtr > >::const_iterator i=_networks.begin();i!=_networks.end();++i) { + if (i->first == nwid) + return true; + } + return false; + } + inline std::vector< SharedPtr > allNetworks() const { std::vector< SharedPtr > nw; diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index 46116c07a..c26372cbf 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -103,11 +103,11 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { if (_haveCom) { - SharedPtr network(RR->node->network(_nwid)); - if ((network)&&(network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now()))) { + SharedPtr peer(RR->topology->getPeer(toAddr)); + if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) { + //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str()); _packetWithCom.newInitializationVector(); _packetWithCom.setDestination(toAddr); - //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str()); RR->sw->send(_packetWithCom,true,_nwid); return; } diff --git a/node/Path.hpp b/node/Path.hpp index 9d97d2df2..8d662ff77 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -122,14 +122,6 @@ public: */ inline operator bool() const throw() { return (_addr); } - // Comparisons are by address only - inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } - inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } - inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } - inline bool operator>(const Path &p) const throw() { return (_addr > p._addr); } - inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } - inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } - /** * Check whether this address is valid for a ZeroTier path * diff --git a/node/Peer.cpp b/node/Peer.cpp index 48b77c85e..6203d0b4e 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -37,6 +37,8 @@ #include +#define ZT_PEER_PATH_SORT_INTERVAL 5000 + namespace ZeroTier { // Used to send varying values for NAT keepalive @@ -51,17 +53,38 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastAnnouncedTo(0), _lastPathConfirmationSent(0), _lastDirectPathPush(0), + _lastPathSort(0), _vMajor(0), _vMinor(0), _vRevision(0), _id(peerIdentity), _numPaths(0), - _latency(0) + _latency(0), + _networkComs(4), + _lastPushedComs(4) { if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH)) throw std::runtime_error("new peer identity key agreement failed"); } +struct _SortPathsByQuality +{ + uint64_t _now; + _SortPathsByQuality(const uint64_t now) : _now(now) {} + inline bool operator()(const RemotePath &a,const RemotePath &b) const + { + const uint64_t qa = ( + ((uint64_t)a.active(_now) << 63) | + (((uint64_t)(a.preferenceRank() & 0xfff)) << 51) | + ((uint64_t)a.lastReceived() & 0x7ffffffffffffULL) ); + const uint64_t qb = ( + ((uint64_t)b.active(_now) << 63) | + (((uint64_t)(b.preferenceRank() & 0xfff)) << 51) | + ((uint64_t)b.lastReceived() & 0x7ffffffffffffULL) ); + return (qb < qa); // invert sense to sort in descending order + } +}; + void Peer::received( const RuntimeEnvironment *RR, const InetAddress &localAddr, @@ -73,6 +96,8 @@ void Peer::received( Packet::Verb inReVerb) { const uint64_t now = RR->node->now(); + Mutex::Lock _l(_lock); + _lastReceive = now; if (!hops) { @@ -91,6 +116,7 @@ void Peer::received( if (!pathIsConfirmed) { if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) { + // Learn paths if they've been confirmed via a HELLO RemotePath *slot = (RemotePath *)0; if (np < ZT_MAX_PEER_NETWORK_PATHS) { @@ -111,8 +137,11 @@ void Peer::received( slot->received(now); _numPaths = np; pathIsConfirmed = true; + _sortPaths(now); } + } else { + /* If this path is not known, send a HELLO. We don't learn * paths without confirming that a bidirectional link is in * fact present, but any packet that decodes and authenticates @@ -122,6 +151,7 @@ void Peer::received( 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); } + } } } @@ -129,6 +159,7 @@ void Peer::received( /* Announce multicast groups of interest to direct peers if they are * considered authorized members of a given network. Also announce to * root servers and network controllers. */ + /* if ((pathIsConfirmed)&&((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000))) { _lastAnnouncedTo = now; @@ -158,6 +189,7 @@ void Peer::received( RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } } + */ } if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) @@ -166,23 +198,10 @@ void Peer::received( _lastMulticastFrame = now; } -RemotePath *Peer::getBestPath(uint64_t now) -{ - RemotePath *bestPath = (RemotePath *)0; - uint64_t lrMax = 0; - int rank = 0; - for(unsigned int p=0,np=_numPaths;p= lrMax)||(_paths[p].preferenceRank() >= rank)) ) { - lrMax = _paths[p].lastReceived(); - rank = _paths[p].preferenceRank(); - bestPath = &(_paths[p]); - } - } - return bestPath; -} - void Peer::attemptToContactAt(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 + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO); outp.append((unsigned char)ZT_PROTO_VERSION); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); @@ -214,7 +233,8 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) { - RemotePath *const bestPath = getBestPath(now); + Mutex::Lock _l(_lock); + RemotePath *const bestPath = _getBestPath(now); if (bestPath) { if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str()); @@ -231,6 +251,8 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) { + Mutex::Lock _l(_lock); + if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { _lastDirectPathPush = now; @@ -299,13 +321,16 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ } } -void Peer::addPath(const RemotePath &newp) +void Peer::addPath(const RemotePath &newp,uint64_t now) { + Mutex::Lock _l(_lock); + unsigned int np = _numPaths; for(unsigned int p=0;pcom.agreesWith(com); + return false; +} + +bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment *RR,uint64_t nwid,const CertificateOfMembership &com) +{ + // Sanity checks + if ((!com)||(com.issuedTo() != _id.address())) + return false; + + // Return true if we already have this *exact* COM + { + Mutex::Lock _l(_lock); + _NetworkCom *ourCom = _networkComs.get(nwid); + if ((ourCom)&&(ourCom->com == com)) + return true; + } + + // Check signature, log and return if cert is invalid + if (com.signedBy() != Network::controllerFor(nwid)) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signer + } + + if (com.signedBy() == RR->identity.address()) { + + // We are the controller: RR->identity.address() == controller() == cert.signedBy() + // So, verify that we signed th cert ourself + if (!com.verify(RR->identity)) { + TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature + } + + } else { + + SharedPtr signer(RR->topology->getPeer(com.signedBy())); + + if (!signer) { + // This would be rather odd, since this is our controller... could happen + // if we get packets before we've gotten config. + RR->sw->requestWhois(com.signedBy()); + return false; // signer unknown + } + + if (!com.verify(signer->identity())) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature + } + } + + // If we made it past all those checks, add or update cert in our cert info store + { + Mutex::Lock _l(_lock); + _networkComs.set(nwid,_NetworkCom(RR->node->now(),com)); + } + + return true; +} + +bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime) +{ + Mutex::Lock _l(_lock); + uint64_t &lastPushed = _lastPushedComs[nwid]; + const uint64_t tmp = lastPushed; + if (updateLastPushedTime) + lastPushed = now; + return ((now - tmp) < (ZT_NETWORK_AUTOCONF_DELAY / 2)); +} + +void Peer::clean(const RuntimeEnvironment *RR,uint64_t now) +{ + Mutex::Lock _l(_lock); + + { + unsigned int np = _numPaths; + unsigned int x = 0; + unsigned int y = 0; + while (x < np) { + if (_paths[x].active(now)) + _paths[y++] = _paths[x]; + ++x; + } + _numPaths = y; + } + + { + uint64_t *k = (uint64_t *)0; + _NetworkCom *v = (_NetworkCom *)0; + Hashtable< uint64_t,_NetworkCom >::Iterator i(_networkComs); + while (i.next(k,v)) { + if ( (!RR->node->belongsToNetwork(*k)) && ((now - v->ts) >= ZT_PEER_NETWORK_COM_EXPIRATION) ) + _networkComs.erase(*k); + } + } + + { + uint64_t *k = (uint64_t *)0; + uint64_t *v = (uint64_t *)0; + Hashtable< uint64_t,uint64_t >::Iterator i(_lastPushedComs); + while (i.next(k,v)) { + if ((now - *v) > (ZT_NETWORK_AUTOCONF_DELAY * 2)) + _lastPushedComs.erase(*k); + } + } +} + +void Peer::_sortPaths(const uint64_t now) +{ + // assumes _lock is locked + _lastPathSort = now; + std::sort(&(_paths[0]),&(_paths[_numPaths]),_SortPathsByQuality(now)); +} + +RemotePath *Peer::_getBestPath(const uint64_t now) +{ + // assumes _lock is locked + if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL) + _sortPaths(now); + if (_paths[0].active(now)) { + return &(_paths[0]); + } else { + _sortPaths(now); + if (_paths[0].active(now)) + return &(_paths[0]); + } + return (RemotePath *)0; +} + } // namespace ZeroTier diff --git a/node/Peer.hpp b/node/Peer.hpp index 4d1031b8d..c1692846c 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -49,6 +49,8 @@ #include "Packet.hpp" #include "SharedPtr.hpp" #include "AtomicCounter.hpp" +#include "Hashtable.hpp" +#include "Mutex.hpp" #include "NonCopyable.hpp" namespace ZeroTier { @@ -129,7 +131,11 @@ public: * @param now Current time * @return Best path or NULL if there are no active (or fixed) direct paths */ - RemotePath *getBestPath(uint64_t now); + inline RemotePath *getBestPath(uint64_t now) + { + Mutex::Lock _l(_lock); + return _getBestPath(now); + } /** * Send via best path @@ -293,8 +299,9 @@ public: * Add a path (if we don't already have it) * * @param p New path to add + * @param now Current time */ - void addPath(const RemotePath &newp); + void addPath(const RemotePath &newp,uint64_t now); /** * Clear paths @@ -381,6 +388,37 @@ public: */ void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const; + /** + * Check network COM agreement with this peer + * + * @param nwid Network ID + * @param com Another certificate of membership + * @return True if supplied COM agrees with ours, false if not or if we don't have one + */ + bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const; + + /** + * Check the validity of the COM and add/update if valid and new + * + * @param RR Runtime Environment + * @param nwid Network ID + * @param com Externally supplied COM + */ + bool validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment *RR,uint64_t nwid,const CertificateOfMembership &com); + + /** + * @param nwid Network ID + * @param now Current time + * @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value + * @return Whether or not this peer needs another COM push from us + */ + bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime); + + /** + * Perform periodic cleaning operations + */ + void clean(const RuntimeEnvironment *RR,uint64_t now); + /** * Find a common set of addresses by which two peers can link, if any * @@ -402,7 +440,8 @@ public: } private: - void _announceMulticastGroups(const RuntimeEnvironment *RR,uint64_t now); + void _sortPaths(const uint64_t now); + RemotePath *_getBestPath(const uint64_t now); unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; uint64_t _lastUsed; @@ -412,6 +451,7 @@ private: uint64_t _lastAnnouncedTo; uint64_t _lastPathConfirmationSent; uint64_t _lastDirectPathPush; + uint64_t _lastPathSort; uint16_t _vProto; uint16_t _vMajor; uint16_t _vMinor; @@ -421,6 +461,17 @@ private: unsigned int _numPaths; unsigned int _latency; + struct _NetworkCom + { + _NetworkCom() {} + _NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {} + uint64_t ts; + CertificateOfMembership com; + }; + Hashtable _networkComs; + Hashtable _lastPushedComs; + + Mutex _lock; AtomicCounter __refCount; }; diff --git a/node/Switch.cpp b/node/Switch.cpp index ecae9b769..9ea8ac49a 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -202,7 +202,8 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c // Destination is another ZeroTier peer on the same network Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this - const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); + SharedPtr toPeer(RR->topology->getPeer(toZT)); + const bool includeCom = ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true)));; if ((fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); @@ -267,9 +268,10 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c } for(unsigned int b=0;b bridgePeer(RR->topology->getPeer(bridges[b])); Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); - if (network->peerNeedsOurMembershipCertificate(bridges[b],RR->node->now())) { + if ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) { outp.append((unsigned char)0x01); // 0x01 -- COM included nconf->com().serialize(outp); } else { @@ -747,7 +749,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) return false; // no paths, no root servers? } - if ((network)&&(relay)&&(network->isAllowed(peer->address()))) { + if ((network)&&(relay)&&(network->isAllowed(peer))) { // Push hints for direct connectivity to this peer if we are relaying peer->pushDirectPaths(RR,viaPath,now,false); } diff --git a/node/Topology.cpp b/node/Topology.cpp index e931df1e1..b2b4ebbda 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -62,7 +62,7 @@ void Topology::setRootServers(const std::map< Identity,std::vector if (!p) p = SharedPtr(new Peer(RR->identity,i->first)); for(std::vector::const_iterator j(i->second.begin());j!=i->second.end();++j) - p->addPath(RemotePath(InetAddress(),*j,true)); + p->addPath(RemotePath(InetAddress(),*j,true),now); p->use(now); _rootPeers.push_back(p); } @@ -252,9 +252,12 @@ void Topology::clean(uint64_t now) Hashtable< Address,SharedPtr >::Iterator i(_activePeers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) + while (i.next(a,p)) { if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) { _activePeers.erase(*a); + } else { + (*p)->clean(RR,now); + } } } From 9405150b1147189586c427bc9e1fd9abb00b7ca0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 11:37:02 -0700 Subject: [PATCH 008/195] Restore group announcement on Peer::receive() but centralize packet composition in one place. --- node/Network.cpp | 115 +++++++++++++++++++----------------- node/Network.hpp | 11 +++- node/Peer.cpp | 148 ++++++++++++++++++++--------------------------- 3 files changed, 137 insertions(+), 137 deletions(-) diff --git a/node/Network.cpp b/node/Network.cpp index 9c8aabfa9..d86145da7 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -141,6 +141,12 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg) _myMulticastGroups.swap(nmg); } +bool Network::tryAnnounceMulticastGroupsTo(const SharedPtr &peer) +{ + Mutex::Lock _l(_lock); + return _tryAnnounceMulticastGroupsTo(RR->topology->rootAddresses(),_allMulticastGroups(),peer,RR->node->now()); +} + bool Network::applyConfiguration(const SharedPtr &conf) { if (_destroyed) // sanity check @@ -394,6 +400,63 @@ bool Network::_isAllowed(const SharedPtr &peer) const return false; // default position on any failure } +// Used in Network::_announceMulticastGroups() +class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths +{ +public: + _AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) : + _now(renv->node->now()), + RR(renv), + _network(nw), + _rootAddresses(renv->topology->rootAddresses()), + _allMulticastGroups(nw->_allMulticastGroups()) + {} + + inline void operator()(Topology &t,const SharedPtr &p) { _network->_tryAnnounceMulticastGroupsTo(_rootAddresses,_allMulticastGroups,p,_now); } + +private: + uint64_t _now; + const RuntimeEnvironment *RR; + Network *_network; + std::vector

_rootAddresses; + std::vector _allMulticastGroups; +}; + +bool Network::_tryAnnounceMulticastGroupsTo(const std::vector
&alwaysAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const +{ + if ( ( (peer->hasActiveDirectPath(now)) && ( _isAllowed(peer) || (peer->address() == this->controller()) ) ) || (std::find(alwaysAddresses.begin(),alwaysAddresses.end(),peer->address()) != alwaysAddresses.end()) ) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + + for(std::vector::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) { + if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { + outp.armor(peer->key(),true); + peer->send(RR,outp.data(),outp.size(),now); + outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + } + + // network ID, MAC, ADI + outp.append((uint64_t)_id); + mg->mac().appendTo(outp); + outp.append((uint32_t)mg->adi()); + } + + if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { + outp.armor(peer->key(),true); + peer->send(RR,outp.data(),outp.size(),now); + } + + return true; + } + return false; +} + +void Network::_announceMulticastGroups() +{ + // Assumes _lock is locked + _AnnounceMulticastGroupsToPeersWithActiveDirectPaths afunc(RR,this); + RR->topology->eachPeer<_AnnounceMulticastGroupsToPeersWithActiveDirectPaths &>(afunc); +} + std::vector Network::_allMulticastGroups() const { // Assumes _lock is locked @@ -408,56 +471,4 @@ std::vector Network::_allMulticastGroups() const return mgs; } -// Used in Network::_announceMulticastGroups() -class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths -{ -public: - _AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) : - RR(renv), - _now(renv->node->now()), - _network(nw), - _rootAddresses(renv->topology->rootAddresses()), - _allMulticastGroups(nw->_allMulticastGroups()) - {} - - inline void operator()(Topology &t,const SharedPtr &p) - { - if ( ( (p->hasActiveDirectPath(_now)) && ( (_network->_isAllowed(p)) || (p->address() == _network->controller()) ) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) { - Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - - for(std::vector::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) { - if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { - outp.armor(p->key(),true); - p->send(RR,outp.data(),outp.size(),_now); - outp.reset(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - } - - // network ID, MAC, ADI - outp.append((uint64_t)_network->id()); - mg->mac().appendTo(outp); - outp.append((uint32_t)mg->adi()); - } - - if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { - outp.armor(p->key(),true); - p->send(RR,outp.data(),outp.size(),_now); - } - } - } - -private: - const RuntimeEnvironment *RR; - uint64_t _now; - Network *_network; - std::vector
_rootAddresses; - std::vector _allMulticastGroups; -}; - -void Network::_announceMulticastGroups() -{ - // Assumes _lock is locked - _AnnounceMulticastGroupsToPeersWithActiveDirectPaths afunc(RR,this); - RR->topology->eachPeer<_AnnounceMulticastGroupsToPeersWithActiveDirectPaths &>(afunc); -} - } // namespace ZeroTier diff --git a/node/Network.hpp b/node/Network.hpp index 370776506..b942e5f92 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -93,7 +93,7 @@ public: /** * @return Address of network's controller (most significant 40 bits of ID) */ - inline Address controller() throw() { return Address(_id >> 24); } + inline Address controller() const throw() { return Address(_id >> 24); } /** * @param nwid Network ID @@ -140,6 +140,14 @@ public: */ void multicastUnsubscribe(const MulticastGroup &mg); + /** + * Announce multicast groups to a peer if that peer is authorized on this network + * + * @param peer Peer to try to announce multicast groups to + * @return True if peer was authorized and groups were announced + */ + bool tryAnnounceMulticastGroupsTo(const SharedPtr &peer); + /** * Apply a NetworkConfig to this network * @@ -334,6 +342,7 @@ private: ZT_VirtualNetworkStatus _status() const; void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked bool _isAllowed(const SharedPtr &peer) const; + bool _tryAnnounceMulticastGroupsTo(const std::vector
&rootAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const; void _announceMulticastGroups(); std::vector _allMulticastGroups() const; diff --git a/node/Peer.cpp b/node/Peer.cpp index 6203d0b4e..d98e0807f 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -96,106 +96,86 @@ void Peer::received( Packet::Verb inReVerb) { const uint64_t now = RR->node->now(); - Mutex::Lock _l(_lock); + bool needMulticastGroupAnnounce = false; - _lastReceive = now; + { + Mutex::Lock _l(_lock); - if (!hops) { - bool pathIsConfirmed = false; + _lastReceive = now; - /* Learn new paths from direct (hops == 0) packets */ - { - unsigned int np = _numPaths; - for(unsigned int p=0;preceived(now); - _numPaths = np; - pathIsConfirmed = true; - _sortPaths(now); - } - - } else { - - /* If this path is not known, send a HELLO. We don't learn - * paths without confirming that a bidirectional link is in - * fact present, but any packet that decodes and authenticates - * correctly is considered valid. */ - 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); - } - - } - } - } - - /* Announce multicast groups of interest to direct peers if they are - * considered authorized members of a given network. Also announce to - * root servers and network controllers. */ - /* - if ((pathIsConfirmed)&&((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000))) { - _lastAnnouncedTo = now; - - const bool isRoot = RR->topology->isRoot(_id); - - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - const std::vector< SharedPtr > networks(RR->node->allNetworks()); - for(std::vector< SharedPtr >::const_iterator n(networks.begin());n!=networks.end();++n) { - if ( (isRoot) || ((*n)->isAllowed(_id.address())) || (_id.address() == (*n)->controller()) ) { - const std::vector mgs((*n)->allMulticastGroups()); - for(std::vector::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) { - if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { - outp.armor(_key,true); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); - outp.reset(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + if (slot) { + *slot = RemotePath(localAddr,remoteAddr,false); + slot->received(now); + _numPaths = np; + pathIsConfirmed = true; + _sortPaths(now); + } + + } else { + + /* If this path is not known, send a HELLO. We don't learn + * paths without confirming that a bidirectional link is in + * fact present, but any packet that decodes and authenticates + * correctly is considered valid. */ + 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); } - // network ID, MAC, ADI - outp.append((uint64_t)(*n)->id()); - mg->mac().appendTo(outp); - outp.append((uint32_t)mg->adi()); } } } - if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { - outp.armor(_key,true); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); - } } - */ + + if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { + _lastAnnouncedTo = now; + needMulticastGroupAnnounce = true; + } + + if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) + _lastUnicastFrame = now; + else if (verb == Packet::VERB_MULTICAST_FRAME) + _lastMulticastFrame = now; } - if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) - _lastUnicastFrame = now; - else if (verb == Packet::VERB_MULTICAST_FRAME) - _lastMulticastFrame = now; + if (needMulticastGroupAnnounce) { + const std::vector< SharedPtr > networks(RR->node->allNetworks()); + for(std::vector< SharedPtr >::const_iterator n(networks.begin());n!=networks.end();++n) + (*n)->tryAnnounceMulticastGroupsTo(SharedPtr(this)); + } } void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now) From 64bf3ffe6ccd0344c4bd86537ef285253e44b185 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 11:44:09 -0700 Subject: [PATCH 009/195] Mutex cleanup. --- node/Peer.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/Peer.hpp b/node/Peer.hpp index c1692846c..568de0d5f 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -193,6 +193,7 @@ public: inline std::vector paths() const { std::vector pp; + Mutex::Lock _l(_lock); for(unsigned int p=0,np=_numPaths;p 0)||(_vMinor > 0)||(_vRevision > 0)) { if (_vMajor > major) return true; From 53e5f94b996af73517940cb70d8bf8ca8c7b112f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 12:25:43 -0700 Subject: [PATCH 010/195] . --- node/Peer.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index d98e0807f..6d9b8cc70 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -67,24 +67,6 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) throw std::runtime_error("new peer identity key agreement failed"); } -struct _SortPathsByQuality -{ - uint64_t _now; - _SortPathsByQuality(const uint64_t now) : _now(now) {} - inline bool operator()(const RemotePath &a,const RemotePath &b) const - { - const uint64_t qa = ( - ((uint64_t)a.active(_now) << 63) | - (((uint64_t)(a.preferenceRank() & 0xfff)) << 51) | - ((uint64_t)a.lastReceived() & 0x7ffffffffffffULL) ); - const uint64_t qb = ( - ((uint64_t)b.active(_now) << 63) | - (((uint64_t)(b.preferenceRank() & 0xfff)) << 51) | - ((uint64_t)b.lastReceived() & 0x7ffffffffffffULL) ); - return (qb < qa); // invert sense to sort in descending order - } -}; - void Peer::received( const RuntimeEnvironment *RR, const InetAddress &localAddr, @@ -511,6 +493,23 @@ void Peer::clean(const RuntimeEnvironment *RR,uint64_t now) } } +struct _SortPathsByQuality +{ + uint64_t _now; + _SortPathsByQuality(const uint64_t now) : _now(now) {} + inline bool operator()(const RemotePath &a,const RemotePath &b) const + { + const uint64_t qa = ( + ((uint64_t)a.active(_now) << 63) | + (((uint64_t)(a.preferenceRank() & 0xfff)) << 51) | + ((uint64_t)a.lastReceived() & 0x7ffffffffffffULL) ); + const uint64_t qb = ( + ((uint64_t)b.active(_now) << 63) | + (((uint64_t)(b.preferenceRank() & 0xfff)) << 51) | + ((uint64_t)b.lastReceived() & 0x7ffffffffffffULL) ); + return (qb < qa); // invert sense to sort in descending order + } +}; void Peer::_sortPaths(const uint64_t now) { // assumes _lock is locked From a7409850d6f01db9558088127d7975cb9e6d2191 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 12:37:18 -0700 Subject: [PATCH 011/195] Get trim() out of core where it is not needed. --- node/Utils.cpp | 19 ------------------- node/Utils.hpp | 8 -------- service/OneService.cpp | 21 ++++++++++++++++++++- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/node/Utils.cpp b/node/Utils.cpp index 9630e6b30..658c397d0 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -261,25 +261,6 @@ std::vector Utils::split(const char *s,const char *const sep,const return fields; } -std::string Utils::trim(const std::string &s) -{ - unsigned long end = (unsigned long)s.length(); - while (end) { - char c = s[end - 1]; - if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) - --end; - else break; - } - unsigned long start = 0; - while (start < end) { - char c = s[start]; - if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) - ++start; - else break; - } - return s.substr(start,end - start); -} - unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...) throw(std::length_error) { diff --git a/node/Utils.hpp b/node/Utils.hpp index 70918eb5d..a0ac93a29 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -256,14 +256,6 @@ public: return true; } - /** - * Trim whitespace from the start and end of a string - * - * @param s String to trim - * @return Trimmed string - */ - static std::string trim(const std::string &s); - /** * Variant of snprintf that is portable and throws an exception * diff --git a/service/OneService.cpp b/service/OneService.cpp index 6e6de8bd2..35f8e806a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -338,6 +338,25 @@ public: static BackgroundSoftwareUpdateChecker backgroundSoftwareUpdateChecker; #endif // ZT_AUTO_UPDATE +static std::string _trimString(const std::string &s) +{ + unsigned long end = (unsigned long)s.length(); + while (end) { + char c = s[end - 1]; + if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) + --end; + else break; + } + unsigned long start = 0; + while (start < end) { + char c = s[start]; + if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) + ++start; + else break; + } + return s.substr(start,end - start); +} + class OneServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf); @@ -521,7 +540,7 @@ public: } else OSUtils::lockDownFile(authTokenPath.c_str(),false); } } - authToken = Utils::trim(authToken); + authToken = _trimString(authToken); _node = new Node( OSUtils::now(), From d6676a9d6cffed96850d7da7daffbf329109a7d5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 12:50:19 -0700 Subject: [PATCH 012/195] Always announce multicast groups, not just to peers with direct links, and push network COMs to any MULTICAST_LIKE recipient for future use. --- node/Network.cpp | 81 ++++++++++++++++++++++++++++-------------------- node/Network.hpp | 4 +-- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/node/Network.cpp b/node/Network.cpp index d86145da7..a9e4e55b7 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -400,11 +400,53 @@ bool Network::_isAllowed(const SharedPtr &peer) const return false; // default position on any failure } -// Used in Network::_announceMulticastGroups() -class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths +bool Network::_tryAnnounceMulticastGroupsTo(const std::vector
&alwaysAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const +{ + // assumes _lock is locked + if ( + (_isAllowed(peer)) || + (peer->address() == this->controller()) || + (std::find(alwaysAddresses.begin(),alwaysAddresses.end(),peer->address()) != alwaysAddresses.end()) + ) { + + if ((_config)&&(_config->com())&&(!_config->isPublic())&&(peer->needsOurNetworkMembershipCertificate(_id,now,true))) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + _config->com().serialize(outp); + outp.armor(peer->key(),true); + peer->send(RR,outp.data(),outp.size(),now); + } + + { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + + for(std::vector::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) { + if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { + outp.armor(peer->key(),true); + peer->send(RR,outp.data(),outp.size(),now); + outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + } + + // network ID, MAC, ADI + outp.append((uint64_t)_id); + mg->mac().appendTo(outp); + outp.append((uint32_t)mg->adi()); + } + + if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { + outp.armor(peer->key(),true); + peer->send(RR,outp.data(),outp.size(),now); + } + } + + return true; + } + return false; +} + +class _AnnounceMulticastGroupsToAll { public: - _AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) : + _AnnounceMulticastGroupsToAll(const RuntimeEnvironment *renv,Network *nw) : _now(renv->node->now()), RR(renv), _network(nw), @@ -421,40 +463,11 @@ private: std::vector
_rootAddresses; std::vector _allMulticastGroups; }; - -bool Network::_tryAnnounceMulticastGroupsTo(const std::vector
&alwaysAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const -{ - if ( ( (peer->hasActiveDirectPath(now)) && ( _isAllowed(peer) || (peer->address() == this->controller()) ) ) || (std::find(alwaysAddresses.begin(),alwaysAddresses.end(),peer->address()) != alwaysAddresses.end()) ) { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - - for(std::vector::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) { - if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { - outp.armor(peer->key(),true); - peer->send(RR,outp.data(),outp.size(),now); - outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - } - - // network ID, MAC, ADI - outp.append((uint64_t)_id); - mg->mac().appendTo(outp); - outp.append((uint32_t)mg->adi()); - } - - if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { - outp.armor(peer->key(),true); - peer->send(RR,outp.data(),outp.size(),now); - } - - return true; - } - return false; -} - void Network::_announceMulticastGroups() { // Assumes _lock is locked - _AnnounceMulticastGroupsToPeersWithActiveDirectPaths afunc(RR,this); - RR->topology->eachPeer<_AnnounceMulticastGroupsToPeersWithActiveDirectPaths &>(afunc); + _AnnounceMulticastGroupsToAll afunc(RR,this); + RR->topology->eachPeer<_AnnounceMulticastGroupsToAll &>(afunc); } std::vector Network::_allMulticastGroups() const diff --git a/node/Network.hpp b/node/Network.hpp index b942e5f92..f7939323e 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -55,8 +55,8 @@ namespace ZeroTier { class RuntimeEnvironment; -class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths; class Peer; +class _AnnounceMulticastGroupsToAll; // internal function object in Network.cpp /** * A virtual LAN @@ -64,7 +64,7 @@ class Peer; class Network : NonCopyable { friend class SharedPtr; - friend class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths; + friend class _AnnounceMulticastGroupsToAll; public: /** From 2c196307ee39be1ee8d317ad83f2b5e85cbe6d8d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 13:01:18 -0700 Subject: [PATCH 013/195] --bugs; --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index 6d9b8cc70..e135faa52 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -453,7 +453,7 @@ bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool const uint64_t tmp = lastPushed; if (updateLastPushedTime) lastPushed = now; - return ((now - tmp) < (ZT_NETWORK_AUTOCONF_DELAY / 2)); + return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 2)); } void Peer::clean(const RuntimeEnvironment *RR,uint64_t now) From 6693149f3e2d1557f007ace99987694a34ebe6f8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 13:34:12 -0700 Subject: [PATCH 014/195] Send COM with MULTICAST_GATHER for future use. --- node/Multicaster.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 07792737a..420a00ff4 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -237,12 +237,25 @@ void Multicaster::send( if (sn) { TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str()); + const CertificateOfMembership *com = (CertificateOfMembership *)0; + if (sn->needsOurNetworkMembershipCertificate(nwid,now,true)) { + SharedPtr nw = RR->node->network(nwid); + SharedPtr nconf; + if (nw) { + nconf = nw->config2(); + if (nconf) + com = &(nconf->com()); + } + } + Packet outp(sn->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER); outp.append(nwid); - outp.append((uint8_t)0); + 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(sn->key(),true); sn->send(RR,outp.data(),outp.size(),now); } From 3999e468b78fd4b8ada57ba6801f672e8ed08999 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 13:43:15 -0700 Subject: [PATCH 015/195] Need to hold nconf so *com does not die while being used. --- node/Multicaster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 420a00ff4..87a1df9cc 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -238,9 +238,9 @@ void Multicaster::send( TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str()); const CertificateOfMembership *com = (CertificateOfMembership *)0; + SharedPtr nconf; if (sn->needsOurNetworkMembershipCertificate(nwid,now,true)) { SharedPtr nw = RR->node->network(nwid); - SharedPtr nconf; if (nw) { nconf = nw->config2(); if (nconf) From 2fa21aa676d2115b287a2c6d131afcf95c6dada9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 13:43:57 -0700 Subject: [PATCH 016/195] . --- node/Multicaster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 87a1df9cc..6a8d63793 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -240,7 +240,7 @@ void Multicaster::send( const CertificateOfMembership *com = (CertificateOfMembership *)0; SharedPtr nconf; if (sn->needsOurNetworkMembershipCertificate(nwid,now,true)) { - SharedPtr nw = RR->node->network(nwid); + SharedPtr nw(RR->node->network(nwid)); if (nw) { nconf = nw->config2(); if (nconf) From 72e7e36a5b6eadcb9c2ce016269ef5bc9a54b13c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 14:40:28 -0700 Subject: [PATCH 017/195] No reason to randomly pick uPnP secondary port. In fact it would likely cause problems on restarts and uPnP rule bloat. --- service/OneService.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 35f8e806a..7b3c4ff69 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -493,10 +493,8 @@ public: // (cough Ubiquity Edge cough) barf up a lung if you do both conventional // NAT-t and uPnP from behind the same port. I think this is a bug, but // everyone else's router bugs are our problem. :P - for(int k=0;k<256;++k) { - unsigned int randp = 0; - Utils::getSecureRandom(&randp,sizeof(randp)); - unsigned int upnport = 40000 + (randp % 25500); + for(int k=0;k<512;++k) { + unsigned int upnport = 40000 + (((port + 1) * (k + 1)) % 25500); _v4UpnpLocalAddress = InetAddress(0,upnport); _v4UpnpUdpSocket = _phy.udpBind((const struct sockaddr *)&_v4UpnpLocalAddress,reinterpret_cast(&_v4UpnpLocalAddress),131072); From 5076c49210542243075556aa1ab74f33d4d50ba3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 15:40:54 -0700 Subject: [PATCH 018/195] Peer serialization and related changes. --- include/ZeroTierOne.h | 4 +- node/CertificateOfMembership.hpp | 72 ------------------ node/Identity.hpp | 2 - node/InetAddress.hpp | 46 ++++++++++++ node/Path.hpp | 8 +- node/Peer.hpp | 121 +++++++++++++++++++++++++++++++ node/RemotePath.hpp | 46 ++++++++++-- service/OneService.cpp | 5 +- 8 files changed, 214 insertions(+), 90 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index e8a19e331..8eacc9939 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -627,8 +627,8 @@ typedef struct */ typedef enum { ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 1, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 2 + ZT_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 10, + ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20 } ZT_LocalInterfaceAddressTrust; /** diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index 9a03374dc..81e00fbb5 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -315,78 +315,6 @@ public: */ inline const Address &signedBy() const throw() { return _signedBy; } - /** - * Serialize to std::string or compatible class - * - * @param b String or other class supporting push_back() and append() like std::string - */ - template - inline void serialize2(T &b) const - { - uint64_t tmp[3]; - char tmp2[ZT_ADDRESS_LENGTH]; - b.push_back((char)COM_UINT64_ED25519); - b.push_back((char)((_qualifiers.size() >> 8) & 0xff)); - b.push_back((char)(_qualifiers.size() & 0xff)); - for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) { - tmp[0] = Utils::hton(q->id); - tmp[1] = Utils::hton(q->value); - tmp[2] = Utils::hton(q->maxDelta); - b.append(reinterpret_cast(reinterpret_cast(tmp)),sizeof(tmp)); - } - _signedBy.copyTo(tmp2,ZT_ADDRESS_LENGTH); - b.append(tmp2,ZT_ADDRESS_LENGTH); - if (_signedBy) - b.append((const char *)_signature.data,_signature.size()); - } - - /** - * Deserialize from std::string::iterator or compatible iterator or char* pointer - * - * @param p Iterator - * @param end End of buffer - */ - template - inline void deserialize2(T &p,const T &end) - { - uint64_t tmp[3]; - char tmp2[ZT_ADDRESS_LENGTH]; - unsigned int qcount; - - _qualifiers.clear(); - _signedBy.zero(); - - if (p == end) throw std::out_of_range("incomplete certificate of membership"); - if (*(p++) != (char)COM_UINT64_ED25519) throw std::invalid_argument("unknown certificate of membership type"); - - if (p == end) throw std::out_of_range("incomplete certificate of membership"); - qcount = (unsigned int)*(p++) << 8; - if (p == end) throw std::out_of_range("incomplete certificate of membership"); - qcount |= (unsigned int)*(p++); - - for(unsigned int i=0;i(reinterpret_cast(tmp)); - for(unsigned int j=0;j inline void serialize(Buffer &b) const { diff --git a/node/Identity.hpp b/node/Identity.hpp index cc72632e3..18e67eb60 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -220,7 +220,6 @@ public: */ template inline void serialize(Buffer &b,bool includePrivate = false) const - throw(std::out_of_range) { _address.appendTo(b); b.append((unsigned char)IDENTITY_TYPE_C25519); @@ -245,7 +244,6 @@ public: */ template inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) - throw(std::out_of_range,std::invalid_argument) { delete _privateKey; _privateKey = (C25519::Private *)0; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 3c05d83b5..c376a032e 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -38,6 +38,7 @@ #include "../include/ZeroTierOne.h" #include "Utils.hpp" #include "MAC.hpp" +#include "Buffer.hpp" namespace ZeroTier { @@ -362,6 +363,51 @@ struct InetAddress : public sockaddr_storage */ inline operator bool() const throw() { return (ss_family != 0); } + template + inline void serialize(Buffer &b) const + { + // Format is the same as in VERB_HELLO in Packet.hpp + switch(ss_family) { + case AF_INET: + b.append((uint8_t)0x04); + b.append(&(reinterpret_cast(this)->sin_addr.s_addr),4); + b.append((uint16_t)port()); // just in case sin_port != uint16_t + return; + case AF_INET6: + b.append((uint8_t)0x06); + b.append(reinterpret_cast(this)->sin6_addr.s6_addr,16); + b.append((uint16_t)port()); // just in case sin_port != uint16_t + return; + default: + b.append((uint8_t)0); + return; + } + } + + template + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + { + unsigned int p = startAt; + memset(this,0,sizeof(InetAddress)); + switch(b[p++]) { + case 0: + return 1; + case 0x04: + ss_family = AF_INET; + memcpy(&(reinterpret_cast(this)->sin_addr.s_addr),b.field(p,4),4); p += 4; + reinterpret_cast(this)->sin_port = Utils::hton(b.template at(p)); p += 2; + break; + case 0x06: + ss_family = AF_INET6; + memcpy(reinterpret_cast(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16; + reinterpret_cast(this)->sin_port = Utils::hton(b.template at(p)); p += 2; + break; + default: + throw std::invalid_argument("invalid serialized InetAddress"); + } + return (p - startAt); + } + bool operator==(const InetAddress &a) const throw(); bool operator<(const InetAddress &a) const throw(); inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); } diff --git a/node/Path.hpp b/node/Path.hpp index 8d662ff77..6a69e071f 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -59,11 +59,11 @@ public: * * These values MUST match ZT_LocalInterfaceAddressTrust in ZeroTierOne.h */ - enum Trust + enum Trust // NOTE: max 255 { TRUST_NORMAL = 0, - TRUST_PRIVACY = 1, - TRUST_ULTIMATE = 2 + TRUST_PRIVACY = 10, + TRUST_ULTIMATE = 20 }; Path() : @@ -155,7 +155,7 @@ public: return false; } -private: +protected: InetAddress _addr; InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often Trust _trust; diff --git a/node/Peer.hpp b/node/Peer.hpp index 568de0d5f..482c0a828 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -445,6 +445,127 @@ public: else return std::pair(); } + template + inline void serialize(Buffer &b) const + { + Mutex::Lock _l(_lock); + + const unsigned int lengthAt = b.size(); + b.addSize(4); // space for uint32_t field length + + b.append((uint32_t)1); // version of serialized Peer data + + _id.serialize(b,false); + + b.append((uint64_t)_lastUsed); + b.append((uint64_t)_lastReceive); + b.append((uint64_t)_lastUnicastFrame); + b.append((uint64_t)_lastMulticastFrame); + b.append((uint64_t)_lastAnnouncedTo); + b.append((uint64_t)_lastPathConfirmationSent); + b.append((uint64_t)_lastDirectPathPush); + b.append((uint64_t)_lastPathSort); + b.append((uint16_t)_vProto); + b.append((uint16_t)_vMajor); + b.append((uint16_t)_vMinor); + b.append((uint16_t)_vRevision); + b.append((uint32_t)_latency); + + b.append((uint32_t)_numPaths); + for(unsigned int i=0;i<_numPaths;++i) + _paths[i].serialize(b); + + b.append((uint32_t)_networkComs.size()); + { + uint64_t *k = (uint64_t *)0; + _NetworkCom *v = (_NetworkCom *)0; + Hashtable::Iterator i(const_cast(this)->_networkComs); + while (i.next(k,v)) { + b.append((uint64_t)*k); + b.append((uint64_t)v->ts); + v->com.serialize(b); + } + } + + b.append((uint32_t)_lastPushedComs.size()); + { + uint64_t *k = (uint64_t *)0; + uint64_t *v = (uint64_t *)0; + Hashtable::Iterator i(const_cast(this)->_lastPushedComs); + while (i.next(k,v)) { + b.append((uint64_t)*k); + b.append((uint64_t)*v); + } + } + + b.setAt(lengthAt,(uint32_t)((b.size() - 4) - lengthAt)); // set size, not including size field itself + } + + /** + * Create a new Peer from a serialized instance + * + * @param myIdentity This node's identity + * @param b Buffer containing serialized Peer data + * @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result) + * @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer) + */ + template + static inline SharedPtr deserializeNew(const Identity &myIdentity,const Buffer &b,unsigned int &p) + { + const uint32_t recSize = b.template at(p); p += 4; + if ((p + recSize) > b.size()) + return SharedPtr(); // size invalid + if (b.template at(p) != 1) + return SharedPtr(); // version mismatch + p += 4; + + Identity npid; + p += npid.deserialize(b,p); + if (!npid) + return SharedPtr(); + + SharedPtr np(new Peer(myIdentity,npid)); + + np->_lastUsed = b.template at(p); p += 8; + np->_lastReceive = b.template at(p); p += 8; + np->_lastUnicastFrame = b.template at(p); p += 8; + np->_lastMulticastFrame = b.template at(p); p += 8; + np->_lastAnnouncedTo = b.template at(p); p += 8; + np->_lastPathConfirmationSent = b.template at(p); p += 8; + np->_lastDirectPathPush = b.template at(p); p += 8; + np->_lastPathSort = b.template at(p); p += 8; + np->_vProto = b.template at(p); p += 2; + np->_vMajor = b.template at(p); p += 2; + np->_vMinor = b.template at(p); p += 2; + np->_vRevision = b.template at(p); p += 2; + np->_latency = b.template at(p); p += 4; + + const unsigned int numPaths = b.template at(p); p += 2; + for(unsigned int i=0;i_paths[np->_numPaths++].deserialize(b,p); + } else { + // Skip any paths beyond max, but still read stream + RemotePath foo; + p += foo.deserialize(b,p); + } + } + + const unsigned int numNetworkComs = b.template at(p); p += 4; + for(unsigned int i=0;i_networkComs[b.template at(p)]; p += 8; + c.ts = b.template at(p); p += 8; + p += c.com.deserialize(b,p); + } + + const unsigned int numLastPushed = b.template at(p); p += 4; + for(unsigned int i=0;i(p); p += 8; + const uint64_t ts = b.template at(p); p += 8; + np->_lastPushedComs.set(nwid,ts); + } + } + private: void _sortPaths(const uint64_t now); RemotePath *_getBestPath(const uint64_t now); diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp index 0034242e1..9a8a3ff8f 100644 --- a/node/RemotePath.hpp +++ b/node/RemotePath.hpp @@ -39,6 +39,8 @@ #include "AntiRecursion.hpp" #include "RuntimeEnvironment.hpp" +#define ZT_REMOTEPATH_FLAG_FIXED 0x0001 + namespace ZeroTier { /** @@ -54,14 +56,14 @@ public: _lastSend(0), _lastReceived(0), _localAddress(), - _fixed(false) {} + _flags(0) {} RemotePath(const InetAddress &localAddress,const InetAddress &addr,bool fixed) : Path(addr,0,TRUST_NORMAL), _lastSend(0), _lastReceived(0), _localAddress(localAddress), - _fixed(fixed) {} + _flags(fixed ? ZT_REMOTEPATH_FLAG_FIXED : 0) {} inline const InetAddress &localAddress() const throw() { return _localAddress; } @@ -71,7 +73,7 @@ public: /** * @return Is this a fixed path? */ - inline bool fixed() const throw() { return _fixed; } + inline bool fixed() const throw() { return ((_flags & ZT_REMOTEPATH_FLAG_FIXED) != 0); } /** * @param f New value of fixed flag @@ -79,7 +81,9 @@ public: inline void setFixed(const bool f) throw() { - _fixed = f; + if (f) + _flags |= ZT_REMOTEPATH_FLAG_FIXED; + else _flags &= ~ZT_REMOTEPATH_FLAG_FIXED; } /** @@ -113,7 +117,7 @@ public: inline bool active(uint64_t now) const throw() { - return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); + return ( ((_flags & ZT_REMOTEPATH_FLAG_FIXED) != 0) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); } /** @@ -135,11 +139,39 @@ public: return false; } -private: + 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 += 4; + return (startAt - p); + } + +protected: uint64_t _lastSend; uint64_t _lastReceived; InetAddress _localAddress; - bool _fixed; + uint16_t _flags; }; } // namespace ZeroTier diff --git a/service/OneService.cpp b/service/OneService.cpp index 7b3c4ff69..4b374cd7e 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -489,13 +489,12 @@ public: OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr)); #ifdef ZT_USE_MINIUPNPC - // Bind a random secondary port for use with uPnP, since some NAT routers + // Bind a secondary port for use with uPnP, since some NAT routers // (cough Ubiquity Edge cough) barf up a lung if you do both conventional // NAT-t and uPnP from behind the same port. I think this is a bug, but // everyone else's router bugs are our problem. :P for(int k=0;k<512;++k) { - unsigned int upnport = 40000 + (((port + 1) * (k + 1)) % 25500); - + const unsigned int upnport = 40000 + (((port + 1) * (k + 1)) % 25500); _v4UpnpLocalAddress = InetAddress(0,upnport); _v4UpnpUdpSocket = _phy.udpBind((const struct sockaddr *)&_v4UpnpLocalAddress,reinterpret_cast(&_v4UpnpLocalAddress),131072); if (_v4UpnpUdpSocket) { From 76a95dc58fdc35037c69d915ea9fe13522edf502 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 17:09:01 -0700 Subject: [PATCH 019/195] The return of peer peristence. --- node/Peer.hpp | 15 +++++++--- node/RemotePath.hpp | 4 +-- node/Topology.cpp | 73 ++++++++++++++++++++++++++++++++++++++++----- node/Topology.hpp | 6 ++-- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/node/Peer.hpp b/node/Peer.hpp index 482c0a828..0988561a8 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -53,6 +53,10 @@ #include "Mutex.hpp" #include "NonCopyable.hpp" +// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16)) +// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted +#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576 + namespace ZeroTier { /** @@ -450,7 +454,7 @@ public: { Mutex::Lock _l(_lock); - const unsigned int lengthAt = b.size(); + const unsigned int atPos = b.size(); b.addSize(4); // space for uint32_t field length b.append((uint32_t)1); // version of serialized Peer data @@ -498,7 +502,7 @@ public: } } - b.setAt(lengthAt,(uint32_t)((b.size() - 4) - lengthAt)); // set size, not including size field itself + b.setAt(atPos,(uint32_t)(b.size() - atPos)); // set size } /** @@ -512,9 +516,10 @@ public: template static inline SharedPtr deserializeNew(const Identity &myIdentity,const Buffer &b,unsigned int &p) { - const uint32_t recSize = b.template at(p); p += 4; + const uint32_t recSize = b.template at(p); if ((p + recSize) > b.size()) return SharedPtr(); // size invalid + p += 4; if (b.template at(p) != 1) return SharedPtr(); // version mismatch p += 4; @@ -540,7 +545,7 @@ public: np->_vRevision = b.template at(p); p += 2; np->_latency = b.template at(p); p += 4; - const unsigned int numPaths = b.template at(p); p += 2; + const unsigned int numPaths = b.template at(p); p += 4; for(unsigned int i=0;i_paths[np->_numPaths++].deserialize(b,p); @@ -564,6 +569,8 @@ public: const uint64_t ts = b.template at(p); p += 8; np->_lastPushedComs.set(nwid,ts); } + + return np; } private: diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp index 9a8a3ff8f..d2f999971 100644 --- a/node/RemotePath.hpp +++ b/node/RemotePath.hpp @@ -163,8 +163,8 @@ public: _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 += 4; - return (startAt - p); + _flags = b.template at(p); p += 2; + return (p - startAt); } protected: diff --git a/node/Topology.cpp b/node/Topology.cpp index b2b4ebbda..65abfc6f0 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -31,6 +31,7 @@ #include "Defaults.hpp" #include "Dictionary.hpp" #include "Node.hpp" +#include "Buffer.hpp" namespace ZeroTier { @@ -38,10 +39,68 @@ Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), _amRoot(false) { + std::string alls(RR->node->dataStoreGet("peers.save")); + const uint8_t *all = reinterpret_cast(alls.data()); + RR->node->dataStoreDelete("peers.save"); + + unsigned int ptr = 0; + while ((ptr + 4) < alls.size()) { + // Each Peer serializes itself prefixed by a record length (not including the size of the length itself) + unsigned int reclen = (unsigned int)all[ptr] & 0xff; + reclen <<= 8; + reclen |= (unsigned int)all[ptr + 1] & 0xff; + reclen <<= 8; + reclen |= (unsigned int)all[ptr + 2] & 0xff; + reclen <<= 8; + reclen |= (unsigned int)all[ptr + 3] & 0xff; + + if (((ptr + reclen) > alls.size())||(reclen > ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE)) + break; + + try { + unsigned int pos = 0; + SharedPtr p(Peer::deserializeNew(RR->identity,Buffer(all + ptr,reclen),pos)); + if (pos != reclen) + break; + ptr += pos; + if ((p)&&(p->address() != RR->identity.address())) { + _peers[p->address()] = p; + } else { + break; // stop if invalid records + } + } catch (std::exception &exc) { + break; + } catch ( ... ) { + break; // stop if invalid records + } + } + + clean(RR->node->now()); } Topology::~Topology() { + Buffer pbuf; + std::string all; + + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + Hashtable< Address,SharedPtr >::Iterator i(_peers); + while (i.next(a,p)) { + if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) { + pbuf.clear(); + try { + (*p)->serialize(pbuf); + try { + all.append((const char *)pbuf.data(),pbuf.size()); + } catch ( ... ) { + return; // out of memory? just skip + } + } catch ( ... ) {} // peer too big? shouldn't happen, but it so skip + } + } + + RR->node->dataStorePut("peers.save",all,true); } void Topology::setRootServers(const std::map< Identity,std::vector > &sn) @@ -58,7 +117,7 @@ void Topology::setRootServers(const std::map< Identity,std::vector for(std::map< Identity,std::vector >::const_iterator i(sn.begin());i!=sn.end();++i) { if (i->first != RR->identity) { // do not add self as a peer - SharedPtr &p = _activePeers[i->first.address()]; + SharedPtr &p = _peers[i->first.address()]; if (!p) p = SharedPtr(new Peer(RR->identity,i->first)); for(std::vector::const_iterator j(i->second.begin());j!=i->second.end();++j) @@ -103,7 +162,7 @@ SharedPtr Topology::addPeer(const SharedPtr &peer) const uint64_t now = RR->node->now(); Mutex::Lock _l(_lock); - SharedPtr &p = _activePeers.set(peer->address(),peer); + SharedPtr &p = _peers.set(peer->address(),peer); p->use(now); _saveIdentity(p->identity()); @@ -120,7 +179,7 @@ SharedPtr Topology::getPeer(const Address &zta) const uint64_t now = RR->node->now(); Mutex::Lock _l(_lock); - SharedPtr &ap = _activePeers[zta]; + SharedPtr &ap = _peers[zta]; if (ap) { ap->use(now); @@ -136,7 +195,7 @@ SharedPtr Topology::getPeer(const Address &zta) } catch ( ... ) {} // invalid identity? } - _activePeers.erase(zta); + _peers.erase(zta); return SharedPtr(); } @@ -160,7 +219,7 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou if (++sna == _rootAddresses.end()) sna = _rootAddresses.begin(); // wrap around at end if (*sna != RR->identity.address()) { // pick one other than us -- starting from me+1 in sorted set order - SharedPtr *p = _activePeers.get(*sna); + SharedPtr *p = _peers.get(*sna); if ((p)&&((*p)->hasActiveDirectPath(now))) { bestRoot = *p; break; @@ -249,12 +308,12 @@ bool Topology::isRoot(const Identity &id) const void Topology::clean(uint64_t now) { Mutex::Lock _l(_lock); - Hashtable< Address,SharedPtr >::Iterator i(_activePeers); + Hashtable< Address,SharedPtr >::Iterator i(_peers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) { - _activePeers.erase(*a); + _peers.erase(*a); } else { (*p)->clean(RR,now); } diff --git a/node/Topology.hpp b/node/Topology.hpp index 3066b50c9..4df545e1f 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -164,7 +164,7 @@ public: inline void eachPeer(F f) { Mutex::Lock _l(_lock); - Hashtable< Address,SharedPtr >::Iterator i(_activePeers); + Hashtable< Address,SharedPtr >::Iterator i(_peers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) @@ -177,7 +177,7 @@ public: inline std::vector< std::pair< Address,SharedPtr > > allPeers() const { Mutex::Lock _l(_lock); - return _activePeers.entries(); + return _peers.entries(); } /** @@ -194,7 +194,7 @@ private: const RuntimeEnvironment *RR; - Hashtable< Address,SharedPtr > _activePeers; + Hashtable< Address,SharedPtr > _peers; std::map< Identity,std::vector > _roots; std::vector< Address > _rootAddresses; std::vector< SharedPtr > _rootPeers; From 5384f185ae761a0efd46090a5824178f5dcd74bc Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Oct 2015 18:12:16 -0700 Subject: [PATCH 020/195] Simplify Dictionary and reduce memory usage, now no more std::maps in core. --- node/Dictionary.cpp | 72 ++++++++++++++++++++++++++++++++++ node/Dictionary.hpp | 88 ++++++++++-------------------------------- node/NetworkConfig.cpp | 10 ++--- node/Topology.cpp | 2 +- 4 files changed, 99 insertions(+), 73 deletions(-) diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp index fb49002a5..578fdedc4 100644 --- a/node/Dictionary.cpp +++ b/node/Dictionary.cpp @@ -32,6 +32,68 @@ namespace ZeroTier { +Dictionary::iterator Dictionary::find(const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i; + } + return end(); +} +Dictionary::const_iterator Dictionary::find(const std::string &key) const +{ + for(const_iterator i(begin());i!=end();++i) { + if (i->first == key) + return i; + } + return end(); +} + +bool Dictionary::getBoolean(const std::string &key,bool dfl) const +{ + const_iterator e(find(key)); + if (e == end()) + return dfl; + if (e->second.length() < 1) + return dfl; + switch(e->second[0]) { + case '1': + case 't': + case 'T': + case 'y': + case 'Y': + return true; + } + return false; +} + +std::string &Dictionary::operator[](const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i->second; + } + push_back(std::pair(key,std::string())); + std::sort(begin(),end()); + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i->second; + } + return front().second; // should be unreachable! +} + +std::string Dictionary::toString() const +{ + std::string s; + for(const_iterator kv(begin());kv!=end();++kv) { + _appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s); + s.push_back('='); + _appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s); + s.append(ZT_EOL_S); + } + return s; +} + void Dictionary::updateFromString(const char *s,unsigned int maxlen) { bool escapeState = false; @@ -80,6 +142,16 @@ void Dictionary::fromString(const char *s,unsigned int maxlen) updateFromString(s,maxlen); } +void Dictionary::eraseKey(const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) { + this->erase(i); + return; + } + } +} + bool Dictionary::sign(const Identity &id,uint64_t now) { try { diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 1e643788e..24f2ac8ff 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -31,8 +31,9 @@ #include #include -#include +#include #include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -56,12 +57,12 @@ class Identity; * * Keys beginning with "~!" are reserved for signature data fields. * - * Note: the signature code depends on std::map<> being sorted, but no - * other code does. So if the underlying data structure is ever swapped - * out for an unsorted one, the signature code will have to be updated - * to sort before composing the string to sign. + * It's stored as a simple vector and can be linearly scanned or + * binary searched. Dictionaries are only used for very small things + * outside the core loop, so this is not a significant performance + * issue and it reduces memory use and code footprint. */ -class Dictionary : public std::map +class Dictionary : public std::vector< std::pair > { public: Dictionary() {} @@ -77,21 +78,8 @@ public: */ Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); } - /** - * Get a key, throwing an exception if it is not present - * - * @param key Key to look up - * @return Reference to value - * @throws std::invalid_argument Key not found - */ - inline const std::string &get(const std::string &key) const - throw(std::invalid_argument) - { - const_iterator e(find(key)); - if (e == end()) - throw std::invalid_argument(std::string("missing required field: ")+key); - return e->second; - } + iterator find(const std::string &key); + const_iterator find(const std::string &key) const; /** * Get a key, returning a default if not present @@ -113,23 +101,7 @@ public: * @param dfl Default boolean result if key not found or empty (default: false) * @return Boolean value of key */ - inline bool getBoolean(const std::string &key,bool dfl = false) const - { - const_iterator e(find(key)); - if (e == end()) - return dfl; - if (e->second.length() < 1) - return dfl; - switch(e->second[0]) { - case '1': - case 't': - case 'T': - case 'y': - case 'Y': - return true; - } - return false; - } + bool getBoolean(const std::string &key,bool dfl = false) const; /** * @param key Key to get @@ -170,6 +142,8 @@ public: return Utils::strTo64(e->second.c_str()); } + std::string &operator[](const std::string &key); + /** * @param key Key to set * @param value String value @@ -239,17 +213,7 @@ public: /** * @return String-serialized dictionary */ - inline std::string toString() const - { - std::string s; - for(const_iterator kv(begin());kv!=end();++kv) { - _appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s); - s.push_back('='); - _appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s); - s.append(ZT_EOL_S); - } - return s; - } + std::string toString() const; /** * Clear and initialize from a string @@ -278,14 +242,19 @@ public: */ uint64_t signatureTimestamp() const; + /** + * @param key Key to erase + */ + void eraseKey(const std::string &key); + /** * Remove any signature from this dictionary */ inline void removeSignature() { - erase(ZT_DICTIONARY_SIGNATURE); - erase(ZT_DICTIONARY_SIGNATURE_IDENTITY); - erase(ZT_DICTIONARY_SIGNATURE_TIMESTAMP); + eraseKey(ZT_DICTIONARY_SIGNATURE); + eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY); + eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP); } /** @@ -305,21 +274,6 @@ public: */ bool verify(const Identity &id) const; - inline bool operator==(const Dictionary &d) const - { - // std::map::operator== is broken on uclibc++ - if (size() != d.size()) - return false; - const_iterator a(begin()); - const_iterator b(d.begin()); - while (a != end()) { - if (*(a++) != *(b++)) - return false; - } - return true; - } - inline bool operator!=(const Dictionary &d) const { return (!(*this == d)); } - private: void _mkSigBuf(std::string &buf) const; static void _appendEsc(const char *data,unsigned int len,std::string &to); diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index e46da4a4b..cd32600f1 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -87,27 +87,27 @@ void NetworkConfig::_fromDictionary(const Dictionary &d) // NOTE: d.get(name) throws if not found, d.get(name,default) returns default - _nwid = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID).c_str()); + _nwid = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,"0").c_str()); if (!_nwid) throw std::invalid_argument("configuration contains zero network ID"); - _timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP).c_str()); + _timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,"0").c_str()); _revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers don't send this, so default to 1 memset(_etWhitelist,0,sizeof(_etWhitelist)); - std::vector ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","","")); + std::vector ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","","")); for(std::vector::const_iterator et(ets.begin());et!=ets.end();++et) { unsigned int tmp = Utils::hexStrToUInt(et->c_str()) & 0xffff; _etWhitelist[tmp >> 3] |= (1 << (tmp & 7)); } - _issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO)); + _issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,"0")); _multicastLimit = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,zero).c_str()); if (_multicastLimit == 0) _multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; _allowPassiveBridging = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING,zero).c_str()) != 0); _private = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,one).c_str()) != 0); _enableBroadcast = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST,one).c_str()) != 0); - _name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME); + _name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,""); if (_name.length() > ZT_MAX_NETWORK_SHORT_NAME_LENGTH) throw std::invalid_argument("network short name too long (max: 255 characters)"); diff --git a/node/Topology.cpp b/node/Topology.cpp index 65abfc6f0..908acbc82 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -140,7 +140,7 @@ void Topology::setRootServers(const Dictionary &sn) if ((d->first.length() == ZT_ADDRESS_LENGTH_HEX)&&(d->second.length() > 0)) { try { Dictionary snspec(d->second); - std::vector &a = m[Identity(snspec.get("id"))]; + std::vector &a = m[Identity(snspec.get("id",""))]; std::string udp(snspec.get("udp",std::string())); if (udp.length() > 0) a.push_back(InetAddress(udp)); From 6080a45c9ce2494ff787063ab06831158f17ec2b Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 2 Oct 2015 19:39:13 -0700 Subject: [PATCH 021/195] change cert to com. no variable named cert. --- node/Peer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index e135faa52..757f822c5 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -407,7 +407,7 @@ bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment * // Check signature, log and return if cert is invalid if (com.signedBy() != Network::controllerFor(nwid)) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); + TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,com.signedBy().toString().c_str()); return false; // invalid signer } @@ -416,7 +416,7 @@ bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment * // We are the controller: RR->identity.address() == controller() == cert.signedBy() // So, verify that we signed th cert ourself if (!com.verify(RR->identity)) { - TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,com.signedBy().toString().c_str()); return false; // invalid signature } @@ -432,7 +432,7 @@ bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment * } if (!com.verify(signer->identity())) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,com.signedBy().toString().c_str()); return false; // invalid signature } } From c16ad053b69206c8c710ceabeb4c20ea15865466 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 2 Oct 2015 19:39:46 -0700 Subject: [PATCH 022/195] no toString() method on peer. Commenting out for now. --- node/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/Network.cpp b/node/Network.cpp index a9e4e55b7..69f8245e0 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -393,9 +393,9 @@ bool Network::_isAllowed(const SharedPtr &peer) const return true; return ((_config->com())&&(peer->networkMembershipCertificatesAgree(_id,_config->com()))); } catch (std::exception &exc) { - TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what()); + TRACE("isAllowed() check failed for peer %s: unexpected exception: %s","" /*peer->toString().c_str() */,exc.what()); } catch ( ... ) { - TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer.toString().c_str()); + TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception", "" /*peer.toString().c_str()*/); } return false; // default position on any failure } From 57c857e89a1b4616ffb091b9d834c68b4b8d93d0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 06:57:00 -0700 Subject: [PATCH 023/195] Fix TRACE output. --- node/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/Network.cpp b/node/Network.cpp index 69f8245e0..9ce58c630 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -393,9 +393,9 @@ bool Network::_isAllowed(const SharedPtr &peer) const return true; return ((_config->com())&&(peer->networkMembershipCertificatesAgree(_id,_config->com()))); } catch (std::exception &exc) { - TRACE("isAllowed() check failed for peer %s: unexpected exception: %s","" /*peer->toString().c_str() */,exc.what()); + TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer->address().toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception", "" /*peer.toString().c_str()*/); + TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer->address().toString().c_str()); } return false; // default position on any failure } From 5341afcdcd7d2d1ae5546ae44024d039b03ccad3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 11:47:16 -0700 Subject: [PATCH 024/195] Handling of CIRCUIT_TEST, should be ready to test. --- node/Buffer.hpp | 17 +++++ node/IncomingPacket.cpp | 152 ++++++++++++++++++++++++++++++++++------ node/Packet.hpp | 44 ++++-------- 3 files changed, 162 insertions(+), 51 deletions(-) diff --git a/node/Buffer.hpp b/node/Buffer.hpp index 789b835a5..46924c144 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -391,6 +391,23 @@ public: ::memmove(_b,_b + at,_l -= at); } + /** + * Erase something from the middle of the buffer + * + * @param start Starting position + * @param length Length of block to erase + * @throw std::out_of_range Position plus length is beyond size of buffer + */ + inline void erase(const unsigned int at,const unsigned int length) + throw(std::out_of_range) + { + const unsigned int endr = at + length; + if (endr > _l) + throw std::out_of_range("Buffer: erase() range beyond end of buffer"); + ::memmove(_b + at,_b + endr,_l - endr); + _l -= length; + } + /** * Set buffer data length to zero */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index e892edc21..49a5981bb 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -933,38 +933,146 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt const uint64_t timestamp = at(ZT_PACKET_IDX_PAYLOAD + 7); const uint64_t testId = at(ZT_PACKET_IDX_PAYLOAD + 15); - unsigned int vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); // variable length field length - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { - case 0x01: { // 64-bit network ID, originator must be controller - } break; - default: break; + // Tracks total length of variable length fields, initialized to originator credential length below + unsigned int vlf; + + // Originator credentials + const unsigned int originatorCredentialLength = vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); + uint64_t originatorCredentialNetworkId = 0; + if (originatorCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { + case 0x01: { // 64-bit network ID, originator must be controller + if (originatorCredentialLength >= 9) + originatorCredentialNetworkId = at(ZT_PACKET_IDX_PAYLOAD + 26); + } break; + default: break; + } } - vlf += at(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused + // Add length of "additional fields," which are currently unused + vlf += at(ZT_PACKET_IDX_PAYLOAD + 25 + vlf); - const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 28 + vlf); - if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,28 + vlf),28 + vlf,field(30 + vlf,signatureLength),signatureLength)) { + // Verify signature -- only tests signed by their originators are allowed + const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 27 + vlf); + if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) { TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); return true; } vlf += signatureLength; - vlf += at(ZT_PACKET_IDX_PAYLOAD + 30 + vlf); - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]) { - case 0x01: { // network certificate of membership for previous hop - } break; - default: break; + // Save this length so we can copy the immutable parts of this test + // into the one we send along to next hops. + const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf; + + // Get previous hop's credential, if any + const unsigned int previousHopCredentialLength = at(ZT_PACKET_IDX_PAYLOAD + 29 + vlf); + CertificateOfMembership previousHopCom; + if (previousHopCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]) { + case 0x01: { // network certificate of membership for previous hop + if (previousHopCom.deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 32 + vlf) != (previousHopCredentialLength - 1)) { + TRACE("dropped CIRCUIT_TEST from %s(%s): previous hop COM invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } break; + default: break; + } + } + vlf += previousHopCredentialLength; + + // Check credentials (signature already verified) + SharedPtr originatorCredentialNetworkConfig; + if (originatorCredentialNetworkId) { + if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) { + SharedPtr nw(RR->node->network(originatorCredentialNetworkId)); + if (nw) { + originatorCredentialNetworkConfig = nw->config2(); + if ( (originatorCredentialNetworkConfig) && (originatorCredentialNetworkConfig->isPublic()||((originatorCredentialNetworkConfig->com())&&(previousHopCom)&&(originatorCredentialNetworkConfig->com().agreesWith(previousHopCom)))) ) { + TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str()); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we are not a member",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID as credential, is not controller for %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); + return true; } - if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) { - const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf]; - Address nextHops[255]; - SharedPtr nextHopPeers[255]; - unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf; - for(unsigned int h=0;((h256 but be safe anyway - nextHops[h].setTo(field(hptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - hptr += ZT_ADDRESS_LENGTH; - nextHopPeers[h] = RR->topology->getPeer(nextHops[h]); + const uint64_t now = RR->node->now(); + + unsigned int breadth = 0; + Address nextHop[256]; // breadth is a uin8_t, so this is the max + InetAddress nextHopBestPathAddress[256]; + unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf; + if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) { + // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf] + breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]; + for(unsigned int h=0;h nhp(RR->topology->getPeer(nextHop[h])); + if (nhp) { + RemotePath *const rp = nhp->getBestPath(now); + if (rp) + nextHopBestPathAddress[h] = rp->address(); + } + } + } + + // Report back to originator, depending on flags and whether we are last hop + if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) { + Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT); + outp.append((uint64_t)timestamp); + outp.append((uint64_t)testId); + outp.append((uint64_t)now); + outp.append((uint8_t)0); // vendor ID, currently unused + outp.append((uint8_t)ZT_PROTO_VERSION); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); + outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED); + outp.append((uint16_t)0); // error code, currently unused + outp.append((uint64_t)0); // flags, currently unused + outp.append((uint64_t)packetId()); + outp.append((uint8_t)hops()); + _localAddress.serialize(outp); + _remoteAddress.serialize(outp); + outp.append((uint16_t)0); // no additional fields + outp.append((uint8_t)breadth); + for(unsigned int h=0;hsw->send(outp,true,0); + } + + // If there are next hops, forward the test along through the graph + if (breadth > 0) { + Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST); + outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature); + const unsigned int previousHopCredentialPos = outp.size(); + outp.append((uint16_t)0); // no previous hop credentials: default + if ((originatorCredentialNetworkConfig)&&(!originatorCredentialNetworkConfig->isPublic())&&(originatorCredentialNetworkConfig->com())) { + outp.append((uint8_t)0x01); // COM + originatorCredentialNetworkConfig->com().serialize(outp); + outp.setAt(previousHopCredentialPos,(uint16_t)(size() - previousHopCredentialPos)); + } + if (remainingHopsPtr < size()) + outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr); + + for(unsigned int h=0;hsw->send(outp,true,originatorCredentialNetworkId); } } } catch (std::exception &exc) { diff --git a/node/Packet.hpp b/node/Packet.hpp index 71fa6e569..eaffb922d 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -911,39 +911,34 @@ public: * <[2] 16-bit flags> * <[8] 64-bit timestamp> * <[8] 64-bit test ID (arbitrary, set by tester)> - * <[2] 16-bit originator credential length> - * <[1] originator credential type (for authorizing test)> - * <[...] credential> + * <[2] 16-bit originator credential length (includes type)> + * [[1] originator credential type (for authorizing test)] + * [[...] originator credential] * <[2] 16-bit length of additional fields> - * <[...] additional fields> + * [[...] additional fields] * [ ... end of signed portion of request ... ] * <[2] 16-bit length of signature of request> * <[...] signature of request by originator> - * <[2] 16-bit previous hop credential length> - * <[1] previous hop credential type> - * <[...] previous hop credential> + * <[2] 16-bit previous hop credential length (including type)> + * [[1] previous hop credential type] + * [[...] previous hop credential] * <[...] next hop(s) in path> * * Flags: - * 0x01 - Report back to originator at each hop + * 0x01 - Report back to originator at middle hops * 0x02 - Report back to originator at last hop * * Originator credential types: - * 0x00 - No credentials included * 0x01 - 64-bit network ID for which originator is controller * * Previous hop credential types: - * 0x00 - No credentials included * 0x01 - Certificate of network membership * * Path record format: - * <[1] 8-bit flags> + * <[1] 8-bit flags (unused, must be zero)> * <[1] 8-bit breadth (number of next hops)> * <[...] one or more ZeroTier addresses of next hops> * - * Path record flags (in each path record): - * (unused, must be zero) - * * The circuit test allows a device to send a message that will traverse * the network along a specified path, with each hop optionally reporting * back to the tester via VERB_CIRCUIT_TEST_REPORT. @@ -1001,28 +996,19 @@ public: * <[2] 16-bit reporter OS/platform> * <[2] 16-bit reporter architecture> * <[2] 16-bit error code (set to 0, currently unused)> - * <[8] 64-bit report flags> + * <[8] 64-bit report flags (set to 0, currently unused)> * <[8] 64-bit source packet ID> - * <[1] 8-bit source packet hop count> - * <[1] 8-bit source address type> - * [<[...] source address>] - * <[2] 16-bit length of network information> - * <[...] network information> + * <[1] 8-bit source packet hop count (ZeroTier hop count)> + * <[...] local wire address on which packet was received> + * <[...] remote wire address from which packet was received> * <[2] 16-bit length of additional fields> * <[...] additional fields> - * <[2] 16-bit number of next hops to which something is being sent> + * <[1] 8-bit number of next hops (breadth)> * <[...] next hop information> * - * Circuit test report flags: - * (currently none, must be zero) - * * Next hop information record format: * <[5] ZeroTier address of next hop> - * <[1] 8-bit destination wire address type> - * <[...] destination wire address> - * - * See enums below for OS/platform and architecture. Source address format - * is the same as specified in HELLO. + * <[...] current best direct path address, if any, 0 if none> * * Circuit test reports can be sent by hops in a circuit test to report * back results. They should include information about the sender as well From d3f29d09e8eb7fdbbbed682b383da73bc44928d6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 14:42:51 -0700 Subject: [PATCH 025/195] Plumbing through circuit test stuff. --- include/ZeroTierOne.h | 271 ++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.cpp | 7 +- node/Node.cpp | 52 ++++++++ node/Node.hpp | 12 ++ node/Packet.hpp | 38 ------ 5 files changed, 339 insertions(+), 41 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 8eacc9939..43c8fc0bb 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -115,6 +115,19 @@ extern "C" { */ #define ZT_FEATURE_FLAG_FIPS 0x00000002 +/** + * Maximum number of hops in a ZeroTier circuit test + * + * This is more or less the max that can be fit in a given packet (with + * fragmentation) and only one address per hop. + */ +#define ZT_CIRCUIT_TEST_MAX_HOPS 512 + +/** + * Maximum number of addresses per hop in a circuit test + */ +#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 256 + /** * A null/empty sockaddr (all zero) to signify an unspecified socket address */ @@ -631,6 +644,231 @@ typedef enum { ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20 } ZT_LocalInterfaceAddressTrust; +/** + * Vendor ID + */ +typedef enum { + ZT_VENDOR_UNSPECIFIED = 0, + ZT_VENDOR_ZEROTIER = 1 +} ZT_Vendor; + +/** + * Platform type + */ +typedef enum { + ZT_PLATFORM_UNSPECIFIED = 0, + ZT_PLATFORM_LINUX = 1, + ZT_PLATFORM_WINDOWS = 2, + ZT_PLATFORM_MACOS = 3, + ZT_PLATFORM_ANDROID = 4, + ZT_PLATFORM_IOS = 5, + ZT_PLATFORM_SOLARIS_SMARTOS = 6, + ZT_PLATFORM_FREEBSD = 7, + ZT_PLATFORM_NETBSD = 8, + ZT_PLATFORM_OPENBSD = 9, + ZT_PLATFORM_RISCOS = 10, + ZT_PLATFORM_VXWORKS = 11, + ZT_PLATFORM_FREERTOS = 12, + ZT_PLATFORM_SYSBIOS = 13, + ZT_PLATFORM_HURD = 14 +} ZT_Platform; + +/** + * Architecture type + */ +typedef enum { + ZT_ARCHITECTURE_UNSPECIFIED = 0, + ZT_ARCHITECTURE_X86 = 1, + ZT_ARCHITECTURE_X64 = 2, + ZT_ARCHITECTURE_ARM32 = 3, + ZT_ARCHITECTURE_ARM64 = 4, + ZT_ARCHITECTURE_MIPS32 = 5, + ZT_ARCHITECTURE_MIPS64 = 6, + ZT_ARCHITECTURE_POWER32 = 7, + ZT_ARCHITECTURE_POWER64 = 8 +} ZT_Architecture; + +/** + * ZeroTier circuit test configuration and path + */ +typedef struct { + /** + * Test ID -- an arbitrary 64-bit identifier + */ + uint64_t testId; + + /** + * Timestamp -- sent with test and echoed back by each reporter + */ + uint64_t timestamp; + + /** + * Originator credential: network ID + * + * If this is nonzero, a network ID will be set for this test and + * the originator must be its primary network controller. This is + * currently the only authorization method available, so it must + * be set to run a test. + */ + uint64_t credentialNetworkId; + + /** + * Hops in circuit test (a.k.a. FIFO for graph traversal) + */ + struct { + /** + * Hop flags (currently unused, must be zero) + */ + unsigned int flags; + + /** + * Number of addresses in this hop (max: ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) + */ + unsigned int breadth; + + /** + * 40-bit ZeroTier addresses (most significant 24 bits ignored) + */ + uint64_t addresses[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH]; + } hops[ZT_CIRCUIT_TEST_MAX_HOPS]; + + /** + * Number of hops (max: ZT_CIRCUIT_TEST_MAX_HOPS) + */ + unsigned int hopCount; + + /** + * If non-zero, circuit test will report back at every hop + */ + int reportAtEveryHop; + + /** + * An arbitrary user-settable pointer + */ + void *ptr; + + /** + * Pointer for internal use -- initialize to zero and do not modify + */ + void *_internalPtr; +} ZT_CircuitTest; + +/** + * Circuit test result report + */ +typedef struct { + /** + * 64-bit test ID + */ + uint64_t testId; + + /** + * Timestamp from original test (echoed back at each hop) + */ + uint64_t timestamp; + + /** + * Timestamp on remote device + */ + uint64_t remoteTimestamp; + + /** + * 64-bit packet ID of packet received by the reporting device + */ + uint64_t sourcePacketId; + + /** + * Flags (currently unused, will be zero) + */ + uint64_t flags; + + /** + * ZeroTier protocol-level hop count of packet received by reporting device (>0 indicates relayed) + */ + unsigned int sourcePacketHopCount; + + /** + * Error code (currently unused, will be zero) + */ + unsigned int errorCode; + + /** + * Remote device vendor ID + */ + ZT_Vendor vendor; + + /** + * Remote device protocol compliance version + */ + unsigned int protocolVersion; + + /** + * Software major version + */ + unsigned int majorVersion; + + /** + * Software minor version + */ + unsigned int minorVersion; + + /** + * Software revision + */ + unsigned int revision; + + /** + * Platform / OS + */ + ZT_Platform platform; + + /** + * System architecture + */ + ZT_Architecture architecture; + + /** + * Local device address on which packet was received by reporting device + * + * This may have ss_family equal to zero (null address) if unspecified. + */ + struct sockaddr_storage receivedOnLocalAddress; + + /** + * Remote address from which reporter received the test packet + * + * This may have ss_family set to zero (null address) if unspecified. + */ + struct sockaddr_storage receivedFromAddress; + + /** + * Next hops to which packets are being or will be sent by the reporter + * + * In addition to reporting back, the reporter may send the test on if + * there are more recipients in the FIFO. If it does this, it can report + * back the address(es) that make up the next hop and the physical address + * for each if it has one. The physical address being null/unspecified + * typically indicates that no direct path exists and the next packet + * will be relayed. + */ + struct { + /** + * 40-bit ZeroTier address + */ + uint64_t address; + + /** + * Physical address or null address (ss_family == 0) if unspecified or unknown + */ + struct sockaddr_storage physicalAddress; + } nextHops[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH]; + + /** + * Number of next hops reported in nextHops[] + */ + unsigned int nextHopCount; +} ZT_CircuitTestReport; + /** * An instance of a ZeroTier One node (opaque) */ @@ -1061,6 +1299,39 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node); */ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance); +/** + * Initiate a VL1 circuit test + * + * This sends an initial VERB_CIRCUIT_TEST and reports results back to the + * supplied callback until circuitTestEnd() is called. The supplied + * ZT_CircuitTest structure should be initially zeroed and then filled + * in with settings and hops. + * + * It is the caller's responsibility to call circuitTestEnd() and then + * to dispose of the test structure. Otherwise this node will listen + * for results forever. + * + * @param node Node instance + * @param test Test configuration + * @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 *)); + +/** + * Stop listening for results to a given circuit test + * + * This does not free the 'test' structure. The caller may do that + * after calling this method to unregister it. + * + * Any reports that are received for a given test ID after it is + * terminated are ignored. + * + * @param node Node instance + * @param test Test configuration to unregister + */ +void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test); + /** * Get ZeroTier One version * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 49a5981bb..c8e4cf5fd 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -30,6 +30,7 @@ #include #include "../version.h" +#include "../include/ZeroTierOne.h" #include "Constants.hpp" #include "Defaults.hpp" @@ -1033,13 +1034,13 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt outp.append((uint64_t)timestamp); outp.append((uint64_t)testId); outp.append((uint64_t)now); - outp.append((uint8_t)0); // vendor ID, currently unused + outp.append((uint8_t)ZT_VENDOR_ZEROTIER); outp.append((uint8_t)ZT_PROTO_VERSION); outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - outp.append((uint16_t)CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED); - outp.append((uint16_t)CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED); + outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED); + outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED); outp.append((uint16_t)0); // error code, currently unused outp.append((uint64_t)0); // flags, currently unused outp.append((uint64_t)packetId()); diff --git a/node/Node.cpp b/node/Node.cpp index 6dc83d4eb..cd20972b0 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -464,6 +464,28 @@ void Node::setNetconfMaster(void *networkControllerInstance) RR->localNetworkController = reinterpret_cast(networkControllerInstance); } +ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) +{ + { + test->_internalPtr = reinterpret_cast(reportCallback); + Mutex::Lock _l(_circuitTests_m); + if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end()) + _circuitTests.push_back(test); + } + return ZT_RESULT_OK; +} + +void Node::circuitTestEnd(ZT_CircuitTest *test) +{ + Mutex::Lock _l(_circuitTests_m); + for(;;) { + std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test)); + if (ct == _circuitTests.end()) + break; + else _circuitTests.erase(ct); + } +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -533,6 +555,20 @@ uint64_t Node::prng() return _prngStream[p]; } +void Node::postCircuitTestReport(const ZT_CircuitTestReport *report) +{ + std::vector< ZT_CircuitTest * > toNotify; + { + Mutex::Lock _l(_circuitTests_m); + for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) { + if ((*i)->testId == report->testId) + toNotify.push_back(*i); + } + } + for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i) + (reinterpret_cast((*i)->_internalPtr))(reinterpret_cast(this),*i,report); +} + } // namespace ZeroTier /****************************************************************************/ @@ -721,6 +757,22 @@ 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 *)) +{ + try { + return reinterpret_cast(node)->circuitTestBegin(test,reportCallback); + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; + } +} + +void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test) +{ + try { + reinterpret_cast(node)->circuitTestEnd(test); + } catch ( ... ) {} +} + int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust) { try { diff --git a/node/Node.hpp b/node/Node.hpp index 0f659f47c..20c544718 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -109,6 +109,8 @@ public: int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); + ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); + void circuitTestEnd(ZT_CircuitTest *test); // Internal functions ------------------------------------------------------ @@ -238,6 +240,13 @@ public: */ uint64_t prng(); + /** + * Post a circuit test report to any listeners for a given test ID + * + * @param report Report (includes test ID) + */ + void postCircuitTestReport(const ZT_CircuitTestReport *report); + private: inline SharedPtr _network(uint64_t nwid) const { @@ -264,6 +273,9 @@ private: std::vector< std::pair< uint64_t, SharedPtr > > _networks; Mutex _networks_m; + std::vector< ZT_CircuitTest * > _circuitTests; + Mutex _circuitTests_m; + std::vector _directPaths; Mutex _directPaths_m; diff --git a/node/Packet.hpp b/node/Packet.hpp index eaffb922d..409762c7e 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1020,44 +1020,6 @@ public: VERB_CIRCUIT_TEST_REPORT = 18 }; - /** - * Platforms reported in circuit tests - */ - enum CircuitTestReportPlatform - { - CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED = 0, - CIRCUIT_TEST_REPORT_PLATFORM_LINUX = 1, - CIRCUIT_TEST_REPORT_PLATFORM_WINDOWS = 2, - CIRCUIT_TEST_REPORT_PLATFORM_MACOS = 3, - CIRCUIT_TEST_REPORT_PLATFORM_ANDROID = 4, - CIRCUIT_TEST_REPORT_PLATFORM_IOS = 5, - CIRCUIT_TEST_REPORT_PLATFORM_SOLARIS_SMARTOS = 6, - CIRCUIT_TEST_REPORT_PLATFORM_FREEBSD = 7, - CIRCUIT_TEST_REPORT_PLATFORM_NETBSD = 8, - CIRCUIT_TEST_REPORT_PLATFORM_OPENBSD = 9, - CIRCUIT_TEST_REPORT_PLATFORM_RISCOS = 10, - CIRCUIT_TEST_REPORT_PLATFORM_VXWORKS = 11, - CIRCUIT_TEST_REPORT_PLATFORM_FREERTOS = 12, - CIRCUIT_TEST_REPORT_PLATFORM_SYSBIOS = 13, - CIRCUIT_TEST_REPORT_PLATFORM_HURD = 14 - }; - - /** - * Architectures reported in circuit tests - */ - enum CircuitTestReportArchitecture - { - CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED = 0, - CIRCUIT_TEST_REPORT_ARCH_X86 = 1, - CIRCUIT_TEST_REPORT_ARCH_X64 = 2, - CIRCUIT_TEST_REPORT_ARCH_ARM32 = 3, - CIRCUIT_TEST_REPORT_ARCH_ARM64 = 4, - CIRCUIT_TEST_REPORT_ARCH_MIPS32 = 5, - CIRCUIT_TEST_REPORT_ARCH_MIPS64 = 6, - CIRCUIT_TEST_REPORT_ARCH_POWER32 = 7, - CIRCUIT_TEST_REPORT_ARCH_POWER64 = 8 - }; - /** * Error codes for VERB_ERROR */ From 3593fb3462f277cca9241563ac0b97ade1582c91 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 15:16:41 -0700 Subject: [PATCH 026/195] Send initial CIRCUIT_TEST packet. --- node/IncomingPacket.cpp | 2 +- node/Node.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c8e4cf5fd..305232ee3 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -988,7 +988,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt SharedPtr nw(RR->node->network(originatorCredentialNetworkId)); if (nw) { originatorCredentialNetworkConfig = nw->config2(); - if ( (originatorCredentialNetworkConfig) && (originatorCredentialNetworkConfig->isPublic()||((originatorCredentialNetworkConfig->com())&&(previousHopCom)&&(originatorCredentialNetworkConfig->com().agreesWith(previousHopCom)))) ) { + if ( (originatorCredentialNetworkConfig) && ((originatorCredentialNetworkConfig->isPublic())||(peer->address() == originatorAddress)||((originatorCredentialNetworkConfig->com())&&(previousHopCom)&&(originatorCredentialNetworkConfig->com().agreesWith(previousHopCom)))) ) { TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); } else { TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str()); diff --git a/node/Node.cpp b/node/Node.cpp index cd20972b0..d5cc50b99 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -466,12 +466,48 @@ void Node::setNetconfMaster(void *networkControllerInstance) ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) { + if (test->hopCount > 0) { + try { + Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST); + RR->identity.address().appendTo(outp); + outp.append((uint16_t)((test->reportAtEveryHop != 0) ? 0x03 : 0x02)); + outp.append((uint64_t)test->timestamp); + outp.append((uint64_t)test->testId); + outp.append((uint16_t)0); // originator credential length, updated later + if (test->credentialNetworkId) { + outp.append((uint8_t)0x01); + outp.append((uint64_t)test->credentialNetworkId); + outp.setAt(ZT_PACKET_IDX_PAYLOAD + 23,(uint16_t)9); + } + 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((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); + outp.append((uint8_t)(test->hops[h].breadth & 0xff)); + for(unsigned int a=0;ahops[h].breadth;++a) + Address(test->hops[h].addresses[a]).appendTo(outp); + } + + 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); + } + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet + } + } + { test->_internalPtr = reinterpret_cast(reportCallback); Mutex::Lock _l(_circuitTests_m); if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end()) _circuitTests.push_back(test); } + return ZT_RESULT_OK; } From 7394ec6f6ab38c48e84edf3bf2fdb46e6966fa35 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 15:56:18 -0700 Subject: [PATCH 027/195] Prep in controller code to run tests. --- controller/SqliteNetworkController.cpp | 7 ++++++- controller/SqliteNetworkController.hpp | 6 +++++- service/OneService.cpp | 12 ++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 3aa843301..334ccc750 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -44,12 +44,15 @@ #include "../ext/json-parser/json.h" #include "SqliteNetworkController.hpp" + +#include "../node/Node.hpp" #include "../node/Utils.hpp" #include "../node/CertificateOfMembership.hpp" #include "../node/NetworkConfig.hpp" #include "../node/InetAddress.hpp" #include "../node/MAC.hpp" #include "../node/Address.hpp" + #include "../osdep/OSUtils.hpp" // Include ZT_NETCONF_SCHEMA_SQL constant to init database @@ -117,8 +120,10 @@ struct NetworkRecord { } // anonymous namespace -SqliteNetworkController::SqliteNetworkController(const char *dbPath) : +SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath) : + _node(node), _dbPath(dbPath), + _circuitTestPath(circuitTestPath), _db((sqlite3 *)0) { if (sqlite3_open_v2(dbPath,&_db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index f0b61c401..68529e397 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -45,10 +45,12 @@ namespace ZeroTier { +class Node; + class SqliteNetworkController : public NetworkController { public: - SqliteNetworkController(const char *dbPath); + SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath); virtual ~SqliteNetworkController(); virtual NetworkController::ResultCode doNetworkConfigRequest( @@ -104,7 +106,9 @@ private: const Dictionary &metaData, Dictionary &netconf); + Node *_node; std::string _dbPath; + std::string _circuitTestPath; std::string _instanceId; // A circular buffer last log diff --git a/service/OneService.cpp b/service/OneService.cpp index 4b374cd7e..071a2cbce 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -422,7 +422,7 @@ public: _homePath((hp) ? hp : "."), _tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY), #ifdef ZT_ENABLE_NETWORK_CONTROLLER - _controller((_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str()), + _controller((SqliteNetworkController *)0), #endif _phy(this,false,true), _overrideRootTopology((overrideRootTopology) ? overrideRootTopology : ""), @@ -514,6 +514,9 @@ public: #ifdef ZT_USE_MINIUPNPC _phy.close(_v4UpnpUdpSocket); delete _upnpClient; +#endif +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + delete _controller; #endif } @@ -551,14 +554,15 @@ public: ((_overrideRootTopology.length() > 0) ? _overrideRootTopology.c_str() : (const char *)0)); #ifdef ZT_ENABLE_NETWORK_CONTROLLER - _node->setNetconfMaster((void *)&_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()); + _node->setNetconfMaster((void *)_controller); #endif _controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str()); _controlPlane->addAuthToken(authToken.c_str()); #ifdef ZT_ENABLE_NETWORK_CONTROLLER - _controlPlane->setController(&_controller); + _controlPlane->setController(_controller); #endif { // Remember networks from previous session @@ -1322,7 +1326,7 @@ private: const std::string _homePath; BackgroundResolver _tcpFallbackResolver; #ifdef ZT_ENABLE_NETWORK_CONTROLLER - SqliteNetworkController _controller; + SqliteNetworkController *_controller; #endif Phy _phy; std::string _overrideRootTopology; From 477feee8a3fbc84d00c2939b5fc8a9bbf19af2ca Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 17:55:57 -0700 Subject: [PATCH 028/195] Some work on CIRCUIT_TEST, and a significant speedup to Poly1305. --- include/ZeroTierOne.h | 7 +- node/Poly1305.cpp | 279 +++++++++++++++++++++++++++++++++++++++++- selftest.cpp | 16 +++ 3 files changed, 296 insertions(+), 6 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 43c8fc0bb..341bb767a 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -757,6 +757,11 @@ typedef struct { * Circuit test result report */ typedef struct { + /** + * Sender of report + */ + uint64_t address; + /** * 64-bit test ID */ @@ -839,7 +844,7 @@ typedef struct { * * This may have ss_family set to zero (null address) if unspecified. */ - struct sockaddr_storage receivedFromAddress; + struct sockaddr_storage receivedFromRemoteAddress; /** * Next hops to which packets are being or will be sent by the reporter diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index 230b2effc..77b32a800 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -7,14 +7,18 @@ Public domain. #include "Constants.hpp" #include "Poly1305.hpp" +#include +#include +#include +#include + #ifdef __WINDOWS__ #pragma warning(disable: 4146) #endif namespace ZeroTier { -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// +#if 0 static inline void add(unsigned int h[17],const unsigned int c[17]) { @@ -113,13 +117,278 @@ static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in, return 0; } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key) throw() { crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key); } +#endif + +namespace { + +typedef struct poly1305_context { + size_t aligner; + unsigned char opaque[136]; +} poly1305_context; + +/* + poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition +*/ + +#define poly1305_block_size 16 + +/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ +typedef struct poly1305_state_internal_t { + unsigned long r[5]; + unsigned long h[5]; + unsigned long pad[4]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static unsigned long +U8TO32(const unsigned char *p) { + return + (((unsigned long)(p[0] & 0xff) ) | + ((unsigned long)(p[1] & 0xff) << 8) | + ((unsigned long)(p[2] & 0xff) << 16) | + ((unsigned long)(p[3] & 0xff) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void +U32TO8(unsigned char *p, unsigned long v) { + p[0] = (v ) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +static inline void +poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff; + st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03; + st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff; + st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff; + st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = U8TO32(&key[16]); + st->pad[1] = U8TO32(&key[20]); + st->pad[2] = U8TO32(&key[24]); + st->pad[3] = U8TO32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static inline void +poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { + const unsigned long hibit = (st->final) ? 0 : (1 << 24); /* 1 << 128 */ + unsigned long r0,r1,r2,r3,r4; + unsigned long s1,s2,s3,s4; + unsigned long h0,h1,h2,h3,h4; + unsigned long long d0,d1,d2,d3,d4; + unsigned long c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while (bytes >= poly1305_block_size) { + /* h += m[i] */ + h0 += (U8TO32(m+ 0) ) & 0x3ffffff; + h1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff; + h2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff; + h3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff; + h4 += (U8TO32(m+12) >> 8) | hibit; + + /* h *= r */ + d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1); + d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2); + d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3); + d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4); + d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0); + + /* (partial) h %= p */ + c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff; + d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff; + d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff; + d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff; + d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff; + h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +static inline void +poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + size_t i; + + /* handle leftover */ + if (st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if (st->leftover < poly1305_block_size) + return; + poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if (bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if (bytes) { + for (i = 0; i < bytes; i++) + st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + +static inline void +poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + unsigned long h0,h1,h2,h3,h4,c; + unsigned long g0,g1,g2,g3,g4; + unsigned long long f; + unsigned long mask; + + /* process the remaining block */ + if (st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for (; i < poly1305_block_size; i++) + st->buffer[i] = 0; + st->final = 1; + poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + c - (1 << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f; + f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f; + f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f; + f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f; + + U32TO8(mac + 0, h0); + U32TO8(mac + 4, h1); + U32TO8(mac + 8, h2); + U32TO8(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; +} + +} // anonymous namespace + +void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key) + throw() +{ + poly1305_context ctx; + poly1305_init(&ctx,reinterpret_cast(key)); + poly1305_update(&ctx,reinterpret_cast(data),(size_t)len); + poly1305_finish(&ctx,reinterpret_cast(auth)); +} + } // namespace ZeroTier diff --git a/selftest.cpp b/selftest.cpp index b899ee5ad..e938cf4d6 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -269,6 +269,22 @@ static int testCrypto() } std::cout << "PASS" << std::endl; + std::cout << "[crypto] Benchmarking Poly1305... "; std::cout.flush(); + { + unsigned char *bb = (unsigned char *)::malloc(1234567); + for(unsigned int i=0;i<1234567;++i) + bb[i] = (unsigned char)i; + double bytes = 0.0; + uint64_t start = OSUtils::now(); + for(unsigned int i=0;i<200;++i) { + Poly1305::compute(buf1,bb,1234567,poly1305TV0Key); + bytes += 1234567.0; + } + uint64_t end = OSUtils::now(); + std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" << std::endl; + ::free((void *)bb); + } + std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush(); for(int k=0;k Date: Tue, 6 Oct 2015 18:04:53 -0700 Subject: [PATCH 029/195] Cleanup, and add an even faster Poly1305 on systems that support it. --- node/Poly1305.cpp | 311 ++++++++++++++++++++++++++++++++++++++++------ node/Utils.cpp | 4 +- 2 files changed, 273 insertions(+), 42 deletions(-) diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index 77b32a800..0968170eb 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -132,9 +132,236 @@ typedef struct poly1305_context { unsigned char opaque[136]; } poly1305_context; -/* - poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition -*/ +#if defined(_MSC_VER) || defined(__GNUC__) + +////////////////////////////////////////////////////////////////////////////// +// 128-bit implementation for MSC and GCC + +#if defined(_MSC_VER) + #include + + typedef struct uint128_t { + unsigned long long lo; + unsigned long long hi; + } uint128_t; + + #define MUL(out, x, y) out.lo = _umul128((x), (y), &out.hi) + #define ADD(out, in) { unsigned long long t = out.lo; out.lo += in.lo; out.hi += (out.lo < t) + in.hi; } + #define ADDLO(out, in) { unsigned long long t = out.lo; out.lo += in; out.hi += (out.lo < t); } + #define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift))) + #define LO(in) (in.lo) + +// #define POLY1305_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) + #if defined(__SIZEOF_INT128__) + typedef unsigned __int128 uint128_t; + #else + typedef unsigned uint128_t __attribute__((mode(TI))); + #endif + + #define MUL(out, x, y) out = ((uint128_t)x * y) + #define ADD(out, in) out += in + #define ADDLO(out, in) out += in + #define SHR(in, shift) (unsigned long long)(in >> (shift)) + #define LO(in) (unsigned long long)(in) + +// #define POLY1305_NOINLINE __attribute__((noinline)) +#endif + +#define poly1305_block_size 16 + +/* 17 + sizeof(size_t) + 8*sizeof(unsigned long long) */ +typedef struct poly1305_state_internal_t { + unsigned long long r[3]; + unsigned long long h[3]; + unsigned long long pad[2]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in little endian */ +static inline unsigned long long +U8TO64(const unsigned char *p) { + return + (((unsigned long long)(p[0] & 0xff) ) | + ((unsigned long long)(p[1] & 0xff) << 8) | + ((unsigned long long)(p[2] & 0xff) << 16) | + ((unsigned long long)(p[3] & 0xff) << 24) | + ((unsigned long long)(p[4] & 0xff) << 32) | + ((unsigned long long)(p[5] & 0xff) << 40) | + ((unsigned long long)(p[6] & 0xff) << 48) | + ((unsigned long long)(p[7] & 0xff) << 56)); +} + +/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little endian */ +static inline void +U64TO8(unsigned char *p, unsigned long long v) { + p[0] = (v ) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + p[4] = (v >> 32) & 0xff; + p[5] = (v >> 40) & 0xff; + p[6] = (v >> 48) & 0xff; + p[7] = (v >> 56) & 0xff; +} + +static inline void +poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + unsigned long long t0,t1; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + t0 = U8TO64(&key[0]); + t1 = U8TO64(&key[8]); + + st->r[0] = ( t0 ) & 0xffc0fffffff; + st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; + st->r[2] = ((t1 >> 24) ) & 0x00ffffffc0f; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + + /* save pad for later */ + st->pad[0] = U8TO64(&key[16]); + st->pad[1] = U8TO64(&key[24]); + + st->leftover = 0; + st->final = 0; +} + +static inline void +poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { + const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */ + unsigned long long r0,r1,r2; + unsigned long long s1,s2; + unsigned long long h0,h1,h2; + unsigned long long c; + uint128_t d0,d1,d2,d; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + + s1 = r1 * (5 << 2); + s2 = r2 * (5 << 2); + + while (bytes >= poly1305_block_size) { + unsigned long long t0,t1; + + /* h += m[i] */ + t0 = U8TO64(&m[0]); + t1 = U8TO64(&m[8]); + + h0 += (( t0 ) & 0xfffffffffff); + h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff); + h2 += (((t1 >> 24) ) & 0x3ffffffffff) | hibit; + + /* h *= r */ + MUL(d0, h0, r0); MUL(d, h1, s2); ADD(d0, d); MUL(d, h2, s1); ADD(d0, d); + MUL(d1, h0, r1); MUL(d, h1, r0); ADD(d1, d); MUL(d, h2, s2); ADD(d1, d); + MUL(d2, h0, r2); MUL(d, h1, r1); ADD(d2, d); MUL(d, h2, r0); ADD(d2, d); + + /* (partial) h %= p */ + c = SHR(d0, 44); h0 = LO(d0) & 0xfffffffffff; + ADDLO(d1, c); c = SHR(d1, 44); h1 = LO(d1) & 0xfffffffffff; + ADDLO(d2, c); c = SHR(d2, 42); h2 = LO(d2) & 0x3ffffffffff; + h0 += c * 5; c = (h0 >> 44); h0 = h0 & 0xfffffffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; +} + +static inline void +poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + unsigned long long h0,h1,h2,c; + unsigned long long g0,g1,g2; + unsigned long long t0,t1; + + /* process the remaining block */ + if (st->leftover) { + size_t i = st->leftover; + st->buffer[i] = 1; + for (i = i + 1; i < poly1305_block_size; i++) + st->buffer[i] = 0; + st->final = 1; + poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + + c = (h1 >> 44); h1 &= 0xfffffffffff; + h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff; + h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff; + h1 += c; c = (h1 >> 44); h1 &= 0xfffffffffff; + h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff; + h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff; + g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff; + g2 = h2 + c - ((unsigned long long)1 << 42); + + /* select h if h < p, or h + -p if h >= p */ + c = (g2 >> ((sizeof(unsigned long long) * 8) - 1)) - 1; + g0 &= c; + g1 &= c; + g2 &= c; + c = ~c; + h0 = (h0 & c) | g0; + h1 = (h1 & c) | g1; + h2 = (h2 & c) | g2; + + /* h = (h + pad) */ + t0 = st->pad[0]; + t1 = st->pad[1]; + + h0 += (( t0 ) & 0xfffffffffff) ; c = (h0 >> 44); h0 &= 0xfffffffffff; + h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff; + h2 += (((t1 >> 24) ) & 0x3ffffffffff) + c; h2 &= 0x3ffffffffff; + + /* mac = h % (2^128) */ + h0 = ((h0 ) | (h1 << 44)); + h1 = ((h1 >> 20) | (h2 << 24)); + + U64TO8(&mac[0], h0); + U64TO8(&mac[8], h1); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->pad[0] = 0; + st->pad[1] = 0; +} + +////////////////////////////////////////////////////////////////////////////// + +#else + +////////////////////////////////////////////////////////////////////////////// +// More portable 64-bit implementation #define poly1305_block_size 16 @@ -256,43 +483,6 @@ poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t by st->h[4] = h4; } -static inline void -poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { - poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; - size_t i; - - /* handle leftover */ - if (st->leftover) { - size_t want = (poly1305_block_size - st->leftover); - if (want > bytes) - want = bytes; - for (i = 0; i < want; i++) - st->buffer[st->leftover + i] = m[i]; - bytes -= want; - m += want; - st->leftover += want; - if (st->leftover < poly1305_block_size) - return; - poly1305_blocks(st, st->buffer, poly1305_block_size); - st->leftover = 0; - } - - /* process full blocks */ - if (bytes >= poly1305_block_size) { - size_t want = (bytes & ~(poly1305_block_size - 1)); - poly1305_blocks(st, m, want); - m += want; - bytes -= want; - } - - /* store leftover */ - if (bytes) { - for (i = 0; i < bytes; i++) - st->buffer[st->leftover + i] = m[i]; - st->leftover += bytes; - } -} - static inline void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; @@ -380,6 +570,47 @@ poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { st->pad[3] = 0; } +////////////////////////////////////////////////////////////////////////////// + +#endif // MSC/GCC or not + +static inline void +poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + size_t i; + + /* handle leftover */ + if (st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if (st->leftover < poly1305_block_size) + return; + poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if (bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if (bytes) { + for (i = 0; i < bytes; i++) + st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + } // anonymous namespace void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key) diff --git a/node/Utils.cpp b/node/Utils.cpp index 658c397d0..3c6dee32b 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -191,7 +191,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) if (devURandomFd <= 0) { devURandomFd = ::open("/dev/urandom",O_RDONLY); if (devURandomFd <= 0) { - fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\r\n"); + fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n"); exit(1); return; } @@ -200,7 +200,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) for(unsigned int i=0;i= sizeof(randomBuf)) { if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) { - fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\r\n"); + fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n"); exit(1); return; } From 598a1d8dd7f621bb75a7f0537c5a63ee93e85a4c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 18:10:40 -0700 Subject: [PATCH 030/195] Try reopening /dev/urandom if there is a problem. --- node/Utils.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/node/Utils.cpp b/node/Utils.cpp index 3c6dee32b..6c5d8c7d6 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -181,7 +181,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) #ifdef __UNIX_LIKE__ - static char randomBuf[65536]; + static char randomBuf[131072]; static unsigned int randomPtr = sizeof(randomBuf); static int devURandomFd = -1; static Mutex globalLock; @@ -199,10 +199,16 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) for(unsigned int i=0;i= sizeof(randomBuf)) { - if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) { - fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\n"); - exit(1); - return; + for(;;) { + if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) { + ::close(devURandomFd); + devURandomFd = ::open("/dev/urandom",O_RDONLY); + if (devURandomFd <= 0) { + fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n"); + exit(1); + return; + } + } else break; } randomPtr = 0; } From 1b2cac0cc59790620262f447588274440c7fbefa Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 09:38:33 -0700 Subject: [PATCH 031/195] Trim some cruft that is not used and probably never would be. --- include/ZeroTierOne.h | 27 +-------------------------- node/IncomingPacket.cpp | 18 ++---------------- node/Node.cpp | 16 +--------------- node/Node.hpp | 6 ------ 4 files changed, 4 insertions(+), 63 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 341bb767a..611e36997 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -255,31 +255,6 @@ enum ZT_Event */ ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION = 4, - /** - * A more recent version was observed on the network - * - * Right now this is only triggered if a hub or rootserver reports a - * more recent version, and only once. It can be used to trigger a - * software update check. - * - * Meta-data: unsigned int[3], more recent version number - */ - ZT_EVENT_SAW_MORE_RECENT_VERSION = 5, - - /** - * A packet failed authentication - * - * Meta-data: struct sockaddr_storage containing origin address of packet - */ - ZT_EVENT_AUTHENTICATION_FAILURE = 6, - - /** - * A received packet was not valid - * - * Meta-data: struct sockaddr_storage containing origin address of packet - */ - ZT_EVENT_INVALID_PACKET = 7, - /** * Trace (debugging) message * @@ -287,7 +262,7 @@ enum ZT_Event * * Meta-data: C string, TRACE message */ - ZT_EVENT_TRACE = 8 + ZT_EVENT_TRACE = 5 }; /** diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 305232ee3..26d4bda8b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -227,7 +227,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) 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 - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); 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); @@ -236,11 +235,9 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) outp.armor(key,true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); } } else { - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -249,7 +246,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // Identity is the same as the one we already have -- check packet integrity if (!dearmor(peer->key())) { - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } @@ -261,7 +257,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // Check identity proof of work if (!id.locallyValidate()) { - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } @@ -269,7 +264,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // Check packet integrity and authentication SharedPtr newPeer(new Peer(RR->identity,id)); if (!dearmor(newPeer->key())) { - RR->node->postEvent(ZT_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } @@ -284,11 +278,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_HELLO,0,Packet::VERB_NOP); peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); - bool trusted = false; - if (RR->topology->isRoot(id)) { - RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision); - trusted = true; - } + bool trusted = RR->topology->isRoot(id); if (destAddr) RR->sa->iam(id.address(),_remoteAddress,destAddr,trusted,RR->node->now()); @@ -369,11 +359,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p peer->addDirectLatencyMeasurment(latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); - bool trusted = false; - if (RR->topology->isRoot(peer->identity())) { - RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision); - trusted = true; - } + bool trusted = RR->topology->isRoot(peer->identity()); if (destAddr) RR->sa->iam(peer->address(),_remoteAddress,destAddr,trusted,RR->node->now()); } break; diff --git a/node/Node.cpp b/node/Node.cpp index d5cc50b99..d5ca147c5 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 } } diff --git a/node/Node.hpp b/node/Node.hpp index 20c544718..0ae176c0d 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -226,11 +226,6 @@ public: */ inline bool online() const throw() { return _online; } - /** - * If this version is newer than the newest we've seen, post a new version seen event - */ - void postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev); - #ifdef ZT_TRACE void postTrace(const char *module,unsigned int line,const char *fmt,...); #endif @@ -288,7 +283,6 @@ private: uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; - unsigned int _newestVersionSeen[3]; // major, minor, revision bool _online; }; From 6c7ce79c8960cd2360657f9247788ff5640ae974 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 09:51:35 -0700 Subject: [PATCH 032/195] Be consistent in how enums are defined in the main .h file. --- include/ZeroTierOne.h | 130 +++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 611e36997..990f682d1 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -389,6 +389,68 @@ 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? + */ +enum ZT_PeerRole { + ZT_PEER_ROLE_LEAF = 0, // ordinary node + ZT_PEER_ROLE_RELAY = 1, // relay node + ZT_PEER_ROLE_ROOT = 2 // root server +}; + +/** + * Vendor ID + */ +enum ZT_Vendor { + ZT_VENDOR_UNSPECIFIED = 0, + ZT_VENDOR_ZEROTIER = 1 +}; + +/** + * Platform type + */ +enum ZT_Platform { + ZT_PLATFORM_UNSPECIFIED = 0, + ZT_PLATFORM_LINUX = 1, + ZT_PLATFORM_WINDOWS = 2, + ZT_PLATFORM_MACOS = 3, + ZT_PLATFORM_ANDROID = 4, + ZT_PLATFORM_IOS = 5, + ZT_PLATFORM_SOLARIS_SMARTOS = 6, + ZT_PLATFORM_FREEBSD = 7, + ZT_PLATFORM_NETBSD = 8, + ZT_PLATFORM_OPENBSD = 9, + ZT_PLATFORM_RISCOS = 10, + ZT_PLATFORM_VXWORKS = 11, + ZT_PLATFORM_FREERTOS = 12, + ZT_PLATFORM_SYSBIOS = 13, + ZT_PLATFORM_HURD = 14 +}; + +/** + * Architecture type + */ +enum ZT_Architecture { + ZT_ARCHITECTURE_UNSPECIFIED = 0, + ZT_ARCHITECTURE_X86 = 1, + ZT_ARCHITECTURE_X64 = 2, + ZT_ARCHITECTURE_ARM32 = 3, + ZT_ARCHITECTURE_ARM64 = 4, + ZT_ARCHITECTURE_MIPS32 = 5, + ZT_ARCHITECTURE_MIPS64 = 6, + ZT_ARCHITECTURE_POWER32 = 7, + ZT_ARCHITECTURE_POWER64 = 8 +}; + /** * Virtual network configuration */ @@ -536,15 +598,6 @@ typedef struct int preferred; } ZT_PeerPhysicalPath; -/** - * What trust hierarchy role does this peer have? - */ -enum ZT_PeerRole { - ZT_PEER_ROLE_LEAF = 0, // ordinary node - ZT_PEER_ROLE_RELAY = 1, // relay node - ZT_PEER_ROLE_ROOT = 2 // root server -}; - /** * Peer status result buffer */ @@ -610,59 +663,6 @@ typedef struct unsigned long peerCount; } ZT_PeerList; -/** - * Local interface trust levels - */ -typedef enum { - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 10, - ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20 -} ZT_LocalInterfaceAddressTrust; - -/** - * Vendor ID - */ -typedef enum { - ZT_VENDOR_UNSPECIFIED = 0, - ZT_VENDOR_ZEROTIER = 1 -} ZT_Vendor; - -/** - * Platform type - */ -typedef enum { - ZT_PLATFORM_UNSPECIFIED = 0, - ZT_PLATFORM_LINUX = 1, - ZT_PLATFORM_WINDOWS = 2, - ZT_PLATFORM_MACOS = 3, - ZT_PLATFORM_ANDROID = 4, - ZT_PLATFORM_IOS = 5, - ZT_PLATFORM_SOLARIS_SMARTOS = 6, - ZT_PLATFORM_FREEBSD = 7, - ZT_PLATFORM_NETBSD = 8, - ZT_PLATFORM_OPENBSD = 9, - ZT_PLATFORM_RISCOS = 10, - ZT_PLATFORM_VXWORKS = 11, - ZT_PLATFORM_FREERTOS = 12, - ZT_PLATFORM_SYSBIOS = 13, - ZT_PLATFORM_HURD = 14 -} ZT_Platform; - -/** - * Architecture type - */ -typedef enum { - ZT_ARCHITECTURE_UNSPECIFIED = 0, - ZT_ARCHITECTURE_X86 = 1, - ZT_ARCHITECTURE_X64 = 2, - ZT_ARCHITECTURE_ARM32 = 3, - ZT_ARCHITECTURE_ARM64 = 4, - ZT_ARCHITECTURE_MIPS32 = 5, - ZT_ARCHITECTURE_MIPS64 = 6, - ZT_ARCHITECTURE_POWER32 = 7, - ZT_ARCHITECTURE_POWER64 = 8 -} ZT_Architecture; - /** * ZeroTier circuit test configuration and path */ @@ -775,7 +775,7 @@ typedef struct { /** * Remote device vendor ID */ - ZT_Vendor vendor; + enum ZT_Vendor vendor; /** * Remote device protocol compliance version @@ -800,12 +800,12 @@ typedef struct { /** * Platform / OS */ - ZT_Platform platform; + enum ZT_Platform platform; /** * System architecture */ - ZT_Architecture architecture; + enum ZT_Architecture architecture; /** * Local device address on which packet was received by reporting device From ab0228f626573381db93173cd5849cb934481ca5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 10:30:47 -0700 Subject: [PATCH 033/195] More cleanup and simple refactoring, consolidate InetAddres serialize/deserialize into the class. --- node/IncomingPacket.cpp | 102 ++++++++++++++-------------------------- node/InetAddress.hpp | 5 +- node/Packet.hpp | 15 ++---- node/Peer.cpp | 19 +------- 4 files changed, 43 insertions(+), 98 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 26d4bda8b..6186affdf 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -46,6 +46,7 @@ namespace ZeroTier { bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) { + 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 @@ -54,23 +55,24 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) return _doHELLO(RR); } - SharedPtr peer = RR->topology->getPeer(source()); + SharedPtr peer = RR->topology->getPeer(sourceAddress); if (peer) { if (!dearmor(peer->key())) { - TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size()); + TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size()); return true; } if (!uncompress()) { - TRACE("dropped packet from %s(%s), compressed data invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped packet from %s(%s), compressed data invalid",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } - //TRACE("<< %s from %s(%s)",Packet::verbString(verb()),source().toString().c_str(),_remoteAddress.toString().c_str()); + //TRACE("<< %s from %s(%s)",Packet::verbString(v),sourceAddress.toString().c_str(),_remoteAddress.toString().c_str()); - switch(verb()) { + const Packet::Verb v = verb(); + switch(v) { //case Packet::VERB_NOP: default: // ignore unknown verbs, but if they pass auth check they are "received" - peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),verb(),0,Packet::VERB_NOP); + peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP); return true; case Packet::VERB_HELLO: return _doHELLO(RR); case Packet::VERB_ERROR: return _doERROR(RR,peer); @@ -90,13 +92,13 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer); } } else { - RR->sw->requestWhois(source()); + RR->sw->requestWhois(sourceAddress); return false; } } catch ( ... ) { // Exceptions are more informatively caught in _do...() handlers but // this outer try/catch will catch anything else odd. - TRACE("dropped ??? from %s(%s): unexpected exception in tryDecode()",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped ??? from %s(%s): unexpected exception in tryDecode()",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str()); return true; } } @@ -108,7 +110,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr const uint64_t inRePacketId = at(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID); const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE]; - //TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb)); + //TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb)); switch(errorCode) { @@ -118,7 +120,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr RR->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH)); } else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); - if ((network)&&(network->controller() == source())) + if ((network)&&(network->controller() == peer->address())) network->setNotFound(); } break; @@ -126,7 +128,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr case Packet::ERROR_UNSUPPORTED_OPERATION: if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); - if ((network)&&(network->controller() == source())) + if ((network)&&(network->controller() == peer->address())) network->setNotFound(); } break; @@ -154,7 +156,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr case Packet::ERROR_NETWORK_ACCESS_DENIED_: { SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); - if ((network)&&(network->controller() == source())) + if ((network)&&(network->controller() == peer->address())) network->setAccessDenied(); } break; @@ -170,9 +172,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb); } catch (std::exception &ex) { - TRACE("dropped ERROR from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + TRACE("dropped ERROR from %s(%s): unexpected exception: %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped ERROR from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped ERROR from %s(%s): unexpected exception: (unknown)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -187,36 +189,29 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) * in the first place. */ try { + const uint64_t pid = packetId(); + const Address fromAddress(source()); const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO_IDX_REVISION); const uint64_t timestamp = at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); + Identity id; - unsigned int destAddrPtr = id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY) + ZT_PROTO_VERB_HELLO_IDX_IDENTITY; + 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); 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()); return true; } - if (source() != id.address()) { - TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); + if (fromAddress != id.address()) { + TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_remoteAddress.toString().c_str()); return true; } - InetAddress destAddr; - if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field - const unsigned int destAddrType = (*this)[destAddrPtr++]; - switch(destAddrType) { - case ZT_PROTO_DEST_ADDRESS_TYPE_IPV4: - destAddr.set(field(destAddrPtr,4),4,at(destAddrPtr + 4)); - break; - case ZT_PROTO_DEST_ADDRESS_TYPE_IPV6: - destAddr.set(field(destAddrPtr,16),16,at(destAddrPtr + 16)); - break; - } - } - SharedPtr peer(RR->topology->getPeer(id.address())); if (peer) { // We already have an identity with this address -- check for collisions @@ -230,7 +225,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) 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(packetId()); + 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()); @@ -273,41 +268,23 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // Continue at // VALID } - // VALID -- continues here + // VALID -- if we made it here, packet passed identity and authenticity checks! - peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_HELLO,0,Packet::VERB_NOP); + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); - bool trusted = RR->topology->isRoot(id); if (destAddr) - RR->sa->iam(id.address(),_remoteAddress,destAddr,trusted,RR->node->now()); + RR->sa->iam(id.address(),_remoteAddress,destAddr,RR->topology->isRoot(id),RR->node->now()); Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK); - outp.append((unsigned char)Packet::VERB_HELLO); - outp.append(packetId()); - outp.append(timestamp); + outp.append((uint64_t)pid); + outp.append((uint64_t)timestamp); outp.append((unsigned char)ZT_PROTO_VERSION); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - - switch(_remoteAddress.ss_family) { - case AF_INET: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV4); - outp.append(_remoteAddress.rawIpData(),4); - outp.append((uint16_t)_remoteAddress.port()); - break; - case AF_INET6: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV6); - outp.append(_remoteAddress.rawIpData(),16); - outp.append((uint16_t)_remoteAddress.port()); - break; - default: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_NONE); - break; - } - + _remoteAddress.serialize(outp); outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } catch (std::exception &ex) { @@ -336,18 +313,9 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); InetAddress destAddr; - unsigned int destAddrPtr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2; // dest address, if present, will start after 16-bit revision - if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field - const unsigned int destAddrType = (*this)[destAddrPtr++]; - switch(destAddrType) { - case ZT_PROTO_DEST_ADDRESS_TYPE_IPV4: - destAddr.set(field(destAddrPtr,4),4,at(destAddrPtr + 4)); - break; - case ZT_PROTO_DEST_ADDRESS_TYPE_IPV6: - destAddr.set(field(destAddrPtr,16),16,at(destAddrPtr + 16)); - break; - } - } + if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < size()) // ZeroTier One < 1.0.3 did not include this field + destAddr.deserialize(*this,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2); + printf("OK(HELLO) %s\n",destAddr.toString().c_str()); if (vProto < ZT_PROTO_VERSION_MIN) { TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index c376a032e..6970e92d2 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -366,7 +366,8 @@ struct InetAddress : public sockaddr_storage template inline void serialize(Buffer &b) const { - // Format is the same as in VERB_HELLO in Packet.hpp + // This is used in the protocol and must be the same as describe in places + // like VERB_HELLO in Packet.hpp. switch(ss_family) { case AF_INET: b.append((uint8_t)0x04); @@ -387,8 +388,8 @@ struct InetAddress : public sockaddr_storage template inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { - unsigned int p = startAt; memset(this,0,sizeof(InetAddress)); + unsigned int p = startAt; switch(b[p++]) { case 0: return 1; diff --git a/node/Packet.hpp b/node/Packet.hpp index 409762c7e..1413db107 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -55,13 +55,15 @@ * 3 - 0.5.0 ... 0.6.0 * * Yet another multicast redesign * * New crypto completely changes key agreement cipher - * 4 - 0.6.0 ... CURRENT + * 4 - 0.6.0 ... 1.0.6 * * New identity format based on hashcash design + * 5 - 1.0.6 ... CURRENT + * * Supports CIRCUIT_TEST and friends, otherwise compatibie w/v4 * * This isn't going to change again for a long time unless your * author wakes up again at 4am with another great idea. :P */ -#define ZT_PROTO_VERSION 4 +#define ZT_PROTO_VERSION 5 /** * Minimum supported protocol version @@ -233,15 +235,6 @@ */ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD -// Destination address types from HELLO, OK(HELLO), and other message types -#define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0 -#define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused -#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 2 // future use -#define ZT_PROTO_DEST_ADDRESS_TYPE_BLUETOOTH 3 // future use -#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4 -#define ZT_PROTO_DEST_ADDRESS_TYPE_LTE_DIRECT 5 // future use -#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6 - // Ephemeral key record flags #define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 // future use diff --git a/node/Peer.cpp b/node/Peer.cpp index 757f822c5..15648e0f0 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -170,25 +170,8 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); outp.append(now); - RR->identity.serialize(outp,false); - - switch(atAddress.ss_family) { - case AF_INET: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV4); - outp.append(atAddress.rawIpData(),4); - outp.append((uint16_t)atAddress.port()); - break; - case AF_INET6: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV6); - outp.append(atAddress.rawIpData(),16); - outp.append((uint16_t)atAddress.port()); - break; - default: - outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_NONE); - break; - } - + atAddress.serialize(outp); outp.armor(_key,false); // HELLO is sent in the clear RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size()); } From c952fbbd8d72c7f4e295bcacfe6e5b0b448505af Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 10:40:59 -0700 Subject: [PATCH 034/195] Only enable 128-bit Poly1305 on X86_64 right now. Has compilation issues on ARM, but the 64-bit version should be fine. --- node/Poly1305.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index 0968170eb..b78071f65 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -20,6 +20,9 @@ namespace ZeroTier { #if 0 +// "Naive" implementation, which is slower... might still want this on some older +// or weird platforms if the later versions have issues. + static inline void add(unsigned int h[17],const unsigned int c[17]) { unsigned int j; @@ -132,10 +135,10 @@ typedef struct poly1305_context { unsigned char opaque[136]; } poly1305_context; -#if defined(_MSC_VER) || defined(__GNUC__) +#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__)) ////////////////////////////////////////////////////////////////////////////// -// 128-bit implementation for MSC and GCC +// 128-bit implementation for MSC and GCC from Poly1305-donna #if defined(_MSC_VER) #include From 13f14c2f4ccdb38f033a8092f0088f62d2a17348 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 10:56:47 -0700 Subject: [PATCH 035/195] Kill debug line. --- node/IncomingPacket.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 6186affdf..19bea2430 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -315,7 +315,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p InetAddress destAddr; if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < size()) // ZeroTier One < 1.0.3 did not include this field destAddr.deserialize(*this,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2); - printf("OK(HELLO) %s\n",destAddr.toString().c_str()); if (vProto < ZT_PROTO_VERSION_MIN) { TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); From 7d62dbe9f7fa620982c71f44089d319120023e26 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 11:57:59 -0700 Subject: [PATCH 036/195] Tune NAT-t keepalives so that timing is better obeyed, clean up a build warning, and fix a potential source of network recursion (though harmless). --- node/Constants.hpp | 2 +- node/Network.cpp | 2 -- node/Peer.cpp | 7 +++++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index 27c43aa7d..9ab390eb6 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -264,7 +264,7 @@ * This is also how often pings will be retried to upstream peers (relays, roots) * constantly until something is heard. */ -#define ZT_PING_CHECK_INVERVAL 6250 +#define ZT_PING_CHECK_INVERVAL 9500 /** * Delay between ordinary case pings of direct links diff --git a/node/Network.cpp b/node/Network.cpp index 9ce58c630..46f932419 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -448,7 +448,6 @@ class _AnnounceMulticastGroupsToAll public: _AnnounceMulticastGroupsToAll(const RuntimeEnvironment *renv,Network *nw) : _now(renv->node->now()), - RR(renv), _network(nw), _rootAddresses(renv->topology->rootAddresses()), _allMulticastGroups(nw->_allMulticastGroups()) @@ -458,7 +457,6 @@ public: private: uint64_t _now; - const RuntimeEnvironment *RR; Network *_network; std::vector
_rootAddresses; std::vector _allMulticastGroups; diff --git a/node/Peer.cpp b/node/Peer.cpp index 15648e0f0..111c849e7 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -173,6 +173,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo RR->identity.serialize(outp,false); atAddress.serialize(outp); 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()); } @@ -182,12 +183,12 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) RemotePath *const bestPath = _getBestPath(now); if (bestPath) { if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { - TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str()); + 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()); _natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads - TRACE("NAT keepalive %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str()); RR->node->putPacket(bestPath->localAddress(),bestPath->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); bestPath->sent(now); } @@ -202,6 +203,8 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ _lastDirectPathPush = now; std::vector dps(RR->node->directPaths()); + if (dps.empty()) + return; #ifdef ZT_TRACE { From e5f168f599ba053ee5e6029387dd7ad4b95a7d28 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 13:35:46 -0700 Subject: [PATCH 037/195] Add proof of work request for future DDOS mitigation use. --- node/IncomingPacket.cpp | 121 ++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.hpp | 22 ++++++++ node/Packet.cpp | 2 +- node/Packet.hpp | 102 ++++++++++++++++++++++++++------- selftest.cpp | 14 +++++ 5 files changed, 240 insertions(+), 21 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 19bea2430..de9017790 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -41,6 +41,8 @@ #include "Peer.hpp" #include "NetworkController.hpp" #include "SelfAwareness.hpp" +#include "Salsa20.hpp" +#include "SHA512.hpp" namespace ZeroTier { @@ -90,6 +92,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer); case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer); + case Packet::VERB_REQUEST_PROOF_OF_WORK: return _doREQUEST_PROOF_OF_WORK(RR,peer); } } else { RR->sw->requestWhois(sourceAddress); @@ -1042,6 +1045,124 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S return true; } +bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + try { + // Right now this is only allowed from root servers -- may be allowed from controllers and relays later. + if (RR->topology->isRoot(peer->identity())) { + const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1]; + const unsigned int challengeLength = at(ZT_PACKET_IDX_PAYLOAD + 2); + if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH) + return true; // sanity check, drop invalid size + const unsigned char *challenge = field(ZT_PACKET_IDX_PAYLOAD + 4,challengeLength); + + switch((*this)[ZT_PACKET_IDX_PAYLOAD]) { + + // Salsa20/12+SHA512 hashcash + case 0x01: { + unsigned char result[16]; + computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result); + + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); + outp.append(packetId()); + outp.append((uint16_t)sizeof(result)); + outp.append(result,sizeof(result)); + outp.armor(peer->key(),true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + } break; + + default: + TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + break; + } + } else { + TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + } + } catch ( ... ) { + TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]) +{ + unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function + char candidatebuf[ZT_PROTO_MAX_PACKET_LENGTH + 256]; + unsigned char shabuf[ZT_SHA512_DIGEST_LEN]; + const uint64_t s20iv = 0; // zero IV for Salsa20 + char *const candidate = (char *)(( ((uintptr_t)&(candidatebuf[0])) | 0xf ) + 1); // align to 16-byte boundary to ensure that uint64_t type punning of initial nonce is okay + Salsa20 s20; + unsigned int d; + unsigned char *p; + + Utils::getSecureRandom(candidate,16); + memcpy(candidate + 16,challenge,challengeLength); + + if (difficulty > 512) + difficulty = 512; // sanity check + +try_salsa2012sha512_again: + ++*(reinterpret_cast(candidate)); + + SHA512::hash(shabuf,candidate,16 + challengeLength); + s20.init(shabuf,256,&s20iv,12); + memset(salsabuf,0,sizeof(salsabuf)); + s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf)); + SHA512::hash(shabuf,salsabuf,sizeof(salsabuf)); + + d = difficulty; + p = shabuf; + while (d >= 8) { + if (*(p++)) + goto try_salsa2012sha512_again; + d -= 8; + } + if (d > 0) { + if ( ((((unsigned int)*p) << d) & 0xff00) != 0 ) + goto try_salsa2012sha512_again; + } + + memcpy(result,candidate,16); +} + +bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]) +{ + unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function + char candidate[ZT_PROTO_MAX_PACKET_LENGTH + 256]; + unsigned char shabuf[ZT_SHA512_DIGEST_LEN]; + const uint64_t s20iv = 0; // zero IV for Salsa20 + Salsa20 s20; + unsigned int d; + unsigned char *p; + + if (difficulty > 512) + difficulty = 512; // sanity check + + memcpy(candidate,proposedResult,16); + memcpy(candidate + 16,challenge,challengeLength); + + SHA512::hash(shabuf,candidate,16 + challengeLength); + s20.init(shabuf,256,&s20iv,12); + memset(salsabuf,0,sizeof(salsabuf)); + s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf)); + SHA512::hash(shabuf,salsabuf,sizeof(salsabuf)); + + d = difficulty; + p = shabuf; + while (d >= 8) { + if (*(p++)) + return false; + d -= 8; + } + if (d > 0) { + if ( ((((unsigned int)*p) << d) & 0xff00) != 0 ) + return false; + } + + return true; +} + void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid) { Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index 06220c4bc..fd7a06c0e 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -107,6 +107,27 @@ public: */ inline uint64_t receiveTime() const throw() { return _receiveTime; } + /** + * Compute the Salsa20/12+SHA512 proof of work function + * + * @param difficulty Difficulty in bits (max: 64) + * @param challenge Challenge string + * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH) + * @param result Buffer to fill with 16-byte result + */ + static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]); + + /** + * Verify the result of Salsa20/12+SHA512 proof of work + * + * @param difficulty Difficulty in bits (max: 64) + * @param challenge Challenge bytes + * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH) + * @param proposedResult Result supplied by client + * @return True if result is valid + */ + static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]); + private: // These are called internally to handle packet contents once it has // been authenticated, decrypted, decompressed, and classified. @@ -126,6 +147,7 @@ private: bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr &peer); // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid); diff --git a/node/Packet.cpp b/node/Packet.cpp index f69e4e79b..2d973dff9 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -45,7 +45,6 @@ const char *Packet::verbString(Verb v) case VERB_RENDEZVOUS: return "RENDEZVOUS"; case VERB_FRAME: return "FRAME"; case VERB_EXT_FRAME: return "EXT_FRAME"; - case VERB_P5_MULTICAST_FRAME: return "P5_MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE"; case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; @@ -56,6 +55,7 @@ const char *Packet::verbString(Verb v) case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST"; case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT"; + case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 1413db107..93b594e59 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -523,10 +523,13 @@ public: */ enum Verb /* Max value: 32 (5 bits) */ { - /* No operation, payload ignored, no reply */ + /** + * No operation (ignored, no reply) + */ VERB_NOP = 0, - /* Announcement of a node's existence: + /** + * Announcement of a node's existence: * <[1] protocol version> * <[1] software major version> * <[1] software minor version> @@ -564,7 +567,8 @@ public: */ VERB_HELLO = 1, - /* Error response: + /** + * Error response: * <[1] in-re verb> * <[8] in-re packet ID> * <[1] error code> @@ -572,14 +576,16 @@ public: */ VERB_ERROR = 2, - /* Success response: + /** + * Success response: * <[1] in-re verb> * <[8] in-re packet ID> * <[...] request-specific payload> */ VERB_OK = 3, - /* Query an identity by address: + /** + * Query an identity by address: * <[5] address to look up> * * OK response payload: @@ -590,7 +596,8 @@ public: */ VERB_WHOIS = 4, - /* Meet another node at a given protocol address: + /** + * Meet another node at a given protocol address: * <[1] flags (unused, currently 0)> * <[5] ZeroTier address of peer that might be found at this address> * <[2] 16-bit protocol address port> @@ -613,7 +620,8 @@ public: */ VERB_RENDEZVOUS = 5, - /* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME): + /** + * ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME): * <[8] 64-bit network ID> * <[2] 16-bit ethertype> * <[...] ethernet payload> @@ -628,7 +636,8 @@ public: */ VERB_FRAME = 6, - /* Full Ethernet frame with MAC addressing and optional fields: + /** + * Full Ethernet frame with MAC addressing and optional fields: * <[8] 64-bit network ID> * <[1] flags> * [<[...] certificate of network membership>] @@ -652,9 +661,10 @@ public: VERB_EXT_FRAME = 7, /* DEPRECATED */ - VERB_P5_MULTICAST_FRAME = 8, + //VERB_P5_MULTICAST_FRAME = 8, - /* Announce interest in multicast group(s): + /** + * Announce interest in multicast group(s): * <[8] 64-bit network ID> * <[6] multicast Ethernet address> * <[4] multicast additional distinguishing information (ADI)> @@ -667,7 +677,8 @@ public: */ VERB_MULTICAST_LIKE = 9, - /* Network member certificate replication/push: + /** + * Network member certificate replication/push: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * @@ -678,7 +689,8 @@ public: */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, - /* Network configuration request: + /** + * Network configuration request: * <[8] 64-bit network ID> * <[2] 16-bit length of request meta-data dictionary> * <[...] string-serialized request meta-data> @@ -713,7 +725,8 @@ public: */ VERB_NETWORK_CONFIG_REQUEST = 11, - /* Network configuration refresh request: + /** + * Network configuration refresh request: * <[...] array of 64-bit network IDs> * * This can be sent by the network controller to inform a node that it @@ -724,7 +737,8 @@ public: */ VERB_NETWORK_CONFIG_REFRESH = 12, - /* Request endpoints for multicast distribution: + /** + * Request endpoints for multicast distribution: * <[8] 64-bit network ID> * <[1] flags> * <[6] MAC address of multicast group being queried> @@ -762,7 +776,8 @@ public: */ VERB_MULTICAST_GATHER = 13, - /* Multicast frame: + /** + * Multicast frame: * <[8] 64-bit network ID> * <[1] flags> * [<[...] network certificate of membership>] @@ -803,7 +818,8 @@ public: */ VERB_MULTICAST_FRAME = 14, - /* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET) + /** + * Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET) * <[2] flags (unused and reserved, must be 0)> * <[2] length of padding / extra field section> * <[...] padding / extra field section> @@ -859,7 +875,8 @@ public: */ VERB_SET_EPHEMERAL_KEY = 15, - /* Push of potential endpoints for direct communication: + /** + * Push of potential endpoints for direct communication: * <[2] 16-bit number of paths> * <[...] paths> * @@ -899,7 +916,8 @@ public: */ VERB_PUSH_DIRECT_PATHS = 16, - /* Source-routed circuit test message: + /** + * Source-routed circuit test message: * <[5] address of originator of circuit test> * <[2] 16-bit flags> * <[8] 64-bit timestamp> @@ -977,7 +995,8 @@ public: */ VERB_CIRCUIT_TEST = 17, - /* Circuit test hop report: + /** + * Circuit test hop report: * <[8] 64-bit timestamp (from original test)> * <[8] 64-bit test ID (from original test)> * <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)> @@ -1010,7 +1029,50 @@ public: * If a test report is received and no circuit test was sent, it should be * ignored. This message generates no OK or ERROR response. */ - VERB_CIRCUIT_TEST_REPORT = 18 + VERB_CIRCUIT_TEST_REPORT = 18, + + /** + * Request proof of work: + * <[1] 8-bit proof of work type> + * <[1] 8-bit proof of work difficulty> + * <[2] 16-bit length of proof of work challenge> + * <[...] proof of work challenge> + * + * This requests that a peer perform a proof of work calucation. It can be + * sent by highly trusted peers (e.g. root servers, network controllers) + * under suspected denial of service conditions in an attempt to filter + * out "non-serious" peers and remain responsive to those proving their + * intent to actually communicate. + * + * If the peer obliges to perform the work, it does so and responds with + * an OK containing the result. Otherwise it may ignore the message or + * response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION. + * + * Proof of work type IDs: + * 0x01 - Salsa20/12+SHA512 hashcash function + * + * Salsa20/12+SHA512 is based on the following composite hash function: + * + * (1) Compute SHA512(candidate) + * (2) Use the first 256 bits of the result of #1 as a key to encrypt + * 131072 zero bytes with Salsa20/12 (with a zero IV). + * (3) Compute SHA512(the result of step #2) + * (4) Accept this candiate if the first [difficulty] bits of the result + * from step #3 are zero. Otherwise generate a new candidate and try + * again. + * + * This is performed repeatedly on candidates generated by appending the + * supplied challenge to an arbitrary nonce until a valid candidate + * is found. This chosen prepended nonce is then returned as the result + * in OK. + * + * OK payload: + * <[2] 16-bit length of result> + * <[...] computed proof of work> + * + * ERROR has no payload. + */ + VERB_REQUEST_PROOF_OF_WORK = 19 }; /** diff --git a/selftest.cpp b/selftest.cpp index e938cf4d6..090839eed 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -52,6 +52,7 @@ #include "node/CertificateOfMembership.hpp" #include "node/Defaults.hpp" #include "node/Node.hpp" +#include "node/IncomingPacket.hpp" #include "osdep/OSUtils.hpp" #include "osdep/Phy.hpp" @@ -285,6 +286,19 @@ static int testCrypto() ::free((void *)bb); } + /* + for(unsigned int d=8;d<=10;++d) { + for(int k=0;k<8;++k) { + std::cout << "[crypto] computeSalsa2012Sha512ProofOfWork(" << d << ",\"foobarbaz\",9) == "; std::cout.flush(); + unsigned char result[16]; + uint64_t start = OSUtils::now(); + IncomingPacket::computeSalsa2012Sha512ProofOfWork(d,"foobarbaz",9,result); + uint64_t end = OSUtils::now(); + std::cout << Utils::hex(result,16) << " -- valid: " << IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(d,"foobarbaz",9,result) << ", " << (end - start) << "ms" << std::endl; + } + } + */ + std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush(); for(int k=0;k Date: Wed, 7 Oct 2015 13:46:44 -0700 Subject: [PATCH 038/195] Limit proof of work difficulty to something sane. --- node/IncomingPacket.cpp | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index de9017790..cfe5a6c30 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -1060,24 +1060,33 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const // Salsa20/12+SHA512 hashcash case 0x01: { - unsigned char result[16]; - computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result); - - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); - outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); - outp.append(packetId()); - outp.append((uint16_t)sizeof(result)); - outp.append(result,sizeof(result)); - outp.armor(peer->key(),true); - RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + if (difficulty <= 14) { + unsigned char result[16]; + computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result); + TRACE("PROOF_OF_WORK computed for %s: difficulty==%u, challengeLength==%u, result: %.16llx%.16llx",peer->address().toString().c_str(),difficulty,challengeLength,Utils::ntoh(*(reinterpret_cast(result))),Utils::ntoh(*(reinterpret_cast(result + 8)))); + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); + outp.append(packetId()); + outp.append((uint16_t)sizeof(result)); + outp.append(result,sizeof(result)); + outp.armor(peer->key(),true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + } else { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST); + outp.armor(peer->key(),true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + } } break; default: - TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); break; } } else { - TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } } catch ( ... ) { TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); From 69b44bf9a56ea742e05035f31660fbb23762df1b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 16:11:50 -0700 Subject: [PATCH 039/195] Finally add an ECHO. --- node/IncomingPacket.cpp | 15 ++++++++++++++- node/IncomingPacket.hpp | 1 + node/Packet.cpp | 1 + node/Packet.hpp | 28 ++++++++++++++++------------ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index cfe5a6c30..083c47f82 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -83,6 +83,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_RENDEZVOUS: return _doRENDEZVOUS(RR,peer); case Packet::VERB_FRAME: return _doFRAME(RR,peer); case Packet::VERB_EXT_FRAME: return _doEXT_FRAME(RR,peer); + case Packet::VERB_ECHO: return _doECHO(RR,peer); case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,peer); case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return _doNETWORK_MEMBERSHIP_CERTIFICATE(RR,peer); case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,peer); @@ -569,6 +570,18 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

&peer) +{ + try { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_ECHO); + outp.append(packetId()); + outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD); + RR->sw->send(outp,true,0); + } catch ( ... ) {} + return true; +} + bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr &peer) { try { @@ -636,7 +649,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append(netconfStr.data(),(unsigned int)netconfStr.length()); outp.compress(); outp.armor(peer->key(),true); - if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { + if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length()); } else { RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index fd7a06c0e..f5dd4b27a 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -138,6 +138,7 @@ private: bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doFRAME(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr &peer); diff --git a/node/Packet.cpp b/node/Packet.cpp index 2d973dff9..2fb7d4888 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -45,6 +45,7 @@ const char *Packet::verbString(Verb v) case VERB_RENDEZVOUS: return "RENDEZVOUS"; case VERB_FRAME: return "FRAME"; case VERB_EXT_FRAME: return "EXT_FRAME"; + case VERB_ECHO: return "ECHO"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE"; case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; diff --git a/node/Packet.hpp b/node/Packet.hpp index 93b594e59..01aadad00 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -46,22 +46,20 @@ #include "../ext/lz4/lz4.h" /** - * Protocol version -- incremented only for MAJOR changes + * Protocol version -- incremented only for major changes * * 1 - 0.2.0 ... 0.2.5 * 2 - 0.3.0 ... 0.4.5 - * * Added signature and originating peer to multicast frame - * * Double size of multicast frame bloom filter + * + Added signature and originating peer to multicast frame + * + Double size of multicast frame bloom filter * 3 - 0.5.0 ... 0.6.0 - * * Yet another multicast redesign - * * New crypto completely changes key agreement cipher + * + Yet another multicast redesign + * + New crypto completely changes key agreement cipher * 4 - 0.6.0 ... 1.0.6 - * * New identity format based on hashcash design + * + New identity format based on hashcash design * 5 - 1.0.6 ... CURRENT - * * Supports CIRCUIT_TEST and friends, otherwise compatibie w/v4 - * - * This isn't going to change again for a long time unless your - * author wakes up again at 4am with another great idea. :P + * + Supports circuit test, proof of work, and echo + * + Otherwise backward compatible with 4 */ #define ZT_PROTO_VERSION 5 @@ -660,8 +658,14 @@ public: */ VERB_EXT_FRAME = 7, - /* DEPRECATED */ - //VERB_P5_MULTICAST_FRAME = 8, + /** + * ECHO request (a.k.a. ping): + * <[...] arbitrary payload to be echoed back> + * + * This generates OK with a copy of the transmitted payload. No ERROR + * is generated. Response to ECHO requests is optional. + */ + VERB_ECHO = 8, /** * Announce interest in multicast group(s): From 0ce0bc00d220ba67af03bf0cc01c8fe9f9104039 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 16:20:54 -0700 Subject: [PATCH 040/195] Make sure received() gets called for some new messages, and docs. --- node/IncomingPacket.cpp | 17 +++++++++++++---- node/Packet.hpp | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 083c47f82..4dd308cdf 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -573,11 +573,13 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

&peer) { try { + const uint64_t pid = packetId(); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_ECHO); - outp.append(packetId()); + outp.append((uint64_t)pid); outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD); - RR->sw->send(outp,true,0); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP); } catch ( ... ) {} return true; } @@ -881,6 +883,8 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha } ptr += addrLen; } + + peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP); } catch (std::exception &exc) { TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { @@ -1045,6 +1049,8 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt RR->sw->send(outp,true,originatorCredentialNetworkId); } } + + peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP); } catch (std::exception &exc) { TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { @@ -1063,6 +1069,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const try { // Right now this is only allowed from root servers -- may be allowed from controllers and relays later. if (RR->topology->isRoot(peer->identity())) { + const uint64_t pid = packetId(); const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1]; const unsigned int challengeLength = at(ZT_PACKET_IDX_PAYLOAD + 2); if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH) @@ -1079,7 +1086,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const TRACE("PROOF_OF_WORK computed for %s: difficulty==%u, challengeLength==%u, result: %.16llx%.16llx",peer->address().toString().c_str(),difficulty,challengeLength,Utils::ntoh(*(reinterpret_cast(result))),Utils::ntoh(*(reinterpret_cast(result + 8)))); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); - outp.append(packetId()); + outp.append(pid); outp.append((uint16_t)sizeof(result)); outp.append(result,sizeof(result)); outp.armor(peer->key(),true); @@ -1087,7 +1094,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK); - outp.append(packetId()); + outp.append(pid); outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST); outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); @@ -1098,6 +1105,8 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); break; } + + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP); } else { TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } diff --git a/node/Packet.hpp b/node/Packet.hpp index 01aadad00..6f3cb72fa 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -664,6 +664,8 @@ public: * * This generates OK with a copy of the transmitted payload. No ERROR * is generated. Response to ECHO requests is optional. + * + * Note that fragmented ECHO packets may not work. */ VERB_ECHO = 8, From fea1b6b2c3d004ad53c5997800229c070ccee79b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 16:25:08 -0700 Subject: [PATCH 041/195] docs --- node/Packet.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/Packet.hpp b/node/Packet.hpp index 6f3cb72fa..1c7a6f5ee 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -365,6 +365,10 @@ namespace ZeroTier { * immutable. This is because intermediate nodes can increment the hop * count up to 7 (protocol max). * + * A hop count of 7 also indicates that receiving peers should not attempt + * to learn direct paths from this packet. (Right now direct paths are only + * learned from direct packets anyway.) + * * http://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken * * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever From 9347d6c866f6cae357448762f064a481fb765c00 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 7 Oct 2015 18:04:40 -0700 Subject: [PATCH 042/195] 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(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 990f682d1..175cedc50 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 d5ca147c5..7f469b970 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); From 273f0d18b0bc907d90e746f5836576150f4cde22 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Oct 2015 09:05:25 -0700 Subject: [PATCH 043/195] docs --- node/Packet.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/Packet.hpp b/node/Packet.hpp index 1c7a6f5ee..4dfa73f0c 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -345,11 +345,11 @@ namespace ZeroTier { * ZeroTier packet * * Packet format: - * <[8] random initialization vector (doubles as 64-bit packet ID)> + * <[8] 64-bit random packet ID and crypto initialization vector> * <[5] destination ZT address> * <[5] source ZT address> * <[1] flags/cipher (top 5 bits) and ZT hop count (last 3 bits)> - * <[8] 8-bit MAC (currently first 8 bytes of poly1305 tag)> + * <[8] 64-bit MAC> * [... -- begin encryption envelope -- ...] * <[1] encrypted flags (top 3 bits) and verb (last 5 bits)> * [... verb-specific payload ...] From a3876353ca1cb07213c5e1c5208b531caeda5523 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Oct 2015 13:25:38 -0700 Subject: [PATCH 044/195] Abiltiy to post a test via the controller web API, and parsing of CIRCUIT_TEST_REPORT messages. --- controller/SqliteNetworkController.cpp | 50 ++++++++++++++ controller/SqliteNetworkController.hpp | 8 +++ node/IncomingPacket.cpp | 94 +++++++++++++++----------- 3 files changed, 111 insertions(+), 41 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 334ccc750..861cdbcf0 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -505,6 +505,52 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( } return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType); + } else if ((path.size() == 3)&&(path[2] == "test")) { + ZT_CircuitTest *test = (ZT_CircuitTest *)malloc(sizeof(ZT_CircuitTest)); + memset(test,0,sizeof(ZT_CircuitTest)); + + Utils::getSecureRandom(&(test->testId),sizeof(test->testId)); + test->credentialNetworkId = nwid; + test->ptr = (void *)this; + + json_value *j = json_parse(body.c_str(),body.length()); + if (j) { + if (j->type == json_object) { + for(unsigned int k=0;ku.object.length;++k) { + + if (!strcmp(j->u.object.values[k].name,"hops")) { + if (j->u.object.values[k].value->type == json_array) { + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *hop = j->u.object.values[k].value->u.array.values[kk]; + if (hop->type == json_array) { + for(unsigned int kkk=0;kkku.array.length;++kkk) { + if (hop->u.array.values[kkk].type == json_string) { + test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(hop->u.array.values[kkk].u.string.ptr) & 0xffffffffffULL; + } + } + ++test->hopCount; + } + } + } + } else if (!strcmp(j->u.object.values[k].name,"reportAtEveryHop")) { + if (j->u.object.values[k].value->type == json_boolean) + test->reportAtEveryHop = (j->u.object.values[k].value->u.boolean == 0) ? 0 : 1; + } + + } + } + json_value_free(j); + } + + if (!test->hopCount) { + ::free((void *)test); + return 500; + } + + test->timestamp = OSUtils::now(); + _node->circuitTestBegin(test,&(SqliteNetworkController::_circuitTestCallback)); + + return 200; } // else 404 } else { @@ -1819,4 +1865,8 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c return NetworkController::NETCONF_QUERY_OK; } +static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) +{ +} + } // namespace ZeroTier diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 68529e397..7a01487ce 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -43,6 +43,9 @@ // Number of in-memory last log entries to maintain per user #define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32 +// How long do circuit tests "live"? This is just to prevent buildup in memory. +#define ZT_SQLITENETWORKCONTROLLER_CIRCUIT_TEST_TIMEOUT 300000 + namespace ZeroTier { class Node; @@ -106,6 +109,8 @@ private: const Dictionary &metaData, Dictionary &netconf); + static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report); + Node *_node; std::string _dbPath; std::string _circuitTestPath; @@ -140,6 +145,9 @@ private: // Last log entries by address and network ID pair std::map< std::pair,_LLEntry > _lastLog; + // Circuit tests outstanding + std::map< uint64_t,ZT_CircuitTest * > _circuitTests; + sqlite3 *_db; sqlite3_stmt *_sGetNetworkById; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 4dd308cdf..0aadc104f 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -175,10 +175,8 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb); - } catch (std::exception &ex) { - TRACE("dropped ERROR from %s(%s): unexpected exception: %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped ERROR from %s(%s): unexpected exception: (unknown)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -291,8 +289,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) _remoteAddress.serialize(outp); outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); - } catch (std::exception &ex) { - TRACE("dropped HELLO from %s(%s): %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -395,10 +391,8 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb); - } catch (std::exception &ex) { - TRACE("dropped OK from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped OK from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -453,8 +447,6 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr< } else { TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str()); } - } catch (std::exception &ex) { - TRACE("dropped RENDEZVOUS from %s(%s): %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -487,10 +479,8 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr } else { TRACE("dropped FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); } - } catch (std::exception &ex) { - TRACE("dropped FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -562,10 +552,8 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); } - } catch (std::exception &ex) { - TRACE("dropped EXT_FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped EXT_FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped EXT_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -580,7 +568,9 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP); - } catch ( ... ) {} + } catch ( ... ) { + TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + } return true; } @@ -594,10 +584,8 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared RR->mc->add(now,at(ptr),MulticastGroup(MAC(field(ptr + 8,6),6),at(ptr + 14)),peer->address()); peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP); - } catch (std::exception &ex) { - TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -614,10 +602,8 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP); - } catch (std::exception &ex) { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); } catch ( ... ) { - TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -700,10 +686,8 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } - } catch (std::exception &exc) { - TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -720,10 +704,8 @@ bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,cons ptr += 8; } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP); - } catch (std::exception &exc) { - TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -751,10 +733,8 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP); - } catch (std::exception &exc) { - TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -842,10 +822,8 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share } // else ignore -- not a member of this network peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP); - } catch (std::exception &exc) { - TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -885,10 +863,8 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP); - } catch (std::exception &exc) { - TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } @@ -1051,16 +1027,52 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP); - } catch (std::exception &exc) { - TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer) { + try { + ZT_CircuitTestReport report; + memset(&report,0,sizeof(report)); + + report.address = peer->address().toInt(); + report.testId = at(ZT_PACKET_IDX_PAYLOAD + 8); + report.timestamp = at(ZT_PACKET_IDX_PAYLOAD); + report.remoteTimestamp = at(ZT_PACKET_IDX_PAYLOAD + 16); + report.sourcePacketId = at(ZT_PACKET_IDX_PAYLOAD + 44); + report.flags = at(ZT_PACKET_IDX_PAYLOAD + 36); + report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 52]; + report.errorCode = at(ZT_PACKET_IDX_PAYLOAD + 34); + report.vendor = (enum ZT_Vendor)((*this)[ZT_PACKET_IDX_PAYLOAD + 24]); + report.protocolVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 25]; + report.majorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 26]; + report.minorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 27]; + report.revision = at(ZT_PACKET_IDX_PAYLOAD + 28); + report.platform = (enum ZT_Platform)at(ZT_PACKET_IDX_PAYLOAD + 30); + report.architecture = (enum ZT_Architecture)at(ZT_PACKET_IDX_PAYLOAD + 32); + + const unsigned int receivedOnLocalAddressLen = reinterpret_cast(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 53); + const unsigned int receivedFromRemoteAddressLen = reinterpret_cast(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 53 + receivedOnLocalAddressLen); + + unsigned int nhptr = ZT_PACKET_IDX_PAYLOAD + 53 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen; + nhptr += at(nhptr) + 2; // add "additional field" length, which right now will be zero + + report.nextHopCount = (*this)[nhptr++]; + if (report.nextHopCount > ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) // sanity check, shouldn't be possible + report.nextHopCount = ZT_CIRCUIT_TEST_MAX_HOP_BREADTH; + for(unsigned int h=0;h(nhptr); nhptr += 8; + nhptr += reinterpret_cast(&(report.nextHops[h].physicalAddress))->deserialize(*this,nhptr); + } + + RR->node->postCircuitTestReport(&report); + } catch ( ... ) { + TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + } return true; } From 59da8b2a4b3e36605886944f3fa111870bbb8a2c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Oct 2015 15:44:06 -0700 Subject: [PATCH 045/195] Logging of circuit test results to disk. --- controller/SqliteNetworkController.cpp | 72 ++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 861cdbcf0..13a0cadd4 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -524,8 +524,8 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( json_value *hop = j->u.object.values[k].value->u.array.values[kk]; if (hop->type == json_array) { for(unsigned int kkk=0;kkku.array.length;++kkk) { - if (hop->u.array.values[kkk].type == json_string) { - test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(hop->u.array.values[kkk].u.string.ptr) & 0xffffffffffULL; + if (hop->u.array.values[kkk]->type == json_string) { + test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(hop->u.array.values[kkk]->u.string.ptr) & 0xffffffffffULL; } } ++test->hopCount; @@ -548,6 +548,7 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( } test->timestamp = OSUtils::now(); + _circuitTests[test->testId] = test; _node->circuitTestBegin(test,&(SqliteNetworkController::_circuitTestCallback)); return 200; @@ -1865,8 +1866,73 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c return NetworkController::NETCONF_QUERY_OK; } -static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) +void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) { + static Mutex circuitTestWriteLock; + + const uint64_t now = OSUtils::now(); + + SqliteNetworkController *const c = reinterpret_cast(test->ptr); + char tmp[128]; + + std::string reportSavePath(c->_circuitTestPath); + OSUtils::mkdir(reportSavePath); + Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx",test->credentialNetworkId); + reportSavePath.append(tmp); + OSUtils::mkdir(reportSavePath); + Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.16llx",test->timestamp,test->testId); + reportSavePath.append(tmp); + OSUtils::mkdir(reportSavePath); + Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.10llx",report->address); + reportSavePath.append(tmp); + + { + Mutex::Lock _l(circuitTestWriteLock); + FILE *f = fopen(reportSavePath.c_str(),"a"); + if (!f) + return; + fseek(f,0,SEEK_END); + fprintf(f,"%s{\n" + "\t\"address\": \"%.10llx\","ZT_EOL_S + "\t\"testId\": \"%.16llx\","ZT_EOL_S + "\t\"timestamp\": %llu,"ZT_EOL_S + "\t\"receivedTimestamp\": %llu,"ZT_EOL_S + "\t\"remoteTimestamp\": %llu,"ZT_EOL_S + "\t\"sourcePacketId\": \"%.16llx\","ZT_EOL_S + "\t\"flags\": %llu,"ZT_EOL_S + "\t\"sourcePacketHopCount\": %u,"ZT_EOL_S + "\t\"errorCode\": %u,"ZT_EOL_S + "\t\"vendor\": %d,"ZT_EOL_S + "\t\"protocolVersion\": %u,"ZT_EOL_S + "\t\"majorVersion\": %u,"ZT_EOL_S + "\t\"minorVersion\": %u,"ZT_EOL_S + "\t\"revision\": %u,"ZT_EOL_S + "\t\"platform\": %d,"ZT_EOL_S + "\t\"architecture\": %d,"ZT_EOL_S + "\t\"receivedOnLocalAddress\": \"%s\","ZT_EOL_S + "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S + "}", + ((ftell(f) > 0) ? ",\n" : ""), + report->address, + test->testId, + report->timestamp, + now, + report->remoteTimestamp, + report->sourcePacketId, + report->flags, + report->sourcePacketHopCount, + report->errorCode, + (int)report->vendor, + report->protocolVersion, + report->majorVersion, + report->minorVersion, + report->revision, + (int)report->platform, + (int)report->architecture, + reinterpret_cast(&(report->receivedOnLocalAddress))->toString().c_str(), + reinterpret_cast(&(report->receivedFromRemoteAddress))->toString().c_str()); + fclose(f); + } } } // namespace ZeroTier From 160278c489b8ec2f11235f839836f0f014990fda Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Oct 2015 17:42:53 -0700 Subject: [PATCH 046/195] Little bit of reorg in Salsa20 which seems to speed things up very slightly. --- node/Salsa20.cpp | 75 +++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp index f8cf8591d..dec14faff 100644 --- a/node/Salsa20.cpp +++ b/node/Salsa20.cpp @@ -175,41 +175,34 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) __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; for (i=0;i<_roundsDiv4;++i) { - __m128i T = _mm_add_epi32(X0, X3); - X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); - X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); + 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(X2, _mm_slli_epi32(T, 9)); - X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + 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(X3, _mm_slli_epi32(T, 13)); - X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); + 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(X0, _mm_slli_epi32(T, 18)); - X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + 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(X3, _mm_slli_epi32(T, 7)); - X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); + 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(X2, _mm_slli_epi32(T, 9)); - X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + 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(X1, _mm_slli_epi32(T, 13)); - X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); + 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(X0, _mm_slli_epi32(T, 18)); - X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + 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); @@ -218,34 +211,26 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) // -- T = _mm_add_epi32(X0, X3); - X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); - X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); + 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(X2, _mm_slli_epi32(T, 9)); - X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + 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(X3, _mm_slli_epi32(T, 13)); - X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); + 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(X0, _mm_slli_epi32(T, 18)); - X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + 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(X3, _mm_slli_epi32(T, 7)); - X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); + 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(X2, _mm_slli_epi32(T, 9)); - X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + 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(X1, _mm_slli_epi32(T, 13)); - X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); + 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(X0, _mm_slli_epi32(T, 18)); - X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + 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); @@ -257,22 +242,14 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes) X2 = _mm_add_epi32(X2s,X2); X3 = _mm_add_epi32(X3s,X3); - { - __m128i k02 = _mm_or_si128(_mm_slli_epi64(X0, 32), _mm_srli_epi64(X3, 32)); - k02 = _mm_shuffle_epi32(k02, _MM_SHUFFLE(0, 1, 2, 3)); - __m128i k13 = _mm_or_si128(_mm_slli_epi64(X1, 32), _mm_srli_epi64(X0, 32)); - k13 = _mm_shuffle_epi32(k13, _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)); - - const float *const mv = (const float *)m; - float *const cv = (float *)c; - - _mm_storeu_ps(cv,_mm_castsi128_ps(_mm_xor_si128(_mm_unpackhi_epi64(k02,k20),_mm_castps_si128(_mm_loadu_ps(mv))))); - _mm_storeu_ps(cv + 4,_mm_castsi128_ps(_mm_xor_si128(_mm_unpackhi_epi64(k13,k31),_mm_castps_si128(_mm_loadu_ps(mv + 4))))); - _mm_storeu_ps(cv + 8,_mm_castsi128_ps(_mm_xor_si128(_mm_unpacklo_epi64(k20,k02),_mm_castps_si128(_mm_loadu_ps(mv + 8))))); - _mm_storeu_ps(cv + 12,_mm_castsi128_ps(_mm_xor_si128(_mm_unpacklo_epi64(k31,k13),_mm_castps_si128(_mm_loadu_ps(mv + 12))))); - } + __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 From 3fa6dd377f479774ae2726f24748f41458329272 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 08:51:57 -0700 Subject: [PATCH 047/195] docs --- node/Packet.hpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/node/Packet.hpp b/node/Packet.hpp index 4dfa73f0c..5c7253bf0 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -669,7 +669,8 @@ public: * This generates OK with a copy of the transmitted payload. No ERROR * is generated. Response to ECHO requests is optional. * - * Note that fragmented ECHO packets may not work. + * Support for fragmented echo packets is optional and their use is not + * recommended. */ VERB_ECHO = 8, @@ -680,8 +681,18 @@ public: * <[4] multicast additional distinguishing information (ADI)> * [... additional tuples of network/address/adi ...] * - * LIKEs are sent to peers with whom you have a direct peer to peer - * connection, and always including root servers. + * LIKEs may be sent to any peer, though a good implementation should + * restrict them to peers on the same network they're for and to network + * controllers and root servers. In the current network, root servers + * will provide the service of final multicast cache. + * + * It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent + * along with MULTICAST_LIKE when pushing LIKEs to peers that do not + * share a network membership (such as root servers), since this can be + * used to authenticate GATHER requests and limit responses to peers + * authorized to talk on a network. (Should be an optional field here, + * but saving one or two packets every five minutes is not worth an + * ugly hack or protocol rev.) * * OK/ERROR are not generated. */ From 0c498556d5b11c101d2b18cf85cff2d53aa97d58 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 09:39:27 -0700 Subject: [PATCH 048/195] 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 | 1192 +++++++++++++++++++++++++++++++++++---- node/Salsa20.hpp | 40 +- selftest.cpp | 42 +- 7 files changed, 1131 insertions(+), 178 deletions(-) diff --git a/node/Identity.cpp b/node/Identity.cpp index 8765da514..e5aaf13db 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 7f469b970..84452146c 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 2fb7d4888..f11ae1b82 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 dec14faff..3aa19ac62 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)); + // 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); - X1 = _mm_shuffle_epi32(X1, 0x93); - X2 = _mm_shuffle_epi32(X2, 0x4E); - X3 = _mm_shuffle_epi32(X3, 0x39); + // 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); - 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)); + // 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); - 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); - 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,75 +359,941 @@ 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)); + // 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)); - 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); diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index 84baf3da7..a2082bead 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,19 +48,28 @@ 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(); /** @@ -71,10 +79,23 @@ public: * @param out Output buffer * @param bytes Length of data */ - inline void decrypt(const void *in,void *out,unsigned int bytes) + inline void decrypt12(const void *in,void *out,unsigned int bytes) throw() { - encrypt(in,out,bytes); + encrypt12(in,out,bytes); + } + + /** + * Decrypt data + * + * @param in Input data + * @param out Output buffer + * @param bytes Length of data + */ + inline void decrypt20(const void *in,void *out,unsigned int bytes) + throw() + { + 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 090839eed..4ba76c0b7 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(); From c2bbec2f050da996f660f2ae28b365330ebff633 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 10:14:20 -0700 Subject: [PATCH 049/195] Docker example (and useful for testing) --- .gitignore | 2 ++ examples/docker/Dockerfile | 19 +++++++++++++++++++ examples/docker/README.md | 8 ++++++++ examples/docker/main.sh | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 examples/docker/Dockerfile create mode 100644 examples/docker/README.md create mode 100644 examples/docker/main.sh diff --git a/.gitignore b/.gitignore index 498119e3c..b5d71690f 100755 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ Thumbs.db # *nix/Mac build droppings /build-* /ZeroTierOneInstaller-* +/examples/docker/zerotier-one +/examples/docker/test-*.env # Miscellaneous file types that we don't want to check in *.log diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile new file mode 100644 index 000000000..a42749240 --- /dev/null +++ b/examples/docker/Dockerfile @@ -0,0 +1,19 @@ +FROM centos:7 + +MAINTAINER https://www.zerotier.com/ + +RUN yum -y update && yum clean all + +EXPOSE 9993/udp + +RUN mkdir -p /var/lib/zerotier-one +RUN mkdir -p /var/lib/zerotier-one/networks.d +RUN ln -sf /var/lib/zerotier-one/zerotier-one /usr/local/bin/zerotier-cli +RUN ln -sf /var/lib/zerotier-one/zerotier-one /usr/local/bin/zerotier-idtool + +ADD zerotier-one /var/lib/zerotier-one/ + +ADD main.sh / +RUN chmod a+x /main.sh + +CMD ["./main.sh"] diff --git a/examples/docker/README.md b/examples/docker/README.md new file mode 100644 index 000000000..4dae52f35 --- /dev/null +++ b/examples/docker/README.md @@ -0,0 +1,8 @@ +Simple Dockerfile Example +====== + +This is a simple Docker example using ZeroTier One in normal tun/tap mode. It uses a Dockerfile to build an image containing ZeroTier One and a main.sh that launches it with an identity supplied via the Docker environment via the ZEROTIER\_IDENTITY\_SECRET and ZEROTIER\_NETWORK variables. The Dockerfile assumes that the zerotier-one binary is in the build folder. + +This is not a very secure way to load an identity secret, but it's useful for testing since it allows you to repeatedly launch Docker containers with the same identity. For production we'd recommend using something like Hashicorp Vault, or modifying main.sh to leave identities unspecified and allow the container to generate a new identity at runtime. Then you could script approval of containers using the controller API, approving them as they launch, etc. (We are working on better ways of doing mass provisioning.) + +To use in normal tun/tap mode with Docker, containers must be run with the options "--device=/dev/net/tun --cap-add=NET_ADMIN". The main.sh script supplied here will complain and exit if these options are not present (no /dev/net/tun device). diff --git a/examples/docker/main.sh b/examples/docker/main.sh new file mode 100644 index 000000000..e9febb139 --- /dev/null +++ b/examples/docker/main.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin + +if [ ! -c "/dev/net/tun" ]; then + echo 'FATAL: must be docker run with: --device=/dev/net/tun --cap-add=NET_ADMIN' + exit 1 +fi + +if [ -z "$ZEROTIER_IDENTITY_SECRET" ]; then + echo 'FATAL: ZEROTIER_IDENTITY_SECRET not set -- aborting!' + exit 1 +fi + +if [ -z "$ZEROTIER_NETWORK" ]; then + echo 'Warning: ZEROTIER_NETWORK not set, you will need to docker exec zerotier-cli to join a network.' +else + # The existence of a .conf will cause the service to "remember" this network + touch /var/lib/zerotier-one/networks.d/$ZEROTIER_NETWORK.conf +fi + +rm -f /var/lib/zerotier-one/identity.* +echo "$ZEROTIER_IDENTITY_SECRET" >identity.secret + +/var/lib/zerotier-one/zerotier-one From 9a2565115119f4c56bada376974ed77c6b2661c7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 10:14:45 -0700 Subject: [PATCH 050/195] . --- examples/docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docker/README.md b/examples/docker/README.md index 4dae52f35..fbc93481c 100644 --- a/examples/docker/README.md +++ b/examples/docker/README.md @@ -5,4 +5,4 @@ This is a simple Docker example using ZeroTier One in normal tun/tap mode. It us This is not a very secure way to load an identity secret, but it's useful for testing since it allows you to repeatedly launch Docker containers with the same identity. For production we'd recommend using something like Hashicorp Vault, or modifying main.sh to leave identities unspecified and allow the container to generate a new identity at runtime. Then you could script approval of containers using the controller API, approving them as they launch, etc. (We are working on better ways of doing mass provisioning.) -To use in normal tun/tap mode with Docker, containers must be run with the options "--device=/dev/net/tun --cap-add=NET_ADMIN". The main.sh script supplied here will complain and exit if these options are not present (no /dev/net/tun device). +To use in normal tun/tap mode with Docker, containers must be run with the options "--device=/dev/net/tun --privileged". The main.sh script supplied here will complain and exit if these options are not present (no /dev/net/tun device). From e33adad8f5b1bb64cc4c5b318a8fb95077407419 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 12:15:42 -0700 Subject: [PATCH 051/195] Script to quickly generate test docker env files. --- examples/docker/maketestenv.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 examples/docker/maketestenv.sh diff --git a/examples/docker/maketestenv.sh b/examples/docker/maketestenv.sh new file mode 100755 index 000000000..275692e15 --- /dev/null +++ b/examples/docker/maketestenv.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "$1" -o -z "$2" ]; then + echo 'Usage: maketestenv.sh ' + exit 1 +fi + +newid=`../../zerotier-idtool generate` + +echo "ZEROTIER_IDENTITY_SECRET=$newid" >$1 +echo "ZEROTIER_NETWORK=$2" >>$1 From 6b5bb0b27874b3005964deaca347289e583899f8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 12:22:13 -0700 Subject: [PATCH 052/195] Eliminate format string warnings. --- controller/SqliteNetworkController.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 13a0cadd4..40fafd794 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1913,13 +1913,13 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S "}", ((ftell(f) > 0) ? ",\n" : ""), - report->address, - test->testId, - report->timestamp, - now, - report->remoteTimestamp, - report->sourcePacketId, - report->flags, + (unsigned long long)report->address, + (unsigned long long)test->testId, + (unsigned long long)report->timestamp, + (unsigned long long)now, + (unsigned long long)report->remoteTimestamp, + (unsigned long long)report->sourcePacketId, + (unsigned long long)report->flags, report->sourcePacketHopCount, report->errorCode, (int)report->vendor, From 97dee9de36a69ed0aba4baf0cce03b9c4f11b30d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 12:50:52 -0700 Subject: [PATCH 053/195] Add more helpful example stuff. --- examples/api/README.md | 20 ++++++++++++++++++++ examples/api/public.json | 27 +++++++++++++++++++++++++++ examples/docker/Dockerfile | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 examples/api/README.md create mode 100644 examples/api/public.json diff --git a/examples/api/README.md b/examples/api/README.md new file mode 100644 index 000000000..8b6d9633b --- /dev/null +++ b/examples/api/README.md @@ -0,0 +1,20 @@ +API Examples +====== + +This folder contains examples that can be posted with curl or another http query utility to a local instance. + +To test querying with curl: + + curl -H 'X-ZT1-Auth:AUTHTOKEN' http://127.0.0.1:9993/status + +To create a public network on a local controller (service must be built with "make ZT\_ENABLE\_NETWORK\_CONTROLLER=1"): + + curl -H 'X-ZT1-Auth:AUTHTOKEN' -X POST -d @public.json http://127.0.0.1:9993/controller/network/################ + +Replace AUTHTOKEN with the contents of this instance's authtoken.secret file and ################ with a valid network ID. Its first 10 hex digits must be the ZeroTier address of the controller itself, while the last 6 hex digits can be anything. Also be sure to change the port if you have this instance listening somewhere other than 9993. + +After POSTing you can double check the network config with: + + curl -H 'X-ZT1-Auth:AUTHTOKEN' http://127.0.0.1:9993/controller/network/################ + +Once this network is created (and if your controller is online, etc.) you can then join this network from any device anywhere in the world and it will receive a valid network configuration. diff --git a/examples/api/public.json b/examples/api/public.json new file mode 100644 index 000000000..4317bd3e2 --- /dev/null +++ b/examples/api/public.json @@ -0,0 +1,27 @@ +{ + "name": "public_test_network", + "private": false, + "enableBroadcast": true, + "allowPassiveBridging": false, + "v4AssignMode": "zt", + "v6AssignMode": "rfc4193", + "multicastLimit": 32, + "relays": [], + "gateways": [], + "ipLocalRoutes": ["10.66.0.0/16"], + "ipAssignmentPools": [{"ipRangeStart":"10.66.0.1","ipRangeEnd":"10.66.255.254"}], + "rules": [ + { + "ruleNo": 10, + "etherType": 2048, + "action": "accept" + },{ + "ruleNo": 20, + "etherType": 2054, + "action": "accept" + },{ + "ruleNo": 30, + "etherType": 34525, + "action": "accept" + }] +} diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile index a42749240..f1ce6bb50 100644 --- a/examples/docker/Dockerfile +++ b/examples/docker/Dockerfile @@ -2,7 +2,7 @@ FROM centos:7 MAINTAINER https://www.zerotier.com/ -RUN yum -y update && yum clean all +RUN yum -y update && yum install -y sqlite net-tools && yum clean all EXPOSE 9993/udp From a95fa379cca0ddbce98d476b143c3606f3ae7bce Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 14:51:38 -0700 Subject: [PATCH 054/195] 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 diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 40fafd794..d87e56240 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 7a01487ce..a3d5dfc7b 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 000000000..8fcc5d944 --- /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 e9febb139..53fb65408 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 6970e92d2..50db272ac 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 84452146c..1eb219145 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 From aec13b50fdbb210e25c9bcfcb8f902da842ac65f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 15:05:26 -0700 Subject: [PATCH 055/195] Be a bit more verbose in circuit test reports to more clearly track current and upstream hop in graph traversal history. --- controller/SqliteNetworkController.cpp | 8 +++++--- include/ZeroTierOne.h | 9 +++++++-- node/IncomingPacket.cpp | 12 +++++++----- node/Packet.hpp | 1 + 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index d87e56240..08bd3a152 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1881,7 +1881,7 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.16llx",test->timestamp,test->testId); reportSavePath.append(tmp); OSUtils::mkdir(reportSavePath); - Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.10llx",report->address); + Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.10llx_%.10llx",report->upstream,report->current); reportSavePath.append(tmp); { @@ -1891,7 +1891,8 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest return; fseek(f,0,SEEK_END); fprintf(f,"%s{\n" - "\t\"address\": \"%.10llx\","ZT_EOL_S + "\t\"current\": \"%.10llx\","ZT_EOL_S + "\t\"upstream\": \"%.10llx\","ZT_EOL_S "\t\"testId\": \"%.16llx\","ZT_EOL_S "\t\"timestamp\": %llu,"ZT_EOL_S "\t\"receivedTimestamp\": %llu,"ZT_EOL_S @@ -1911,7 +1912,8 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S "}", ((ftell(f) > 0) ? ",\n" : ""), - (unsigned long long)report->address, + (unsigned long long)report->current, + (unsigned long long)report->upstream, (unsigned long long)test->testId, (unsigned long long)report->timestamp, (unsigned long long)now, diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 175cedc50..80091f623 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -733,9 +733,14 @@ typedef struct { */ typedef struct { /** - * Sender of report + * Sender of report (current hop) */ - uint64_t address; + uint64_t current; + + /** + * Previous hop + */ + uint64_t upstream; /** * 64-bit test ID diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5d31a5d47..443ffeeb9 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -993,6 +993,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt outp.append((uint16_t)0); // error code, currently unused outp.append((uint64_t)0); // flags, currently unused outp.append((uint64_t)packetId()); + peer->address().appendTo(outp); outp.append((uint8_t)hops()); _localAddress.serialize(outp); _remoteAddress.serialize(outp); @@ -1039,13 +1040,14 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S ZT_CircuitTestReport report; memset(&report,0,sizeof(report)); - report.address = peer->address().toInt(); + report.current = peer->address().toInt(); + report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); report.testId = at(ZT_PACKET_IDX_PAYLOAD + 8); report.timestamp = at(ZT_PACKET_IDX_PAYLOAD); report.remoteTimestamp = at(ZT_PACKET_IDX_PAYLOAD + 16); report.sourcePacketId = at(ZT_PACKET_IDX_PAYLOAD + 44); report.flags = at(ZT_PACKET_IDX_PAYLOAD + 36); - report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 52]; + report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58 report.errorCode = at(ZT_PACKET_IDX_PAYLOAD + 34); report.vendor = (enum ZT_Vendor)((*this)[ZT_PACKET_IDX_PAYLOAD + 24]); report.protocolVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 25]; @@ -1055,10 +1057,10 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S report.platform = (enum ZT_Platform)at(ZT_PACKET_IDX_PAYLOAD + 30); report.architecture = (enum ZT_Architecture)at(ZT_PACKET_IDX_PAYLOAD + 32); - const unsigned int receivedOnLocalAddressLen = reinterpret_cast(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 53); - const unsigned int receivedFromRemoteAddressLen = reinterpret_cast(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 53 + receivedOnLocalAddressLen); + const unsigned int receivedOnLocalAddressLen = reinterpret_cast(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58); + const unsigned int receivedFromRemoteAddressLen = reinterpret_cast(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen); - unsigned int nhptr = ZT_PACKET_IDX_PAYLOAD + 53 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen; + unsigned int nhptr = ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen; nhptr += at(nhptr) + 2; // add "additional field" length, which right now will be zero report.nextHopCount = (*this)[nhptr++]; diff --git a/node/Packet.hpp b/node/Packet.hpp index 5c7253bf0..6c178e666 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1031,6 +1031,7 @@ public: * <[2] 16-bit error code (set to 0, currently unused)> * <[8] 64-bit report flags (set to 0, currently unused)> * <[8] 64-bit source packet ID> + * <[5] upstream ZeroTier address from which test was received> * <[1] 8-bit source packet hop count (ZeroTier hop count)> * <[...] local wire address on which packet was received> * <[...] remote wire address from which packet was received> From c9295a588389d5f8e017f2784a55166c46a641f4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 15:12:05 -0700 Subject: [PATCH 056/195] . --- examples/api/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/api/README.md b/examples/api/README.md index 8b6d9633b..50b1b65e5 100644 --- a/examples/api/README.md +++ b/examples/api/README.md @@ -18,3 +18,9 @@ After POSTing you can double check the network config with: curl -H 'X-ZT1-Auth:AUTHTOKEN' http://127.0.0.1:9993/controller/network/################ Once this network is created (and if your controller is online, etc.) you can then join this network from any device anywhere in the world and it will receive a valid network configuration. + +--- + +**public.json**: A valid configuration for a public network that allows IPv4 and IPv6 traffic. + +**circuit-test-pingpong.json**: An example circuit test that can be posted to /controller/network/################/test to order a test -- you will have to edit this to insert the hops you want since the two hard coded device IDs are from our own test instances. From 7d01fab1326e3156b1327ead708457e5fefe8cdc Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 15:18:01 -0700 Subject: [PATCH 057/195] Reorg fields to be in same order as FS scheme. --- controller/SqliteNetworkController.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 08bd3a152..be6145cf6 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1891,10 +1891,10 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest return; fseek(f,0,SEEK_END); fprintf(f,"%s{\n" - "\t\"current\": \"%.10llx\","ZT_EOL_S - "\t\"upstream\": \"%.10llx\","ZT_EOL_S - "\t\"testId\": \"%.16llx\","ZT_EOL_S "\t\"timestamp\": %llu,"ZT_EOL_S + "\t\"testId\": \"%.16llx\","ZT_EOL_S + "\t\"upstream\": \"%.10llx\","ZT_EOL_S + "\t\"current\": \"%.10llx\","ZT_EOL_S "\t\"receivedTimestamp\": %llu,"ZT_EOL_S "\t\"remoteTimestamp\": %llu,"ZT_EOL_S "\t\"sourcePacketId\": \"%.16llx\","ZT_EOL_S @@ -1912,10 +1912,10 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S "}", ((ftell(f) > 0) ? ",\n" : ""), - (unsigned long long)report->current, - (unsigned long long)report->upstream, - (unsigned long long)test->testId, (unsigned long long)report->timestamp, + (unsigned long long)test->testId, + (unsigned long long)report->upstream, + (unsigned long long)report->current, (unsigned long long)now, (unsigned long long)report->remoteTimestamp, (unsigned long long)report->sourcePacketId, From eff1fe3c61aee8029337971545da5251f470ac53 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 16:22:34 -0700 Subject: [PATCH 058/195] Create files for each hop (more convenient) and fix a packet parse bug. --- controller/SqliteNetworkController.cpp | 2 +- node/IncomingPacket.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index be6145cf6..52b47665f 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1881,7 +1881,7 @@ void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.16llx",test->timestamp,test->testId); reportSavePath.append(tmp); OSUtils::mkdir(reportSavePath); - Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.10llx_%.10llx",report->upstream,report->current); + Utils::snprintf(tmp,sizeof(tmp),ZT_PATH_SEPARATOR_S"%.16llx_%.10llx_%.10llx",now,report->upstream,report->current); reportSavePath.append(tmp); { diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 443ffeeb9..9f53a1c58 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -1021,9 +1021,11 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr); for(unsigned int h=0;hsw->send(outp,true,originatorCredentialNetworkId); + if (RR->identity.address() != nextHop[h]) { // next hops that loop back to the current hop are not valid + outp.newInitializationVector(); + outp.setDestination(nextHop[h]); + RR->sw->send(outp,true,originatorCredentialNetworkId); + } } } @@ -1067,7 +1069,7 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S if (report.nextHopCount > ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) // sanity check, shouldn't be possible report.nextHopCount = ZT_CIRCUIT_TEST_MAX_HOP_BREADTH; for(unsigned int h=0;h(nhptr); nhptr += 8; + report.nextHops[h].address = Address(field(nhptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); nhptr += ZT_ADDRESS_LENGTH; nhptr += reinterpret_cast(&(report.nextHops[h].physicalAddress))->deserialize(*this,nhptr); } From 70fe7dd1fdfd9834e2127bd7fc4af4f53adaff36 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 12 Oct 2015 16:40:57 -0700 Subject: [PATCH 059/195] cleanup --- node/Identity.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/node/Identity.hpp b/node/Identity.hpp index 18e67eb60..19bb2e1fe 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -39,8 +39,6 @@ #include "C25519.hpp" #include "Buffer.hpp" -#define ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN) - namespace ZeroTier { /** From 1b1945c63ee1ba9567b8fc5d5ed2b8946fec5f12 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 12 Oct 2015 18:25:29 -0700 Subject: [PATCH 060/195] Work in progress on refactoring root-topology into World and adding in-band updates. --- node/Defaults.cpp | 82 ------- node/Defaults.hpp | 74 ------ node/IncomingPacket.cpp | 6 +- node/Packet.hpp | 35 ++- node/Topology.cpp | 110 +++------ node/Topology.hpp | 55 ++--- node/World.hpp | 221 ++++++++++++++++++ objects.mk | 1 - root-topology/Makefile | 17 -- root-topology/README.md | 18 -- root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c | 90 ------- root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.dict | 4 - root-topology/bin2c.c | 57 ----- root-topology/mktopology.cpp | 68 ------ root-topology/rootservers/7e19876aba | 4 - root-topology/rootservers/8841408a2e | 4 - root-topology/rootservers/8acf059fe3 | 4 - root-topology/rootservers/9d219039f3 | 4 - root-topology/test/README.md | 6 - .../test/create-test-root-topology.sh | 31 --- selftest.cpp | 1 - 21 files changed, 317 insertions(+), 575 deletions(-) delete mode 100644 node/Defaults.cpp delete mode 100644 node/Defaults.hpp create mode 100644 node/World.hpp delete mode 100644 root-topology/Makefile delete mode 100644 root-topology/README.md delete mode 100644 root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c delete mode 100644 root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.dict delete mode 100644 root-topology/bin2c.c delete mode 100644 root-topology/mktopology.cpp delete mode 100644 root-topology/rootservers/7e19876aba delete mode 100644 root-topology/rootservers/8841408a2e delete mode 100644 root-topology/rootservers/8acf059fe3 delete mode 100644 root-topology/rootservers/9d219039f3 delete mode 100644 root-topology/test/README.md delete mode 100755 root-topology/test/create-test-root-topology.sh diff --git a/node/Defaults.cpp b/node/Defaults.cpp deleted file mode 100644 index b311fb6aa..000000000 --- a/node/Defaults.cpp +++ /dev/null @@ -1,82 +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/ - */ - -#include -#include -#include - -#include "../include/ZeroTierOne.h" - -#include "Constants.hpp" -#include "Defaults.hpp" -#include "Utils.hpp" - -// bin2c'd signed default root topology dictionary -#include "../root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c" - -#ifdef __WINDOWS__ -#include -#include -#include -#endif - -namespace ZeroTier { - -const Defaults ZT_DEFAULTS; - -static inline std::map< Address,Identity > _mkRootTopologyAuth() -{ - std::map< Address,Identity > ua; - - { // 0001 - Identity id("77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6"); - ua[id.address()] = id; - } - { // 0002 - Identity id("86921e6de1:0:9ba04f9f12ed54ef567f548cb69d31e404537d7b0ee000c63f3d7c8d490a1a47a5a5b2af0cbe12d23f9194270593f298d936d7c872612ea509ef1c67ce2c7fc1"); - ua[id.address()] = id; - } - { // 0003 - Identity id("90302b7025:0:358154a57af1b7afa07d0d91b69b92eaad2f11ade7f02343861f0c1b757d15626e8cb7f08fc52993d2202a39cbf5128c5647ee8c63d27d92db5a1d0fbe1eba19"); - ua[id.address()] = id; - } - { // 0004 - Identity id("e5174078ee:0:c3f90daa834a74ee47105f5726ae2e29fc8ae0e939c9326788b52b16d847354de8de3b13a81896bbb509b91e1da21763073a30bbfb2b8e994550798d30a2d709"); - ua[id.address()] = id; - } - - return ua; -} - -Defaults::Defaults() : - defaultRootTopology((const char *)ZT_DEFAULT_ROOT_TOPOLOGY,ZT_DEFAULT_ROOT_TOPOLOGY_LEN), - rootTopologyAuthorities(_mkRootTopologyAuth()), - v4Broadcast(((uint32_t)0xffffffff),ZT_DEFAULT_PORT) -{ -} - -} // namespace ZeroTier diff --git a/node/Defaults.hpp b/node/Defaults.hpp deleted file mode 100644 index c1df919bf..000000000 --- a/node/Defaults.hpp +++ /dev/null @@ -1,74 +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_DEFAULTS_HPP -#define ZT_DEFAULTS_HPP - -#include -#include -#include -#include - -#include "Constants.hpp" -#include "Identity.hpp" -#include "InetAddress.hpp" - -namespace ZeroTier { - -/** - * Static configuration defaults - * - * These are the default values that ship baked into the ZeroTier binary. They - * define the basic parameters required for it to connect to the rest of the - * network and obtain software updates. - */ -class Defaults -{ -public: - Defaults(); - - /** - * Default root topology dictionary - */ - const std::string defaultRootTopology; - - /** - * Identities permitted to sign root topology dictionaries - */ - const std::map< Address,Identity > rootTopologyAuthorities; - - /** - * Address for IPv4 LAN auto-location broadcasts: 255.255.255.255:9993 - */ - const InetAddress v4Broadcast; -}; - -extern const Defaults ZT_DEFAULTS; - -} // namespace ZeroTier - -#endif diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 9f53a1c58..9fcc2e494 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -835,7 +835,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; while (count--) { // if ptr overflows Buffer will throw - // TODO: properly handle blacklisting, support other features... see Packet.hpp. + // TODO: some flags are not yet implemented unsigned int flags = (*this)[ptr++]; unsigned int extLen = at(ptr); ptr += 2; @@ -846,14 +846,14 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) { + 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()); peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) { + 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()); peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); } diff --git a/node/Packet.hpp b/node/Packet.hpp index 6c178e666..958d0f3e7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -564,6 +564,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> * * ERROR has no payload. */ @@ -911,7 +913,7 @@ public: * * Path record flags: * 0x01 - Forget this path if it is currently known - * 0x02 - Blacklist this path, do not use + * 0x02 - (Unused) * 0x04 - Disable encryption (trust: privacy) * 0x08 - Disable encryption and authentication (trust: ultimate) * @@ -1094,7 +1096,36 @@ public: * * ERROR has no payload. */ - VERB_REQUEST_PROOF_OF_WORK = 19 + 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 }; /** diff --git a/node/Topology.cpp b/node/Topology.cpp index 908acbc82..5aedae868 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -28,17 +28,37 @@ #include "Constants.hpp" #include "Topology.hpp" #include "RuntimeEnvironment.hpp" -#include "Defaults.hpp" #include "Dictionary.hpp" #include "Node.hpp" #include "Buffer.hpp" namespace ZeroTier { +// Default World +#define ZT_DEFAULT_WORLD_LENGTH 1 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = { 0 }; + 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"); @@ -76,6 +96,20 @@ 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); + } + } } Topology::~Topology() @@ -103,55 +137,6 @@ Topology::~Topology() RR->node->dataStorePut("peers.save",all,true); } -void Topology::setRootServers(const std::map< Identity,std::vector > &sn) -{ - Mutex::Lock _l(_lock); - - if (_roots == sn) - return; // no change - - _roots = sn; - _rootAddresses.clear(); - _rootPeers.clear(); - const uint64_t now = RR->node->now(); - - for(std::map< Identity,std::vector >::const_iterator i(sn.begin());i!=sn.end();++i) { - if (i->first != RR->identity) { // do not add self as a peer - SharedPtr &p = _peers[i->first.address()]; - if (!p) - p = SharedPtr(new Peer(RR->identity,i->first)); - for(std::vector::const_iterator j(i->second.begin());j!=i->second.end();++j) - p->addPath(RemotePath(InetAddress(),*j,true),now); - p->use(now); - _rootPeers.push_back(p); - } - _rootAddresses.push_back(i->first.address()); - } - - std::sort(_rootAddresses.begin(),_rootAddresses.end()); - - _amRoot = (_roots.find(RR->identity) != _roots.end()); -} - -void Topology::setRootServers(const Dictionary &sn) -{ - std::map< Identity,std::vector > m; - for(Dictionary::const_iterator d(sn.begin());d!=sn.end();++d) { - if ((d->first.length() == ZT_ADDRESS_LENGTH_HEX)&&(d->second.length() > 0)) { - try { - Dictionary snspec(d->second); - std::vector &a = m[Identity(snspec.get("id",""))]; - std::string udp(snspec.get("udp",std::string())); - if (udp.length() > 0) - a.push_back(InetAddress(udp)); - } catch ( ... ) { - TRACE("root server list contained invalid entry for: %s",d->first.c_str()); - } - } - } - this->setRootServers(m); -} - SharedPtr Topology::addPeer(const SharedPtr &peer) { if (peer->address() == RR->identity.address()) { @@ -298,13 +283,6 @@ keep_searching_for_roots: return bestRoot; } -bool Topology::isRoot(const Identity &id) const - throw() -{ - Mutex::Lock _l(_lock); - return (_roots.count(id) != 0); -} - void Topology::clean(uint64_t now) { Mutex::Lock _l(_lock); @@ -320,24 +298,6 @@ void Topology::clean(uint64_t now) } } -bool Topology::authenticateRootTopology(const Dictionary &rt) -{ - try { - std::string signer(rt.signingIdentity()); - if (!signer.length()) - return false; - Identity signerId(signer); - std::map< Address,Identity >::const_iterator authority(ZT_DEFAULTS.rootTopologyAuthorities.find(signerId.address())); - if (authority == ZT_DEFAULTS.rootTopologyAuthorities.end()) - return false; - if (signerId != authority->second) - return false; - return rt.verify(authority->second); - } catch ( ... ) { - return false; - } -} - Identity Topology::_getIdentity(const Address &zta) { char p[128]; diff --git a/node/Topology.hpp b/node/Topology.hpp index 4df545e1f..ed8f3d865 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -43,8 +43,8 @@ #include "Peer.hpp" #include "Mutex.hpp" #include "InetAddress.hpp" -#include "Dictionary.hpp" #include "Hashtable.hpp" +#include "World.hpp" namespace ZeroTier { @@ -59,21 +59,6 @@ public: Topology(const RuntimeEnvironment *renv); ~Topology(); - /** - * @param sn Root server identities and addresses - */ - void setRootServers(const std::map< Identity,std::vector > &sn); - - /** - * Set up root servers for this network - * - * This performs no signature verification of any kind. The caller must - * check the signature of the root topology dictionary first. - * - * @param sn 'rootservers' key from root-topology Dictionary (deserialized as Dictionary) - */ - void setRootServers(const Dictionary &sn); - /** * Add a peer to database * @@ -128,10 +113,20 @@ public: /** * @param id Identity to check - * @return True if this is a designated root server + * @return True if this is a designated root server in this world */ - bool isRoot(const Identity &id) const - throw(); + inline bool isRoot(const Identity &id) const + { + Mutex::Lock _l(_lock); + if (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end()) { + // Double check full identity for security reasons + for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { + if (id == r->identity) + return true; + } + } + return false; + } /** * @return Vector of root server addresses @@ -142,6 +137,15 @@ public: return _rootAddresses; } + /** + * @return Current World (copy) + */ + inline World world() const + { + Mutex::Lock _l(_lock); + return _world; + } + /** * Clean and flush database */ @@ -180,28 +184,19 @@ public: return _peers.entries(); } - /** - * Validate a root topology dictionary against the identities specified in Defaults - * - * @param rt Root topology dictionary - * @return True if dictionary signature is valid - */ - static bool authenticateRootTopology(const Dictionary &rt); - private: Identity _getIdentity(const Address &zta); void _saveIdentity(const Identity &id); const RuntimeEnvironment *RR; + World _world; Hashtable< Address,SharedPtr > _peers; - std::map< Identity,std::vector > _roots; std::vector< Address > _rootAddresses; std::vector< SharedPtr > _rootPeers; + bool _amRoot; Mutex _lock; - - bool _amRoot; }; } // namespace ZeroTier diff --git a/node/World.hpp b/node/World.hpp new file mode 100644 index 000000000..0d26021f2 --- /dev/null +++ b/node/World.hpp @@ -0,0 +1,221 @@ +/* + * 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_WORLD_HPP +#define ZT_WORLD_HPP + +#include +#include + +#include "Constants.hpp" +#include "InetAddress.hpp" +#include "Identity.hpp" +#include "Buffer.hpp" +#include "C25519.hpp" + +/** + * Maximum number of roots (sanity limit, okay to increase) + * + * A given root can (through multi-homing) be distributed across any number of + * physical endpoints, but having more than one is good to permit total failure + * of one root or its withdrawal due to compromise without taking the whole net + * down. + */ +#define ZT_WORLD_MAX_ROOTS 4 + +/** + * Maximum number of stable endpoints per root (sanity limit, okay to increase) + */ +#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32 + +/** + * 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) + +/** + * World ID indicating null / empty World object + */ +#define ZT_WORLD_ID_NULL 0 + +/** + * World ID for a test network with ephemeral or temporary roots + */ +#define ZT_WORLD_ID_TESTNET 1 + +/** + * World ID for Earth -- its approximate distance from the sun in kilometers + * + * 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. + */ +#define ZT_WORLD_ID_EARTH 149604618 + +/** + * World ID for Mars -- for future use by SpaceX or others + */ +#define ZT_WORLD_ID_MARS 227883110 + +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. + */ +class World +{ +public: + struct Root + { + Identity identity; + std::vector stableEndpoints; + + inline bool operator==(const Root &r) const throw() { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); } + inline bool operator!=(const Root &r) const throw() { return (!(*this == r)); } + inline bool operator<(const Root &r) const throw() { return (identity < r.identity); } // for sorting + }; + + /** + * Construct an empty / null World + */ + World() : + _id(ZT_WORLD_ID_NULL), + _ts(0) {} + + /** + * @return Root servers for this world and their stable endpoints + */ + inline const std::vector &roots() const throw() { return _roots; } + + /** + * @return World unique identifier + */ + inline uint64_t id() const throw() { return _id; } + + /** + * @return World definition timestamp + */ + inline uint64_t timestamp() const throw() { return _ts; } + + /** + * Verify a world update + * + * 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 + * @return True if update is newer than current and is properly signed + */ + inline bool verifyUpdate(const World &update) + { + 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); + } + + /** + * @return True if this World is non-empty + */ + inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); } + + template + inline void serialize(Buffer &b) const + { + 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); + b.append((uint8_t)_roots.size()); + for(std::vector::const_iterator r(_roots.begin());r!=_roots.end();++r) { + r->identity.serialize(b); + b.append((uint8_t)r->stableEndpoints.size()); + for(std::vector::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep) + ep->serialize(b); + } + } + + template + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + { + unsigned int p = startAt; + + _roots.clear(); + + if (b[p++] != 0x01) + throw std::invalid_argument("invalid World serialized version"); + + _id = b.template at(p); p += 8; + _ts = b.template at(p); p += 8; + memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN; + memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN; + unsigned int numRoots = b[p++]; + if (numRoots > ZT_WORLD_MAX_ROOTS) + throw std::invalid_argument("too many roots in World"); + for(unsigned int k=0;k ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT) + throw std::invalid_argument("too many stable endpoints in World/Root"); + for(unsigned int kk=0;kk _roots; +}; + +} // namespace ZeroTier + +#endif diff --git a/objects.mk b/objects.mk index 0986ef0dc..64e5cfa76 100644 --- a/objects.mk +++ b/objects.mk @@ -4,7 +4,6 @@ OBJS=\ ext/http-parser/http_parser.o \ node/C25519.o \ node/CertificateOfMembership.o \ - node/Defaults.o \ node/Dictionary.o \ node/Identity.o \ node/IncomingPacket.o \ diff --git a/root-topology/Makefile b/root-topology/Makefile deleted file mode 100644 index 3ddd916f0..000000000 --- a/root-topology/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -all: FORCE - g++ -o mktopology mktopology.cpp ../osdep/OSUtils.cpp ../node/Utils.cpp ../node/InetAddress.cpp ../node/Identity.cpp ../node/C25519.cpp ../node/Salsa20.cpp ../node/Dictionary.cpp ../node/SHA512.cpp - gcc -o bin2c bin2c.c - -official: FORCE - rm -f ZT_DEFAULT_ROOT_TOPOLOGY.dict - ./mktopology >ZT_DEFAULT_ROOT_TOPOLOGY.dict - ./bin2c ZT_DEFAULT_ROOT_TOPOLOGY < ZT_DEFAULT_ROOT_TOPOLOGY.dict > ZT_DEFAULT_ROOT_TOPOLOGY.c - ls -l ZT_DEFAULT_ROOT_TOPOLOGY.c - -clean: - rm -f *.o mktopology bin2c - -realclean: clean - rm -f ZT_DEFAULT_ROOT_TOPOLOGY.c ZT_DEFAULT_ROOT_TOPOLOGY.dict - -FORCE: diff --git a/root-topology/README.md b/root-topology/README.md deleted file mode 100644 index c9c3a9083..000000000 --- a/root-topology/README.md +++ /dev/null @@ -1,18 +0,0 @@ -This folder contains the source files to compile the signed network root topology dictionary. Users outside ZeroTier won't find this useful except for testing, since the root topology must be signed by the root topology authority (public identity in root-topology-authority.public) to be considered valid. - -Keys in the root topology dictionary are: - - * **rootservers**: contains another Dictionary mapping rootserver address to rootserver definition - * **##########**: rootserver address, contains rootserver definition - * **id**: rootserver identity (public) in string-serialized format - * **udp**: comma-delimited list of ip/port UDP addresses of node - * **tcp**: *DEPRECATED* comma-delimited list of ip/port TCP addresses of node - * **desc**: human-readable description (optional) - * **dns**: DNS name (optional, not currently used for anything) - * **noupdate**: if the value of this is '1', do not auto-update from ZeroTier's servers - -ZT_DEFAULT_ROOT_TOPOLOGY.c contains the current default value, and this URL is periodically checked for updates: - -http://download.zerotier.com/net/topology/ROOT - -Obviously nothing prevents OSS users from replacing this topology with their own, changing the hard coded topology signing identity and update URL in Defaults, and signing their own dictionary. But doing so would yield a network that would have a tough(ish) time talking to the main one. Since the main network is a free service, why bother? (Except for building testnets, which ZeroTier already does for internal testing.) diff --git a/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c b/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c deleted file mode 100644 index 96835e058..000000000 --- a/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c +++ /dev/null @@ -1,90 +0,0 @@ -static unsigned char ZT_DEFAULT_ROOT_TOPOLOGY[] = { - 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x3d, 0x37, 0x65, 0x31, 0x39, - 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x37, 0x65, - 0x31, 0x39, 0x38, 0x37, 0x36, 0x61, 0x62, 0x61, 0x3a, 0x30, 0x3a, 0x32, 0x61, 0x36, 0x65, 0x32, - 0x62, 0x32, 0x33, 0x31, 0x38, 0x39, 0x33, 0x30, 0x66, 0x36, 0x30, 0x65, 0x62, 0x30, 0x39, 0x37, - 0x66, 0x37, 0x30, 0x64, 0x30, 0x66, 0x34, 0x62, 0x30, 0x32, 0x38, 0x62, 0x32, 0x63, 0x64, 0x36, - 0x64, 0x33, 0x64, 0x30, 0x63, 0x36, 0x33, 0x63, 0x30, 0x31, 0x34, 0x62, 0x39, 0x30, 0x33, 0x39, - 0x66, 0x66, 0x33, 0x35, 0x33, 0x39, 0x30, 0x65, 0x34, 0x31, 0x31, 0x38, 0x31, 0x66, 0x32, 0x31, - 0x36, 0x66, 0x62, 0x32, 0x65, 0x36, 0x66, 0x61, 0x38, 0x64, 0x39, 0x35, 0x63, 0x31, 0x65, 0x65, - 0x39, 0x36, 0x36, 0x37, 0x31, 0x35, 0x36, 0x34, 0x31, 0x31, 0x39, 0x30, 0x35, 0x63, 0x33, 0x64, - 0x63, 0x63, 0x66, 0x65, 0x61, 0x37, 0x38, 0x64, 0x38, 0x63, 0x36, 0x64, 0x66, 0x61, 0x66, 0x62, - 0x61, 0x36, 0x38, 0x38, 0x31, 0x37, 0x30, 0x62, 0x33, 0x66, 0x61, 0x5c, 0x5c, 0x6e, 0x75, 0x64, - 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, - 0x32, 0x32, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, - 0x5c, 0x3d, 0x31, 0x39, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x39, 0x37, 0x2e, 0x32, 0x32, 0x30, - 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, - 0x61, 0x6e, 0x20, 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2c, 0x20, 0x43, 0x61, - 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x2c, 0x20, 0x55, 0x53, 0x41, 0x5c, 0x5c, 0x6e, - 0x5c, 0x6e, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x5c, 0x3d, 0x69, 0x64, - 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x38, 0x34, 0x31, 0x34, 0x30, 0x38, 0x61, 0x32, 0x65, 0x3a, 0x30, - 0x3a, 0x62, 0x62, 0x31, 0x64, 0x33, 0x31, 0x66, 0x32, 0x63, 0x33, 0x32, 0x33, 0x65, 0x32, 0x36, - 0x34, 0x65, 0x39, 0x65, 0x36, 0x34, 0x31, 0x37, 0x32, 0x63, 0x31, 0x61, 0x37, 0x34, 0x66, 0x37, - 0x37, 0x38, 0x39, 0x39, 0x35, 0x35, 0x35, 0x65, 0x64, 0x31, 0x30, 0x37, 0x35, 0x31, 0x63, 0x64, - 0x35, 0x36, 0x65, 0x38, 0x36, 0x34, 0x30, 0x35, 0x63, 0x64, 0x65, 0x31, 0x31, 0x38, 0x64, 0x30, - 0x32, 0x64, 0x66, 0x66, 0x65, 0x35, 0x35, 0x35, 0x64, 0x34, 0x36, 0x32, 0x63, 0x63, 0x66, 0x36, - 0x61, 0x38, 0x35, 0x62, 0x35, 0x36, 0x33, 0x31, 0x63, 0x31, 0x32, 0x33, 0x35, 0x30, 0x63, 0x38, - 0x64, 0x35, 0x64, 0x63, 0x34, 0x30, 0x39, 0x62, 0x61, 0x31, 0x30, 0x62, 0x39, 0x30, 0x32, 0x35, - 0x64, 0x30, 0x66, 0x34, 0x34, 0x35, 0x63, 0x66, 0x34, 0x34, 0x39, 0x64, 0x39, 0x32, 0x62, 0x31, - 0x63, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, - 0x39, 0x31, 0x2e, 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x39, 0x39, 0x39, 0x33, 0x5c, 0x5c, - 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x30, 0x37, 0x2e, 0x31, 0x39, 0x31, 0x2e, - 0x34, 0x36, 0x2e, 0x32, 0x31, 0x30, 0x2f, 0x34, 0x34, 0x33, 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, - 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x50, 0x61, 0x72, 0x69, 0x73, 0x2c, 0x20, 0x46, 0x72, 0x61, 0x6e, - 0x63, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, 0x66, 0x65, - 0x33, 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x38, 0x61, 0x63, 0x66, 0x30, 0x35, 0x39, - 0x66, 0x65, 0x33, 0x3a, 0x30, 0x3a, 0x34, 0x38, 0x32, 0x66, 0x36, 0x65, 0x65, 0x35, 0x64, 0x66, - 0x65, 0x39, 0x30, 0x32, 0x33, 0x31, 0x39, 0x62, 0x34, 0x31, 0x39, 0x64, 0x65, 0x35, 0x62, 0x64, - 0x63, 0x37, 0x36, 0x35, 0x32, 0x30, 0x39, 0x63, 0x30, 0x65, 0x63, 0x64, 0x61, 0x33, 0x38, 0x63, - 0x34, 0x64, 0x36, 0x65, 0x34, 0x66, 0x63, 0x66, 0x30, 0x64, 0x33, 0x33, 0x36, 0x35, 0x38, 0x33, - 0x39, 0x38, 0x62, 0x34, 0x35, 0x32, 0x37, 0x64, 0x63, 0x64, 0x32, 0x32, 0x66, 0x39, 0x33, 0x31, - 0x31, 0x32, 0x66, 0x62, 0x39, 0x62, 0x65, 0x66, 0x64, 0x30, 0x32, 0x66, 0x64, 0x37, 0x38, 0x62, - 0x66, 0x37, 0x32, 0x36, 0x31, 0x62, 0x33, 0x33, 0x33, 0x66, 0x63, 0x31, 0x30, 0x35, 0x64, 0x31, - 0x39, 0x32, 0x61, 0x36, 0x32, 0x33, 0x63, 0x61, 0x39, 0x65, 0x35, 0x30, 0x66, 0x63, 0x36, 0x30, - 0x62, 0x33, 0x37, 0x34, 0x61, 0x35, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, - 0x31, 0x36, 0x32, 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x39, - 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x36, 0x32, - 0x2e, 0x32, 0x34, 0x33, 0x2e, 0x37, 0x37, 0x2e, 0x31, 0x31, 0x31, 0x2f, 0x34, 0x34, 0x33, 0x5c, - 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, - 0x72, 0x6b, 0x2c, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x55, 0x53, - 0x41, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, 0x66, 0x33, - 0x5c, 0x3d, 0x69, 0x64, 0x5c, 0x5c, 0x5c, 0x3d, 0x39, 0x64, 0x32, 0x31, 0x39, 0x30, 0x33, 0x39, - 0x66, 0x33, 0x3a, 0x30, 0x3a, 0x30, 0x31, 0x66, 0x30, 0x39, 0x32, 0x32, 0x61, 0x39, 0x38, 0x65, - 0x33, 0x62, 0x33, 0x34, 0x65, 0x62, 0x63, 0x62, 0x66, 0x66, 0x33, 0x33, 0x33, 0x32, 0x36, 0x39, - 0x64, 0x63, 0x32, 0x36, 0x35, 0x64, 0x37, 0x61, 0x30, 0x32, 0x30, 0x61, 0x61, 0x62, 0x36, 0x39, - 0x64, 0x37, 0x32, 0x62, 0x65, 0x34, 0x64, 0x34, 0x61, 0x63, 0x63, 0x39, 0x63, 0x38, 0x63, 0x39, - 0x32, 0x39, 0x34, 0x37, 0x38, 0x35, 0x37, 0x37, 0x31, 0x32, 0x35, 0x36, 0x63, 0x64, 0x31, 0x64, - 0x39, 0x34, 0x32, 0x61, 0x39, 0x30, 0x64, 0x31, 0x62, 0x64, 0x31, 0x64, 0x32, 0x64, 0x63, 0x61, - 0x33, 0x65, 0x61, 0x38, 0x34, 0x65, 0x66, 0x37, 0x64, 0x38, 0x35, 0x61, 0x66, 0x65, 0x36, 0x36, - 0x31, 0x31, 0x66, 0x62, 0x34, 0x33, 0x66, 0x66, 0x30, 0x62, 0x37, 0x34, 0x31, 0x32, 0x36, 0x64, - 0x39, 0x30, 0x61, 0x36, 0x65, 0x5c, 0x5c, 0x6e, 0x75, 0x64, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, - 0x32, 0x38, 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x39, - 0x39, 0x39, 0x33, 0x5c, 0x5c, 0x6e, 0x74, 0x63, 0x70, 0x5c, 0x5c, 0x5c, 0x3d, 0x31, 0x32, 0x38, - 0x2e, 0x31, 0x39, 0x39, 0x2e, 0x31, 0x39, 0x37, 0x2e, 0x32, 0x31, 0x37, 0x2f, 0x34, 0x34, 0x33, - 0x5c, 0x5c, 0x6e, 0x64, 0x65, 0x73, 0x63, 0x5c, 0x5c, 0x5c, 0x3d, 0x53, 0x69, 0x6e, 0x67, 0x61, - 0x70, 0x6f, 0x72, 0x65, 0x5c, 0x5c, 0x6e, 0x5c, 0x6e, 0x0a, 0x7e, 0x21, 0x65, 0x64, 0x32, 0x35, - 0x35, 0x31, 0x39, 0x3d, 0x38, 0x33, 0x32, 0x62, 0x33, 0x35, 0x64, 0x61, 0x64, 0x64, 0x37, 0x66, - 0x35, 0x36, 0x66, 0x66, 0x33, 0x38, 0x31, 0x66, 0x61, 0x37, 0x32, 0x31, 0x64, 0x65, 0x37, 0x64, - 0x35, 0x62, 0x65, 0x34, 0x63, 0x65, 0x62, 0x66, 0x63, 0x63, 0x63, 0x32, 0x30, 0x30, 0x32, 0x30, - 0x38, 0x33, 0x38, 0x30, 0x64, 0x33, 0x30, 0x38, 0x34, 0x66, 0x36, 0x34, 0x38, 0x65, 0x32, 0x63, - 0x31, 0x61, 0x35, 0x63, 0x66, 0x34, 0x33, 0x65, 0x35, 0x39, 0x66, 0x39, 0x32, 0x61, 0x36, 0x36, - 0x35, 0x64, 0x66, 0x34, 0x64, 0x62, 0x63, 0x62, 0x38, 0x33, 0x37, 0x38, 0x38, 0x66, 0x36, 0x62, - 0x64, 0x36, 0x37, 0x37, 0x66, 0x30, 0x32, 0x62, 0x32, 0x31, 0x30, 0x65, 0x35, 0x30, 0x63, 0x61, - 0x66, 0x65, 0x66, 0x64, 0x32, 0x65, 0x66, 0x31, 0x38, 0x39, 0x62, 0x62, 0x66, 0x34, 0x38, 0x31, - 0x62, 0x64, 0x30, 0x32, 0x63, 0x64, 0x63, 0x39, 0x38, 0x34, 0x35, 0x33, 0x38, 0x37, 0x64, 0x38, - 0x34, 0x39, 0x62, 0x63, 0x35, 0x36, 0x66, 0x39, 0x63, 0x37, 0x32, 0x35, 0x31, 0x65, 0x35, 0x64, - 0x30, 0x65, 0x61, 0x34, 0x34, 0x34, 0x66, 0x66, 0x63, 0x66, 0x38, 0x66, 0x37, 0x32, 0x32, 0x63, - 0x32, 0x66, 0x65, 0x62, 0x38, 0x39, 0x36, 0x30, 0x33, 0x61, 0x30, 0x65, 0x35, 0x62, 0x61, 0x32, - 0x39, 0x35, 0x66, 0x63, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67, 0x69, 0x64, 0x3d, 0x37, 0x37, 0x37, - 0x39, 0x32, 0x62, 0x31, 0x63, 0x30, 0x32, 0x3a, 0x30, 0x3a, 0x62, 0x35, 0x63, 0x33, 0x36, 0x31, - 0x65, 0x38, 0x65, 0x39, 0x63, 0x32, 0x31, 0x35, 0x34, 0x65, 0x38, 0x32, 0x63, 0x33, 0x65, 0x39, - 0x30, 0x32, 0x66, 0x64, 0x66, 0x63, 0x33, 0x33, 0x37, 0x34, 0x36, 0x38, 0x62, 0x30, 0x39, 0x32, - 0x61, 0x37, 0x63, 0x34, 0x64, 0x38, 0x64, 0x63, 0x36, 0x38, 0x35, 0x63, 0x33, 0x37, 0x65, 0x62, - 0x31, 0x30, 0x65, 0x65, 0x34, 0x66, 0x33, 0x63, 0x31, 0x37, 0x63, 0x63, 0x30, 0x62, 0x62, 0x31, - 0x64, 0x30, 0x32, 0x34, 0x31, 0x36, 0x37, 0x65, 0x38, 0x63, 0x62, 0x30, 0x38, 0x32, 0x34, 0x64, - 0x31, 0x32, 0x32, 0x36, 0x33, 0x34, 0x32, 0x38, 0x33, 0x37, 0x33, 0x35, 0x38, 0x32, 0x64, 0x61, - 0x33, 0x64, 0x30, 0x61, 0x39, 0x61, 0x31, 0x34, 0x62, 0x33, 0x36, 0x65, 0x34, 0x35, 0x34, 0x36, - 0x63, 0x33, 0x31, 0x37, 0x65, 0x38, 0x31, 0x31, 0x65, 0x36, 0x0a, 0x7e, 0x21, 0x73, 0x69, 0x67, - 0x74, 0x73, 0x3d, 0x31, 0x34, 0x65, 0x30, 0x63, 0x62, 0x62, 0x39, 0x38, 0x64, 0x36, 0x0a -}; -#define ZT_DEFAULT_ROOT_TOPOLOGY_LEN 1391 diff --git a/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.dict b/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.dict deleted file mode 100644 index 58144758e..000000000 --- a/root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.dict +++ /dev/null @@ -1,4 +0,0 @@ -rootservers=7e19876aba\=id\\\=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa\\nudp\\\=198.199.97.220/9993\\ntcp\\\=198.199.97.220/443\\ndesc\\\=San Francisco, California, USA\\n\n8841408a2e\=id\\\=8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c\\nudp\\\=107.191.46.210/9993\\ntcp\\\=107.191.46.210/443\\ndesc\\\=Paris, France\\n\n8acf059fe3\=id\\\=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5\\nudp\\\=162.243.77.111/9993\\ntcp\\\=162.243.77.111/443\\ndesc\\\=New York, New York, USA\\n\n9d219039f3\=id\\\=9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e\\nudp\\\=128.199.197.217/9993\\ntcp\\\=128.199.197.217/443\\ndesc\\\=Singapore\\n\n -~!ed25519=832b35dadd7f56ff381fa721de7d5be4cebfccc200208380d3084f648e2c1a5cf43e59f92a665df4dbcb83788f6bd677f02b210e50cafefd2ef189bbf481bd02cdc9845387d849bc56f9c7251e5d0ea444ffcf8f722c2feb89603a0e5ba295fc -~!sigid=77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6 -~!sigts=14e0cbb98d6 diff --git a/root-topology/bin2c.c b/root-topology/bin2c.c deleted file mode 100644 index a30deee50..000000000 --- a/root-topology/bin2c.c +++ /dev/null @@ -1,57 +0,0 @@ -/** - Converts input from stdin into an array of binary data for use in C. - - License: Public Domain - - Usage: app VariableName < input > output.c -*/ - -#include /* uintXX_t */ -#include /* PRIuXX macros */ -#include - -static char const * appName = 0; - -static void usage() -{ - printf("Usage: %s OBJECT_NAME < input > output.c\n\n", appName ); -} - -int main( int argc, char const ** argv ) -{ - appName = argv[0]; - if( (argc != 2) || (argv[1][0] == '-') ) - { - usage(); - return 1; - } - char const * varname = argv[1]; - enum { bufSize = 1024 * 8 }; - unsigned char buf[bufSize]; - size_t rd = 0; - size_t i = 0; - size_t flip = 0; - - printf( "static unsigned char %s[] = {\n\t", varname); - uint32_t size = 0; - while( 0 != (rd = fread( buf, 1, bufSize, stdin ) ) ) - { - size += rd; - for(i = 0; i < rd; ++i ) - { - printf( "0x%02x", buf[i] ); - if( !( (rd < bufSize) && (i == rd-1)) ) putchar(','); - if( 16 == ++flip ) - { - flip = 0; - printf("\n\t"); - } - else putchar(' '); - } - } - printf("\n};\n"); - printf("#define %s_LEN %llu\n",varname,(unsigned long long)size); - //printf( "enum { %s_length = %"PRIu32"%s }; ", varname, size,"UL"); - //printf("enum { %s_length = sizeof(%s) };\n", varname, varname ); - return 0; -} diff --git a/root-topology/mktopology.cpp b/root-topology/mktopology.cpp deleted file mode 100644 index f0ad5b556..000000000 --- a/root-topology/mktopology.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -#include "../osdep/OSUtils.hpp" -#include "../node/Identity.hpp" -#include "../node/Dictionary.hpp" - -using namespace ZeroTier; - -int main(int argc,char **argv) -{ - std::string buf; - - // Read root-topology-authority.secret signing authority, must be symlinked and online - Identity topologyAuthority; - if (OSUtils::readFile("root-topology-authority.secret",buf)) - topologyAuthority.fromString(buf); - else std::cerr << "Warning: root-topology-authority.secret not found, creating unsigned topology." << std::endl; - - Dictionary topology; - - // Read template.dict to populate default fields in root topology - // if this file exists. Otherwise we just start empty. - buf.clear(); - if (OSUtils::readFile("template.dict",buf)) - topology.fromString(buf); - - // Read all entries in rootservers/ that correspond to rootserver entry dictionaries - // and add them to topology under rootservers/ subkey. - Dictionary rootservers; - std::vector rootserverDictionaries(OSUtils::listDirectory("rootservers")); - for(std::vector::const_iterator sn(rootserverDictionaries.begin());sn!=rootserverDictionaries.end();++sn) { - if (sn->length() == 10) { - buf.clear(); - if (!OSUtils::readFile((std::string("rootservers/")+(*sn)).c_str(),buf)) { - std::cerr << "Cannot read rootservers/" << *sn << std::endl; - return 1; - } - rootservers[*sn] = buf; - } - } - topology["rootservers"] = rootservers.toString(); - - if ((topologyAuthority)&&(topologyAuthority.hasPrivate())) { - // Sign topology with root-topology-authority.secret - if (!topology.sign(topologyAuthority,OSUtils::now())) { - std::cerr << "Unable to sign!" << std::endl; - return 1; - } - - // Test signature to make sure signing worked - Dictionary test(topology.toString()); - if (!test.verify(topologyAuthority)) { - std::cerr << "Test verification of signed dictionary failed!" << std::endl; - return 1; - } - } - - // Output to stdout - std::cout << topology.toString(); - - return 0; -} diff --git a/root-topology/rootservers/7e19876aba b/root-topology/rootservers/7e19876aba deleted file mode 100644 index 6bd8dc429..000000000 --- a/root-topology/rootservers/7e19876aba +++ /dev/null @@ -1,4 +0,0 @@ -id=7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa -udp=198.199.97.220/9993 -tcp=198.199.97.220/443 -desc=San Francisco, California, USA diff --git a/root-topology/rootservers/8841408a2e b/root-topology/rootservers/8841408a2e deleted file mode 100644 index 3be3333e4..000000000 --- a/root-topology/rootservers/8841408a2e +++ /dev/null @@ -1,4 +0,0 @@ -id=8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c -udp=107.191.46.210/9993 -tcp=107.191.46.210/443 -desc=Paris, France diff --git a/root-topology/rootservers/8acf059fe3 b/root-topology/rootservers/8acf059fe3 deleted file mode 100644 index 4a569d95b..000000000 --- a/root-topology/rootservers/8acf059fe3 +++ /dev/null @@ -1,4 +0,0 @@ -id=8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5 -udp=162.243.77.111/9993 -tcp=162.243.77.111/443 -desc=New York, New York, USA diff --git a/root-topology/rootservers/9d219039f3 b/root-topology/rootservers/9d219039f3 deleted file mode 100644 index ec9224336..000000000 --- a/root-topology/rootservers/9d219039f3 +++ /dev/null @@ -1,4 +0,0 @@ -id=9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e -udp=128.199.197.217/9993 -tcp=128.199.197.217/443 -desc=Singapore diff --git a/root-topology/test/README.md b/root-topology/test/README.md deleted file mode 100644 index ae7022437..000000000 --- a/root-topology/test/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Test Root Topology Script -====== - -This builds a test-root-topology from any number of running test-rootserver-# Docker containers. This can then be used with the (undocumented) -T (override root topology) option to run test networks under Docker. - -Once you have a local Docker test network running you can use iptables rules to simulate a variety of network pathologies, or you can just use it to test any new changes to the protocol or node behavior at some limited scale. diff --git a/root-topology/test/create-test-root-topology.sh b/root-topology/test/create-test-root-topology.sh deleted file mode 100755 index cb6287295..000000000 --- a/root-topology/test/create-test-root-topology.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -if [ ! -e ../mktopology ]; then - echo 'Build ../mktopology first!' - exit 1 -fi - -echo 'Populating rootservers/* with all Docker test-rootserver-* container IPs and identities...' - -rm -rf rootservers -mkdir rootservers - -for cid in `docker ps -f 'name=test-rootserver-*' -q`; do - id=`docker exec $cid cat /var/lib/zerotier-one/identity.secret | cut -d : -f 1-3` - ztaddr=`echo $id | cut -d : -f 1` - ip=`docker exec $cid ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'` - echo $cid $ztaddr $id $ip - echo "id=$id" >rootservers/$ztaddr - echo "udp=$ip/9993" >>rootservers/$ztaddr -done - -echo 'Creating test-root-topology...' - -rm -f test-root-topology -../mktopology >test-root-topology - -echo 'Done!' -echo -cat test-root-topology - -exit 0 diff --git a/selftest.cpp b/selftest.cpp index 4ba76c0b7..9c357dc4c 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -50,7 +50,6 @@ #include "node/C25519.hpp" #include "node/Poly1305.hpp" #include "node/CertificateOfMembership.hpp" -#include "node/Defaults.hpp" #include "node/Node.hpp" #include "node/IncomingPacket.hpp" From cae58f43f1b18017b90499811772d107ea2f65b9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 08:49:36 -0700 Subject: [PATCH 061/195] 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 diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 80091f623..f69ab54cd 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 6daa6aa0b..9fb613d88 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 000000000..2b41d735f --- /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 9fcc2e494..39abe720b 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 1eb219145..7496b045b 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 0ae176c0d..c7038ed40 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 958d0f3e7..939d84a51 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 5aedae868..0cf4cfe84 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 ed8f3d865..3abc27e4a 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 0d26021f2..7ccd2c536 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 a4d5190c9..c8661fda4 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 071a2cbce..6b28c41e6 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 70d024bc1..2f76ebaa2 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(); From 05677f57e2e6bed58467198f4e65b68a236b00c2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 08:55:21 -0700 Subject: [PATCH 062/195] Add C output to mkworld. --- .gitignore | 1 + mkworld.cpp | 17 +++++++++++++++++ node/World.hpp | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b5d71690f..1cb16da59 100755 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ Thumbs.db /ZeroTierOneInstaller-* /examples/docker/zerotier-one /examples/docker/test-*.env +/mkworld # Miscellaneous file types that we don't want to check in *.log diff --git a/mkworld.cpp b/mkworld.cpp index 2b41d735f..baf693fcb 100644 --- a/mkworld.cpp +++ b/mkworld.cpp @@ -144,10 +144,27 @@ int main(int argc,char **argv) 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/World.hpp b/node/World.hpp index 7ccd2c536..c6d20d842 100644 --- a/node/World.hpp +++ b/node/World.hpp @@ -225,7 +225,7 @@ public: return (p - startAt); } - inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_roots == w._roots)); } + inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); } inline bool operator!=(const World &w) const throw() { return (!(*this == w)); } protected: From 5d2f523e81a56a33405d2b98ccef9d384e269f34 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:10:44 -0700 Subject: [PATCH 063/195] World stuff... --- .gitignore | 3 +- include/ZeroTierOne.h | 5 --- make-mac.mk | 4 -- 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 +++ mkworld.cpp => world/mkworld.cpp | 0 15 files changed, 101 insertions(+), 175 deletions(-) create mode 100644 world/2015-10-13.bin create mode 100644 world/2015-10-13.out rename mkworld.cpp => world/mkworld.cpp (100%) diff --git a/.gitignore b/.gitignore index 1cb16da59..2dbec1e5a 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 f69ab54cd..38db32224 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 9fb613d88..174216fb1 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/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 39abe720b..3c6268ede 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 7496b045b..5468f1028 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 939d84a51..810f5d67e 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 111c849e7..697ba75d7 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 3abc27e4a..6f0170f00 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 6e731bdc3..dd755a309 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 0000000000000000000000000000000000000000..433a7763e7917a3b81d046d6760bace1576d66f0 GIT binary patch literal 494 zcmV&irwa%W#1ZrW6my3NF+UIhn5qGx+QPPu%95sOi_CTdkM6ohCEt+F`&n^hT}mq1b!KZYPtX_ZYv`glMi6)34d_V^sp$h z&22plW55)-1E2F#kmM18@)r9pZ>ZT^9_eOrRzVpB!`#p6c-Y3@uexY~aI^XV0R+a! zVcaJPh(SP#E&#h7G4jJB;$-ROL2|*TPj`uxRqYUU9Mx`yKwRDtjRN2PRb56b&uWFW zV;mAS42@mH3Azxu0$mS8T=YrVD;xj;1Z%%8(kBUu&jp|307x%x<=^Q7F`Gf1<-Nyc zAe;`(ql`^%PtOfAWrLWsQhm)L`7sjvoA1ys*NgWi8#6z_1<{hGBg&po{9v literal 0 HcmV?d00001 diff --git a/world/2015-10-13.out b/world/2015-10-13.out new file mode 100644 index 000000000..754e06921 --- /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/mkworld.cpp b/world/mkworld.cpp similarity index 100% rename from mkworld.cpp rename to world/mkworld.cpp From 123c466843fb910c5496cf6218e56678bd4b43ba Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:17:47 -0700 Subject: [PATCH 064/195] Full integration of World and World updates. --- node/IncomingPacket.cpp | 17 ++++++++++++++--- world/README.md | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 world/README.md diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 3c6268ede..79a700f66 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -335,9 +335,21 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); + const bool trusted = RR->topology->isRoot(peer->identity()); + InetAddress destAddr; - if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < size()) // ZeroTier One < 1.0.3 did not include this field - destAddr.deserialize(*this,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2); + unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2; + if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field + ptr += destAddr.deserialize(*this,ptr); + if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root + World worldUpdate; + const unsigned int worldLen = at(ptr); ptr += 2; + if (worldLen > 0) { + World w; + w.deserialize(*this,ptr); + RR->topology->worldUpdateIfValid(w); + } + } if (vProto < ZT_PROTO_VERSION_MIN) { TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); @@ -349,7 +361,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p peer->addDirectLatencyMeasurment(latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); - bool trusted = RR->topology->isRoot(peer->identity()); if (destAddr) RR->sa->iam(peer->address(),_remoteAddress,destAddr,trusted,RR->node->now()); } break; diff --git a/world/README.md b/world/README.md new file mode 100644 index 000000000..4e0820e66 --- /dev/null +++ b/world/README.md @@ -0,0 +1,6 @@ +World Definitions and Generator Code +====== + +This code can be used to generate a world definition. Actual generation was performed on an airgapped secure system. + +Ordinary users probably will not need to use this. From e268d9492a2357a324345fb94a3f03645a341876 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:18:47 -0700 Subject: [PATCH 065/195] cleanup --- node/Topology.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index cdecfdae0..8b5deffc8 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -28,7 +28,6 @@ #include "Constants.hpp" #include "Topology.hpp" #include "RuntimeEnvironment.hpp" -#include "Dictionary.hpp" #include "Node.hpp" #include "Buffer.hpp" From 71348f3ebbc84fe31d15bb0aa0f6b2b6e158cf28 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:29:01 -0700 Subject: [PATCH 066/195] docs --- world/README.md | 7 +++++-- world/mkworld.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/world/README.md b/world/README.md index 4e0820e66..fbd8a0ae3 100644 --- a/world/README.md +++ b/world/README.md @@ -1,6 +1,9 @@ World Definitions and Generator Code ====== -This code can be used to generate a world definition. Actual generation was performed on an airgapped secure system. +This little bit of code is used to generate world updates. Ordinary users probably will never need this unless they want to test or experiment. + +See mkworld.cpp for documentation. To build from this directory: + + c++ -o mkworld ../node/C25519.cpp ../node/Salsa20.cpp ../node/SHA512.cpp ../node/Identity.cpp ../node/Utils.cpp ../node/InetAddress.cpp ../osdep/OSUtils.cpp mkworld.cpp -Ordinary users probably will not need to use this. diff --git a/world/mkworld.cpp b/world/mkworld.cpp index baf693fcb..75230da42 100644 --- a/world/mkworld.cpp +++ b/world/mkworld.cpp @@ -50,12 +50,12 @@ #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" +#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; From 70d8e3ad94076c51b05b4165cbd68a91806dbde7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:31:38 -0700 Subject: [PATCH 067/195] Expose world ID and world timestamp in ZT_NodeStatus --- include/ZeroTierOne.h | 10 ++++++++++ node/Node.cpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 38db32224..42c904ebf 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 5468f1028..be37b7c7c 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; From 385f1410d24c37ef9cd8a3494a5eaa383ffd0fba Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:33:15 -0700 Subject: [PATCH 068/195] Expose world info in JSON. --- service/ControlPlane.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index dd755a309..7affb08c6 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -360,6 +360,8 @@ unsigned int ControlPlane::handleRequest( "{\n" "\t\"address\": \"%.10llx\",\n" "\t\"publicIdentity\": \"%s\",\n" + "\t\"worldId\": %llu,\n" + "\t\"worldTimestamp\": %llu,\n" "\t\"online\": %s,\n" "\t\"tcpFallbackActive\": %s,\n" "\t\"versionMajor\": %d,\n" @@ -370,6 +372,8 @@ unsigned int ControlPlane::handleRequest( "}\n", status.address, status.publicIdentity, + status.worldId, + status.worldTimestamp, (status.online) ? "true" : "false", (_svc->tcpFallbackActive()) ? "true" : "false", ZEROTIER_ONE_VERSION_MAJOR, From 824ed99160607ddf42a520f46f65c17e4abe9bd0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 12:42:54 -0700 Subject: [PATCH 069/195] . --- node/Packet.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/node/Packet.hpp b/node/Packet.hpp index 810f5d67e..e03190a25 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -59,6 +59,7 @@ * + New identity format based on hashcash design * 5 - 1.0.6 ... CURRENT * + Supports circuit test, proof of work, and echo + * + Supports in-band world (root definition) updates * + Otherwise backward compatible with 4 */ #define ZT_PROTO_VERSION 5 From 489e1a5b8339403288a4a88b91ae01cb0092ddca Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 13 Oct 2015 13:51:54 -0700 Subject: [PATCH 070/195] 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(-) diff --git a/node/Node.cpp b/node/Node.cpp index be37b7c7c..d72bc73fd 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); From 719233617ca9f26c3309d608a38aadf701bb5648 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 10:14:07 -0700 Subject: [PATCH 071/195] Add uint16_t key to Hashtable, and make Salsa20 zero its keyspace on destruction. --- node/Hashtable.hpp | 5 ++++- node/Salsa20.hpp | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index beef14688..1d8d9e5d2 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -382,7 +382,10 @@ private: } static inline unsigned long _hc(const uint32_t i) { - // In the uint32_t case we use a simple multiplier for hashing to ensure coverage + return ((unsigned long)i * (unsigned long)0x9e3779b1); + } + static inline unsigned long _hc(const uint16_t i) + { return ((unsigned long)i * (unsigned long)0x9e3779b1); } diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index a2082bead..7e4c1e53a 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -12,6 +12,7 @@ #include #include "Constants.hpp" +#include "Utils.hpp" #if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || defined(__WINDOWS__)) #define ZT_SALSA20_SSE 1 @@ -31,6 +32,8 @@ class Salsa20 public: Salsa20() throw() {} + ~Salsa20() { Utils::burn(&_state,sizeof(_state)); } + /** * @param key Key bits * @param kbits Number of key bits: 128 or 256 (recommended) From c312ae221f0aa339cce56c411d59d9cc9e34abc5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 10:45:33 -0700 Subject: [PATCH 072/195] Fix for world size in OK(HELLO) --- node/IncomingPacket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 79a700f66..944e3043e 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -302,7 +302,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) 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)); + outp.setAt(sizeAt,(uint16_t)(outp.size() - (sizeAt + 2))); } else { outp.append((uint16_t)0); // no world update needed } @@ -435,12 +435,12 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr { try { if (payloadLength() == ZT_ADDRESS_LENGTH) { - const SharedPtr queried(RR->topology->getPeer(Address(payload(),ZT_ADDRESS_LENGTH))); + Identity queried(RR->topology->getIdentity(Address(payload(),ZT_ADDRESS_LENGTH))); if (queried) { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_WHOIS); outp.append(packetId()); - queried->identity().serialize(outp,false); + queried.serialize(outp,false); outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { From 619e1137480de4682bb46eabaee3ce750c5be3e8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 14:12:12 -0700 Subject: [PATCH 073/195] Work in progress on Cluster for new root infrastructure, multi-homing. --- make-mac.mk | 2 +- node/Cluster.cpp | 398 +++++++++++++++++++++++++++++++++++++++++++++ node/Cluster.hpp | 331 +++++++++++++++++++++++++++++++++++++ node/Constants.hpp | 2 +- node/Identity.hpp | 19 ++- node/Packet.hpp | 4 + node/Peer.hpp | 3 +- node/Topology.cpp | 31 ++-- node/Topology.hpp | 19 ++- objects.mk | 1 + 10 files changed, 794 insertions(+), 16 deletions(-) create mode 100644 node/Cluster.cpp create mode 100644 node/Cluster.hpp diff --git a/make-mac.mk b/make-mac.mk index 174216fb1..e53212c0e 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 new file mode 100644 index 000000000..98b452654 --- /dev/null +++ b/node/Cluster.cpp @@ -0,0 +1,398 @@ +/* + * 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/ + */ + +#ifdef ZT_ENABLE_CLUSTER + +#include +#include +#include +#include + +#include +#include + +#include "Cluster.hpp" +#include "RuntimeEnvironment.hpp" +#include "MulticastGroup.hpp" +#include "CertificateOfMembership.hpp" +#include "Salsa20.hpp" +#include "Poly1305.hpp" +#include "Packet.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) : + RR(renv), + _sendFunction(sendFunction), + _arg(arg), + _x(x), + _y(y), + _z(z), + _da(da), + _id(id) +{ + uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; + + // Generate master secret by hashing the secret from our Identity key pair + RR->identity.sha512PrivateKey(_masterSecret); + + // Generate our inbound message key, which is the master secret XORed with our ID and hashed twice + memcpy(stmp,_masterSecret,sizeof(stmp)); + stmp[0] ^= Utils::hton(id); + SHA512::hash(stmp,stmp,sizeof(stmp)); + SHA512::hash(stmp,stmp,sizeof(stmp)); + memcpy(_key,stmp,sizeof(_key)); + Utils::burn(stmp,sizeof(stmp)); +} + +Cluster::~Cluster() +{ + Utils::burn(_masterSecret,sizeof(_masterSecret)); + Utils::burn(_key,sizeof(_key)); +} + +void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) +{ + Buffer dmsg; + { + // FORMAT: <[16] iv><[8] MAC><... data> + if ((len < 24)||(len > ZT_CLUSTER_MAX_MESSAGE_LENGTH)) + return; + + // 16-byte IV: first 8 bytes XORed with key, last 8 bytes used as Salsa20 64-bit IV + char keytmp[32]; + memcpy(keytmp,_key,32); + for(int i=0;i<8;++i) + keytmp[i] ^= reinterpret_cast(msg)[i]; + Salsa20 s20(keytmp,256,reinterpret_cast(msg) + 8); + Utils::burn(keytmp,sizeof(keytmp)); + + // One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard") + char polykey[ZT_POLY1305_KEY_LEN]; + memset(polykey,0,sizeof(polykey)); + s20.encrypt12(polykey,polykey,sizeof(polykey)); + + // Compute 16-byte MAC + char mac[ZT_POLY1305_MAC_LEN]; + Poly1305::compute(mac,reinterpret_cast(msg) + 24,len - 24,polykey); + + // Check first 8 bytes of MAC against 64-bit MAC in stream + if (!Utils::secureEq(mac,reinterpret_cast(msg) + 16,8)) + return; + + // Decrypt! + dmsg.setSize(len - 16); + s20.decrypt12(reinterpret_cast(msg) + 16,const_cast(dmsg.data()),dmsg.size()); + } + + if (dmsg.size() < 2) + return; + const uint16_t fromMemberId = dmsg.at(0); + unsigned int ptr = 2; + + _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; + const unsigned int nextPtr = ptr + mlen; + + int mtype = -1; + try { + switch((StateMessageType)(mtype = (int)dmsg[ptr++])) { + default: + break; + + case STATE_MESSAGE_ALIVE: { + ptr += 7; // skip version stuff, not used yet + m.x = dmsg.at(ptr); ptr += 4; + m.y = dmsg.at(ptr); ptr += 4; + m.z = dmsg.at(ptr); ptr += 4; + 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; + + case STATE_MESSAGE_HAVE_PEER: { + try { + Identity id; + ptr += id.deserialize(dmsg,ptr); + RR->topology->saveIdentity(id); + + { // Add or update peer affinity entry + _PeerAffinity pa(id.address(),fromMemberId,RR->node->now()); + Mutex::Lock _l2(_peerAffinities_m); + std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) + if ((i != _peerAffinities.end())&&(i->key == pa.key)) { + i->timestamp = pa.timestamp; + } else { + _peerAffinities.push_back(pa); + std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now + } + } + } catch ( ... ) { + // ignore invalid identities + } + } break; + + case STATE_MESSAGE_MULTICAST_LIKE: { + const uint64_t nwid = dmsg.at(ptr); ptr += 8; + const Address address(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; + const MAC mac(dmsg.field(ptr,6),6); ptr += 6; + const uint32_t adi = dmsg.at(ptr); ptr += 4; + RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); + } break; + + case STATE_MESSAGE_COM: { + // TODO: not used yet + } break; + + case STATE_MESSAGE_RELAY: { + const unsigned int numRemotePeerPaths = dmsg[ptr++]; + InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max + for(unsigned int i=0;i(ptr); ptr += 2; + const void *packet = (const void *)dmsg.field(ptr,packetLen); ptr += packetLen; + + if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address + const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); + SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); + if (destinationPeer) { + RemotePath *destinationPath = destinationPeer->send(RR,packet,packetLen,RR->node->now()); + if ((destinationPath)&&(numRemotePeerPaths > 0)&&(packetLen >= 18)&&(reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR)) { + // If remote peer paths were sent with this relayed packet, we do + // RENDEZVOUS. It's handled here for cluster-relayed packets since + // we don't have both Peer records so this is a different path. + + const Address remotePeerAddress(reinterpret_cast(packet) + 13,ZT_ADDRESS_LENGTH); + + InetAddress bestDestV4,bestDestV6; + destinationPeer->getBestActiveAddresses(RR->node->now(),bestDestV4,bestDestV6); + InetAddress bestRemoteV4,bestRemoteV6; + for(unsigned int i=0;iidentity.address(),Packet::VERB_RENDEZVOUS); + rendezvousForDest.append((uint8_t)0); + remotePeerAddress.appendTo(rendezvousForDest); + + Buffer<2048> rendezvousForOtherEnd; + rendezvousForOtherEnd.addSize(2); // leave room for payload size + rendezvousForOtherEnd.append((uint8_t)STATE_MESSAGE_PROXY_SEND); + remotePeerAddress.appendTo(rendezvousForOtherEnd); + rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS); + const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size(); + rendezvousForOtherEnd.addSize(2); // space for actual packet payload length + rendezvousForOtherEnd.append((uint8_t)0); // flags == 0 + destinationAddress.appendTo(rendezvousForOtherEnd); + + bool haveMatch = false; + if ((bestDestV6)&&(bestRemoteV6)) { + haveMatch = true; + + rendezvousForDest.append((uint16_t)bestRemoteV6.port()); + rendezvousForDest.append((uint8_t)16); + rendezvousForDest.append(bestRemoteV6.rawIpData(),16); + + rendezvousForOtherEnd.append((uint16_t)bestDestV6.port()); + rendezvousForOtherEnd.append((uint8_t)16); + rendezvousForOtherEnd.append(bestDestV6.rawIpData(),16); + rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16)); + } else if ((bestDestV4)&&(bestRemoteV4)) { + haveMatch = true; + + rendezvousForDest.append((uint16_t)bestRemoteV4.port()); + rendezvousForDest.append((uint8_t)4); + rendezvousForDest.append(bestRemoteV4.rawIpData(),4); + + rendezvousForOtherEnd.append((uint16_t)bestDestV4.port()); + rendezvousForOtherEnd.append((uint8_t)4); + rendezvousForOtherEnd.append(bestDestV4.rawIpData(),4); + rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4)); + } + + if (haveMatch) { + RR->sw->send(rendezvousForDest,true,0); + rendezvousForOtherEnd.setAt(0,(uint16_t)(rendezvousForOtherEnd.size() - 2)); + _send(fromMemberId,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); + } + } + } + } + } break; + + case STATE_MESSAGE_PROXY_SEND: { + const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + const Packet::Verb verb = (Packet::Verb)dmsg[ptr++]; + const unsigned int len = dmsg.at(ptr); ptr += 2; + Packet outp(rcpt,RR->identity.address(),verb); + outp.append(dmsg.field(ptr,len),len); + RR->sw->send(outp,true,0); + } break; + } + } catch ( ... ) { + TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype); + // drop invalids + } + + ptr = nextPtr; + } + } catch ( ... ) { + TRACE("invalid message (outer loop), discarding"); + // drop invalids + } +} + +void Cluster::replicateHavePeer(const Address &peerAddress) +{ +} + +void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group) +{ +} + +void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembership &com) +{ +} + +void Cluster::doPeriodicTasks() +{ + // Go ahead and flush whenever possible right 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); + } + } +} + +void Cluster::addMember(uint16_t memberId) +{ + Mutex::Lock _l2(_members[memberId].lock); + + Mutex::Lock _l(_memberIds_m); + _memberIds.push_back(memberId); + std::sort(_memberIds.begin(),_memberIds.end()); + + // Generate this member's message key from the master and its ID + uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; + memcpy(stmp,_masterSecret,sizeof(stmp)); + stmp[0] ^= Utils::hton(memberId); + SHA512::hash(stmp,stmp,sizeof(stmp)); + SHA512::hash(stmp,stmp,sizeof(stmp)); + memcpy(_members[memberId].key,stmp,sizeof(_members[memberId].key)); + Utils::burn(stmp,sizeof(stmp)); + + // Prepare q + _members[memberId].q.clear(); + char iv[16]; + Utils::getSecureRandom(iv,16); + _members[memberId].q.append(iv,16); + _members[memberId].q.addSize(8); // room for MAC +} + +void Cluster::_send(uint16_t memberId,const void *msg,unsigned int len) +{ + _Member &m = _members[memberId]; + // assumes m.lock is locked! + for(;;) { + if ((m.q.size() + len) > ZT_CLUSTER_MAX_MESSAGE_LENGTH) + _flush(memberId); + else { + m.q.append(msg,len); + break; + } + } +} + +void Cluster::_flush(uint16_t memberId) +{ + _Member &m = _members[memberId]; + // assumes m.lock is locked! + if (m.q.size() > 24) { + // Create key from member's key and IV + char keytmp[32]; + memcpy(keytmp,m.key,32); + for(int i=0;i<8;++i) + keytmp[i] ^= m.q[i]; + Salsa20 s20(keytmp,256,m.q.field(8,8)); + Utils::burn(keytmp,sizeof(keytmp)); + + // One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard") + char polykey[ZT_POLY1305_KEY_LEN]; + memset(polykey,0,sizeof(polykey)); + s20.encrypt12(polykey,polykey,sizeof(polykey)); + + // Encrypt m.q in place + s20.encrypt12(reinterpret_cast(m.q.data()) + 24,const_cast(reinterpret_cast(m.q.data())) + 24,m.q.size() - 24); + + // Add MAC for authentication (encrypt-then-MAC) + char mac[ZT_POLY1305_MAC_LEN]; + Poly1305::compute(mac,reinterpret_cast(m.q.data()) + 24,m.q.size() - 24,polykey); + memcpy(m.q.field(16,8),mac,8); + + // Send! + _sendFunction(_arg,memberId,m.q.data(),m.q.size()); + + // Prepare for more + m.q.clear(); + char iv[16]; + Utils::getSecureRandom(iv,16); + m.q.append(iv,16); + m.q.addSize(8); // room for MAC + } +} + +} // namespace ZeroTier + +#endif // ZT_ENABLE_CLUSTER diff --git a/node/Cluster.hpp b/node/Cluster.hpp new file mode 100644 index 000000000..13d9e2f50 --- /dev/null +++ b/node/Cluster.hpp @@ -0,0 +1,331 @@ +/* + * 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_CLUSTER_HPP +#define ZT_CLUSTER_HPP + +#ifdef ZT_ENABLE_CLUSTER + +#include +#include + +#include "Constants.hpp" +#include "Address.hpp" +#include "InetAddress.hpp" +#include "SHA512.hpp" +#include "Utils.hpp" +#include "Buffer.hpp" +#include "Mutex.hpp" + +/** + * Timeout for cluster members being considered "alive" + */ +#define ZT_CLUSTER_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT + +/** + * 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 + +namespace ZeroTier { + +class RuntimeEnvironment; +class CertificateOfMembership; +class MulticastGroup; + +/** + * Multi-homing cluster state replication and packet relaying + * + * Multi-homing means more than one node sharing the same ZeroTier identity. + * There is nothing in the protocol to prevent this, but to make it work well + * requires the devices sharing an identity to cooperate and share some + * information. + * + * There are three use cases we want to fulfill: + * + * (1) Multi-homing of root servers with handoff for efficient routing, + * HA, and load balancing across many commodity nodes. + * (2) Multi-homing of network controllers for the same reason. + * (3) Multi-homing of nodes on virtual networks, such as domain servers + * and other important endpoints. + * + * These use cases are in order of escalating difficulty. The initial + * version of Cluster is aimed at satisfying the first, though you are + * free to try #2 and #3. + */ +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 + */ + enum StateMessageType + { + STATE_MESSAGE_NOP = 0, + + /** + * This cluster member is alive: + * <[2] version minor> + * <[2] version major> + * <[2] version revision> + * <[1] protocol version> + * <[4] X location (signed 32-bit)> + * <[4] Y location (signed 32-bit)> + * <[4] Z location (signed 32-bit)> + * <[8] local clock at this member> + * <[8] load average> + * <[8] flags (currently unused, must be zero)> + * <[1] number of preferred ZeroTier endpoints> + * <[...] InetAddress(es) of preferred ZeroTier endpoint(s)> + */ + STATE_MESSAGE_ALIVE = 1, + + /** + * Cluster member has this peer: + * <[...] binary serialized peer identity> + */ + STATE_MESSAGE_HAVE_PEER = 2, + + /** + * Peer subscription to multicast group: + * <[8] network ID> + * <[5] peer ZeroTier address> + * <[6] MAC address of multicast group> + * <[4] 32-bit multicast group ADI> + */ + STATE_MESSAGE_MULTICAST_LIKE = 3, + + /** + * Certificate of network membership for a peer: + * <[...] serialized COM> + */ + STATE_MESSAGE_COM = 4, + + /** + * Relay a packet to a peer: + * <[1] 8-bit number of sending peer active path addresses> + * <[...] series of serialized InetAddresses of sending peer's paths> + * <[2] 16-bit packet length> + * <[...] packet or packet fragment> + */ + STATE_MESSAGE_RELAY = 5, + + /** + * Request to send a packet to a locally-known peer: + * <[5] ZeroTier address of recipient> + * <[1] packet verb> + * <[2] length of packet payload> + * <[...] packet payload> + * + * This differs from RELAY in that it requests the receiving cluster + * member to actually compose a ZeroTier Packet from itself to the + * provided recipient. RELAY simply says "please forward this blob." + * RELAY is used to implement peer-to-peer relaying with RENDEZVOUS, + * while PROXY_SEND is used to implement proxy sending (which right + * now is only used to send RENDEZVOUS). + */ + STATE_MESSAGE_PROXY_SEND = 6 + }; + + /** + * 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, + int32_t x, + int32_t y, + int32_t z, + void (*sendFunction)(void *,uint16_t,const void *,unsigned int), + void *arg); + + ~Cluster(); + + /** + * @return This cluster member's ID + */ + inline uint16_t id() const throw() { return _id; } + + /** + * Handle an incoming intra-cluster message + * + * @param data Message data + * @param len Message length (max: ZT_CLUSTER_MAX_MESSAGE_LENGTH) + */ + void handleIncomingStateMessage(const void *msg,unsigned int len); + + /** + * Advertise to the cluster that we have this peer + * + * @param peerAddress Peer address that we have + */ + void replicateHavePeer(const Address &peerAddress); + + /** + * Advertise a multicast LIKE to the cluster + * + * @param nwid Network ID + * @param peerAddress Peer address that sent LIKE + * @param group Multicast group + */ + void replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group); + + /** + * Advertise a network COM to the cluster + * + * @param com Certificate of network membership (contains peer and network ID) + */ + void replicateCertificateOfNetworkMembership(const CertificateOfMembership &com); + + /** + * This should be called no less frequently than once every 10 seconds. + */ + void doPeriodicTasks(); + + /** + * Add a member ID to this cluster + * + * @param memberId Member ID + */ + void addMember(uint16_t memberId); + +private: + void _send(uint16_t memberId,const void *msg,unsigned int len); + void _flush(uint16_t memberId); + + // These are initialized in the constructor and remain static + 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; + const int32_t _x; + const int32_t _y; + const int32_t _z; + const DistanceAlgorithm _da; + const uint16_t _id; + + 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; + + Buffer q; + + Mutex lock; + + _Member() : + lastReceivedFrom(0), + lastReceivedAliveAnnouncement(0), + lastSentTo(0), + lastAnnouncedAliveTo(0), + load(0), + x(0), + y(0), + z(0), + physicalAddressCount(0) {} + + ~_Member() { Utils::burn(key,sizeof(key)); } + }; + + _Member _members[65536]; // cluster IDs can be from 0 to 65535 (16-bit) + + std::vector _memberIds; + Mutex _memberIds_m; + + // Record tracking which members have which peers and how recently they claimed this + struct _PeerAffinity + { + _PeerAffinity(const Address &a,uint16_t mid,uint64_t ts) : + key((a.toInt() << 16) | (uint64_t)mid), + timestamp(ts) {} + + uint64_t key; + uint64_t timestamp; + + inline Address address() const throw() { return Address(key >> 16); } + inline uint16_t clusterMemberId() const throw() { return (uint16_t)(key & 0xffff); } + + inline bool operator<(const _PeerAffinity &pi) const throw() { return (key < pi.key); } + }; + + // A memory-efficient packed map of _PeerAffinity records searchable with std::binary_search() and std::lower_bound() + std::vector<_PeerAffinity> _peerAffinities; + Mutex _peerAffinities_m; +}; + +} // namespace ZeroTier + +#endif // ZT_ENABLE_CLUSTER + +#endif diff --git a/node/Constants.hpp b/node/Constants.hpp index 9ab390eb6..afcb4e306 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -182,7 +182,7 @@ #define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 1000 /** - * Length of secret key in bytes -- 256-bit for Salsa20 + * Length of secret key in bytes -- 256-bit -- do not change */ #define ZT_PEER_SECRET_KEY_LENGTH 32 diff --git a/node/Identity.hpp b/node/Identity.hpp index 19bb2e1fe..6c33e74f3 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -38,6 +38,7 @@ #include "Address.hpp" #include "C25519.hpp" #include "Buffer.hpp" +#include "SHA512.hpp" namespace ZeroTier { @@ -91,8 +92,7 @@ public: } template - Identity(const Buffer &b,unsigned int startAt = 0) - throw(std::out_of_range,std::invalid_argument) : + Identity(const Buffer &b,unsigned int startAt = 0) : _privateKey((C25519::Private *)0) { deserialize(b,startAt); @@ -137,6 +137,21 @@ public: */ inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); } + /** + * Compute the SHA512 hash of our private key (if we have one) + * + * @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length) + * @return True on success, false if no private key + */ + inline bool sha512PrivateKey(void *sha) const + { + if (_privateKey) { + SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN); + return true; + } + return false; + } + /** * Sign a message with this identity (private key required) * diff --git a/node/Packet.hpp b/node/Packet.hpp index e03190a25..0e8426b68 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -623,6 +623,10 @@ public: * may also ignore these messages if a peer is not known or is not being * actively communicated with. * + * Unfortunately the physical address format in this message pre-dates + * InetAddress's serialization format. :( ZeroTier is four years old and + * yes we've accumulated a tiny bit of cruft here and there. + * * No OK or ERROR is generated. */ VERB_RENDEZVOUS = 5, diff --git a/node/Peer.hpp b/node/Peer.hpp index cabf0793d..0139f3862 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -560,7 +560,8 @@ private: void _sortPaths(const uint64_t now); RemotePath *_getBestPath(const uint64_t now); - unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; + unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized + uint64_t _lastUsed; uint64_t _lastReceive; // direct or indirect uint64_t _lastUnicastFrame; diff --git a/node/Topology.cpp b/node/Topology.cpp index 8b5deffc8..2b9733863 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -136,7 +136,7 @@ SharedPtr Topology::addPeer(const SharedPtr &peer) SharedPtr &p = _peers.set(peer->address(),peer); p->use(now); - _saveIdentity(p->identity()); + saveIdentity(p->identity()); return p; } @@ -172,6 +172,26 @@ SharedPtr Topology::getPeer(const Address &zta) return SharedPtr(); } +Identity Topology::getIdentity(const Address &zta) +{ + { + Mutex::Lock _l(_lock); + SharedPtr &ap = _peers[zta]; + if (ap) + return ap->identity(); + } + return _getIdentity(zta); +} + +void saveIdentity(const Identity &id) +{ + if (id) { + char p[128]; + Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt()); + RR->node->dataStorePut(p,id.toString(false),false); + } +} + SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid) { SharedPtr bestRoot; @@ -315,15 +335,6 @@ Identity Topology::_getIdentity(const Address &zta) return Identity(); } -void Topology::_saveIdentity(const Identity &id) -{ - if (id) { - char p[128]; - Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt()); - RR->node->dataStorePut(p,id.toString(false),false); - } -} - void Topology::_setWorld(const World &newWorld) { // assumed _lock is locked (or in constructor) diff --git a/node/Topology.hpp b/node/Topology.hpp index 6f0170f00..9e9ccc011 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -78,6 +78,24 @@ public: */ SharedPtr getPeer(const Address &zta); + /** + * Get the identity of a peer + * + * @param zta ZeroTier address of peer + * @return Identity or NULL Identity if not found + */ + Identity getIdentity(const Address &zta); + + /** + * Cache an identity + * + * This is done automatically on addPeer(), and so is only useful for + * cluster identity replication. + * + * @param id Identity to cache + */ + void saveIdentity(const Identity &id); + /** * @return Vector of peers that are root servers */ @@ -210,7 +228,6 @@ public: private: Identity _getIdentity(const Address &zta); - void _saveIdentity(const Identity &id); void _setWorld(const World &newWorld); const RuntimeEnvironment *RR; diff --git a/objects.mk b/objects.mk index 64e5cfa76..10e0c334c 100644 --- a/objects.mk +++ b/objects.mk @@ -4,6 +4,7 @@ OBJS=\ ext/http-parser/http_parser.o \ node/C25519.o \ node/CertificateOfMembership.o \ + node/Cluster.o \ node/Dictionary.o \ node/Identity.o \ node/IncomingPacket.o \ From 59389b3dcefa00541ec923ab091d400b941d6c47 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 14:17:55 -0700 Subject: [PATCH 074/195] Untested cluster code, not enabled. --- make-mac.mk | 2 +- node/Cluster.cpp | 8 ++++++-- node/Topology.cpp | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/make-mac.mk b/make-mac.mk index e53212c0e..174216fb1 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -6,7 +6,7 @@ ifeq ($(origin CXX),default) endif INCLUDES= -DEFS=-DZT_ENABLE_CLUSTER +DEFS= LIBS= ARCH_FLAGS=-arch x86_64 diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 98b452654..8a942cb0a 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -195,8 +195,12 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); if (destinationPeer) { - RemotePath *destinationPath = destinationPeer->send(RR,packet,packetLen,RR->node->now()); - if ((destinationPath)&&(numRemotePeerPaths > 0)&&(packetLen >= 18)&&(reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR)) { + if ( + (destinationPeer->send(RR,packet,packetLen,RR->node->now()))&& + (numRemotePeerPaths > 0)&& + (packetLen >= 18)&& + (reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) + ) { // If remote peer paths were sent with this relayed packet, we do // RENDEZVOUS. It's handled here for cluster-relayed packets since // we don't have both Peer records so this is a different path. diff --git a/node/Topology.cpp b/node/Topology.cpp index 2b9733863..a35585582 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -183,7 +183,7 @@ Identity Topology::getIdentity(const Address &zta) return _getIdentity(zta); } -void saveIdentity(const Identity &id) +void Topology::saveIdentity(const Identity &id) { if (id) { char p[128]; From 9ece8c465e4d98770e24f08af97fa1b380e7d9e0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 15:49:41 -0700 Subject: [PATCH 075/195] decrypt fix --- node/Cluster.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 8a942cb0a..c08bf0020 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -108,8 +108,8 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) return; // Decrypt! - dmsg.setSize(len - 16); - s20.decrypt12(reinterpret_cast(msg) + 16,const_cast(dmsg.data()),dmsg.size()); + dmsg.setSize(len - 24); + s20.decrypt12(reinterpret_cast(msg) + 24,const_cast(dmsg.data()),dmsg.size()); } if (dmsg.size() < 2) @@ -343,6 +343,7 @@ void Cluster::addMember(uint16_t memberId) Utils::getSecureRandom(iv,16); _members[memberId].q.append(iv,16); _members[memberId].q.addSize(8); // room for MAC + _members[memberId].q.append((uint16_t)_id); } void Cluster::_send(uint16_t memberId,const void *msg,unsigned int len) @@ -363,7 +364,7 @@ void Cluster::_flush(uint16_t memberId) { _Member &m = _members[memberId]; // assumes m.lock is locked! - if (m.q.size() > 24) { + if (m.q.size() > 26) { // 16-byte IV + 8-byte MAC + 2-byte cluster member ID (latter two bytes are inside crypto envelope) // Create key from member's key and IV char keytmp[32]; memcpy(keytmp,m.key,32); @@ -394,6 +395,7 @@ 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); } } From a775ee7d315fd960f472a6e655c8fda92e53f44a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 14 Oct 2015 16:21:39 -0700 Subject: [PATCH 076/195] . --- make-linux.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make-linux.mk b/make-linux.mk index 2ef9b9850..5e0a20726 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -29,7 +29,7 @@ endif UNAME_M=$(shell uname -m) INCLUDES= -DEFS= +DEFS=-DZT_ENABLE_CLUSTER LDLIBS?= include objects.mk From 2debde3451838f62ed9b53d9b0086f7112416636 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 15 Oct 2015 07:22:17 -0700 Subject: [PATCH 077/195] GitHub issue #235, and I also see no reason not to communicate with people from other Worlds. --- node/Constants.hpp | 5 +++++ node/IncomingPacket.cpp | 31 +++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index afcb4e306..a60b76eba 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -347,6 +347,11 @@ */ #define ZT_MAX_BRIDGE_SPAM 16 +/** + * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) + */ +#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 8 + /** * A test pseudo-network-ID that can be joined * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 944e3043e..d444258df 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -296,22 +296,18 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); _remoteAddress.serialize(outp); - 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 + 2))); - } else { - outp.append((uint16_t)0); // no world update needed - } - - outp.armor(peer->key(),true); - RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + if ((worldId != ZT_WORLD_ID_NULL)&&(RR->topology->worldTimestamp() > worldTimestamp)&&(worldId == RR->topology->worldId())) { + 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 + 2))); } 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()); + outp.append((uint16_t)0); // no world update needed } + + outp.armor(peer->key(),true); + RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } catch ( ... ) { TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -867,6 +863,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha try { unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + unsigned int v4Count = 0,v6Count = 0; while (count--) { // if ptr overflows Buffer will throw // TODO: some flags are not yet implemented @@ -882,14 +879,16 @@ 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()); - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + if (v4Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) + 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()); - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + if (v6Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) + peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); } } break; } From 2229e91b57676c1218b550749a2108372e0f37ad Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:10:12 -0700 Subject: [PATCH 078/195] IPv6 support fixes. --- node/Node.cpp | 39 +++++++++++++++++++++++++++++---------- node/Peer.cpp | 51 +++++++++++++++++++++++++++++++++++++-------------- node/Peer.hpp | 8 +++++--- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/node/Node.cpp b/node/Node.cpp index d72bc73fd..5aa4b7d31 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 697ba75d7..becc77f93 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()); - _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); - } + if (inetAddressFamily != 0) { + p = _getBestPath(now,inetAddressFamily); + } else { + p = _getBestPath(now); } - return bestPath; + + 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(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); + p->sent(now); + } + return true; + } + 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 0139f3862..4fb399c5f 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 From 5ce3aac929ef217f3e813b5bc948dd28d021835f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:28:09 -0700 Subject: [PATCH 079/195] Add rate limit on receive of DIRECT_PATH_PUSH to prevent DOS exploitation. --- node/Constants.hpp | 7 ++++++- node/IncomingPacket.cpp | 7 +++++++ node/Peer.cpp | 7 ++++--- node/Peer.hpp | 32 ++++++++++++++++++++++---------- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index a60b76eba..e45602f72 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -322,7 +322,12 @@ /** * Interval between direct path pushes in milliseconds */ -#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000 +#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 + +/** + * Minimum interval between direct path pushes from a given peer or we will ignore them + */ +#define ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL 2500 /** * How long (max) to remember network certificates of membership? diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index d444258df..4386e3708 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -861,6 +861,13 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer) { try { + const uint64_t now = RR->node->now(); + if ((now - peer->lastDirectPathPushReceived()) >= ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): too frequent!",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + peer->setLastDirectPathPushReceived(now); + unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; unsigned int v4Count = 0,v6Count = 0; diff --git a/node/Peer.cpp b/node/Peer.cpp index becc77f93..8c5c57831 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -52,7 +52,8 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastMulticastFrame(0), _lastAnnouncedTo(0), _lastPathConfirmationSent(0), - _lastDirectPathPush(0), + _lastDirectPathPushSent(0), + _lastDirectPathPushReceived(0), _lastPathSort(0), _vMajor(0), _vMinor(0), @@ -210,8 +211,8 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_ { Mutex::Lock _l(_lock); - if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { - _lastDirectPathPush = now; + if (((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { + _lastDirectPathPushSent = now; std::vector dps(RR->node->directPaths()); if (dps.empty()) diff --git a/node/Peer.hpp b/node/Peer.hpp index 4fb399c5f..043519d4d 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -408,6 +408,16 @@ public: */ bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime); + /** + * @return Time last direct path push was received + */ + inline uint64_t lastDirectPathPushReceived() const throw() { return _lastDirectPathPushReceived; } + + /** + * @param t New time of last direct path push received + */ + inline void setLastDirectPathPushReceived(uint64_t t) throw() { _lastDirectPathPushReceived = t; } + /** * Perform periodic cleaning operations */ @@ -438,10 +448,10 @@ public: { Mutex::Lock _l(_lock); - const unsigned int atPos = b.size(); + const unsigned int recSizePos = b.size(); b.addSize(4); // space for uint32_t field length - b.append((uint32_t)1); // version of serialized Peer data + b.append((uint16_t)1); // version of serialized Peer data _id.serialize(b,false); @@ -451,7 +461,8 @@ public: b.append((uint64_t)_lastMulticastFrame); b.append((uint64_t)_lastAnnouncedTo); b.append((uint64_t)_lastPathConfirmationSent); - b.append((uint64_t)_lastDirectPathPush); + b.append((uint64_t)_lastDirectPathPushSent); + b.append((uint64_t)_lastDirectPathPushReceived); b.append((uint64_t)_lastPathSort); b.append((uint16_t)_vProto); b.append((uint16_t)_vMajor); @@ -486,7 +497,7 @@ public: } } - b.setAt(atPos,(uint32_t)(b.size() - atPos)); // set size + b.setAt(recSizePos,(uint32_t)((b.size() - 4) - recSizePos)); // set size } /** @@ -500,13 +511,12 @@ public: template static inline SharedPtr deserializeNew(const Identity &myIdentity,const Buffer &b,unsigned int &p) { - const uint32_t recSize = b.template at(p); + const uint32_t recSize = b.template at(p); p += 4; if ((p + recSize) > b.size()) return SharedPtr(); // size invalid - p += 4; - if (b.template at(p) != 1) + if (b.template at(p) != 1) return SharedPtr(); // version mismatch - p += 4; + p += 2; Identity npid; p += npid.deserialize(b,p); @@ -521,7 +531,8 @@ public: np->_lastMulticastFrame = b.template at(p); p += 8; np->_lastAnnouncedTo = b.template at(p); p += 8; np->_lastPathConfirmationSent = b.template at(p); p += 8; - np->_lastDirectPathPush = b.template at(p); p += 8; + np->_lastDirectPathPushSent = b.template at(p); p += 8; + np->_lastDirectPathPushReceived = b.template at(p); p += 8; np->_lastPathSort = b.template at(p); p += 8; np->_vProto = b.template at(p); p += 2; np->_vMajor = b.template at(p); p += 2; @@ -570,7 +581,8 @@ private: uint64_t _lastMulticastFrame; uint64_t _lastAnnouncedTo; uint64_t _lastPathConfirmationSent; - uint64_t _lastDirectPathPush; + uint64_t _lastDirectPathPushSent; + uint64_t _lastDirectPathPushReceived; uint64_t _lastPathSort; uint16_t _vProto; uint16_t _vMajor; From f9f60f89d95e43e5c439bf7c86b015e1b41a2f14 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:45:58 -0700 Subject: [PATCH 080/195] Peer save/restore fix. --- node/Peer.hpp | 8 ++++---- node/Topology.cpp | 31 ++++++++++--------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/node/Peer.hpp b/node/Peer.hpp index 043519d4d..7e7e7f465 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -470,7 +470,7 @@ public: b.append((uint16_t)_vRevision); b.append((uint32_t)_latency); - b.append((uint32_t)_numPaths); + b.append((uint16_t)_numPaths); for(unsigned int i=0;i<_numPaths;++i) _paths[i].serialize(b); @@ -497,7 +497,7 @@ public: } } - b.setAt(recSizePos,(uint32_t)((b.size() - 4) - recSizePos)); // set size + b.template setAt(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size } /** @@ -511,7 +511,7 @@ public: template static inline SharedPtr deserializeNew(const Identity &myIdentity,const Buffer &b,unsigned int &p) { - const uint32_t recSize = b.template at(p); p += 4; + const unsigned int recSize = b.template at(p); p += 4; if ((p + recSize) > b.size()) return SharedPtr(); // size invalid if (b.template at(p) != 1) @@ -540,7 +540,7 @@ public: np->_vRevision = b.template at(p); p += 2; np->_latency = b.template at(p); p += 4; - const unsigned int numPaths = b.template at(p); p += 4; + const unsigned int numPaths = b.template at(p); p += 2; for(unsigned int i=0;i_paths[np->_numPaths++].deserialize(b,p); diff --git a/node/Topology.cpp b/node/Topology.cpp index a35585582..6e8467c1a 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -47,31 +47,20 @@ Topology::Topology(const RuntimeEnvironment *renv) : unsigned int ptr = 0; while ((ptr + 4) < alls.size()) { - // Each Peer serializes itself prefixed by a record length (not including the size of the length itself) - unsigned int reclen = (unsigned int)all[ptr] & 0xff; - reclen <<= 8; - reclen |= (unsigned int)all[ptr + 1] & 0xff; - reclen <<= 8; - reclen |= (unsigned int)all[ptr + 2] & 0xff; - reclen <<= 8; - reclen |= (unsigned int)all[ptr + 3] & 0xff; - - if (((ptr + reclen) > alls.size())||(reclen > ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE)) - break; - try { + const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length + ((((unsigned int)all[ptr]) & 0xff) << 24) | + ((((unsigned int)all[ptr + 1]) & 0xff) << 16) | + ((((unsigned int)all[ptr + 2]) & 0xff) << 8) | + (((unsigned int)all[ptr + 3]) & 0xff) + ); unsigned int pos = 0; - SharedPtr p(Peer::deserializeNew(RR->identity,Buffer(all + ptr,reclen),pos)); - if (pos != reclen) - break; + SharedPtr p(Peer::deserializeNew(RR->identity,Buffer(all + ptr,reclen + 4),pos)); ptr += pos; - if ((p)&&(p->address() != RR->identity.address())) { - _peers[p->address()] = p; - } else { + if (!p) break; // stop if invalid records - } - } catch (std::exception &exc) { - break; + if (p->address() != RR->identity.address()) + _peers[p->address()] = p; } catch ( ... ) { break; // stop if invalid records } From 781f06ef82033cdf2488f80266b3f7a8497eba69 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:48:38 -0700 Subject: [PATCH 081/195] Accept OK for confirm of HELLO or ECHO. --- node/Peer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index 8c5c57831..76b1d537f 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -101,9 +101,9 @@ void Peer::received( } if (!pathIsConfirmed) { - if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) { + if ((verb == Packet::VERB_OK)&&((inReVerb == Packet::VERB_HELLO)||(inReVerb == Packet::VERB_ECHO))) { - // Learn paths if they've been confirmed via a HELLO + // Learn paths if they've been confirmed via a HELLO or an ECHO RemotePath *slot = (RemotePath *)0; if (np < ZT_MAX_PEER_NETWORK_PATHS) { // Add new path From cc4d0199e7aa56d08e91dd9e21bbaba318afa8a4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 10:58:59 -0700 Subject: [PATCH 082/195] Fix vProto init. --- node/Peer.cpp | 1 + node/Peer.hpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index 76b1d537f..fdc8dca71 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -55,6 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastDirectPathPushSent(0), _lastDirectPathPushReceived(0), _lastPathSort(0), + _vProto(0), _vMajor(0), _vMinor(0), _vRevision(0), diff --git a/node/Peer.hpp b/node/Peer.hpp index 7e7e7f465..0aca70b4f 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -330,7 +330,6 @@ public: */ inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev) { - Mutex::Lock _l(_lock); _vProto = (uint16_t)vproto; _vMajor = (uint16_t)vmaj; _vMinor = (uint16_t)vmin; From 738fa5a5e5fc99512465032aec5916867f70e86b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 16 Oct 2015 12:10:57 -0700 Subject: [PATCH 083/195] . --- node/Cluster.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 13d9e2f50..01db3641c 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -61,6 +61,11 @@ */ #define ZT_CLUSTER_MEMBER_MAX_PHYSICAL_ADDRS 8 +/** + * How frequently should doPeriodicTasks() be ideally called? (ms) + */ +#define ZT_CLUSTER_PERIODIC_TASK_DEADLINE 10 + namespace ZeroTier { class RuntimeEnvironment; @@ -238,7 +243,7 @@ public: void replicateCertificateOfNetworkMembership(const CertificateOfMembership &com); /** - * This should be called no less frequently than once every 10 seconds. + * Call every ~ZT_CLUSTER_PERIODIC_TASK_DEADLINE milliseconds. */ void doPeriodicTasks(); From 0c43d34ce3294ca581f8918904c035e9d57aa72d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 10:00:35 -0700 Subject: [PATCH 084/195] World test stuff... some of this will be yanked before release. --- world/README.md | 4 +- world/alice-test/build.sh | 1 + world/alice-test/mkworld.cpp | 180 +++++++++++++++++++++++++++++++++++ world/build.sh | 1 + world/mkworld.cpp | 12 +-- 5 files changed, 189 insertions(+), 9 deletions(-) create mode 100755 world/alice-test/build.sh create mode 100644 world/alice-test/mkworld.cpp create mode 100755 world/build.sh diff --git a/world/README.md b/world/README.md index fbd8a0ae3..dda4920ae 100644 --- a/world/README.md +++ b/world/README.md @@ -3,7 +3,5 @@ World Definitions and Generator Code This little bit of code is used to generate world updates. Ordinary users probably will never need this unless they want to test or experiment. -See mkworld.cpp for documentation. To build from this directory: - - c++ -o mkworld ../node/C25519.cpp ../node/Salsa20.cpp ../node/SHA512.cpp ../node/Identity.cpp ../node/Utils.cpp ../node/InetAddress.cpp ../osdep/OSUtils.cpp mkworld.cpp +See mkworld.cpp for documentation. To build from this directory use 'source ./build.sh'. diff --git a/world/alice-test/build.sh b/world/alice-test/build.sh new file mode 100755 index 000000000..58f212118 --- /dev/null +++ b/world/alice-test/build.sh @@ -0,0 +1 @@ +c++ -I../.. -o mkworld ../../node/C25519.cpp ../../node/Salsa20.cpp ../../node/SHA512.cpp ../../node/Identity.cpp ../../node/Utils.cpp ../../node/InetAddress.cpp ../../osdep/OSUtils.cpp mkworld.cpp diff --git a/world/alice-test/mkworld.cpp b/world/alice-test/mkworld.cpp new file mode 100644 index 000000000..42f3ab726 --- /dev/null +++ b/world/alice-test/mkworld.cpp @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; + +#if 0 + // Old pre-October-2015 root server infrastructure with four independent single node roots -- it served us well! + // 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()); +#endif + + // ALICE TEST + roots.push_back(World::Root()); + roots.back().identity = Identity(""); + roots.back().stableEndpoints.push_back(InetAddress("169.57.143.104/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/world/build.sh b/world/build.sh new file mode 100755 index 000000000..b783702c3 --- /dev/null +++ b/world/build.sh @@ -0,0 +1 @@ +c++ -I.. -o mkworld ../node/C25519.cpp ../node/Salsa20.cpp ../node/SHA512.cpp ../node/Identity.cpp ../node/Utils.cpp ../node/InetAddress.cpp ../osdep/OSUtils.cpp mkworld.cpp diff --git a/world/mkworld.cpp b/world/mkworld.cpp index 75230da42..fd7bb51f6 100644 --- a/world/mkworld.cpp +++ b/world/mkworld.cpp @@ -50,12 +50,12 @@ #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" +#include +#include +#include +#include +#include +#include using namespace ZeroTier; From aa6e3c79a0d5942b289a6efc68c61667ce4059a7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 10:49:31 -0700 Subject: [PATCH 085/195] Some test stuff that will not be pushed elsewhere. --- node/Topology.cpp | 8 ++++++-- world/alice-test/alice-test.bin | Bin 0 -> 257 bytes world/alice-test/alice-test.out | 5 +++++ world/alice-test/current.c25519 | 3 +++ world/alice-test/mkworld.cpp | 2 +- world/alice-test/previous.c25519 | 3 +++ 6 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 world/alice-test/alice-test.bin create mode 100644 world/alice-test/alice-test.out create mode 100644 world/alice-test/current.c25519 create mode 100644 world/alice-test/previous.c25519 diff --git a/node/Topology.cpp b/node/Topology.cpp index 6e8467c1a..e36a06097 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -34,8 +34,12 @@ namespace ZeroTier { // Default World -#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}; +//#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}; + +// ALICE-TEST +#define ZT_DEFAULT_WORLD_LENGTH 257 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0x81,0x2a,0x54,0x6f,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0x63,0x8b,0xb3,0xb6,0x72,0x45,0xa9,0x81,0x81,0xcc,0xea,0x7f,0x2f,0xd9,0x59,0xce,0xc8,0x51,0x12,0xc3,0xe3,0x44,0x76,0x54,0xed,0xe7,0x8d,0x34,0x0b,0x5d,0x10,0x3d,0x52,0x04,0x9b,0xe1,0xb2,0x36,0x51,0x75,0x14,0x30,0x53,0xe8,0x4b,0xe4,0x91,0x9a,0xed,0x99,0x56,0xa3,0x8d,0x5e,0x14,0xff,0x66,0xd8,0x4f,0xf7,0x3c,0x23,0xbe,0x02,0xbb,0x1e,0xb6,0x7e,0x07,0xfa,0x7c,0x7e,0x50,0xe8,0x40,0xf9,0x37,0x70,0x1a,0x75,0xcf,0x19,0xe6,0x83,0xe1,0x5c,0x20,0x1d,0x1e,0x5b,0xe5,0x6a,0xbe,0xe7,0xab,0xec,0x01,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x01,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09}; Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), diff --git a/world/alice-test/alice-test.bin b/world/alice-test/alice-test.bin new file mode 100644 index 0000000000000000000000000000000000000000..2fdaf7b3066231ea0663e5da6f566610b3e43ad3 GIT binary patch literal 257 zcmV+c0sj60000002*%QPe+bDp-91C znGp`>8)R5*+7XUGBCBI)Ea5lWi>Md;e{-raWAfuOlRhbw#A{DM88Cb3g<5g|0R*Wz Hk7y?eM5=X^ literal 0 HcmV?d00001 diff --git a/world/alice-test/alice-test.out b/world/alice-test/alice-test.out new file mode 100644 index 000000000..20077bf2e --- /dev/null +++ b/world/alice-test/alice-test.out @@ -0,0 +1,5 @@ +INFO: generating and signing id==149604618 ts==1445276046447 +INFO: wrote 257 bytes to stdout + +#define ZT_DEFAULT_WORLD_LENGTH 257 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0x81,0x2a,0x54,0x6f,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0x63,0x8b,0xb3,0xb6,0x72,0x45,0xa9,0x81,0x81,0xcc,0xea,0x7f,0x2f,0xd9,0x59,0xce,0xc8,0x51,0x12,0xc3,0xe3,0x44,0x76,0x54,0xed,0xe7,0x8d,0x34,0x0b,0x5d,0x10,0x3d,0x52,0x04,0x9b,0xe1,0xb2,0x36,0x51,0x75,0x14,0x30,0x53,0xe8,0x4b,0xe4,0x91,0x9a,0xed,0x99,0x56,0xa3,0x8d,0x5e,0x14,0xff,0x66,0xd8,0x4f,0xf7,0x3c,0x23,0xbe,0x02,0xbb,0x1e,0xb6,0x7e,0x07,0xfa,0x7c,0x7e,0x50,0xe8,0x40,0xf9,0x37,0x70,0x1a,0x75,0xcf,0x19,0xe6,0x83,0xe1,0x5c,0x20,0x1d,0x1e,0x5b,0xe5,0x6a,0xbe,0xe7,0xab,0xec,0x01,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x01,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09}; diff --git a/world/alice-test/current.c25519 b/world/alice-test/current.c25519 new file mode 100644 index 000000000..a3e6d5968 --- /dev/null +++ b/world/alice-test/current.c25519 @@ -0,0 +1,3 @@ +r°;¾sÚ½û…wŸÉ.ÈnÚa€ÑAË|-+¤4ud € +"2òlþy¦}ì~ó¢ È|ýlVR¨ûÜû“>–“H6_¬±ŸýoFÑ@`ð¥5©þ+\ú&³•tå<þ2 +›Å£¡îÖŠjnã(fJÃç´®Uݸ†Î)¶™$oî \ No newline at end of file diff --git a/world/alice-test/mkworld.cpp b/world/alice-test/mkworld.cpp index 42f3ab726..e680b97ee 100644 --- a/world/alice-test/mkworld.cpp +++ b/world/alice-test/mkworld.cpp @@ -135,7 +135,7 @@ int main(int argc,char **argv) // ALICE TEST roots.push_back(World::Root()); - roots.back().identity = Identity(""); + roots.back().identity = Identity("d6ddca6ab5:0:4e761207d8b4200be44f478e3da148c16099110ee71b64586dda118e4022ab63682ce137da8ba817fc7f73aa3163f2e333933e2994c46b4f4119307be8855a72"); roots.back().stableEndpoints.push_back(InetAddress("169.57.143.104/9993")); std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); diff --git a/world/alice-test/previous.c25519 b/world/alice-test/previous.c25519 new file mode 100644 index 000000000..a3e6d5968 --- /dev/null +++ b/world/alice-test/previous.c25519 @@ -0,0 +1,3 @@ +r°;¾sÚ½û…wŸÉ.ÈnÚa€ÑAË|-+¤4ud € +"2òlþy¦}ì~ó¢ È|ýlVR¨ûÜû“>–“H6_¬±ŸýoFÑ@`ð¥5©þ+\ú&³•tå<þ2 +›Å£¡îÖŠjnã(fJÃç´®Uݸ†Î)¶™$oî \ No newline at end of file From 95953b48f963213a803b230e2d83416257716e65 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 12:56:29 -0700 Subject: [PATCH 086/195] Do not allow VERB_RENDEZVOUS from non-upstream peers to block potential DOS vector. --- node/IncomingPacket.cpp | 29 +++++++++++++++++------------ node/Topology.cpp | 19 +++++++++++++++++++ node/Topology.hpp | 15 +++++++-------- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 4386e3708..6b39963ab 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -461,21 +461,26 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr &peer) { try { - const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - const SharedPtr withPeer(RR->topology->getPeer(with)); - if (withPeer) { - const unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); - const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; - if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { - InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); - TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); - peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP); - RR->sw->rendezvous(withPeer,_localAddress,atAddr); + if (RR->topology->isUpstream(peer->identity())) { + const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + const SharedPtr withPeer(RR->topology->getPeer(with)); + if (withPeer) { + const unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); + const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; + if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { + InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); + TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); + peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP); + RR->sw->rendezvous(withPeer,_localAddress,atAddr); + } else { + TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + } } else { - TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); + RR->sw->requestWhois(with); + TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str()); } } else { - TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str()); + TRACE("ignored RENDEZVOUS from %s(%s): not a root server or a network relay",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } } catch ( ... ) { TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); diff --git a/node/Topology.cpp b/node/Topology.cpp index e36a06097..dc21b2430 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -29,6 +29,8 @@ #include "Topology.hpp" #include "RuntimeEnvironment.hpp" #include "Node.hpp" +#include "Network.hpp" +#include "NetworkConfig.hpp" #include "Buffer.hpp" namespace ZeroTier { @@ -283,6 +285,23 @@ keep_searching_for_roots: return bestRoot; } +bool Topology::isUpstream(const Identity &id) const +{ + if (isRoot(id)) + return true; + std::vector< SharedPtr > nws(RR->node->allNetworks()); + for(std::vector< SharedPtr >::const_iterator nw(nws.begin());nw!=nws.end();++nw) { + SharedPtr nc((*nw)->config2()); + if (nc) { + for(std::vector< std::pair >::const_iterator r(nc->relays().begin());r!=nc->relays().end();++r) { + if (r->first == id.address()) + return true; + } + } + } + return false; +} + bool Topology::worldUpdateIfValid(const World &newWorld) { Mutex::Lock _l(_lock); diff --git a/node/Topology.hpp b/node/Topology.hpp index 9e9ccc011..48e264a87 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -136,16 +136,15 @@ public: inline bool isRoot(const Identity &id) const { Mutex::Lock _l(_lock); - if (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end()) { - // Double check full identity for security reasons - for(std::vector::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) { - if (id == r->identity) - return true; - } - } - return false; + return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end()); } + /** + * @param id Identity to check + * @return True if this is a root server or a network preferred relay from one of our networks + */ + bool isUpstream(const Identity &id) const; + /** * @return Vector of root server addresses */ From 3adb183c5f76b69013d052383c4b812e3947041e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 13:38:27 -0700 Subject: [PATCH 087/195] Fix bad COM attachment bug and eliminate an unnecessary redundant check. --- node/IncomingPacket.cpp | 6 ++---- node/Switch.cpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 6b39963ab..19747bbd9 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -529,15 +529,13 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndSetNetworkMembershipCertificate(RR,network->id(),com)) - comFailed = true; + peer->validateAndSetNetworkMembershipCertificate(RR,network->id(),com); } - if ((comFailed)||(!network->isAllowed(peer))) { + if (!network->isAllowed(peer)) { TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; diff --git a/node/Switch.cpp b/node/Switch.cpp index 9ea8ac49a..249a21d55 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -203,7 +203,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this SharedPtr toPeer(RR->topology->getPeer(toZT)); - const bool includeCom = ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true)));; + const bool includeCom = ( (nconf->isPrivate()) && (nconf->com()) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ); if ((fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); @@ -271,7 +271,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c SharedPtr bridgePeer(RR->topology->getPeer(bridges[b])); Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); - if ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) { + if ( (nconf->isPrivate()) && (nconf->com()) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) { outp.append((unsigned char)0x01); // 0x01 -- COM included nconf->com().serialize(outp); } else { From 584072fa6a86b7cca0a708d7dd4c302aa444f2b6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 14:04:36 -0700 Subject: [PATCH 088/195] Fix for V4/V6 stable addressing. --- node/Node.cpp | 32 ++++++++++++++++++++++++++------ node/Peer.cpp | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/node/Node.cpp b/node/Node.cpp index 5aa4b7d31..26d5513e1 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 fdc8dca71..a9d2f671a 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; } From 50f3ccd3c9630dfc0169973573f5b3729524441c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 15:03:58 -0700 Subject: [PATCH 089/195] . --- node/Peer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/Peer.cpp b/node/Peer.cpp index a9d2f671a..e04081565 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -202,6 +202,8 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet _natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); p->sent(now); + } else { + TRACE("no PING or NAT keepalive: %llums/%llums send/receive inactivity",now - p->lastSend(),now - p->lastReceived()) } return true; } From 915077875759f1040b8bba281ef3d4f82e4ac1ab Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 15:04:26 -0700 Subject: [PATCH 090/195] . --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index e04081565..ba6ef4af8 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -203,7 +203,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); p->sent(now); } else { - TRACE("no PING or NAT keepalive: %llums/%llums send/receive inactivity",now - p->lastSend(),now - p->lastReceived()) + TRACE("no PING or NAT keepalive: %llums/%llums send/receive inactivity",now - p->lastSend(),now - p->lastReceived()); } return true; } From 0b2e5ed499aea0076caa2dc1d8784796e54da538 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 15:11:43 -0700 Subject: [PATCH 091/195] Fix some broken logic in Path::reliable() --- node/Path.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/node/Path.hpp b/node/Path.hpp index 6a69e071f..6fa3c52e6 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -114,7 +114,9 @@ public: */ inline bool reliable() const throw() { - return ( (_addr.ss_family == AF_INET6) || ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE)) ); + if (_addr.ss_family == AF_INET) + return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE)); + return true; } /** From cfdcce6d1299b4bc407b9b44c0abb55c5ea9f4b5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 15:19:04 -0700 Subject: [PATCH 092/195] Fix very obscure IP scope classification logic bug. --- node/InetAddress.cpp | 12 +++++------- node/Peer.cpp | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index e542f0d4e..abb46240f 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -77,14 +77,12 @@ InetAddress::IpScope InetAddress::ipScope() const if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE; // 192.168.0.0/16 break; case 0xff: return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable) - default: - switch(ip >> 28) { - case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4 - case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable) - default: return IP_SCOPE_GLOBAL; // everything else - } - break; } + switch(ip >> 28) { + case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4 + case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable) + } + return IP_SCOPE_GLOBAL; } break; case AF_INET6: { diff --git a/node/Peer.cpp b/node/Peer.cpp index ba6ef4af8..6f566be42 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -203,7 +203,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); p->sent(now); } else { - TRACE("no PING or NAT keepalive: %llums/%llums send/receive inactivity",now - p->lastSend(),now - p->lastReceived()); + TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived()); } return true; } From 69dad37d8f6fdc1ca57077b1fecdc64b23254588 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 15:23:43 -0700 Subject: [PATCH 093/195] Restore default World for commit to upstream --- node/Topology.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index dc21b2430..56ca47c84 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -36,12 +36,12 @@ namespace ZeroTier { // Default World -//#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}; +#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}; // ALICE-TEST -#define ZT_DEFAULT_WORLD_LENGTH 257 -static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0x81,0x2a,0x54,0x6f,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0x63,0x8b,0xb3,0xb6,0x72,0x45,0xa9,0x81,0x81,0xcc,0xea,0x7f,0x2f,0xd9,0x59,0xce,0xc8,0x51,0x12,0xc3,0xe3,0x44,0x76,0x54,0xed,0xe7,0x8d,0x34,0x0b,0x5d,0x10,0x3d,0x52,0x04,0x9b,0xe1,0xb2,0x36,0x51,0x75,0x14,0x30,0x53,0xe8,0x4b,0xe4,0x91,0x9a,0xed,0x99,0x56,0xa3,0x8d,0x5e,0x14,0xff,0x66,0xd8,0x4f,0xf7,0x3c,0x23,0xbe,0x02,0xbb,0x1e,0xb6,0x7e,0x07,0xfa,0x7c,0x7e,0x50,0xe8,0x40,0xf9,0x37,0x70,0x1a,0x75,0xcf,0x19,0xe6,0x83,0xe1,0x5c,0x20,0x1d,0x1e,0x5b,0xe5,0x6a,0xbe,0xe7,0xab,0xec,0x01,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x01,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09}; +//#define ZT_DEFAULT_WORLD_LENGTH 257 +//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0x81,0x2a,0x54,0x6f,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0x63,0x8b,0xb3,0xb6,0x72,0x45,0xa9,0x81,0x81,0xcc,0xea,0x7f,0x2f,0xd9,0x59,0xce,0xc8,0x51,0x12,0xc3,0xe3,0x44,0x76,0x54,0xed,0xe7,0x8d,0x34,0x0b,0x5d,0x10,0x3d,0x52,0x04,0x9b,0xe1,0xb2,0x36,0x51,0x75,0x14,0x30,0x53,0xe8,0x4b,0xe4,0x91,0x9a,0xed,0x99,0x56,0xa3,0x8d,0x5e,0x14,0xff,0x66,0xd8,0x4f,0xf7,0x3c,0x23,0xbe,0x02,0xbb,0x1e,0xb6,0x7e,0x07,0xfa,0x7c,0x7e,0x50,0xe8,0x40,0xf9,0x37,0x70,0x1a,0x75,0xcf,0x19,0xe6,0x83,0xe1,0x5c,0x20,0x1d,0x1e,0x5b,0xe5,0x6a,0xbe,0xe7,0xab,0xec,0x01,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x01,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09}; Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), From 5e6eae620bec49086e2ad80c119f3386d84092b1 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 19 Oct 2015 16:18:57 -0700 Subject: [PATCH 094/195] Make _members dynamically allocated due to static array limit on ARM. --- node/Cluster.cpp | 4 +++- node/Cluster.hpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index c08bf0020..bfa39d225 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -56,7 +56,8 @@ Cluster::Cluster(const RuntimeEnvironment *renv,uint16_t id,DistanceAlgorithm da _y(y), _z(z), _da(da), - _id(id) + _id(id), + _members(new _Member[65536]) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -76,6 +77,7 @@ Cluster::~Cluster() { Utils::burn(_masterSecret,sizeof(_masterSecret)); Utils::burn(_key,sizeof(_key)); + delete [] _members; } void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 01db3641c..016730e32 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -303,7 +303,7 @@ private: ~_Member() { Utils::burn(key,sizeof(key)); } }; - _Member _members[65536]; // cluster IDs can be from 0 to 65535 (16-bit) + _Member *const _members; // cluster IDs can be from 0 to 65535 (16-bit) std::vector _memberIds; Mutex _memberIds_m; From 57e29857cf79019af03f6a3dfe0bf6fd36e2fab2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 15:27:53 -0700 Subject: [PATCH 095/195] Cluster work -- integrating with the rest of the code. --- include/ZeroTierOne.h | 121 +++++++++++++++++++++- make-mac.mk | 2 +- node/Cluster.cpp | 193 ++++++++++++++++++++++++++++++++---- node/Cluster.hpp | 116 ++++++++++------------ node/IncomingPacket.cpp | 14 ++- node/Node.cpp | 155 +++++++++++++++++++++++++++-- node/Node.hpp | 14 +++ node/Peer.cpp | 6 ++ node/RuntimeEnvironment.hpp | 24 +++-- 9 files changed, 538 insertions(+), 107 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 42c904ebf..135c8e114 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 174216fb1..e53212c0e 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 bfa39d225..5b76d1f02 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 016730e32..f253e6f6a 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). + * Desired period between doPeriodicTasks() in milliseconds */ -#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) - */ -#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 19747bbd9..a4d450687 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 26d5513e1..6eea3d3df 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 c7038ed40..b8bd4dc54 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 6f566be42..0ba379c6f 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 e5d1f446b..2ec88f723 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 From eb79d4a2f34b34c49cd2d69efac22d9bc8ac27cb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 16:24:21 -0700 Subject: [PATCH 096/195] Wire up peer announcement in cluster. --- node/Cluster.cpp | 72 +++++++++++++++++++++++++++++++++-------- node/Cluster.hpp | 10 ++++-- node/IncomingPacket.cpp | 1 - node/Peer.cpp | 7 +++- node/Topology.cpp | 27 +++++++++------- 5 files changed, 87 insertions(+), 30 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 5b76d1f02..f1dc45b96 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -263,8 +263,6 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) remotePeerAddress.appendTo(rendezvousForDest); Buffer<2048> rendezvousForOtherEnd; - rendezvousForOtherEnd.addSize(2); // leave room for payload size - rendezvousForOtherEnd.append((uint8_t)STATE_MESSAGE_PROXY_SEND); remotePeerAddress.appendTo(rendezvousForOtherEnd); rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS); const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size(); @@ -298,9 +296,8 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } if (haveMatch) { + _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); RR->sw->send(rendezvousForDest,true,0); - rendezvousForOtherEnd.setAt(0,(uint16_t)(rendezvousForOtherEnd.size() - 2)); - _send(fromMemberId,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); } } } @@ -331,14 +328,64 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) void Cluster::replicateHavePeer(const Identity &peerId) { + { // Use peer affinity table to track our own last announce time for peers + _PeerAffinity pa(peerId.address(),_id,RR->node->now()); + Mutex::Lock _l2(_peerAffinities_m); + std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) + if ((i != _peerAffinities.end())&&(i->key == pa.key)) { + if ((pa.timestamp - i->timestamp) >= ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) { + i->timestamp = pa.timestamp; + // continue to announcement + } else { + // we've already announced this peer recently, so skip + return; + } + } else { + _peerAffinities.push_back(pa); + std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now + // continue to announcement + } + } + + // announcement + Buffer<4096> buf; + peerId.serialize(buf,false); + { + 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()); + } + } } void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group) { + Buffer<4096> buf; + buf.append((uint64_t)nwid); + peerAddress.appendTo(buf); + group.mac().appendTo(buf); + buf.append((uint32_t)group.adi()); + { + 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_MULTICAST_LIKE,buf.data(),buf.size()); + } + } } void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembership &com) { + Buffer<4096> buf; + com.serialize(buf); + { + 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_COM,buf.data(),buf.size()); + } + } } void Cluster::doPeriodicTasks() @@ -371,7 +418,7 @@ void Cluster::doPeriodicTasks() 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()); + _send(*mid,STATE_MESSAGE_ALIVE,alive.data(),alive.size()); _members[*mid].lastAnnouncedAliveTo = now; } @@ -498,18 +545,15 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPh } } -void Cluster::_send(uint16_t memberId,const void *msg,unsigned int len) +void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len) { _Member &m = _members[memberId]; // assumes m.lock is locked! - for(;;) { - if ((m.q.size() + len) > ZT_CLUSTER_MAX_MESSAGE_LENGTH) - _flush(memberId); - else { - m.q.append(msg,len); - break; - } - } + if ((m.q.size() + len + 3) > ZT_CLUSTER_MAX_MESSAGE_LENGTH) + _flush(memberId); + m.q.append((uint16_t)(len + 1)); + m.q.append((uint8_t)type); + m.q.append(msg,len); } void Cluster::_flush(uint16_t memberId) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index f253e6f6a..df061c604 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -42,12 +42,18 @@ #include "Buffer.hpp" #include "Mutex.hpp" #include "SharedPtr.hpp" +#include "Hashtable.hpp" /** * Timeout for cluster members being considered "alive" */ #define ZT_CLUSTER_TIMEOUT 30000 +/** + * How often should we announce that we have a peer? + */ +#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD 60000 + /** * Desired period between doPeriodicTasks() in milliseconds */ @@ -238,7 +244,7 @@ public: bool redirectPeer(const SharedPtr &peer,const InetAddress &peerPhysicalAddress,bool offload); private: - void _send(uint16_t memberId,const void *msg,unsigned int len); + void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len); void _flush(uint16_t memberId); // These are initialized in the constructor and remain static @@ -292,7 +298,7 @@ private: std::vector _memberIds; Mutex _memberIds_m; - // Record tracking which members have which peers and how recently they claimed this + // Record tracking which members have which peers and how recently they claimed this -- also used to track our last claimed time struct _PeerAffinity { _PeerAffinity(const Address &a,uint16_t mid,uint64_t ts) : diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index a4d450687..7a47c0c6a 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -272,7 +272,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) 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 diff --git a/node/Peer.cpp b/node/Peer.cpp index 0ba379c6f..45e2feddb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -34,6 +34,7 @@ #include "Network.hpp" #include "AntiRecursion.hpp" #include "SelfAwareness.hpp" +#include "Cluster.hpp" #include @@ -107,7 +108,6 @@ void Peer::received( // Learn paths if they've been confirmed via a HELLO or an ECHO RemotePath *slot = (RemotePath *)0; if (np < ZT_MAX_PEER_NETWORK_PATHS) { - // Add new path slot = &(_paths[np++]); } else { uint64_t slotLRmin = 0xffffffffffffffffULL; @@ -141,6 +141,11 @@ void Peer::received( } } } + +#ifdef ZT_ENABLE_CLUSTER + if ((pathIsConfirmed)&&(RR->cluster)) + RR->cluster->replicateHavePeer(_id); +#endif } if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { diff --git a/node/Topology.cpp b/node/Topology.cpp index 56ca47c84..88c8856c4 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -122,18 +122,22 @@ Topology::~Topology() SharedPtr Topology::addPeer(const SharedPtr &peer) { if (peer->address() == RR->identity.address()) { - TRACE("BUG: addNewPeer() caught and ignored attempt to add peer for self"); + TRACE("BUG: addPeer() caught and ignored attempt to add peer for self"); throw std::logic_error("cannot add peer for self"); } - const uint64_t now = RR->node->now(); - Mutex::Lock _l(_lock); + SharedPtr np; + { + Mutex::Lock _l(_lock); + SharedPtr &hp = _peers[peer->address()]; + if (!hp) + hp = peer; + np = hp; + } + np->use(RR->node->now()); + saveIdentity(np->identity()); - SharedPtr &p = _peers.set(peer->address(),peer); - p->use(now); - saveIdentity(p->identity()); - - return p; + return np; } SharedPtr Topology::getPeer(const Address &zta) @@ -143,13 +147,12 @@ SharedPtr Topology::getPeer(const Address &zta) return SharedPtr(); } - const uint64_t now = RR->node->now(); Mutex::Lock _l(_lock); SharedPtr &ap = _peers[zta]; if (ap) { - ap->use(now); + ap->use(RR->node->now()); return ap; } @@ -157,13 +160,13 @@ SharedPtr Topology::getPeer(const Address &zta) if (id) { try { ap = SharedPtr(new Peer(RR->identity,id)); - ap->use(now); + ap->use(RR->node->now()); return ap; } catch ( ... ) {} // invalid identity? } + // If we get here it means we read an invalid cache identity or had some other error _peers.erase(zta); - return SharedPtr(); } From 59e1444b274465f33ed95aab6a9214d25910f85d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 16:31:41 -0700 Subject: [PATCH 097/195] Finish wiring up Cluster, fix some issues with other recent changes. --- node/Cluster.cpp | 26 ++++++++++++++------------ node/IncomingPacket.cpp | 14 +++++++++++--- node/Peer.cpp | 5 +++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index f1dc45b96..8afd3debb 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -186,19 +186,21 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) try { Identity id; ptr += id.deserialize(dmsg,ptr); - RR->topology->saveIdentity(id); + if (id) { + RR->topology->saveIdentity(id); - { // Add or update peer affinity entry - _PeerAffinity pa(id.address(),fromMemberId,RR->node->now()); - Mutex::Lock _l2(_peerAffinities_m); - std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) - if ((i != _peerAffinities.end())&&(i->key == pa.key)) { - i->timestamp = pa.timestamp; - } else { - _peerAffinities.push_back(pa); - std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now - } - } + { // Add or update peer affinity entry + _PeerAffinity pa(id.address(),fromMemberId,RR->node->now()); + Mutex::Lock _l2(_peerAffinities_m); + std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) + if ((i != _peerAffinities.end())&&(i->key == pa.key)) { + i->timestamp = pa.timestamp; + } else { + _peerAffinities.push_back(pa); + std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now + } + } + } } catch ( ... ) { // ignore invalid identities } diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7a47c0c6a..83e50a9e2 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -43,6 +43,7 @@ #include "Salsa20.hpp" #include "SHA512.hpp" #include "World.hpp" +#include "Cluster.hpp" namespace ZeroTier { @@ -612,8 +613,15 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared const uint64_t now = RR->node->now(); // Iterate through 18-byte network,MAC,ADI tuples - for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptrmc->add(now,at(ptr),MulticastGroup(MAC(field(ptr + 8,6),6),at(ptr + 14)),peer->address()); + for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr(ptr)); + const MulticastGroup group(MAC(field(ptr + 8,6),6),at(ptr + 14)); + RR->mc->add(now,nwid,group,peer->address()); +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateMulticastLike(nwid,peer->address(),group); +#endif + } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP); } catch ( ... ) { @@ -870,7 +878,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha } peer->setLastDirectPathPushReceived(now); - const RemotePath *currentBest = peer->getBestPath(); + const RemotePath *currentBest = peer->getBestPath(now); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; diff --git a/node/Peer.cpp b/node/Peer.cpp index 45e2feddb..b01c0c4a8 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -400,6 +400,11 @@ bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment * _networkComs.set(nwid,_NetworkCom(RR->node->now(),com)); } +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateCertificateOfNetworkMembership(com); +#endif + return true; } From 2258e36a598e3d91a654ad4938b4fc6fbf05382a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 16:34:21 -0700 Subject: [PATCH 098/195] Move replication of COMs to avoid race condition. --- node/IncomingPacket.cpp | 16 ++++++++++++++++ node/Peer.cpp | 5 ----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 83e50a9e2..c8526dfba 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -406,6 +406,10 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS); peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com); +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateCertificateOfNetworkMembership(com); +#endif } if ((flags & 0x02) != 0) { @@ -533,6 +537,10 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndSetNetworkMembershipCertificate(RR,network->id(),com); +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateCertificateOfNetworkMembership(com); +#endif } if (!network->isAllowed(peer)) { @@ -639,6 +647,10 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment while (ptr < size()) { ptr += com.deserialize(*this,ptr); peer->validateAndSetNetworkMembershipCertificate(RR,com.networkId(),com); +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateCertificateOfNetworkMembership(com); +#endif } peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP); @@ -794,6 +806,10 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM); peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com); +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->replicateCertificateOfNetworkMembership(com); +#endif } // Check membership after we've read any included COM, since diff --git a/node/Peer.cpp b/node/Peer.cpp index b01c0c4a8..45e2feddb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -400,11 +400,6 @@ bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment * _networkComs.set(nwid,_NetworkCom(RR->node->now(),com)); } -#ifdef ZT_ENABLE_CLUSTER - if (RR->cluster) - RR->cluster->replicateCertificateOfNetworkMembership(com); -#endif - return true; } From 35a12b94ea99e0236ff9455209161180d45e8eae Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 16:48:49 -0700 Subject: [PATCH 099/195] Outfit Cluster with TRACE for debugging. --- node/Cluster.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 8afd3debb..41685a073 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -172,14 +172,28 @@ 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 +#ifdef ZT_TRACE + std::string addrs; +#endif unsigned int physicalAddressCount = dmsg[ptr++]; for(unsigned int i=0;i 0) + addrs.push_back(','); + addrs.append(m.zeroTierPhysicalEndpoints.back().toString()); + } +#endif } m.lastReceivedAliveAnnouncement = RR->node->now(); +#ifdef ZT_TRACE + TRACE("[%u] I'm alive! send me peers at %s",(unsigned int)fromMemberId,addrs.c_str()); +#endif } break; case STATE_MESSAGE_HAVE_PEER: { @@ -200,6 +214,8 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now } } + + TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str()); } } catch ( ... ) { // ignore invalid identities @@ -212,10 +228,15 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) const MAC mac(dmsg.field(ptr,6),6); ptr += 6; const uint32_t adi = dmsg.at(ptr); ptr += 4; RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); + TRACE("[%u] %s likes %s/%u on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); } break; case STATE_MESSAGE_COM: { - // TODO: not used yet + CertificateOfMembership com; + ptr += com.deserialize(dmsg,ptr); + if (com) { + TRACE("[%u] COM for %s on %.16llu rev %llu",(unsigned int)fromMemberId,com.issuedTo().toString().c_str(),com.networkId(),com.revision()); + } } break; case STATE_MESSAGE_RELAY: { @@ -228,6 +249,8 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); + TRACE("[%u] relay %u bytes to %s (%u remote paths included)",(unsigned int)fromMemberId,packetLen,destinationAddress.toString().c_str(),numRemotePeerPaths); + SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); if (destinationPeer) { if ( @@ -313,6 +336,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) Packet outp(rcpt,RR->identity.address(),verb); outp.append(dmsg.field(ptr,len),len); RR->sw->send(outp,true,0); + TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len); } break; } } catch ( ... ) { From d6dee7bb5ccb4ae1fcf818afc246befc9133be72 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 17:22:53 -0700 Subject: [PATCH 100/195] Clustered handling of relaying. --- node/Cluster.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ node/Cluster.hpp | 11 +++++++++++ node/Switch.cpp | 12 +++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 41685a073..7f4a21b04 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -352,6 +352,53 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } } +bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len) +{ + if (len > 16384) // sanity check + return false; + + uint64_t mostRecentTimestamp = 0; + uint16_t canHasPeer = 0; + + { // Anyone got this peer? + Mutex::Lock _l2(_peerAffinities_m); + std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),_PeerAffinity(toPeerAddress,0,0))); // O(log(n)) + while ((i != _peerAffinities.end())&&(i->address() == toPeerAddress)) { + uint16_t mid = i->clusterMemberId(); + if ((mid != _id)&&(i->timestamp > mostRecentTimestamp)) { + mostRecentTimestamp = i->timestamp; + canHasPeer = mid; + } + } + } + + const uint64_t now = RR->node->now(); + if ((now - mostRecentTimestamp) < ZT_PEER_ACTIVITY_TIMEOUT) { + Buffer<16384> buf; + + InetAddress v4,v6; + if (fromPeerAddress) { + SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); + if (fromPeer) + fromPeer->getBestActiveAddresses(now,v4,v6); + } + buf.append((uint8_t)( (v4) ? ((v6) ? 2 : 1) : ((v6) ? 1 : 0) )); + if (v4) + v4.serialize(buf); + if (v6) + v6.serialize(buf); + buf.append((uint16_t)len); + buf.append(data,len); + + { + Mutex::Lock _l2(_members[canHasPeer].lock); + _send(canHasPeer,STATE_MESSAGE_RELAY,buf.data(),buf.size()); + } + } + + return false; +} + void Cluster::replicateHavePeer(const Identity &peerId) { { // Use peer affinity table to track our own last announce time for peers diff --git a/node/Cluster.hpp b/node/Cluster.hpp index df061c604..60157b159 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -191,6 +191,17 @@ public: */ void handleIncomingStateMessage(const void *msg,unsigned int len); + /** + * Send this packet via another node in this cluster if another node has this peer + * + * @param fromPeerAddress Source peer address (if known, should be NULL for fragments) + * @param toPeerAddress Destination peer address + * @param data Packet or packet fragment data + * @param len Length of packet or fragment + * @return True if this data was sent via another cluster member, false if none have this peer + */ + bool sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len); + /** * Advertise to the cluster that we have this peer * diff --git a/node/Switch.cpp b/node/Switch.cpp index 249a21d55..0edaa96d2 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -45,6 +45,7 @@ #include "AntiRecursion.hpp" #include "SelfAwareness.hpp" #include "Packet.hpp" +#include "Cluster.hpp" namespace ZeroTier { @@ -567,6 +568,11 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet // It wouldn't hurt anything, just redundant and unnecessary. SharedPtr relayTo = RR->topology->getPeer(destination); if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) { +#ifdef ZT_ENABLE_CLUSTER + if ((RR->cluster)&&(RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size()))) + return; // sent by way of another member of this cluster +#endif + // Don't know peer or no direct path -- so relay via root server relayTo = RR->topology->getBestRoot(); if (relayTo) @@ -642,7 +648,11 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) { unite(source,destination,false); } else { - // Don't know peer or no direct path -- so relay via root server +#ifdef ZT_ENABLE_CLUSTER + if ((RR->cluster)&&(RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size()))) + return; // sent by way of another member of this cluster +#endif + relayTo = RR->topology->getBestRoot(&source,1,true); if (relayTo) relayTo->send(RR,packet->data(),packet->size(),RR->node->now()); From 6a7b47e5e19687b2dda19449d4f0758388065077 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 17:27:57 -0700 Subject: [PATCH 101/195] Forgot a return true. --- node/Cluster.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 7f4a21b04..abe970e0b 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -394,6 +394,8 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee Mutex::Lock _l2(_members[canHasPeer].lock); _send(canHasPeer,STATE_MESSAGE_RELAY,buf.data(),buf.size()); } + + return true; } return false; From 978b056a0134edd9598a3edb219b31c61405c3b4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 20 Oct 2015 17:36:10 -0700 Subject: [PATCH 102/195] Wire in redirectPeer(), now about ready to test clustering! --- node/Cluster.cpp | 8 ++++---- node/Cluster.hpp | 4 ++-- node/Peer.cpp | 16 +++++++++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index abe970e0b..d9514db53 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -549,7 +549,7 @@ void Cluster::removeMember(uint16_t memberId) _memberIds = newMemberIds; } -bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPhysicalAddress,bool offload) +bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload) { if (!peerPhysicalAddress) // sanity check return false; @@ -585,7 +585,7 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPh } 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); + TRACE("peer %s is at [%d,%d,%d], distance to us is %f, sending to %u instead for better distance %f",peerAddress.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 @@ -593,7 +593,7 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPh // 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); + Packet outp(peerAddress,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()); @@ -611,7 +611,7 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &peerPh 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); + TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peerAddress.toString().c_str(),px,py,pz,currentDistance); return false; } } else { diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 60157b159..2e60fd6ba 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -247,12 +247,12 @@ public: /** * Redirect this peer to a better cluster member if needed * - * @param peer Peer to (possibly) redirect + * @param peerAddress 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); + bool redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); private: void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len); diff --git a/node/Peer.cpp b/node/Peer.cpp index 45e2feddb..fa7b3aa40 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -82,6 +82,7 @@ void Peer::received( { const uint64_t now = RR->node->now(); bool needMulticastGroupAnnounce = false; + bool pathIsConfirmed = false; { Mutex::Lock _l(_lock); @@ -89,8 +90,6 @@ void Peer::received( _lastReceive = now; if (!hops) { - bool pathIsConfirmed = false; - /* Learn new paths from direct (hops == 0) packets */ { unsigned int np = _numPaths; @@ -141,11 +140,6 @@ void Peer::received( } } } - -#ifdef ZT_ENABLE_CLUSTER - if ((pathIsConfirmed)&&(RR->cluster)) - RR->cluster->replicateHavePeer(_id); -#endif } if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { @@ -159,6 +153,14 @@ void Peer::received( _lastMulticastFrame = now; } +#ifdef ZT_ENABLE_CLUSTER + if ((pathIsConfirmed)&&(RR->cluster)) { + // Either shuttle this peer off somewhere else or report to other members that we have it + if (!RR->cluster->redirectPeer(_id.address(),remoteAddr,false)) + RR->cluster->replicateHavePeer(_id); + } +#endif + if (needMulticastGroupAnnounce) { const std::vector< SharedPtr > networks(RR->node->allNetworks()); for(std::vector< SharedPtr >::const_iterator n(networks.begin());n!=networks.end();++n) From 25a84e30fc7cedf2cc45970ecbe0df64886394e0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 21 Oct 2015 12:41:46 -0700 Subject: [PATCH 103/195] Code for cluster-geo service. --- .gitignore | 14 ++---- cluster-geo/config.js.sample | 7 +++ cluster-geo/index.js | 94 ++++++++++++++++++++++++++++++++++++ cluster-geo/package.json | 16 ++++++ 4 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 cluster-geo/config.js.sample create mode 100644 cluster-geo/index.js create mode 100644 cluster-geo/package.json diff --git a/.gitignore b/.gitignore index 2dbec1e5a..d314b3fc5 100755 --- a/.gitignore +++ b/.gitignore @@ -49,23 +49,17 @@ Thumbs.db *.rpm *.autosave *.tmp +node_modules -# Root topology build files, temporaries, and never check in secrets -/root-topology/bin2c -/root-topology/mktopology -/root-topology/*.secret -/root-topology/test/supernodes -/root-topology/test/test-root-topology +# cluster-geo stuff +cluster-geo/config.js +cluster-geo/cache.* # MacGap wrapper build files /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/* /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/xcuserdata/* /ext/mac-ui-macgap1-wrapper/src/build -# Web UI dev temporaries -/ui/.module-cache -node_modules - # Java/Android/JNI build droppings java/obj/ java/libs/ diff --git a/cluster-geo/config.js.sample b/cluster-geo/config.js.sample new file mode 100644 index 000000000..ec1ebfea4 --- /dev/null +++ b/cluster-geo/config.js.sample @@ -0,0 +1,7 @@ +// MaxMind GeoIP2 config +module.exports.maxmind = { + userId: 1234, + licenseKey: 'asdf', + service: 'city', + requestTimeout: 1000 +}; diff --git a/cluster-geo/index.js b/cluster-geo/index.js new file mode 100644 index 000000000..0e903ade7 --- /dev/null +++ b/cluster-geo/index.js @@ -0,0 +1,94 @@ +// +// GeoIP lookup service +// + +// GeoIP cache TTL in ms +var CACHE_TTL = (60 * 60 * 24 * 60 * 1000); // 60 days + +var config = require(__dirname + '/config.js'); + +if (!config.maxmind) { + console.error('FATAL: only MaxMind GeoIP2 is currently supported and is not configured in config.js'); + process.exit(1); +} +var geo = require('geoip2ws')(config.maxmind); + +var cache = require('levelup')(__dirname + '/cache.leveldb'); + +function lookup(ip,callback) +{ + cache.get(ip,function(err,cachedEntryJson) { + if ((!err)&&(cachedEntryJson)) { + try { + var cachedEntry = JSON.parse(cachedEntryJson.toString()); + if (cachedEntry) { + var ts = cachedEntry.ts; + var r = cachedEntry.r; + if ((ts)&&(r)) { + if ((Date.now() - ts) < CACHE_TTL) { + r._cached = true; + return callback(null,r); + } + } + } + } catch (e) {} + } + + geo(ip,function(err,result) { + if (err) + return callback(err,null); + if ((!result)||(!result.location)) + return callback(new Error('null result'),null); + + cache.put(ip,JSON.stringify({ + ts: Date.now(), + r: result + }),function(err) { + if (err) + console.error('Error saving to cache: '+err); + return callback(null,result); + }); + }); + }); +}; + +var linebuf = ''; +process.stdin.on('readable',function() { + var chunk; + while (null !== (chunk = process.stdin.read())) { + for(var i=0;i 0) { + var ip = linebuf; + lookup(ip,function(err,result) { + if ((err)||(!result)||(!result.location)) { + return process.stdout.write(ip+',0,0,0,0,0,0\n'); + } else { + var lat = parseFloat(result.location.latitude); + var lon = parseFloat(result.location.longitude); + + // Convert to X,Y,Z coordinates from Earth's origin, Earth-as-sphere approximation. + var latRadians = lat * 0.01745329251994; // PI / 180 + var lonRadians = lon * 0.01745329251994; // PI / 180 + var cosLat = Math.cos(latRadians); + var x = Math.round((-6371.0) * cosLat * Math.cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers + var y = Math.round(6371.0 * Math.sin(latRadians)); + var z = Math.round(6371.0 * cosLat * Math.sin(lonRadians)); + + return process.stdout.write(ip+',1,'+lat+','+lon+','+x+','+y+','+z+'\n'); + } + }); + } + linebuf = ''; + } else { + linebuf += String.fromCharCode(c); + } + } + } +}); + +process.stdin.on('end',function() { + cache.close(); + process.exit(0); +}); diff --git a/cluster-geo/package.json b/cluster-geo/package.json new file mode 100644 index 000000000..1927197ef --- /dev/null +++ b/cluster-geo/package.json @@ -0,0 +1,16 @@ +{ + "name": "cluster-geo", + "version": "1.0.0", + "description": "Cluster GEO-IP Query Service", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "ZeroTier, Inc.", + "license": "GPL-3.0", + "dependencies": { + "geoip2ws": "^1.7.1", + "leveldown": "^1.4.2", + "levelup": "^1.2.1" + } +} From a46514b397acdf42451d41398ef77eec60744256 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 21 Oct 2015 12:47:02 -0700 Subject: [PATCH 104/195] Cluster-geo launcher. --- .gitignore | 4 ++-- cluster-geo/cluster-geo.exe | 13 +++++++++++++ cluster-geo/{ => cluster-geo}/config.js.sample | 0 cluster-geo/{ => cluster-geo}/index.js | 0 cluster-geo/{ => cluster-geo}/package.json | 0 5 files changed, 15 insertions(+), 2 deletions(-) create mode 100755 cluster-geo/cluster-geo.exe rename cluster-geo/{ => cluster-geo}/config.js.sample (100%) rename cluster-geo/{ => cluster-geo}/index.js (100%) rename cluster-geo/{ => cluster-geo}/package.json (100%) diff --git a/.gitignore b/.gitignore index d314b3fc5..06b06b7d3 100755 --- a/.gitignore +++ b/.gitignore @@ -52,8 +52,8 @@ Thumbs.db node_modules # cluster-geo stuff -cluster-geo/config.js -cluster-geo/cache.* +cluster-geo/cluster-geo/config.js +cluster-geo/cluster-geo/cache.* # MacGap wrapper build files /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/* diff --git a/cluster-geo/cluster-geo.exe b/cluster-geo/cluster-geo.exe new file mode 100755 index 000000000..ae7206109 --- /dev/null +++ b/cluster-geo/cluster-geo.exe @@ -0,0 +1,13 @@ +#!/bin/bash + +export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin + +cd `dirname $0` +if [ ! -d cluster-geo -o ! -f cluster-geo/index.js ]; then + echo 'Cannot find ./cluster-geo containing NodeJS script files.' + exit 1 +fi + +cd cluster-geo + +exec node index.js diff --git a/cluster-geo/config.js.sample b/cluster-geo/cluster-geo/config.js.sample similarity index 100% rename from cluster-geo/config.js.sample rename to cluster-geo/cluster-geo/config.js.sample diff --git a/cluster-geo/index.js b/cluster-geo/cluster-geo/index.js similarity index 100% rename from cluster-geo/index.js rename to cluster-geo/cluster-geo/index.js diff --git a/cluster-geo/package.json b/cluster-geo/cluster-geo/package.json similarity index 100% rename from cluster-geo/package.json rename to cluster-geo/cluster-geo/package.json From 5304b0d8d156de3f62fe700fa6800a1be9abbc73 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 09:09:15 -0700 Subject: [PATCH 105/195] Rename index.js so process is distinguishable. --- cluster-geo/cluster-geo.exe | 2 +- cluster-geo/cluster-geo/{index.js => cluster-geo.js} | 0 cluster-geo/cluster-geo/package.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename cluster-geo/cluster-geo/{index.js => cluster-geo.js} (100%) diff --git a/cluster-geo/cluster-geo.exe b/cluster-geo/cluster-geo.exe index ae7206109..20bc487ad 100755 --- a/cluster-geo/cluster-geo.exe +++ b/cluster-geo/cluster-geo.exe @@ -10,4 +10,4 @@ fi cd cluster-geo -exec node index.js +exec node cluster-geo.js diff --git a/cluster-geo/cluster-geo/index.js b/cluster-geo/cluster-geo/cluster-geo.js similarity index 100% rename from cluster-geo/cluster-geo/index.js rename to cluster-geo/cluster-geo/cluster-geo.js diff --git a/cluster-geo/cluster-geo/package.json b/cluster-geo/cluster-geo/package.json index 1927197ef..4cd1ce007 100644 --- a/cluster-geo/cluster-geo/package.json +++ b/cluster-geo/cluster-geo/package.json @@ -2,7 +2,7 @@ "name": "cluster-geo", "version": "1.0.0", "description": "Cluster GEO-IP Query Service", - "main": "index.js", + "main": "cluster-geo.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, From e07bae2525c21002b8e61a2ecc57fa1e17764241 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 10:18:05 -0700 Subject: [PATCH 106/195] Run geoip cluster service sub-process. --- objects.mk | 1 + service/ClusterGeoIpService.cpp | 187 ++++++++++++++++++++++++++++++++ service/ClusterGeoIpService.hpp | 94 ++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 service/ClusterGeoIpService.cpp create mode 100644 service/ClusterGeoIpService.hpp diff --git a/objects.mk b/objects.mk index 10e0c334c..6dd5ea30e 100644 --- a/objects.mk +++ b/objects.mk @@ -26,5 +26,6 @@ OBJS=\ osdep/BackgroundResolver.o \ osdep/Http.o \ osdep/OSUtils.o \ + service/ClusterGeoIpService.o \ service/ControlPlane.o \ service/OneService.o diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp new file mode 100644 index 000000000..00d95d7e1 --- /dev/null +++ b/service/ClusterGeoIpService.cpp @@ -0,0 +1,187 @@ +/* + * 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/ + */ + +#ifdef ZT_ENABLE_CLUSTER + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ClusterGeoIpService.hpp" +#include "../node/Utils.hpp" +#include "../osdep/OSUtils.hpp" + +#define ZT_CLUSTERGEOIPSERVICE_INTERNAL_CACHE_TTL (60 * 60 * 1000) + +namespace ZeroTier { + +ClusterGeoIpService::ClusterGeoIpService(const char *pathToExe) : + _pathToExe(pathToExe), + _sOutputFd(-1), + _sInputFd(-1), + _sPid(0), + _run(true) +{ + _thread = Thread::start(this); +} + +ClusterGeoIpService::~ClusterGeoIpService() +{ + _run = false; + long p = _sPid; + if (p > 0) { + ::kill(p,SIGTERM); + Thread::sleep(500); + ::kill(p,SIGKILL); + } + Thread::join(_thread); +} + +bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z) +{ + const uint64_t now = OSUtils::now(); + bool r = false; + { + Mutex::Lock _l(_cache_m); + std::map< InetAddress,_CE >::iterator c(_cache.find(ip)); + if (c != _cache.end()) { + x = c->second.x; + y = c->second.y; + z = c->second.z; + if ((now - c->second.ts) < ZT_CLUSTERGEOIPSERVICE_INTERNAL_CACHE_TTL) + return true; + else r = true; // return true but refresh as well + } + } + + { + Mutex::Lock _l(_sOutputLock); + if (_sOutputFd >= 0) { + std::string ips(ip.toIpString()); + ips.push_back('\n'); + ::write(_sOutputFd,ips.data(),ips.length()); + } + } + + return r; +} + +void ClusterGeoIpService::threadMain() + throw() +{ + char linebuf[65536]; + char buf[65536]; + long n,lineptr; + + while (_run) { + { + Mutex::Lock _l(_sOutputLock); + + _sOutputFd = -1; + _sInputFd = -1; + _sPid = 0; + + int stdinfds[2] = { 0,0 }; // sub-process's stdin, our output + int stdoutfds[2] = { 0,0 }; // sub-process's stdout, our input + ::pipe(stdinfds); + ::pipe(stdoutfds); + + long p = (long)::vfork(); + if (p < 0) { + Thread::sleep(500); + continue; + } else if (p == 0) { + ::close(stdinfds[1]); + ::close(stdoutfds[0]); + ::dup2(stdinfds[0],STDIN_FILENO); + ::dup2(stdoutfds[1],STDOUT_FILENO); + ::execl(_pathToExe.c_str(),_pathToExe.c_str(),(const char *)0); + ::exit(1); + } else { + ::close(stdinfds[0]); + ::close(stdoutfds[1]); + _sOutputFd = stdinfds[1]; + _sInputFd = stdoutfds[0]; + _sPid = p; + } + } + + lineptr = 0; + while (_run) { + n = ::read(_sInputFd,buf,sizeof(buf)); + if (n <= 0) { + if (errno == EINTR) + continue; + else break; + } + for(long i=0;i sizeof(linebuf)) + lineptr = 0; + if ((buf[i] == '\n')||(buf[i] == '\r')) { + linebuf[lineptr] = (char)0; + if (lineptr > 0) { + try { + std::vector result(Utils::split(linebuf,",","","")); + if ((result.size() >= 7)&&(result[1] == "1")) { + InetAddress rip(result[0],0); + if ((rip.ss_family == AF_INET)||(rip.ss_family == AF_INET6)) { + _CE ce; + ce.ts = OSUtils::now(); + ce.x = (int)::strtol(result[4].c_str(),(char **)0,10); + ce.y = (int)::strtol(result[5].c_str(),(char **)0,10); + ce.z = (int)::strtol(result[6].c_str(),(char **)0,10); + { + Mutex::Lock _l2(_cache_m); + _cache[rip] = ce; + } + } + } + } catch ( ... ) {} + } + lineptr = 0; + } else linebuf[lineptr++] = buf[i]; + } + } + + ::close(_sOutputFd); + ::close(_sInputFd); + ::kill(_sPid,SIGTERM); + Thread::sleep(250); + ::kill(_sPid,SIGKILL); + ::waitpid(_sPid,(int *)0,0); + } +} + +} // namespace ZeroTier + +#endif // ZT_ENABLE_CLUSTER diff --git a/service/ClusterGeoIpService.hpp b/service/ClusterGeoIpService.hpp new file mode 100644 index 000000000..fd04ba1d6 --- /dev/null +++ b/service/ClusterGeoIpService.hpp @@ -0,0 +1,94 @@ +/* + * 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_CLUSTERGEOIPSERVICE_HPP +#define ZT_CLUSTERGEOIPSERVICE_HPP + +#ifdef ZT_ENABLE_CLUSTER + +#include +#include +#include + +#include "../node/Constants.hpp" +#include "../node/InetAddress.hpp" +#include "../node/Mutex.hpp" +#include "../osdep/Thread.hpp" + +namespace ZeroTier { + +/** + * Runs the Cluster GeoIP service in the background and resolves geoIP queries + */ +class ClusterGeoIpService +{ +public: + /** + * @param pathToExe Path to cluster geo-resolution service executable + */ + ClusterGeoIpService(const char *pathToExe); + + ~ClusterGeoIpService(); + + /** + * Attempt to locate an IP + * + * This returns true if x, y, and z are set. Otherwise it returns false + * and a geo-locate job is ordered in the background. This usually takes + * 500-1500ms to complete, after which time results will be available. + * If false is returned the supplied coordinate variables are unchanged. + * + * @param ip IPv4 or IPv6 address + * @param x Reference to variable to receive X + * @param y Reference to variable to receive Y + * @param z Reference to variable to receive Z + * @return True if coordinates were set + */ + bool locate(const InetAddress &ip,int &x,int &y,int &z); + + void threadMain() + throw(); + +private: + const std::string _pathToExe; + int _sOutputFd; + int _sInputFd; + volatile long _sPid; + volatile bool _run; + Thread _thread; + Mutex _sOutputLock; + + struct _CE { uint64_t ts; int x,y,z; }; + std::map< InetAddress,_CE > _cache; + Mutex _cache_m; +}; + +} // namespace ZeroTier + +#endif // ZT_ENABLE_CLUSTER + +#endif From 1bc451ed10b43e7c1113c9d8129ad468821ab0cb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 10:41:15 -0700 Subject: [PATCH 107/195] GeoIP cluster service works. --- cluster-geo/cluster-geo.exe | 2 +- cluster-geo/cluster-geo/cluster-geo.js | 28 ++++++++++++++------------ service/ClusterGeoIpService.cpp | 3 +++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/cluster-geo/cluster-geo.exe b/cluster-geo/cluster-geo.exe index 20bc487ad..7ca791afd 100755 --- a/cluster-geo/cluster-geo.exe +++ b/cluster-geo/cluster-geo.exe @@ -3,7 +3,7 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin cd `dirname $0` -if [ ! -d cluster-geo -o ! -f cluster-geo/index.js ]; then +if [ ! -d cluster-geo -o ! -f cluster-geo/cluster-geo.js ]; then echo 'Cannot find ./cluster-geo containing NodeJS script files.' exit 1 fi diff --git a/cluster-geo/cluster-geo/cluster-geo.js b/cluster-geo/cluster-geo/cluster-geo.js index 0e903ade7..44af84920 100644 --- a/cluster-geo/cluster-geo/cluster-geo.js +++ b/cluster-geo/cluster-geo/cluster-geo.js @@ -1,3 +1,5 @@ +"use strict"; + // // GeoIP lookup service // @@ -20,10 +22,10 @@ function lookup(ip,callback) cache.get(ip,function(err,cachedEntryJson) { if ((!err)&&(cachedEntryJson)) { try { - var cachedEntry = JSON.parse(cachedEntryJson.toString()); + let cachedEntry = JSON.parse(cachedEntryJson.toString()); if (cachedEntry) { - var ts = cachedEntry.ts; - var r = cachedEntry.r; + let ts = cachedEntry.ts; + let r = cachedEntry.r; if ((ts)&&(r)) { if ((Date.now() - ts) < CACHE_TTL) { r._cached = true; @@ -57,24 +59,24 @@ process.stdin.on('readable',function() { var chunk; while (null !== (chunk = process.stdin.read())) { for(var i=0;i 0) { - var ip = linebuf; + let ip = linebuf; lookup(ip,function(err,result) { if ((err)||(!result)||(!result.location)) { return process.stdout.write(ip+',0,0,0,0,0,0\n'); } else { - var lat = parseFloat(result.location.latitude); - var lon = parseFloat(result.location.longitude); + let lat = parseFloat(result.location.latitude); + let lon = parseFloat(result.location.longitude); // Convert to X,Y,Z coordinates from Earth's origin, Earth-as-sphere approximation. - var latRadians = lat * 0.01745329251994; // PI / 180 - var lonRadians = lon * 0.01745329251994; // PI / 180 - var cosLat = Math.cos(latRadians); - var x = Math.round((-6371.0) * cosLat * Math.cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers - var y = Math.round(6371.0 * Math.sin(latRadians)); - var z = Math.round(6371.0 * cosLat * Math.sin(lonRadians)); + let latRadians = lat * 0.01745329251994; // PI / 180 + let lonRadians = lon * 0.01745329251994; // PI / 180 + let cosLat = Math.cos(latRadians); + let x = Math.round((-6371.0) * cosLat * Math.cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers + let y = Math.round(6371.0 * Math.sin(latRadians)); + let z = Math.round(6371.0 * cosLat * Math.sin(lonRadians)); return process.stdout.write(ip+',1,'+lat+','+lon+','+x+','+y+','+z+'\n'); } diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp index 00d95d7e1..83afe770f 100644 --- a/service/ClusterGeoIpService.cpp +++ b/service/ClusterGeoIpService.cpp @@ -37,6 +37,8 @@ #include #include +#include + #include "ClusterGeoIpService.hpp" #include "../node/Utils.hpp" #include "../osdep/OSUtils.hpp" @@ -163,6 +165,7 @@ void ClusterGeoIpService::threadMain() { Mutex::Lock _l2(_cache_m); _cache[rip] = ce; + std::cout << ">> " << linebuf << std::endl; } } } From 7711eba297955d4cf3852f36fe7c6d118da38171 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 16:02:01 -0700 Subject: [PATCH 108/195] More cluster wiring... --- include/ZeroTierOne.h | 4 +- node/Cluster.cpp | 4 +- node/Cluster.hpp | 4 +- osdep/Phy.hpp | 9 ++ service/ClusterDefinition.hpp | 125 +++++++++++++++++++++++++++ service/ClusterGeoIpService.cpp | 1 - service/OneService.cpp | 145 ++++++++++++++++++++++++++++---- service/OneService.hpp | 3 + 8 files changed, 272 insertions(+), 23 deletions(-) create mode 100644 service/ClusterDefinition.hpp diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 135c8e114..4de4a765c 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -131,12 +131,12 @@ extern "C" { /** * Maximum number of cluster members (and max member ID plus one) */ -#define ZT_CLUSTER_MAX_MEMBERS 256 +#define ZT_CLUSTER_MAX_MEMBERS 128 /** * Maximum allowed cluster message length in bytes */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH 65535 +#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1444 * 6) /** * A null/empty sockaddr (all zero) to signify an unspecified socket address diff --git a/node/Cluster.cpp b/node/Cluster.cpp index d9514db53..e7aa5a411 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -504,7 +504,7 @@ void Cluster::doPeriodicTasks() void Cluster::addMember(uint16_t memberId) { - if (memberId >= ZT_CLUSTER_MAX_MEMBERS) + if ((memberId >= ZT_CLUSTER_MAX_MEMBERS)||(memberId == _id)) return; Mutex::Lock _l2(_members[memberId].lock); @@ -622,6 +622,8 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len) { + if ((len + 3) > (ZT_CLUSTER_MAX_MESSAGE_LENGTH - (24 + 2 + 2))) // sanity check + return; _Member &m = _members[memberId]; // assumes m.lock is locked! if ((m.q.size() + len + 3) > ZT_CLUSTER_MAX_MESSAGE_LENGTH) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 2e60fd6ba..6c9a2917b 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -47,7 +47,7 @@ /** * Timeout for cluster members being considered "alive" */ -#define ZT_CLUSTER_TIMEOUT 30000 +#define ZT_CLUSTER_TIMEOUT 10000 /** * How often should we announce that we have a peer? @@ -57,7 +57,7 @@ /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 50 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 namespace ZeroTier { diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index 7f790e5d2..6737034e3 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -64,6 +64,12 @@ #include #include +#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) +#ifndef IPV6_DONTFRAG +#define IPV6_DONTFRAG 62 +#endif +#endif + #define ZT_PHY_SOCKFD_TYPE int #define ZT_PHY_SOCKFD_NULL (-1) #define ZT_PHY_SOCKFD_VALID(s) ((s) > -1) @@ -374,6 +380,9 @@ public: f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); #ifdef IPV6_MTU_DISCOVER f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); +#endif +#ifdef IPV6_DONTFRAG + f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f)); #endif } f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); diff --git a/service/ClusterDefinition.hpp b/service/ClusterDefinition.hpp new file mode 100644 index 000000000..d02894e4e --- /dev/null +++ b/service/ClusterDefinition.hpp @@ -0,0 +1,125 @@ +/* + * 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_CLUSTERDEFINITION_HPP +#define ZT_CLUSTERDEFINITION_HPP + +#ifdef ZT_ENABLE_CLUSTER + +#include +#include + +#include "../node/Constants.hpp" +#include "../node/Utils.hpp" +#include "../osdep/OSUtils.hpp" + +namespace ZeroTier { + +/** + * Parser for cluster definition file + */ +class ClusterDefinition +{ +public: + struct MemberDefinition + { + MemberDefinition() : id(0),x(0),y(0),z(0) { name[0] = (char)0; } + + unsigned int id; + int x,y,z; + char name[256]; + InetAddress clusterEndpoint; + std::vector zeroTierEndpoints; + }; + + ClusterDefinition(uint64_t myAddress,const char *pathToClusterFile) + { + std::string cf; + if (!OSUtils::readFile(pathToClusterFile,cf)) + return; + + char myAddressStr[64]; + Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress); + + std::vector lines(Utils::split(cf.c_str(),"\r\n","","")); + for(std::vector::iterator l(lines.begin());l!=lines.end();++l) { + std::vector fields(Utils::split(l->c_str()," \t","","")); + if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr)) + continue; + + int id = Utils::strToUInt(fields[1].c_str()); + if ((id < 0)||(id > ZT_CLUSTER_MAX_MEMBERS)) + continue; + MemberDefinition &md = _md[id]; + + md.id = (unsigned int)id; + if (fields.size() >= 6) { + std::vector xyz(Utils::split(fields[5].c_str(),",","","")); + md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0; + md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0; + md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0; + } + Utils::scopy(md.name,sizeof(md.name),fields[2].c_str()); + md.clusterEndpoint.fromString(fields[3]); + if (!md.clusterEndpoint) + continue; + std::vector zips(Utils::split(fields[4].c_str(),",","","")); + for(std::vector::iterator zip(zips.begin());zip!=zips.end();++zip) { + InetAddress i; + i.fromString(*zip); + if (i) + md.zeroTierEndpoints.push_back(i); + } + + _ids.push_back((unsigned int)id); + } + + std::sort(_ids.begin(),_ids.end()); + } + + inline const MemberDefinition &operator[](unsigned int id) const throw() { return _md[id]; } + inline unsigned int size() const throw() { return (unsigned int)_ids.size(); } + inline const std::vector &ids() const throw() { return _ids; } + + inline std::vector members() const + { + std::vector m; + for(std::vector::const_iterator i(_ids.begin());i!=_ids.end();++i) + m.push_back(_md[*i]); + return m; + } + +private: + MemberDefinition _md[ZT_CLUSTER_MAX_MEMBERS]; + std::vector _ids; +}; + +} // namespace ZeroTier + +#endif // ZT_ENABLE_CLUSTER + +#endif diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp index 83afe770f..0c8152725 100644 --- a/service/ClusterGeoIpService.cpp +++ b/service/ClusterGeoIpService.cpp @@ -165,7 +165,6 @@ void ClusterGeoIpService::threadMain() { Mutex::Lock _l2(_cache_m); _cache[rip] = ce; - std::cout << ">> " << linebuf << std::endl; } } } diff --git a/service/OneService.cpp b/service/OneService.cpp index 6b28c41e6..a64d680b9 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -58,6 +58,8 @@ #include "OneService.hpp" #include "ControlPlane.hpp" +#include "ClusterGeoIpService.hpp" +#include "ClusterDefinition.hpp" /** * Uncomment to enable UDP breakage switch @@ -366,6 +368,11 @@ static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,c static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len); static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); +#ifdef ZT_ENABLE_CLUSTER +static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len); +static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z); +#endif + static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); static int ShttpOnMessageBegin(http_parser *parser); @@ -419,26 +426,32 @@ class OneServiceImpl : public OneService { public: OneServiceImpl(const char *hp,unsigned int port) : - _homePath((hp) ? hp : "."), - _tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY), + _homePath((hp) ? hp : ".") + ,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY) #ifdef ZT_ENABLE_NETWORK_CONTROLLER - _controller((SqliteNetworkController *)0), + ,_controller((SqliteNetworkController *)0) #endif - _phy(this,false,true), - _node((Node *)0), - _controlPlane((ControlPlane *)0), - _lastDirectReceiveFromGlobal(0), - _lastSendToGlobal(0), - _lastRestart(0), - _nextBackgroundTaskDeadline(0), - _tcpFallbackTunnel((TcpConnection *)0), - _termReason(ONE_STILL_RUNNING), - _port(0), + ,_phy(this,false,true) + ,_node((Node *)0) + ,_controlPlane((ControlPlane *)0) + ,_lastDirectReceiveFromGlobal(0) + ,_lastSendToGlobal(0) + ,_lastRestart(0) + ,_nextBackgroundTaskDeadline(0) + ,_tcpFallbackTunnel((TcpConnection *)0) + ,_termReason(ONE_STILL_RUNNING) + ,_port(0) #ifdef ZT_USE_MINIUPNPC - _v4UpnpUdpSocket((PhySocket *)0), - _upnpClient((UPNPClient *)0), + ,_v4UpnpUdpSocket((PhySocket *)0) + ,_upnpClient((UPNPClient *)0) #endif - _run(true) +#ifdef ZT_ENABLE_CLUSTER + ,_clusterMessageSocket((PhySocket *)0) + ,_clusterGeoIpService((ClusterGeoIpService *)0) + ,_clusterDefinition((ClusterDefinition *)0) + ,_clusterMemberId(0) +#endif + ,_run(true) { const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random for(int k=0;ksetNetconfMaster((void *)_controller); #endif +#ifdef ZT_ENABLE_CLUSTER + if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str())) { + _clusterDefinition = new ClusterDefinition(_node->address(),(_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str()); + if (_clusterDefinition->size() > 0) { + std::vector members(_clusterDefinition->members()); + for(std::vector::iterator m(members.begin());m!=members.end();++m) { + PhySocket *cs = _phy.udpBind(reinterpret_cast(&(m->clusterEndpoint))); + if (cs) { + if (_clusterMessageSocket) { + _phy.close(_clusterMessageSocket,false); + _phy.close(cs,false); + + Mutex::Lock _l(_termReason_m); + _termReason = ONE_UNRECOVERABLE_ERROR; + _fatalErrorMessage = "Cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!"; + return _termReason; + } + _clusterMessageSocket = cs; + _clusterMemberId = m->id; + } + } + + if (!_clusterMessageSocket) { + Mutex::Lock _l(_termReason_m); + _termReason = ONE_UNRECOVERABLE_ERROR; + _fatalErrorMessage = "Cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port."; + return _termReason; + } + + if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S + "cluster-geo.exe").c_str())) + _clusterGeoIpService = new ClusterGeoIpService((_homePath + ZT_PATH_SEPARATOR_S + "cluster-geo.exe").c_str()); + + const ClusterDefinition::MemberDefinition &me = (*_clusterDefinition)[_clusterMemberId]; + InetAddress endpoints[255]; + unsigned int numEndpoints = 0; + for(std::vector::const_iterator i(me.zeroTierEndpoints.begin());i!=me.zeroTierEndpoints.end();++i) + endpoints[numEndpoints++] = *i; + + if (_node->clusterInit( + _clusterMemberId, + reinterpret_cast(endpoints), + numEndpoints, + me.x, + me.y, + me.z, + &SclusterSendFunction, + this, + (_clusterGeoIpService) ? &SclusterGeoIpFunction : 0, + this) == ZT_RESULT_OK) { + + std::vector members(_clusterDefinition->members()); + for(std::vector::iterator m(members.begin());m!=members.end();++m) { + if (m->id != _clusterMemberId) + _node->clusterAddMember(m->id); + } + + } + } else { + delete _clusterDefinition; + _clusterDefinition = (ClusterDefinition *)0; + } + } +#endif + _controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str()); _controlPlane->addAuthToken(authToken.c_str()); @@ -781,10 +865,18 @@ public: inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) { +#ifdef ZT_ENABLE_CLUSTER + if (sock == _clusterMessageSocket) { + _node->clusterHandleIncomingMessage(data,len); + return; + } +#endif + #ifdef ZT_BREAK_UDP if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) return; #endif + if ((len >= 16)&&(reinterpret_cast(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) _lastDirectReceiveFromGlobal = OSUtils::now(); ZT_ResultCode rc = _node->processWirePacket( @@ -1303,7 +1395,6 @@ public: _phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections } -private: std::string _dataStorePrepPath(const char *name) const { std::string p(_homePath); @@ -1358,6 +1449,13 @@ private: UPNPClient *_upnpClient; #endif +#ifdef ZT_ENABLE_CLUSTER + PhySocket *_clusterMessageSocket; + ClusterGeoIpService *_clusterGeoIpService; + ClusterDefinition *_clusterDefinition; + unsigned int _clusterMemberId; +#endif + bool _run; Mutex _run_m; }; @@ -1375,6 +1473,19 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); } +static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len) +{ + OneServiceImpl *const impl = reinterpret_cast(uptr); + const ClusterDefinition::MemberDefinition &md = (*(impl->_clusterDefinition))[toMemberId]; + if (md.clusterEndpoint) + impl->_phy.udpSend(impl->_clusterMessageSocket,reinterpret_cast(&(md.clusterEndpoint)),data,len); +} +static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z) +{ + OneServiceImpl *const impl = reinterpret_cast(uptr); + return (int)(impl->_clusterGeoIpService->locate(*(reinterpret_cast(addr)),*x,*y,*z)); +} + static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } diff --git a/service/OneService.hpp b/service/OneService.hpp index 2f76ebaa2..4f7b988bf 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -43,6 +43,9 @@ namespace ZeroTier { * periodically checked and updates are automatically downloaded, verified * against a built-in list of update signing keys, and installed. This is * only supported for certain platforms. + * + * If built with ZT_ENABLE_CLUSTER, a 'cluster' file is checked and if + * present is read to determine the identity of other cluster members. */ class OneService { From dee6e7e3c17643f924796bd87d300f36e39aed0a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 16:11:48 -0700 Subject: [PATCH 109/195] . --- service/ClusterGeoIpService.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp index 0c8152725..90b0e3e62 100644 --- a/service/ClusterGeoIpService.cpp +++ b/service/ClusterGeoIpService.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include From 2a3dd53952606baf7e3cf76793ba151feeca371d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 17:50:00 -0700 Subject: [PATCH 110/195] . --- service/ClusterGeoIpService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp index 90b0e3e62..b47a9b2a8 100644 --- a/service/ClusterGeoIpService.cpp +++ b/service/ClusterGeoIpService.cpp @@ -148,7 +148,7 @@ void ClusterGeoIpService::threadMain() else break; } for(long i=0;i sizeof(linebuf)) + if (lineptr > (long)sizeof(linebuf)) lineptr = 0; if ((buf[i] == '\n')||(buf[i] == '\r')) { linebuf[lineptr] = (char)0; From 964b30902ad93d2ea8599721611a488dac6adbca Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 11:51:18 -0700 Subject: [PATCH 111/195] Cluster fix: was accumulating remote endpoints endlessly. --- node/Cluster.cpp | 361 ++++++++++++++++++++++++----------------------- 1 file changed, 186 insertions(+), 175 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index e7aa5a411..c943e62bf 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -143,212 +143,223 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) return; const uint16_t fromMemberId = dmsg.at(0); unsigned int ptr = 2; - if (fromMemberId == _id) + if (fromMemberId == _id) // sanity check: we don't talk to ourselves return; const uint16_t toMemberId = dmsg.at(ptr); ptr += 2; - if (toMemberId != _id) + if (toMemberId != _id) // sanity check: message not for us? return; - _Member &m = _members[fromMemberId]; - Mutex::Lock mlck(m.lock); + { // make sure sender is actually considered a member + Mutex::Lock _l3(_memberIds_m); + if (std::find(_memberIds.begin(),_memberIds.end(),fromMemberId) == _memberIds.end()) + return; + } - try { - while (ptr < dmsg.size()) { - const unsigned int mlen = dmsg.at(ptr); ptr += 2; - const unsigned int nextPtr = ptr + mlen; + { + _Member &m = _members[fromMemberId]; + Mutex::Lock mlck(m.lock); - int mtype = -1; - try { - switch((StateMessageType)(mtype = (int)dmsg[ptr++])) { - default: - break; + try { + while (ptr < dmsg.size()) { + const unsigned int mlen = dmsg.at(ptr); ptr += 2; + const unsigned int nextPtr = ptr + mlen; + if (nextPtr > dmsg.size()) + break; - case STATE_MESSAGE_ALIVE: { - ptr += 7; // skip version stuff, not used yet - m.x = dmsg.at(ptr); ptr += 4; - m.y = dmsg.at(ptr); ptr += 4; - m.z = dmsg.at(ptr); ptr += 4; - ptr += 8; // skip local clock, not used - m.load = dmsg.at(ptr); ptr += 8; - ptr += 8; // skip flags, unused + int mtype = -1; + try { + switch((StateMessageType)(mtype = (int)dmsg[ptr++])) { + default: + break; + + case STATE_MESSAGE_ALIVE: { + ptr += 7; // skip version stuff, not used yet + m.x = dmsg.at(ptr); ptr += 4; + m.y = dmsg.at(ptr); ptr += 4; + m.z = dmsg.at(ptr); ptr += 4; + ptr += 8; // skip local clock, not used + m.load = dmsg.at(ptr); ptr += 8; + ptr += 8; // skip flags, unused #ifdef ZT_TRACE - std::string addrs; + std::string addrs; +#endif + unsigned int physicalAddressCount = dmsg[ptr++]; + m.zeroTierPhysicalEndpoints.clear(); + for(unsigned int i=0;i 0) + addrs.push_back(','); + addrs.append(m.zeroTierPhysicalEndpoints.back().toString()); + } #endif - unsigned int physicalAddressCount = dmsg[ptr++]; - for(unsigned int i=0;inode->now(); #ifdef ZT_TRACE - else { - if (addrs.length() > 0) - addrs.push_back(','); - addrs.append(m.zeroTierPhysicalEndpoints.back().toString()); - } + TRACE("[%u] I'm alive! peers may be redirected to: %s",(unsigned int)fromMemberId,addrs.c_str()); #endif - } - m.lastReceivedAliveAnnouncement = RR->node->now(); -#ifdef ZT_TRACE - TRACE("[%u] I'm alive! send me peers at %s",(unsigned int)fromMemberId,addrs.c_str()); -#endif - } break; + } break; - case STATE_MESSAGE_HAVE_PEER: { - try { - Identity id; - ptr += id.deserialize(dmsg,ptr); - if (id) { - RR->topology->saveIdentity(id); + case STATE_MESSAGE_HAVE_PEER: { + try { + Identity id; + ptr += id.deserialize(dmsg,ptr); + if (id) { + RR->topology->saveIdentity(id); - { // Add or update peer affinity entry - _PeerAffinity pa(id.address(),fromMemberId,RR->node->now()); - Mutex::Lock _l2(_peerAffinities_m); - std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) - if ((i != _peerAffinities.end())&&(i->key == pa.key)) { - i->timestamp = pa.timestamp; - } else { - _peerAffinities.push_back(pa); - std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now - } - } - - TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str()); - } - } catch ( ... ) { - // ignore invalid identities - } - } break; - - case STATE_MESSAGE_MULTICAST_LIKE: { - const uint64_t nwid = dmsg.at(ptr); ptr += 8; - const Address address(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; - const MAC mac(dmsg.field(ptr,6),6); ptr += 6; - const uint32_t adi = dmsg.at(ptr); ptr += 4; - RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); - TRACE("[%u] %s likes %s/%u on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); - } break; - - case STATE_MESSAGE_COM: { - CertificateOfMembership com; - ptr += com.deserialize(dmsg,ptr); - if (com) { - TRACE("[%u] COM for %s on %.16llu rev %llu",(unsigned int)fromMemberId,com.issuedTo().toString().c_str(),com.networkId(),com.revision()); - } - } break; - - case STATE_MESSAGE_RELAY: { - const unsigned int numRemotePeerPaths = dmsg[ptr++]; - InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max - for(unsigned int i=0;i(ptr); ptr += 2; - const void *packet = (const void *)dmsg.field(ptr,packetLen); ptr += packetLen; - - if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address - const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); - TRACE("[%u] relay %u bytes to %s (%u remote paths included)",(unsigned int)fromMemberId,packetLen,destinationAddress.toString().c_str(),numRemotePeerPaths); - - SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); - if (destinationPeer) { - if ( - (destinationPeer->send(RR,packet,packetLen,RR->node->now()))&& - (numRemotePeerPaths > 0)&& - (packetLen >= 18)&& - (reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) - ) { - // If remote peer paths were sent with this relayed packet, we do - // RENDEZVOUS. It's handled here for cluster-relayed packets since - // we don't have both Peer records so this is a different path. - - const Address remotePeerAddress(reinterpret_cast(packet) + 13,ZT_ADDRESS_LENGTH); - - InetAddress bestDestV4,bestDestV6; - destinationPeer->getBestActiveAddresses(RR->node->now(),bestDestV4,bestDestV6); - InetAddress bestRemoteV4,bestRemoteV6; - for(unsigned int i=0;inode->now()); + Mutex::Lock _l2(_peerAffinities_m); + std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) + if ((i != _peerAffinities.end())&&(i->key == pa.key)) { + i->timestamp = pa.timestamp; + } else { + _peerAffinities.push_back(pa); + std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now } - } + } - Packet rendezvousForDest(destinationAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS); - rendezvousForDest.append((uint8_t)0); - remotePeerAddress.appendTo(rendezvousForDest); + TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str()); + } + } catch ( ... ) { + // ignore invalid identities + } + } break; - Buffer<2048> rendezvousForOtherEnd; - remotePeerAddress.appendTo(rendezvousForOtherEnd); - rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS); - const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size(); - rendezvousForOtherEnd.addSize(2); // space for actual packet payload length - rendezvousForOtherEnd.append((uint8_t)0); // flags == 0 - destinationAddress.appendTo(rendezvousForOtherEnd); + case STATE_MESSAGE_MULTICAST_LIKE: { + const uint64_t nwid = dmsg.at(ptr); ptr += 8; + const Address address(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; + const MAC mac(dmsg.field(ptr,6),6); ptr += 6; + const uint32_t adi = dmsg.at(ptr); ptr += 4; + RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); + TRACE("[%u] %s likes %s/%u on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); + } break; - bool haveMatch = false; - if ((bestDestV6)&&(bestRemoteV6)) { - haveMatch = true; + case STATE_MESSAGE_COM: { + CertificateOfMembership com; + ptr += com.deserialize(dmsg,ptr); + if (com) { + TRACE("[%u] COM for %s on %.16llu rev %llu",(unsigned int)fromMemberId,com.issuedTo().toString().c_str(),com.networkId(),com.revision()); + } + } break; - rendezvousForDest.append((uint16_t)bestRemoteV6.port()); - rendezvousForDest.append((uint8_t)16); - rendezvousForDest.append(bestRemoteV6.rawIpData(),16); + case STATE_MESSAGE_RELAY: { + const unsigned int numRemotePeerPaths = dmsg[ptr++]; + InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max + for(unsigned int i=0;i(ptr); ptr += 2; + const void *packet = (const void *)dmsg.field(ptr,packetLen); ptr += packetLen; - rendezvousForOtherEnd.append((uint16_t)bestDestV6.port()); - rendezvousForOtherEnd.append((uint8_t)16); - rendezvousForOtherEnd.append(bestDestV6.rawIpData(),16); - rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16)); - } else if ((bestDestV4)&&(bestRemoteV4)) { - haveMatch = true; + if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address + const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); + TRACE("[%u] relay %u bytes to %s (%u remote paths included)",(unsigned int)fromMemberId,packetLen,destinationAddress.toString().c_str(),numRemotePeerPaths); - rendezvousForDest.append((uint16_t)bestRemoteV4.port()); - rendezvousForDest.append((uint8_t)4); - rendezvousForDest.append(bestRemoteV4.rawIpData(),4); + SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); + if (destinationPeer) { + if ( + (destinationPeer->send(RR,packet,packetLen,RR->node->now()))&& + (numRemotePeerPaths > 0)&& + (packetLen >= 18)&& + (reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) + ) { + // If remote peer paths were sent with this relayed packet, we do + // RENDEZVOUS. It's handled here for cluster-relayed packets since + // we don't have both Peer records so this is a different path. - rendezvousForOtherEnd.append((uint16_t)bestDestV4.port()); - rendezvousForOtherEnd.append((uint8_t)4); - rendezvousForOtherEnd.append(bestDestV4.rawIpData(),4); - rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4)); - } + const Address remotePeerAddress(reinterpret_cast(packet) + 13,ZT_ADDRESS_LENGTH); - if (haveMatch) { - _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); - RR->sw->send(rendezvousForDest,true,0); + InetAddress bestDestV4,bestDestV6; + destinationPeer->getBestActiveAddresses(RR->node->now(),bestDestV4,bestDestV6); + InetAddress bestRemoteV4,bestRemoteV6; + for(unsigned int i=0;iidentity.address(),Packet::VERB_RENDEZVOUS); + rendezvousForDest.append((uint8_t)0); + remotePeerAddress.appendTo(rendezvousForDest); + + Buffer<2048> rendezvousForOtherEnd; + remotePeerAddress.appendTo(rendezvousForOtherEnd); + rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS); + const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size(); + rendezvousForOtherEnd.addSize(2); // space for actual packet payload length + rendezvousForOtherEnd.append((uint8_t)0); // flags == 0 + destinationAddress.appendTo(rendezvousForOtherEnd); + + bool haveMatch = false; + if ((bestDestV6)&&(bestRemoteV6)) { + haveMatch = true; + + rendezvousForDest.append((uint16_t)bestRemoteV6.port()); + rendezvousForDest.append((uint8_t)16); + rendezvousForDest.append(bestRemoteV6.rawIpData(),16); + + rendezvousForOtherEnd.append((uint16_t)bestDestV6.port()); + rendezvousForOtherEnd.append((uint8_t)16); + rendezvousForOtherEnd.append(bestDestV6.rawIpData(),16); + rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16)); + } else if ((bestDestV4)&&(bestRemoteV4)) { + haveMatch = true; + + rendezvousForDest.append((uint16_t)bestRemoteV4.port()); + rendezvousForDest.append((uint8_t)4); + rendezvousForDest.append(bestRemoteV4.rawIpData(),4); + + rendezvousForOtherEnd.append((uint16_t)bestDestV4.port()); + rendezvousForOtherEnd.append((uint8_t)4); + rendezvousForOtherEnd.append(bestDestV4.rawIpData(),4); + rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4)); + } + + if (haveMatch) { + _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); + RR->sw->send(rendezvousForDest,true,0); + } } } } - } - } break; + } break; - case STATE_MESSAGE_PROXY_SEND: { - const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - const Packet::Verb verb = (Packet::Verb)dmsg[ptr++]; - const unsigned int len = dmsg.at(ptr); ptr += 2; - Packet outp(rcpt,RR->identity.address(),verb); - outp.append(dmsg.field(ptr,len),len); - RR->sw->send(outp,true,0); - TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len); - } break; + case STATE_MESSAGE_PROXY_SEND: { + const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + const Packet::Verb verb = (Packet::Verb)dmsg[ptr++]; + const unsigned int len = dmsg.at(ptr); ptr += 2; + Packet outp(rcpt,RR->identity.address(),verb); + outp.append(dmsg.field(ptr,len),len); + RR->sw->send(outp,true,0); + TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len); + } break; + } + } catch ( ... ) { + TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype); + // drop invalids } - } catch ( ... ) { - TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype); - // drop invalids - } - ptr = nextPtr; + ptr = nextPtr; + } + } catch ( ... ) { + TRACE("invalid message (outer loop), discarding"); + // drop invalids } - } catch ( ... ) { - TRACE("invalid message (outer loop), discarding"); - // drop invalids } } From f0160635a27df2e31a0dd1a3bfe28675f0cacc4c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 12:05:17 -0700 Subject: [PATCH 112/195] Add --harmony for older nodeJS. --- cluster-geo/cluster-geo.exe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster-geo/cluster-geo.exe b/cluster-geo/cluster-geo.exe index 7ca791afd..56b76e0d4 100755 --- a/cluster-geo/cluster-geo.exe +++ b/cluster-geo/cluster-geo.exe @@ -10,4 +10,4 @@ fi cd cluster-geo -exec node cluster-geo.js +exec node --harmony cluster-geo.js From 29b966894cb401e6ce396d2c12d94ee90adb4ef7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 13:03:34 -0700 Subject: [PATCH 113/195] (1) Fix bug in geo-ip service that prevented cache lookup, (2) fix problem in SelfAwareness (will need to test ALL versions in the wild with this), and (3) add more TRACE instrumentation to Cluster. --- node/Cluster.cpp | 15 ++++++++++----- node/Cluster.hpp | 2 +- node/SelfAwareness.cpp | 9 +++++---- node/SelfAwareness.hpp | 10 +++++----- service/ClusterGeoIpService.cpp | 10 ++++++++-- service/OneService.cpp | 2 ++ 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index c943e62bf..4088c9678 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -202,7 +202,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } m.lastReceivedAliveAnnouncement = RR->node->now(); #ifdef ZT_TRACE - TRACE("[%u] I'm alive! peers may be redirected to: %s",(unsigned int)fromMemberId,addrs.c_str()); + TRACE("[%u] I'm alive! peers close to %d,%d,%d can be redirected to: %s",(unsigned int)fromMemberId,m.x,m.y,m.z,addrs.c_str()); #endif } break; @@ -406,10 +406,12 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee _send(canHasPeer,STATE_MESSAGE_RELAY,buf.data(),buf.size()); } + TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer); return true; + } else { + TRACE("sendViaCluster(): unable to relay %u bytes from %s to %s since no cluster members seem to have it!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str()); + return false; } - - return false; } void Cluster::replicateHavePeer(const Identity &peerId) @@ -564,11 +566,12 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy { 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 + TRACE("no geolocation available for %s",peerPhysicalAddress.toIpString().c_str()); return false; } @@ -578,6 +581,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); double bestDistance = (offload ? 2147483648.0 : currentDistance); unsigned int bestMember = _id; + TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -588,6 +592,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy 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) { + bestDistance = mdist; bestMember = *mid; best = m.zeroTierPhysicalEndpoints; } @@ -596,7 +601,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy } 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",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestMember,bestDistance); + TRACE("%s seems closer to %u at %fkm, suggesting redirect...",peerAddress.toString().c_str(),bestMember,bestDistance); /* if (peer->remoteVersionProtocol() >= 5) { // If it's a newer peer send VERB_PUSH_DIRECT_PATHS which is more idiomatic diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 6c9a2917b..daa811858 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -145,7 +145,7 @@ public: STATE_MESSAGE_RELAY = 5, /** - * Request to send a packet to a locally-known peer: + * Request that a cluster member send a packet to a locally-known peer: * <[5] ZeroTier address of recipient> * <[1] packet verb> * <[2] length of packet payload> diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 7329322af..1b70f17cb 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -94,7 +94,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi Mutex::Lock _l(_phy_m); - PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,scope)]; + PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,reporterPhysicalAddress,scope)]; if ((now - entry.ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT) { entry.mySurface = myPhysicalAddress; @@ -105,14 +105,15 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi entry.ts = now; TRACE("learned physical address %s for scope %u as seen from %s(%s) (replaced %s, resetting all in scope)",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str()); - // Erase all entries (other than this one) for this scope to prevent thrashing - // Note: we should probably not use 'entry' after this + // Erase all entries in this scope that were not reported by this remote address to prevent 'thrashing' + // due to multiple reports of endpoint change. + // Don't use 'entry' after this since hash table gets modified. { Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy); PhySurfaceKey *k = (PhySurfaceKey *)0; PhySurfaceEntry *e = (PhySurfaceEntry *)0; while (i.next(k,e)) { - if ((k->reporter != reporter)&&(k->scope == scope)) + if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope)) _phy.erase(*k); } } diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp index 3133553ee..400b05e6e 100644 --- a/node/SelfAwareness.hpp +++ b/node/SelfAwareness.hpp @@ -69,14 +69,14 @@ private: struct PhySurfaceKey { Address reporter; + InetAddress reporterPhysicalAddress; InetAddress::IpScope scope; - inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); } - PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {} - PhySurfaceKey(const Address &r,InetAddress::IpScope s) : reporter(r),scope(s) {} - inline bool operator<(const PhySurfaceKey &k) const throw() { return ((reporter < k.reporter) ? true : ((reporter == k.reporter) ? ((int)scope < (int)k.scope) : false)); } - inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(scope == k.scope)); } + PhySurfaceKey(const Address &r,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),reporterPhysicalAddress(ra),scope(s) {} + + inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); } + inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); } }; struct PhySurfaceEntry { diff --git a/service/ClusterGeoIpService.cpp b/service/ClusterGeoIpService.cpp index b47a9b2a8..9baa75064 100644 --- a/service/ClusterGeoIpService.cpp +++ b/service/ClusterGeoIpService.cpp @@ -72,11 +72,14 @@ ClusterGeoIpService::~ClusterGeoIpService() bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z) { + InetAddress ipNoPort(ip); + ipNoPort.setPort(0); // we index cache by IP only const uint64_t now = OSUtils::now(); + bool r = false; { Mutex::Lock _l(_cache_m); - std::map< InetAddress,_CE >::iterator c(_cache.find(ip)); + std::map< InetAddress,_CE >::iterator c(_cache.find(ipNoPort)); if (c != _cache.end()) { x = c->second.x; y = c->second.y; @@ -90,8 +93,9 @@ bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z) { Mutex::Lock _l(_sOutputLock); if (_sOutputFd >= 0) { - std::string ips(ip.toIpString()); + std::string ips(ipNoPort.toIpString()); ips.push_back('\n'); + //fprintf(stderr,"ClusterGeoIpService: << %s",ips.c_str()); ::write(_sOutputFd,ips.data(),ips.length()); } } @@ -153,6 +157,7 @@ void ClusterGeoIpService::threadMain() if ((buf[i] == '\n')||(buf[i] == '\r')) { linebuf[lineptr] = (char)0; if (lineptr > 0) { + //fprintf(stderr,"ClusterGeoIpService: >> %s\n",linebuf); try { std::vector result(Utils::split(linebuf,",","","")); if ((result.size() >= 7)&&(result[1] == "1")) { @@ -163,6 +168,7 @@ void ClusterGeoIpService::threadMain() ce.x = (int)::strtol(result[4].c_str(),(char **)0,10); ce.y = (int)::strtol(result[5].c_str(),(char **)0,10); ce.z = (int)::strtol(result[6].c_str(),(char **)0,10); + //fprintf(stderr,"ClusterGeoIpService: %s is at %d,%d,%d\n",rip.toIpString().c_str(),ce.x,ce.y,ce.z); { Mutex::Lock _l2(_cache_m); _cache[rip] = ce; diff --git a/service/OneService.cpp b/service/OneService.cpp index a64d680b9..729812edb 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1473,6 +1473,7 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); } +#ifdef ZT_ENABLE_CLUSTER static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len) { OneServiceImpl *const impl = reinterpret_cast(uptr); @@ -1485,6 +1486,7 @@ static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr, OneServiceImpl *const impl = reinterpret_cast(uptr); return (int)(impl->_clusterGeoIpService->locate(*(reinterpret_cast(addr)),*x,*y,*z)); } +#endif static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } From e6a63f5547f928f4908b7436210340c9db752284 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 13:57:02 -0700 Subject: [PATCH 114/195] Fix bug in setWorld that might have caused a peer entry for myself (which would never be used) --- node/Topology.cpp | 20 +++++++++++--------- node/Topology.hpp | 5 +++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index 88c8856c4..adfcd1f99 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -358,16 +358,18 @@ void Topology::_setWorld(const World &newWorld) _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); + if (r->identity.address() == RR->identity.address()) { + _amRoot = true; + } else { + SharedPtr *rp = _peers.get(r->identity.address()); + if (rp) { + _rootPeers.push_back(*rp); + } else { + SharedPtr newrp(new Peer(RR->identity,r->identity)); + _peers.set(r->identity.address(),newrp); + _rootPeers.push_back(newrp); + } } } } diff --git a/node/Topology.hpp b/node/Topology.hpp index 48e264a87..324ec0004 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -225,6 +225,11 @@ public: return _peers.entries(); } + /** + * @return True if I am a root server in the current World + */ + inline bool amRoot() const throw() { return _amRoot; } + private: Identity _getIdentity(const Address &zta); void _setWorld(const World &newWorld); From e9648a6cdf5bd1d9f9e08c7cfef50265114c09d3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 14:05:12 -0700 Subject: [PATCH 115/195] Clarify logic in pinging, and prevent roots from pinging "down." --- node/Node.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/node/Node.cpp b/node/Node.cpp index 6eea3d3df..c54d783fe 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, From 35676217e8fea27d271bbc3b976165e1f8436da1 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 14:50:07 -0700 Subject: [PATCH 116/195] Refactor multicast group announcement to work directly or indirectly. --- node/Cluster.cpp | 6 ++- node/Network.cpp | 120 ++++++++++++++++++++++++++--------------------- node/Network.hpp | 5 +- 3 files changed, 73 insertions(+), 58 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 4088c9678..900804b77 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -449,11 +449,12 @@ void Cluster::replicateHavePeer(const Identity &peerId) void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group) { - Buffer<4096> buf; + Buffer<2048> buf; buf.append((uint64_t)nwid); peerAddress.appendTo(buf); group.mac().appendTo(buf); buf.append((uint32_t)group.adi()); + TRACE("replicating %s MULTICAST_LIKE %.16llx/%s/%u to all members",peerAddress.toString().c_str(),nwid,group.mac().toString().c_str(),(unsigned int)group.adi()); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -465,8 +466,9 @@ void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,co void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembership &com) { - Buffer<4096> buf; + Buffer<2048> buf; com.serialize(buf); + TRACE("replicating %s COM for %.16llx to all members",com.issuedTo().toString().c_str(),com.networkId()); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { diff --git a/node/Network.cpp b/node/Network.cpp index 46f932419..cd30e386d 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -144,7 +144,15 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg) bool Network::tryAnnounceMulticastGroupsTo(const SharedPtr &peer) { Mutex::Lock _l(_lock); - return _tryAnnounceMulticastGroupsTo(RR->topology->rootAddresses(),_allMulticastGroups(),peer,RR->node->now()); + if ( + (_isAllowed(peer)) || + (peer->address() == this->controller()) || + (RR->topology->isRoot(peer->identity())) + ) { + _announceMulticastGroupsTo(peer->address(),_allMulticastGroups()); + return true; + } + return false; } bool Network::applyConfiguration(const SharedPtr &conf) @@ -400,77 +408,80 @@ bool Network::_isAllowed(const SharedPtr &peer) const return false; // default position on any failure } -bool Network::_tryAnnounceMulticastGroupsTo(const std::vector

&alwaysAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const -{ - // assumes _lock is locked - if ( - (_isAllowed(peer)) || - (peer->address() == this->controller()) || - (std::find(alwaysAddresses.begin(),alwaysAddresses.end(),peer->address()) != alwaysAddresses.end()) - ) { - - if ((_config)&&(_config->com())&&(!_config->isPublic())&&(peer->needsOurNetworkMembershipCertificate(_id,now,true))) { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); - _config->com().serialize(outp); - outp.armor(peer->key(),true); - peer->send(RR,outp.data(),outp.size(),now); - } - - { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - - for(std::vector::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) { - if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { - outp.armor(peer->key(),true); - peer->send(RR,outp.data(),outp.size(),now); - outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - } - - // network ID, MAC, ADI - outp.append((uint64_t)_id); - mg->mac().appendTo(outp); - outp.append((uint32_t)mg->adi()); - } - - if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { - outp.armor(peer->key(),true); - peer->send(RR,outp.data(),outp.size(),now); - } - } - - return true; - } - return false; -} - -class _AnnounceMulticastGroupsToAll +class _GetPeersThatNeedMulticastAnnouncement { public: - _AnnounceMulticastGroupsToAll(const RuntimeEnvironment *renv,Network *nw) : + _GetPeersThatNeedMulticastAnnouncement(const RuntimeEnvironment *renv,Network *nw) : _now(renv->node->now()), + _controller(nw->controller()), _network(nw), - _rootAddresses(renv->topology->rootAddresses()), - _allMulticastGroups(nw->_allMulticastGroups()) + _rootAddresses(renv->topology->rootAddresses()) {} - - inline void operator()(Topology &t,const SharedPtr &p) { _network->_tryAnnounceMulticastGroupsTo(_rootAddresses,_allMulticastGroups,p,_now); } - + inline void operator()(Topology &t,const SharedPtr &p) + { + if ( + (_network->_isAllowed(p)) || + (p->address() == _controller) || + (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) + ) { + peers.push_back(p->address()); + } + } + std::vector
peers; private: uint64_t _now; + Address _controller; Network *_network; std::vector
_rootAddresses; - std::vector _allMulticastGroups; }; void Network::_announceMulticastGroups() { // Assumes _lock is locked - _AnnounceMulticastGroupsToAll afunc(RR,this); - RR->topology->eachPeer<_AnnounceMulticastGroupsToAll &>(afunc); + + _GetPeersThatNeedMulticastAnnouncement gpfunc(RR,this); + RR->topology->eachPeer<_GetPeersThatNeedMulticastAnnouncement &>(gpfunc); + + std::vector allMulticastGroups(_allMulticastGroups()); + for(std::vector
::const_iterator pa(gpfunc.peers.begin());pa!=gpfunc.peers.end();++pa) + _announceMulticastGroupsTo(*pa,allMulticastGroups); +} + +void Network::_announceMulticastGroupsTo(const Address &peerAddress,const std::vector &allMulticastGroups) const +{ + // Assumes _lock is locked + + // We push COMs ahead of MULTICAST_LIKE since they're used for access control -- a COM is a public + // credential so "over-sharing" isn't really an issue (and we only do so with roots). + if ((_config)&&(_config->com())&&(!_config->isPublic())) { + Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + _config->com().serialize(outp); + RR->sw->send(outp,true,0); + } + + { + Packet outp(peerAddress,RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + + for(std::vector::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) { + if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) { + RR->sw->send(outp,true,0); + outp.reset(peerAddress,RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + } + + // network ID, MAC, ADI + outp.append((uint64_t)_id); + mg->mac().appendTo(outp); + outp.append((uint32_t)mg->adi()); + } + + if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) + RR->sw->send(outp,true,0); + } } std::vector Network::_allMulticastGroups() const { // Assumes _lock is locked + std::vector mgs; mgs.reserve(_myMulticastGroups.size() + _multicastGroupsBehindMe.size() + 1); mgs.insert(mgs.end(),_myMulticastGroups.begin(),_myMulticastGroups.end()); @@ -479,6 +490,7 @@ std::vector Network::_allMulticastGroups() const mgs.push_back(Network::BROADCAST); std::sort(mgs.begin(),mgs.end()); mgs.erase(std::unique(mgs.begin(),mgs.end()),mgs.end()); + return mgs; } diff --git a/node/Network.hpp b/node/Network.hpp index f7939323e..0effa8e20 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -56,7 +56,7 @@ namespace ZeroTier { class RuntimeEnvironment; class Peer; -class _AnnounceMulticastGroupsToAll; // internal function object in Network.cpp +class _GetPeersThatNeedMulticastAnnouncement; /** * A virtual LAN @@ -64,7 +64,7 @@ class _AnnounceMulticastGroupsToAll; // internal function object in Network.cpp class Network : NonCopyable { friend class SharedPtr; - friend class _AnnounceMulticastGroupsToAll; + friend class _GetPeersThatNeedMulticastAnnouncement; // internal function object public: /** @@ -344,6 +344,7 @@ private: bool _isAllowed(const SharedPtr &peer) const; bool _tryAnnounceMulticastGroupsTo(const std::vector
&rootAddresses,const std::vector &allMulticastGroups,const SharedPtr &peer,uint64_t now) const; void _announceMulticastGroups(); + void _announceMulticastGroupsTo(const Address &peerAddress,const std::vector &allMulticastGroups) const; std::vector _allMulticastGroups() const; const RuntimeEnvironment *RR; From d2b1dfe42465680e10e7971ba99279e5cacad207 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 23 Oct 2015 15:51:50 -0700 Subject: [PATCH 117/195] Fully specify new network in alice-test, this will (with different identities) eventually become the World. --- node/Topology.cpp | 5 ----- world/alice-test/mkworld.cpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index adfcd1f99..624c4987e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -35,14 +35,9 @@ namespace ZeroTier { -// Default World #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}; -// ALICE-TEST -//#define ZT_DEFAULT_WORLD_LENGTH 257 -//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0x81,0x2a,0x54,0x6f,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0x63,0x8b,0xb3,0xb6,0x72,0x45,0xa9,0x81,0x81,0xcc,0xea,0x7f,0x2f,0xd9,0x59,0xce,0xc8,0x51,0x12,0xc3,0xe3,0x44,0x76,0x54,0xed,0xe7,0x8d,0x34,0x0b,0x5d,0x10,0x3d,0x52,0x04,0x9b,0xe1,0xb2,0x36,0x51,0x75,0x14,0x30,0x53,0xe8,0x4b,0xe4,0x91,0x9a,0xed,0x99,0x56,0xa3,0x8d,0x5e,0x14,0xff,0x66,0xd8,0x4f,0xf7,0x3c,0x23,0xbe,0x02,0xbb,0x1e,0xb6,0x7e,0x07,0xfa,0x7c,0x7e,0x50,0xe8,0x40,0xf9,0x37,0x70,0x1a,0x75,0xcf,0x19,0xe6,0x83,0xe1,0x5c,0x20,0x1d,0x1e,0x5b,0xe5,0x6a,0xbe,0xe7,0xab,0xec,0x01,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x01,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09}; - Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), _amRoot(false) diff --git a/world/alice-test/mkworld.cpp b/world/alice-test/mkworld.cpp index e680b97ee..8940db2cf 100644 --- a/world/alice-test/mkworld.cpp +++ b/world/alice-test/mkworld.cpp @@ -133,13 +133,35 @@ int main(int argc,char **argv) std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); #endif - // ALICE TEST + // NOTE -- these are temporary test identities -- this is not yet the 'real' network. + // (but these are the real nodes) + + // Alice -- global geo-clustered root #1 roots.push_back(World::Root()); roots.back().identity = Identity("d6ddca6ab5:0:4e761207d8b4200be44f478e3da148c16099110ee71b64586dda118e4022ab63682ce137da8ba817fc7f73aa3163f2e333933e2994c46b4f4119307be8855a72"); - roots.back().stableEndpoints.push_back(InetAddress("169.57.143.104/9993")); - std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + roots.back().stableEndpoints.push_back(InetAddress("188.166.94.177/9993")); // Amsterdam IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:2:d0::7d:1/9993")); // Amsterdam IPv6 + roots.back().stableEndpoints.push_back(InetAddress("159.203.97.171/9993")); // New York IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2604:a880:800:a1::54:6001/9993 ")); // New York IPv6 + roots.back().stableEndpoints.push_back(InetAddress("169.57.143.104/9993")); // Sao Paolo IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2607:f0d0:1d01:57::2/9993")); // Sao Paolo IPv6 + roots.back().stableEndpoints.push_back(InetAddress("104.238.182.83/9993")); // San Francisco IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:ac00:809:5400:ff:fe15:f3f4/9993")); // San Francisco IPv6 + roots.back().stableEndpoints.push_back(InetAddress("128.199.182.9/9993")); // Singapore IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2400:6180:0:d0::1b:1001/9993")); // Singapore IPv6 - std::sort(roots.begin(),roots.end()); + // Bob -- global geo-clustered root #2 + roots.back().identity = Identity("16ebbd6c5d:0:47d39bca9d0a5cf70148e39f6c45199e17e0e32e4e46cac01ae5bcb21224137b097f40bdd982a921c3aabdcb9ada8b4f2bb0593753bfdb21cf12eac28c8d9042"); + roots.back().stableEndpoints.push_back(InetAddress("45.33.4.67/9993")); // Dallas IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2600:3c00::f03c:91ff:fe67:b704/9993")); // Dallas IPv6 + roots.back().stableEndpoints.push_back(InetAddress("139.162.157.243/9993")); // Frankfurt (Germany) IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2a01:7e01::f03c:91ff:fe67:3ffd/9993")); // Frankfurt (Germany) IPv6 + roots.back().stableEndpoints.push_back(InetAddress("45.32.246.179/9993")); // Sydney IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:5800:8bf8:5400:ff:fe15:b39a/9993")); // Sydney IPv6 + roots.back().stableEndpoints.push_back(InetAddress("45.32.248.87/9993")); // Tokyo IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:7000:9bc9:5400:00ff:fe15:c4f5/9993")); // Tokyo IPv6 + roots.back().stableEndpoints.push_back(InetAddress("159.203.2.154/9993")); // Toronto IPv4 + roots.back().stableEndpoints.push_back(InetAddress("2604:a880:cad:d0::26:7001/9993")); // Toronto IPv6 const uint64_t id = ZT_WORLD_ID_EARTH; const uint64_t ts = OSUtils::now(); From 3ce5ad9e2c1a31e1a3cc34f0ac8d950e4f0cf7b4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 10:42:30 -0700 Subject: [PATCH 118/195] For forward compatibility, add minimal parse for some future physical address types. --- node/InetAddress.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 50db272ac..ecafcf51f 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -409,6 +409,16 @@ struct InetAddress : public sockaddr_storage switch(b[p++]) { case 0: return 1; + case 0x01: + // TODO: Ethernet address (but accept for forward compatibility) + return 7; + case 0x02: + // TODO: Bluetooth address (but accept for forward compatibility) + return 7; + case 0x03: + // TODO: Other address types (but accept for forward compatibility) + // These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc. + return (unsigned int)(b.template at(p) + 3); // other addresses begin with 16-bit non-inclusive length case 0x04: ss_family = AF_INET; memcpy(&(reinterpret_cast(this)->sin_addr.s_addr),b.field(p,4),4); p += 4; From 865acfa40f65626f41bbd718b645f57c7d6db9b2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 12:41:08 -0700 Subject: [PATCH 119/195] Cluster status plumbing. --- include/ZeroTierOne.h | 77 +++++++++++++++++++++++++++++++++++++++++++ node/Cluster.cpp | 56 +++++++++++++++++++++++++++++++ node/Cluster.hpp | 7 ++++ node/Topology.cpp | 15 +++++++++ node/Topology.hpp | 5 +++ 5 files changed, 160 insertions(+) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 4de4a765c..86bffcdf8 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -133,6 +133,11 @@ extern "C" { */ #define ZT_CLUSTER_MAX_MEMBERS 128 +/** + * Maximum number of physical ZeroTier addresses a cluster member can report + */ +#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16 + /** * Maximum allowed cluster message length in bytes */ @@ -879,6 +884,78 @@ typedef struct { unsigned int nextHopCount; } ZT_CircuitTestReport; +/** + * A cluster member's status + */ +typedef struct { + /** + * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS) + */ + unsigned int id; + + /** + * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address + */ + unsigned int msSinceLastHeartbeat; + + /** + * Non-zero if cluster member is alive + */ + int alive; + + /** + * X, Y, and Z coordinates of this member (if specified, otherwise zero) + * + * What these mean depends on the location scheme being used for + * location-aware clustering. At present this is GeoIP and these + * will be the X, Y, and Z coordinates of the location on a spherical + * approximation of Earth where Earth's core is the origin (in km). + * They don't have to be perfect and need only be comparable with others + * to find shortest path via the standard vector distance formula. + */ + int x,y,z; + + /** + * Cluster member's last reported load + */ + uint64_t load; + + /** + * Number of peers this cluster member "has" + */ + uint64_t peers; + + /** + * Physical ZeroTier endpoints for this member (where peers are sent when directed here) + */ + struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES]; + + /** + * Number of physical ZeroTier endpoints this member is announcing + */ + unsigned int numZeroTierPhysicalEndpoints; +} ZT_ClusterMemberStatus; + +/** + * ZeroTier cluster status + */ +typedef struct { + /** + * My cluster member ID (a record for 'self' is included in member[]) + */ + unsigned int myId; + + /** + * Number of cluster members + */ + unsigned int clusterSize; + + /** + * Cluster member statuses + */ + ZT_ClusterMemberStatus member[ZT_CLUSTER_MAX_MEMBERS]; +} ZT_ClusterStatus; + /** * An instance of a ZeroTier One node (opaque) */ diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 900804b77..477942141 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -638,6 +638,62 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy } } +void Cluster::status(ZT_ClusterStatus &status) const +{ + const uint64_t now = RR->node->now(); + memset(&status,0,sizeof(ZT_ClusterStatus)); + ZT_ClusterMemberStatus *ms[ZT_CLUSTER_MAX_MEMBERS]; + memset(ms,0,sizeof(ms)); + + status.myId = _id; + + ms[_id] = &(status.member[status.clusterSize++]); + ms[_id]->id = _id; + ms[_id]->alive = 1; + ms[_id]->x = _x; + ms[_id]->y = _y; + ms[_id]->z = _z; + ms[_id]->peers = RR->topology->countAlive(); + for(std::vector::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) { + if (ms[_id]->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check + break; + memcpy(&(ms[_id]->zeroTierPhysicalEndpoints[ms[_id]->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage)); + } + + { + Mutex::Lock _l1(_memberIds_m); + for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { + if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check + break; + ZT_ClusterMemberStatus *s = ms[*mid] = &(status.member[status.clusterSize++]); + _Member &m = _members[*mid]; + Mutex::Lock ml(m.lock); + + s->id = *mid; + s->msSinceLastHeartbeat = (unsigned int)std::min((uint64_t)(~((unsigned int)0)),(now - m.lastReceivedAliveAnnouncement)); + s->alive = (s->msSinceLastHeartbeat < ZT_CLUSTER_TIMEOUT) ? 1 : 0; + s->x = m.x; + s->y = m.y; + s->z = m.z; + s->load = m.load; + for(std::vector::const_iterator ep(m.zeroTierPhysicalEndpoints.begin());ep!=m.zeroTierPhysicalEndpoints.end();++ep) { + if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check + break; + memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage)); + } + } + } + + { + Mutex::Lock _l2(_peerAffinities_m); + for(std::vector<_PeerAffinity>::const_iterator pi(_peerAffinities.begin());pi!=_peerAffinities.end();++pi) { + unsigned int mid = pi->clusterMemberId(); + if ((ms[mid])&&(mid != _id)&&((now - pi->timestamp) < ZT_PEER_ACTIVITY_TIMEOUT)) + ++ms[mid]->peers; + } + } +} + void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len) { if ((len + 3) > (ZT_CLUSTER_MAX_MESSAGE_LENGTH - (24 + 2 + 2))) // sanity check diff --git a/node/Cluster.hpp b/node/Cluster.hpp index daa811858..be3466593 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -254,6 +254,13 @@ public: */ bool redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); + /** + * Fill out ZT_ClusterStatus structure (from core API) + * + * @param status Reference to structure to hold result (anything there is replaced) + */ + void status(ZT_ClusterStatus &status) const; + private: void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len); void _flush(uint16_t memberId); diff --git a/node/Topology.cpp b/node/Topology.cpp index 624c4987e..e56d1f47b 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -332,6 +332,21 @@ void Topology::clean(uint64_t now) } } +unsigned long Topology::countAlive() 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)) { + if ((*p)->alive(now)) + ++cnt; + } + return cnt; +} + Identity Topology::_getIdentity(const Address &zta) { char p[128]; diff --git a/node/Topology.hpp b/node/Topology.hpp index 324ec0004..ee9827b95 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -192,6 +192,11 @@ public: */ void clean(uint64_t now); + /** + * @return Number of 'alive' peers + */ + unsigned long countAlive() const; + /** * Apply a function or function object to all peers * From 5ff7733f84302bbd0ce0a578b3040122d1140f0f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 12:49:17 -0700 Subject: [PATCH 120/195] More plumbing of cluster status. --- include/ZeroTierOne.h | 11 ++++++++++ node/Node.cpp | 48 +++++++++++++++++-------------------------- node/Node.hpp | 1 + 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 86bffcdf8..68c1e4d72 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 c54d783fe..2b2989039 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 b8bd4dc54..4094a79e9 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 ------------------------------------------------------ From debed1ac2dce5822df234ce92bf4692ff1a081db Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 13:06:10 -0700 Subject: [PATCH 121/195] Expose cluster status in /status JSON response. --- include/ZeroTierOne.h | 2 +- node/Cluster.cpp | 4 ++-- service/ControlPlane.cpp | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 68c1e4d72..7af4f7601 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -953,7 +953,7 @@ typedef struct { /** * Cluster member statuses */ - ZT_ClusterMemberStatus member[ZT_CLUSTER_MAX_MEMBERS]; + ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS]; } ZT_ClusterStatus; /** diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 477942141..9d25593aa 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -647,7 +647,7 @@ void Cluster::status(ZT_ClusterStatus &status) const status.myId = _id; - ms[_id] = &(status.member[status.clusterSize++]); + ms[_id] = &(status.members[status.clusterSize++]); ms[_id]->id = _id; ms[_id]->alive = 1; ms[_id]->x = _x; @@ -665,7 +665,7 @@ void Cluster::status(ZT_ClusterStatus &status) const for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check break; - ZT_ClusterMemberStatus *s = ms[*mid] = &(status.member[status.clusterSize++]); + ZT_ClusterMemberStatus *s = ms[*mid] = &(status.members[status.clusterSize++]); _Member &m = _members[*mid]; Mutex::Lock ml(m.lock); diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 7affb08c6..9770db908 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -354,8 +354,36 @@ unsigned int ControlPlane::handleRequest( if (ps[0] == "status") { responseContentType = "application/json"; + ZT_NodeStatus status; _node->status(&status); + + std::string clusterJson; +#ifdef ZT_ENABLE_CLUSTER + { + ZT_ClusterStatus cs; + _node->clusterStatus(&cs); + + char t[4096]; + Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members: [\n",cs.myId,cs.clusterSize); + clusterJson.append(t); + for(unsigned int i=0;i 0) ? clusterJson.c_str() : "null")); responseBody = json; scode = 200; } else if (ps[0] == "config") { From 6625d7929654803f99b7a69f56a400046314acac Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 13:07:00 -0700 Subject: [PATCH 122/195] Fix if cluster compiled in but not enabled. --- service/ControlPlane.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 9770db908..31eca7b61 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -364,23 +364,25 @@ unsigned int ControlPlane::handleRequest( ZT_ClusterStatus cs; _node->clusterStatus(&cs); - char t[4096]; - Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members: [\n",cs.myId,cs.clusterSize); - clusterJson.append(t); - for(unsigned int i=0;i= 1) { + char t[4096]; + Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members: [\n",cs.myId,cs.clusterSize); clusterJson.append(t); + for(unsigned int i=0;i Date: Mon, 26 Oct 2015 15:12:28 -0700 Subject: [PATCH 123/195] Add the whole new World, though with test identities at this point. --- node/Topology.cpp | 8 ++++++-- world/alice-test/alice-test.bin | Bin 257 -> 510 bytes world/alice-test/alice-test.out | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index e56d1f47b..2114e5a73 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -35,8 +35,12 @@ namespace ZeroTier { -#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}; +// Old root servers +//#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}; + +#define ZT_DEFAULT_WORLD_LENGTH 510 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x29,0x29,0xcf,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe9,0x3d,0x15,0xbb,0x20,0x3e,0xd4,0x08,0xc2,0xec,0xd4,0xfd,0xe3,0x49,0x9c,0x3a,0xe2,0x33,0xd4,0x7d,0x62,0xc1,0xec,0x1b,0x74,0x97,0xb7,0x7d,0x2a,0x3e,0xff,0xe5,0x94,0x70,0xe6,0x51,0x6b,0xc2,0x90,0x80,0x50,0x42,0x7e,0x83,0xde,0xe3,0x3a,0xa4,0xc8,0xfb,0xce,0x2b,0x06,0x5b,0x0a,0xf4,0xca,0x1c,0x41,0x3c,0x2a,0x65,0x1c,0x01,0xe3,0x13,0x79,0x9f,0xd1,0xff,0x13,0xa5,0x51,0xf9,0x3e,0x03,0xf6,0x71,0x84,0xed,0x78,0x42,0x82,0x81,0xe0,0x90,0x30,0x7a,0x78,0xd8,0x1b,0x53,0x15,0x49,0x29,0x62,0x01,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x14,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), diff --git a/world/alice-test/alice-test.bin b/world/alice-test/alice-test.bin index 2fdaf7b3066231ea0663e5da6f566610b3e43ad3..eebe6aaf357ed2f6fac641724b206ec05f4aefa2 100644 GIT binary patch delta 458 zcmZo<`o}ED$N&T!uTF9?Ffaxz)6_gaQP83OrLE|01-mO8hu&QI``B}i)g$98wMhrx zNS93CUaMvI|LK&1XMx#=CNu;%)ivLHY_;UX?{nI0(Oh3n$vE0*rOGfq7OtFs@xSoW zz@K)^-wIpaRyZ{^KA2!oRdGW)SkzN9iBatJ-kewl_sg?S&E<;u&gk)YevYf;Jn;vQ z_59pU9jKRjx@VJ+if}b&y~Ey{O)C`-uiATh)~#-T?G2IU!TWD3o)>y`sHb;=6N3oL zo@H?x)j8R;m^U0?U}Crc@(n{R1EV@8%ly-ctAR3VEGrr~7#J3UWkM1_GAk|nGe9!z zA1=r;GK7O=7#Nsz`vneo2epti6!5PB9@c*Ca=Py91hU42fftpkp5*vUT zAQnjrfQ-{sWN`)=$6y0C>4VM0|NqjrvjAnf7tQ?)l+j|W0|zri#{MrzkHWXjKpBud u5e(fwAogsY1(x|?5Dt|pV3>UpB6Z{|P-^~ZCZGzik9gKXouF0#bOHd)zn_c% delta 203 zcmV;+05t#p1Azh&0RR9100`>I3IG5BP=P8`Z;=r|e`AZYwsJ+Ofq~5Ge=pft&d5;` z!{bDDRPE=DGz(o2JyHal;j%VSbrdjD=u700n(diZqm5n^|7O@v_dFxM0=pizeh2z| zeo*K@`8RMHbI>;cx@%4)R$PIeLp*t8%ETrrjzA)-V`wblH`>!DU`%(PeB Date: Mon, 26 Oct 2015 15:47:32 -0700 Subject: [PATCH 124/195] Fix test world def. --- node/Switch.cpp | 7 +++++++ node/Topology.cpp | 4 ++-- world/alice-test/alice-test.bin | Bin 510 -> 582 bytes world/alice-test/alice-test.out | 8 ++++---- world/alice-test/mkworld.cpp | 1 + 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/node/Switch.cpp b/node/Switch.cpp index 0edaa96d2..709ed802a 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -295,6 +295,8 @@ void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid) return; } + //TRACE(">> %s to %s (%u bytes, encrypt==%d, nwid==%.16llx)",Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(int)encrypt,nwid); + if (!_trySend(packet,encrypt,nwid)) { Mutex::Lock _l(_txQueue_m); _txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid)); @@ -637,6 +639,11 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr Address source(packet->source()); Address destination(packet->destination()); + // Catch this and toss it -- it would never work, but it could happen if we somehow + // mistakenly guessed an address we're bound to as a destination for another peer. + if (source == RR->identity.address()) + return; + //TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size()); if (destination != RR->identity.address()) { diff --git a/node/Topology.cpp b/node/Topology.cpp index 2114e5a73..ff4b7225e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -39,8 +39,8 @@ namespace ZeroTier { //#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}; -#define ZT_DEFAULT_WORLD_LENGTH 510 -static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x29,0x29,0xcf,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe9,0x3d,0x15,0xbb,0x20,0x3e,0xd4,0x08,0xc2,0xec,0xd4,0xfd,0xe3,0x49,0x9c,0x3a,0xe2,0x33,0xd4,0x7d,0x62,0xc1,0xec,0x1b,0x74,0x97,0xb7,0x7d,0x2a,0x3e,0xff,0xe5,0x94,0x70,0xe6,0x51,0x6b,0xc2,0x90,0x80,0x50,0x42,0x7e,0x83,0xde,0xe3,0x3a,0xa4,0xc8,0xfb,0xce,0x2b,0x06,0x5b,0x0a,0xf4,0xca,0x1c,0x41,0x3c,0x2a,0x65,0x1c,0x01,0xe3,0x13,0x79,0x9f,0xd1,0xff,0x13,0xa5,0x51,0xf9,0x3e,0x03,0xf6,0x71,0x84,0xed,0x78,0x42,0x82,0x81,0xe0,0x90,0x30,0x7a,0x78,0xd8,0x1b,0x53,0x15,0x49,0x29,0x62,0x01,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x14,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; +#define ZT_DEFAULT_WORLD_LENGTH 582 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x54,0xe4,0x8e,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe4,0xe9,0x51,0xc1,0xe1,0x39,0x50,0xcd,0x17,0x82,0x9d,0x74,0xf1,0xa9,0x5b,0x03,0x14,0x2c,0xa7,0xc0,0x7f,0x21,0x8b,0xad,0xdd,0xa5,0x04,0x26,0x35,0xa6,0xab,0xc1,0x49,0x64,0x2c,0xda,0x65,0x52,0x77,0xf3,0xf0,0x70,0x00,0xcd,0xc3,0xff,0x3b,0x19,0x77,0x4c,0xab,0xb6,0x35,0xbb,0x77,0xcf,0x54,0xe5,0x6d,0x01,0x9d,0x43,0x92,0x0a,0x6d,0x00,0x23,0x8e,0x0a,0x3d,0xba,0x36,0xc3,0xa1,0xa4,0xad,0x13,0x8f,0x46,0xff,0xcc,0x8f,0x9e,0xc2,0x3c,0x06,0xf8,0x3b,0xf3,0xa2,0x5f,0x71,0xcc,0x07,0x35,0x7f,0x02,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x0a,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x0a,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; Topology::Topology(const RuntimeEnvironment *renv) : RR(renv), diff --git a/world/alice-test/alice-test.bin b/world/alice-test/alice-test.bin index eebe6aaf357ed2f6fac641724b206ec05f4aefa2..910f036f3c6fd88853fc0d41615ae51dc92edd29 100644 GIT binary patch delta 208 zcmV;>05AXk1I7dq0RR9100`>I3IG5BP^MJmj*$^Sf8^;=!QnYj%@=~5bn&TM0~9Q$ zz<(i&t=**rCN-w3!AWE++GSFA^YCy0&BOmY8Fx&pwl%wV&s61Y0i8pV3T*%*jtV`x zHp8K$trL$%|ICk`!aN4}JM*Gnam)ube*)Is%4)R$PIeLp*t8%EkjzA)-V`wblH`>!DU`%(PeBI3IG5BP^Kv<&yf*8Vd*^;yC6Q)2*T{t{o_fTI^r|b zePY4v8+4bqeJVcx<&<#dQES4GfKWnygWlshq{#cuD+XH%^vWDTJSt@z0pk;SpV9vl qrBV4l1NL!*?RY|hf#8rZdU)6yQx!=mVgZwU0Y?E8k@|v@!~uf113YN} diff --git a/world/alice-test/alice-test.out b/world/alice-test/alice-test.out index 61d089af5..6b9313ce5 100644 --- a/world/alice-test/alice-test.out +++ b/world/alice-test/alice-test.out @@ -1,5 +1,5 @@ -INFO: generating and signing id==149604618 ts==1445896726991 -INFO: wrote 510 bytes to stdout +INFO: generating and signing id==149604618 ts==1445899592846 +INFO: wrote 582 bytes to stdout -#define ZT_DEFAULT_WORLD_LENGTH 510 -static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x29,0x29,0xcf,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe9,0x3d,0x15,0xbb,0x20,0x3e,0xd4,0x08,0xc2,0xec,0xd4,0xfd,0xe3,0x49,0x9c,0x3a,0xe2,0x33,0xd4,0x7d,0x62,0xc1,0xec,0x1b,0x74,0x97,0xb7,0x7d,0x2a,0x3e,0xff,0xe5,0x94,0x70,0xe6,0x51,0x6b,0xc2,0x90,0x80,0x50,0x42,0x7e,0x83,0xde,0xe3,0x3a,0xa4,0xc8,0xfb,0xce,0x2b,0x06,0x5b,0x0a,0xf4,0xca,0x1c,0x41,0x3c,0x2a,0x65,0x1c,0x01,0xe3,0x13,0x79,0x9f,0xd1,0xff,0x13,0xa5,0x51,0xf9,0x3e,0x03,0xf6,0x71,0x84,0xed,0x78,0x42,0x82,0x81,0xe0,0x90,0x30,0x7a,0x78,0xd8,0x1b,0x53,0x15,0x49,0x29,0x62,0x01,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x14,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; +#define ZT_DEFAULT_WORLD_LENGTH 582 +static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x54,0xe4,0x8e,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe4,0xe9,0x51,0xc1,0xe1,0x39,0x50,0xcd,0x17,0x82,0x9d,0x74,0xf1,0xa9,0x5b,0x03,0x14,0x2c,0xa7,0xc0,0x7f,0x21,0x8b,0xad,0xdd,0xa5,0x04,0x26,0x35,0xa6,0xab,0xc1,0x49,0x64,0x2c,0xda,0x65,0x52,0x77,0xf3,0xf0,0x70,0x00,0xcd,0xc3,0xff,0x3b,0x19,0x77,0x4c,0xab,0xb6,0x35,0xbb,0x77,0xcf,0x54,0xe5,0x6d,0x01,0x9d,0x43,0x92,0x0a,0x6d,0x00,0x23,0x8e,0x0a,0x3d,0xba,0x36,0xc3,0xa1,0xa4,0xad,0x13,0x8f,0x46,0xff,0xcc,0x8f,0x9e,0xc2,0x3c,0x06,0xf8,0x3b,0xf3,0xa2,0x5f,0x71,0xcc,0x07,0x35,0x7f,0x02,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x0a,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x0a,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; diff --git a/world/alice-test/mkworld.cpp b/world/alice-test/mkworld.cpp index 8940db2cf..277105c34 100644 --- a/world/alice-test/mkworld.cpp +++ b/world/alice-test/mkworld.cpp @@ -151,6 +151,7 @@ int main(int argc,char **argv) roots.back().stableEndpoints.push_back(InetAddress("2400:6180:0:d0::1b:1001/9993")); // Singapore IPv6 // Bob -- global geo-clustered root #2 + roots.push_back(World::Root()); roots.back().identity = Identity("16ebbd6c5d:0:47d39bca9d0a5cf70148e39f6c45199e17e0e32e4e46cac01ae5bcb21224137b097f40bdd982a921c3aabdcb9ada8b4f2bb0593753bfdb21cf12eac28c8d9042"); roots.back().stableEndpoints.push_back(InetAddress("45.33.4.67/9993")); // Dallas IPv4 roots.back().stableEndpoints.push_back(InetAddress("2600:3c00::f03c:91ff:fe67:b704/9993")); // Dallas IPv6 From 0b82c9ebad55181b9d668498371be84fb5c81b01 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 16:09:56 -0700 Subject: [PATCH 125/195] Fix infinite loop if there are no live roots (never happened before?!? wow!) --- node/Topology.cpp | 16 +++++++--------- service/ControlPlane.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index ff4b7225e..6a72cf8c3 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -202,18 +202,16 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou * circumnavigate the globe rather than bouncing between just two. */ if (_rootAddresses.size() > 1) { // gotta be one other than me for this to work - std::vector
::const_iterator sna(std::find(_rootAddresses.begin(),_rootAddresses.end(),RR->identity.address())); - if (sna != _rootAddresses.end()) { // sanity check -- _amRoot should've been false in this case - for(;;) { - if (++sna == _rootAddresses.end()) - sna = _rootAddresses.begin(); // wrap around at end - if (*sna != RR->identity.address()) { // pick one other than us -- starting from me+1 in sorted set order - SharedPtr *p = _peers.get(*sna); - if ((p)&&((*p)->hasActiveDirectPath(now))) { - bestRoot = *p; + for(unsigned long p=0;p<_rootAddresses.size();++p) { + if (_rootAddresses[p] == RR->identity.address()) { + for(unsigned long q=1;q<_rootAddresses.size();++q) { + SharedPtr *nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]); + if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) { + bestRoot = *nextsn; break; } } + break; } } } diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 31eca7b61..4978a91d2 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -265,7 +265,7 @@ unsigned int ControlPlane::handleRequest( std::string &responseBody, std::string &responseContentType) { - char json[1024]; + char json[8194]; unsigned int scode = 404; std::vector ps(Utils::split(path.c_str(),"/","","")); std::map urlArgs; @@ -365,11 +365,12 @@ unsigned int ControlPlane::handleRequest( _node->clusterStatus(&cs); if (cs.clusterSize >= 1) { - char t[4096]; - Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members: [\n",cs.myId,cs.clusterSize); + char t[1024]; + Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members\": [",cs.myId,cs.clusterSize); clusterJson.append(t); for(unsigned int i=0;i Date: Mon, 26 Oct 2015 16:55:55 -0700 Subject: [PATCH 126/195] Only send redirects for the same address class, and elminiate some TRACE noise. --- node/Cluster.cpp | 12 +++++++----- node/Peer.cpp | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 9d25593aa..729461e86 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -200,10 +200,12 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } #endif } - m.lastReceivedAliveAnnouncement = RR->node->now(); #ifdef ZT_TRACE - TRACE("[%u] I'm alive! peers close to %d,%d,%d can be redirected to: %s",(unsigned int)fromMemberId,m.x,m.y,m.z,addrs.c_str()); + if ((RR->node->now() - m.lastReceivedAliveAnnouncement) >= ZT_CLUSTER_TIMEOUT) { + TRACE("[%u] I'm alive! peers close to %d,%d,%d can be redirected to: %s",(unsigned int)fromMemberId,m.x,m.y,m.z,addrs.c_str()); + } #endif + m.lastReceivedAliveAnnouncement = RR->node->now(); } break; case STATE_MESSAGE_HAVE_PEER: { @@ -583,7 +585,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); double bestDistance = (offload ? 2147483648.0 : currentDistance); unsigned int bestMember = _id; - TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); + //TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -610,7 +612,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy } 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)) { + if (a->ss_family == peerPhysicalAddress.ss_family) { Packet outp(peerAddress,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! @@ -629,7 +631,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy return true; } else { - TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peerAddress.toString().c_str(),px,py,pz,currentDistance); + //TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peerAddress.toString().c_str(),px,py,pz,currentDistance); return false; } } else { diff --git a/node/Peer.cpp b/node/Peer.cpp index fa7b3aa40..e4f0767ec 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -201,16 +201,16 @@ 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()); + //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()); + //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(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf)); p->sent(now); } else { - TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived()); + //TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived()); } return true; } From 98d856daa2488d3589cba058ec2d74e41dc53287 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 17:58:51 -0700 Subject: [PATCH 127/195] Only send redirects to the sending InetAddress and only in response to a set of certain frame types to avoid potential race conditions. --- node/Cluster.cpp | 16 ++++---- node/Cluster.hpp | 5 ++- node/Peer.cpp | 100 +++++++++++++++++++++++------------------------ 3 files changed, 62 insertions(+), 59 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 729461e86..d4e81ff8b 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -566,7 +566,7 @@ void Cluster::removeMember(uint16_t memberId) _memberIds = newMemberIds; } -bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload) +bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &localAddress,const InetAddress &peerPhysicalAddress,bool offload) { if (!peerPhysicalAddress) // sanity check return false; @@ -575,7 +575,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy // 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) { - TRACE("no geolocation available for %s",peerPhysicalAddress.toIpString().c_str()); + TRACE("NO GEOLOCATION available for %s",peerPhysicalAddress.toIpString().c_str()); return false; } @@ -585,7 +585,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); double bestDistance = (offload ? 2147483648.0 : currentDistance); unsigned int bestMember = _id; - //TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); + TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -605,7 +605,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy } if (best.size() > 0) { - TRACE("%s seems closer to %u at %fkm, suggesting redirect...",peerAddress.toString().c_str(),bestMember,bestDistance); + TRACE("%s seems closer to %u at %fkm, suggesting redirect...",peer->address().toString().c_str(),bestMember,bestDistance); /* if (peer->remoteVersionProtocol() >= 5) { // If it's a newer peer send VERB_PUSH_DIRECT_PATHS which is more idiomatic @@ -613,7 +613,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy // 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 == peerPhysicalAddress.ss_family) { - Packet outp(peerAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS); + 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()); @@ -624,14 +624,16 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy outp.append((uint8_t)16); outp.append(a->rawIpData(),16); } - RR->sw->send(outp,true,0); + outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddress,peerPhysicalAddress,outp.data(),outp.size()); } } //} return true; } else { - //TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peerAddress.toString().c_str(),px,py,pz,currentDistance); + //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 { diff --git a/node/Cluster.hpp b/node/Cluster.hpp index be3466593..b1266f27f 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -247,12 +247,13 @@ public: /** * Redirect this peer to a better cluster member if needed * - * @param peerAddress Peer to (possibly) redirect + * @param peer Peer to (possibly) redirect + * @param localAddress Local address for path or NULL for none/any * @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 Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); + bool redirectPeer(const SharedPtr &peer,const InetAddress &localAddress,const InetAddress &peerPhysicalAddress,bool offload); /** * Fill out ZT_ClusterStatus structure (from core API) diff --git a/node/Peer.cpp b/node/Peer.cpp index e4f0767ec..6cd9baabb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -80,64 +80,67 @@ void Peer::received( uint64_t inRePacketId, Packet::Verb inReVerb) { +#ifdef ZT_ENABLE_CLUSTER + if ((RR->cluster)&&(hops == 0)&&((verb == Packet::VERB_HELLO)||(verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)||(verb == Packet::VERB_MULTICAST_FRAME))) { + if (RR->cluster->redirectPeer(SharedPtr(this),localAddr,remoteAddr,false)) + return; + } +#endif + const uint64_t now = RR->node->now(); bool needMulticastGroupAnnounce = false; bool pathIsConfirmed = false; - { + { // begin _lock Mutex::Lock _l(_lock); _lastReceive = now; - if (!hops) { - /* Learn new paths from direct (hops == 0) packets */ - { - unsigned int np = _numPaths; - for(unsigned int p=0;preceived(now); - _numPaths = np; - pathIsConfirmed = true; - _sortPaths(now); - } - - } else { - - /* If this path is not known, send a HELLO. We don't learn - * paths without confirming that a bidirectional link is in - * fact present, but any packet that decodes and authenticates - * correctly is considered valid. */ - 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); - } - } + if (slot) { + *slot = RemotePath(localAddr,remoteAddr); + slot->received(now); + _numPaths = np; + pathIsConfirmed = true; + _sortPaths(now); + } + + } else { + + /* If this path is not known, send a HELLO. We don't learn + * paths without confirming that a bidirectional link is in + * fact present, but any packet that decodes and authenticates + * correctly is considered valid. */ + 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); + } + } } } @@ -151,14 +154,11 @@ void Peer::received( _lastUnicastFrame = now; else if (verb == Packet::VERB_MULTICAST_FRAME) _lastMulticastFrame = now; - } + } // end _lock #ifdef ZT_ENABLE_CLUSTER - if ((pathIsConfirmed)&&(RR->cluster)) { - // Either shuttle this peer off somewhere else or report to other members that we have it - if (!RR->cluster->redirectPeer(_id.address(),remoteAddr,false)) - RR->cluster->replicateHavePeer(_id); - } + if ((RR->cluster)&&(pathIsConfirmed)) + RR->cluster->replicateHavePeer(_id); #endif if (needMulticastGroupAnnounce) { From e713f7a54c02915120ac3c32e0f28bd1dd744a80 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 18:20:40 -0700 Subject: [PATCH 128/195] Can redirect in response to a few more verbs, just not these. --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index 6cd9baabb..4f2fe9314 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -81,7 +81,7 @@ void Peer::received( Packet::Verb inReVerb) { #ifdef ZT_ENABLE_CLUSTER - if ((RR->cluster)&&(hops == 0)&&((verb == Packet::VERB_HELLO)||(verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)||(verb == Packet::VERB_MULTICAST_FRAME))) { + if ((RR->cluster)&&(hops == 0)&&(verb != VERB_OK)&&(verb != VERB_ERROR)&&(verb != VERB_RENDEZVOUS)&&(verb != VERB_PUSH_DIRECT_PATHS)) { if (RR->cluster->redirectPeer(SharedPtr(this),localAddr,remoteAddr,false)) return; } From 69857b4ba8bb6ce341ca6dcdc03759fb901a831a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 09:36:48 -0700 Subject: [PATCH 129/195] Refactor cluster redirects to move code to push peers out of the actual Cluster function that checks for redirect, and clean up Peer::received() to be a bit more logical. --- node/Cluster.cpp | 54 +++++++++++++----------------------------------- node/Cluster.hpp | 17 ++++++++++----- node/Peer.cpp | 53 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index d4e81ff8b..a2a99ecd4 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -44,11 +44,10 @@ #include "CertificateOfMembership.hpp" #include "Salsa20.hpp" #include "Poly1305.hpp" -#include "Packet.hpp" #include "Identity.hpp" -#include "Peer.hpp" +#include "Topology.hpp" +#include "Packet.hpp" #include "Switch.hpp" -#include "Node.hpp" namespace ZeroTier { @@ -566,17 +565,17 @@ void Cluster::removeMember(uint16_t memberId) _memberIds = newMemberIds; } -bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &localAddress,const InetAddress &peerPhysicalAddress,bool offload) +InetAddress Cluster::findBetterEndpoint(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload) { if (!peerPhysicalAddress) // sanity check - return false; + return InetAddress(); 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) { - TRACE("NO GEOLOCATION available for %s",peerPhysicalAddress.toIpString().c_str()); - return false; + TRACE("no geolocation data for %s (geo-lookup is lazy/async so it may work next time)",peerPhysicalAddress.toIpString().c_str()); + return InetAddress(); } // Find member closest to this peer @@ -585,7 +584,6 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &localA const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); double bestDistance = (offload ? 2147483648.0 : currentDistance); unsigned int bestMember = _id; - TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -604,41 +602,17 @@ bool Cluster::redirectPeer(const SharedPtr &peer,const InetAddress &localA } } - if (best.size() > 0) { - TRACE("%s seems closer to %u at %fkm, suggesting redirect...",peer->address().toString().c_str(),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 == peerPhysicalAddress.ss_family) { - 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); - } - outp.armor(peer->key(),true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddress,peerPhysicalAddress,outp.data(),outp.size()); - } - } - //} - - 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; + for(std::vector::const_iterator a(best.begin());a!=best.end();++a) { + if (a->ss_family == peerPhysicalAddress.ss_family) { + TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str()); + return *a; + } } + TRACE("%s at [%d,%d,%d] is %f from us, no better endpoints found",peerAddress.toString().c_str(),px,py,pz,currentDistance); + return InetAddress(); } else { // TODO: pick based on load if no location info? - return false; + return InetAddress(); } } diff --git a/node/Cluster.hpp b/node/Cluster.hpp index b1266f27f..080c93102 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -245,15 +245,22 @@ public: void removeMember(uint16_t memberId); /** - * Redirect this peer to a better cluster member if needed + * Find a better cluster endpoint for this peer * - * @param peer Peer to (possibly) redirect - * @param localAddress Local address for path or NULL for none/any + * If this endpoint appears to be the best, a NULL/0 InetAddres is returned. + * Otherwise the InetAddress of a better endpoint is returned and the peer + * can then then be told to contact us there. + * + * Redirection is only done within the same address family, so the returned + * endpoint will always be the same ss_family as the supplied physical + * address. + * + * @param peerAddress Address of 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 + * @return InetAddress or NULL if there does not seem to be a better endpoint */ - bool redirectPeer(const SharedPtr &peer,const InetAddress &localAddress,const InetAddress &peerPhysicalAddress,bool offload); + InetAddress findBetterEndpoint(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); /** * Fill out ZT_ClusterStatus structure (from core API) diff --git a/node/Peer.cpp b/node/Peer.cpp index 4f2fe9314..31a20eeab 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -35,6 +35,7 @@ #include "AntiRecursion.hpp" #include "SelfAwareness.hpp" #include "Cluster.hpp" +#include "Packet.hpp" #include @@ -81,9 +82,28 @@ void Peer::received( Packet::Verb inReVerb) { #ifdef ZT_ENABLE_CLUSTER - if ((RR->cluster)&&(hops == 0)&&(verb != VERB_OK)&&(verb != VERB_ERROR)&&(verb != VERB_RENDEZVOUS)&&(verb != VERB_PUSH_DIRECT_PATHS)) { - if (RR->cluster->redirectPeer(SharedPtr(this),localAddr,remoteAddr,false)) - return; + bool redirected = false; + if ((RR->cluster)&&(hops == 0)&&(verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS)) { + InetAddress redirectTo(RR->cluster->findBetterEndpoint(_id.address(),remoteAddr,false)); + if (redirectTo) { + // For older peers we send RENDEZVOUS with ourselves. This will only work if we are + // a root server. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); // no flags + RR->identity.address().appendTo(outp); + outp.append((uint16_t)redirectTo.port()); + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)16); + outp.append(redirectTo.rawIpData(),16); + } + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); + redirected = true; + } } #endif @@ -95,6 +115,23 @@ void Peer::received( Mutex::Lock _l(_lock); _lastReceive = now; + if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) + _lastUnicastFrame = now; + else if (verb == Packet::VERB_MULTICAST_FRAME) + _lastMulticastFrame = now; + +#ifdef ZT_ENABLE_CLUSTER + // If we're in cluster mode and have sent the peer a better endpoint, stop + // here and don't confirm paths, replicate multicast info, etc. The new + // endpoint should do that. + if (redirected) + return; +#endif + + if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { + _lastAnnouncedTo = now; + needMulticastGroupAnnounce = true; + } if (hops == 0) { unsigned int np = _numPaths; @@ -144,16 +181,6 @@ void Peer::received( } } } - - if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { - _lastAnnouncedTo = now; - needMulticastGroupAnnounce = true; - } - - if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) - _lastUnicastFrame = now; - else if (verb == Packet::VERB_MULTICAST_FRAME) - _lastMulticastFrame = now; } // end _lock #ifdef ZT_ENABLE_CLUSTER From fb3b7a3baaf1fc1a6d922ea7aaa24a37c7a14952 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 09:41:12 -0700 Subject: [PATCH 130/195] Take -DZT_ENABLE_CLUSTER out of Mac defaults. --- make-mac.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make-mac.mk b/make-mac.mk index e53212c0e..174216fb1 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -6,7 +6,7 @@ ifeq ($(origin CXX),default) endif INCLUDES= -DEFS=-DZT_ENABLE_CLUSTER +DEFS= LIBS= ARCH_FLAGS=-arch x86_64 From 9617208e4027bec8a72e72a1fd89aa19ea9367d8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 09:53:43 -0700 Subject: [PATCH 131/195] Some cleanup, and use VERB_PUSH_DIRECT_PATHS to redirect newer peers. --- node/IncomingPacket.cpp | 2 +- node/Packet.hpp | 3 --- node/Peer.cpp | 50 ++++++++++++++++++++++++++++------------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c8526dfba..d8f458e86 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -280,8 +280,8 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // VALID -- if we made it here, packet passed identity and authenticity checks! - peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); if (destAddr) RR->sa->iam(id.address(),_remoteAddress,destAddr,RR->topology->isRoot(id),RR->node->now()); diff --git a/node/Packet.hpp b/node/Packet.hpp index 0e8426b68..985d25d0e 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -924,9 +924,6 @@ public: * 0x04 - Disable encryption (trust: privacy) * 0x08 - Disable encryption and authentication (trust: ultimate) * - * Address types and addresses are of the same format as the destination - * address type and address in HELLO. - * * The receiver may, upon receiving a push, attempt to establish a * direct link to one or more of the indicated addresses. It is the * responsibility of the sender to limit which peers it pushes direct diff --git a/node/Peer.cpp b/node/Peer.cpp index 31a20eeab..f16f14212 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -85,23 +85,43 @@ void Peer::received( bool redirected = false; if ((RR->cluster)&&(hops == 0)&&(verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS)) { InetAddress redirectTo(RR->cluster->findBetterEndpoint(_id.address(),remoteAddr,false)); - if (redirectTo) { - // For older peers we send RENDEZVOUS with ourselves. This will only work if we are - // a root server. - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); // no flags - RR->identity.address().appendTo(outp); - outp.append((uint16_t)redirectTo.port()); - if (redirectTo.ss_family == AF_INET) { - outp.append((uint8_t)4); - outp.append(redirectTo.rawIpData(),4); + if ((redirectTo.ss_family == AF_INET)||(redirectTo.ss_family == AF_INET6)) { + if (_vProto >= 5) { + // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); + outp.append((uint16_t)1); // count == 1 + outp.append((uint8_t)0); // no flags + outp.append((uint16_t)0); // no extensions + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append((uint8_t)6); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)6); + outp.append((uint8_t)18); + outp.append(redirectTo.rawIpData(),16); + } + outp.append((uint16_t)redirectTo.port()); + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } else { - outp.append((uint8_t)16); - outp.append(redirectTo.rawIpData(),16); + // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); // no flags + RR->identity.address().appendTo(outp); + outp.append((uint16_t)redirectTo.port()); + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)16); + outp.append(redirectTo.rawIpData(),16); + } + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } - outp.armor(_key,true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); redirected = true; } } From 8a7a0b6b88b2de95ebf98cafc183f7c69ec6c84f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 10:37:39 -0700 Subject: [PATCH 132/195] Cleanup, including simplification of root server picking algorithm since we no longer need all that craziness. --- node/Cluster.cpp | 2 +- node/Cluster.hpp | 15 ++++++++++++++- node/Topology.cpp | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index a2a99ecd4..bd4559335 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -239,7 +239,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) const MAC mac(dmsg.field(ptr,6),6); ptr += 6; const uint32_t adi = dmsg.at(ptr); ptr += 4; RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); - TRACE("[%u] %s likes %s/%u on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); + TRACE("[%u] %s likes %s/%.8x on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); } break; case STATE_MESSAGE_COM: { diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 080c93102..7d0c6a089 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -158,7 +158,20 @@ public: * while PROXY_SEND is used to implement proxy sending (which right * now is only used to send RENDEZVOUS). */ - STATE_MESSAGE_PROXY_SEND = 6 + STATE_MESSAGE_PROXY_SEND = 6, + + /** + * Replicate a network config for a network we belong to: + * <[8] 64-bit network ID> + * <[2] 16-bit length of network config> + * <[...] serialized network config> + * + * This is used by clusters to avoid every member having to query + * for the same netconf for networks all members belong to. + * + * TODO: not implemented yet! + */ + STATE_MESSAGE_NETWORK_CONFIG = 7 }; /** diff --git a/node/Topology.cpp b/node/Topology.cpp index 6a72cf8c3..49854f0e5 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -215,10 +215,45 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou } } } + } else { /* If I am not a root server, the best root server is the active one with * the lowest latency. */ + unsigned int bestLatencyOverall = ~((unsigned int)0); + unsigned int bestLatencyNotAvoid = ~((unsigned int)0); + const SharedPtr *bestOverall = (const SharedPtr *)0; + const SharedPtr *bestNotAvoid = (const SharedPtr *)0; + + for(std::vector< SharedPtr >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) { + if ((*r)->hasActiveDirectPath(now)) { + bool avoiding = false; + for(unsigned int i=0;iaddress()) { + avoiding = true; + break; + } + } + unsigned int l = (*r)->latency(); + if (!l) l = ~l; // zero latency indicates no measurment, so make this 'max' + if (l <= bestLatencyOverall) { + bestLatencyOverall = l; + bestOverall = &(*r); + } + if ((!avoiding)&&(l <= bestLatencyNotAvoid)) { + bestLatencyNotAvoid = l; + bestNotAvoid = &(*r); + } + } + } + + if (bestNotAvoid) + return *bestNotAvoid; + else if ((!strictAvoid)&&(bestOverall)) + return *bestOverall; + return SharedPtr(); + + /* unsigned int l,bestLatency = 65536; uint64_t lds,ldr; @@ -278,6 +313,7 @@ keep_searching_for_roots: } } } + */ } if (bestRoot) From 17e7528e2c1cd0e9f48eec462bef570b9a04164b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 10:40:31 -0700 Subject: [PATCH 133/195] More root cleanup. --- node/Topology.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index 49854f0e5..7bb4b4491 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -191,7 +191,6 @@ void Topology::saveIdentity(const Identity &id) SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid) { - SharedPtr bestRoot; const uint64_t now = RR->node->now(); Mutex::Lock _l(_lock); @@ -207,8 +206,8 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou for(unsigned long q=1;q<_rootAddresses.size();++q) { SharedPtr *nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]); if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) { - bestRoot = *nextsn; - break; + (*nextsn)->use(now); + return *nextsn; } } break; @@ -247,11 +246,13 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou } } - if (bestNotAvoid) + if (bestNotAvoid) { + (*bestNotAvoid)->use(now); return *bestNotAvoid; - else if ((!strictAvoid)&&(bestOverall)) + } else if ((!strictAvoid)&&(bestOverall)) { + (*bestOverall)->use(now); return *bestOverall; - return SharedPtr(); + } /* unsigned int l,bestLatency = 65536; @@ -315,10 +316,7 @@ keep_searching_for_roots: } */ } - - if (bestRoot) - bestRoot->use(now); - return bestRoot; + return SharedPtr(); } bool Topology::isUpstream(const Identity &id) const From 700c3166b7dcdec61b0dc47a4649776b9ba046e8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 10:51:11 -0700 Subject: [PATCH 134/195] Fix inverted sense bug. --- node/IncomingPacket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index d8f458e86..5ade8517b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -888,7 +888,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); - if ((now - peer->lastDirectPathPushReceived()) >= ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL) { + if ((now - peer->lastDirectPathPushReceived()) < ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL) { TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): too frequent!",source().toString().c_str(),_remoteAddress.toString().c_str()); return true; } From f32e9d07dd493bfb1c2fcb8e3638d5c634e40030 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 10:58:01 -0700 Subject: [PATCH 135/195] Don't include COM if not necessary (fix). --- node/Multicaster.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 6a8d63793..6e6cd628a 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -233,22 +233,21 @@ void Multicaster::send( if ((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY) { gs.lastExplicitGather = now; - SharedPtr sn(RR->topology->getBestRoot()); - if (sn) { + 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 nconf; - if (sn->needsOurNetworkMembershipCertificate(nwid,now,true)) { + { SharedPtr nw(RR->node->network(nwid)); if (nw) { - nconf = nw->config2(); - if (nconf) + SharedPtr nconf(nw->config2()); + if ((nconf)&&(nconf->com())&&(nconf->isPrivate())&&(r->needsOurNetworkMembershipCertificate(nwid,now,true))) com = &(nconf->com()); } } - Packet outp(sn->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER); + 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); @@ -256,8 +255,8 @@ void Multicaster::send( outp.append((uint32_t)gatherLimit); if (com) com->serialize(outp); - outp.armor(sn->key(),true); - sn->send(RR,outp.data(),outp.size(),now); + outp.armor(r->key(),true); + r->send(RR,outp.data(),outp.size(),now); } gatherLimit = 0; } From 62db18b6dd0002520d4828b0091995a40b4eb2e9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 11:01:58 -0700 Subject: [PATCH 136/195] Lessen this limit just a bit to make cluster settle faster. --- node/Constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index e45602f72..3ad07e4c0 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -327,7 +327,7 @@ /** * Minimum interval between direct path pushes from a given peer or we will ignore them */ -#define ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL 2500 +#define ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL 2000 /** * How long (max) to remember network certificates of membership? From 54a99d8e32e3ee0aed222069e961d44ddd748399 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 11:14:07 -0700 Subject: [PATCH 137/195] Well that was broken. --- node/IncomingPacket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5ade8517b..51e883643 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -622,7 +622,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared // Iterate through 18-byte network,MAC,ADI tuples for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr(ptr)); + const uint64_t nwid(at(ptr)); const MulticastGroup group(MAC(field(ptr + 8,6),6),at(ptr + 14)); RR->mc->add(now,nwid,group,peer->address()); #ifdef ZT_ENABLE_CLUSTER From a1a0ee4edb0933c9b82abad8715def6a63049658 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 12:01:00 -0700 Subject: [PATCH 138/195] Fix infinite loop in Cluster, clean up some stuff elsewhere, and back out rate limiting in PUSH_DIRECT_PATHS for now (but we will do something else to mitigate amplification attacks) --- node/Cluster.cpp | 5 ++-- node/Constants.hpp | 7 +---- node/IncomingPacket.cpp | 12 ++------ node/Peer.cpp | 1 - node/Peer.hpp | 17 ++--------- node/SelfAwareness.cpp | 12 ++++---- node/Topology.cpp | 62 +---------------------------------------- 7 files changed, 16 insertions(+), 100 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index bd4559335..eef02bc79 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -239,7 +239,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) const MAC mac(dmsg.field(ptr,6),6); ptr += 6; const uint32_t adi = dmsg.at(ptr); ptr += 4; RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address); - TRACE("[%u] %s likes %s/%.8x on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); + TRACE("[%u] %s likes %s/%.8x on %.16llx",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid); } break; case STATE_MESSAGE_COM: { @@ -376,11 +376,12 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee Mutex::Lock _l2(_peerAffinities_m); std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),_PeerAffinity(toPeerAddress,0,0))); // O(log(n)) while ((i != _peerAffinities.end())&&(i->address() == toPeerAddress)) { - uint16_t mid = i->clusterMemberId(); + const uint16_t mid = i->clusterMemberId(); if ((mid != _id)&&(i->timestamp > mostRecentTimestamp)) { mostRecentTimestamp = i->timestamp; canHasPeer = mid; } + ++i; } } diff --git a/node/Constants.hpp b/node/Constants.hpp index 3ad07e4c0..bef1183a8 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,11 +324,6 @@ */ #define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 -/** - * Minimum interval between direct path pushes from a given peer or we will ignore them - */ -#define ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL 2000 - /** * How long (max) to remember network certificates of membership? * @@ -355,7 +350,7 @@ /** * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) */ -#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 8 +#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 2 /** * A test pseudo-network-ID that can be joined diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 51e883643..2514cd64b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -622,7 +622,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared // Iterate through 18-byte network,MAC,ADI tuples for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr(ptr)); + const uint64_t nwid = at(ptr); const MulticastGroup group(MAC(field(ptr + 8,6),6),at(ptr + 14)); RR->mc->add(now,nwid,group,peer->address()); #ifdef ZT_ENABLE_CLUSTER @@ -888,12 +888,6 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); - if ((now - peer->lastDirectPathPushReceived()) < ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL) { - TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): too frequent!",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - peer->setLastDirectPathPushReceived(now); - const RemotePath *currentBest = peer->getBestPath(now); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); @@ -916,7 +910,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha 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) { if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + peer->attemptToContactAt(RR,_localAddress,a,now); } } } break; @@ -926,7 +920,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha 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) { if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,RR->node->now()); + peer->attemptToContactAt(RR,_localAddress,a,now); } } } break; diff --git a/node/Peer.cpp b/node/Peer.cpp index f16f14212..d5367b17c 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -55,7 +55,6 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastAnnouncedTo(0), _lastPathConfirmationSent(0), _lastDirectPathPushSent(0), - _lastDirectPathPushReceived(0), _lastPathSort(0), _vProto(0), _vMajor(0), diff --git a/node/Peer.hpp b/node/Peer.hpp index 0aca70b4f..04b541af0 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -407,16 +407,6 @@ public: */ bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime); - /** - * @return Time last direct path push was received - */ - inline uint64_t lastDirectPathPushReceived() const throw() { return _lastDirectPathPushReceived; } - - /** - * @param t New time of last direct path push received - */ - inline void setLastDirectPathPushReceived(uint64_t t) throw() { _lastDirectPathPushReceived = t; } - /** * Perform periodic cleaning operations */ @@ -450,7 +440,7 @@ public: const unsigned int recSizePos = b.size(); b.addSize(4); // space for uint32_t field length - b.append((uint16_t)1); // version of serialized Peer data + b.append((uint16_t)0); // version of serialized Peer data _id.serialize(b,false); @@ -461,7 +451,6 @@ public: b.append((uint64_t)_lastAnnouncedTo); b.append((uint64_t)_lastPathConfirmationSent); b.append((uint64_t)_lastDirectPathPushSent); - b.append((uint64_t)_lastDirectPathPushReceived); b.append((uint64_t)_lastPathSort); b.append((uint16_t)_vProto); b.append((uint16_t)_vMajor); @@ -513,7 +502,7 @@ public: const unsigned int recSize = b.template at(p); p += 4; if ((p + recSize) > b.size()) return SharedPtr(); // size invalid - if (b.template at(p) != 1) + if (b.template at(p) != 0) return SharedPtr(); // version mismatch p += 2; @@ -531,7 +520,6 @@ public: np->_lastAnnouncedTo = b.template at(p); p += 8; np->_lastPathConfirmationSent = b.template at(p); p += 8; np->_lastDirectPathPushSent = b.template at(p); p += 8; - np->_lastDirectPathPushReceived = b.template at(p); p += 8; np->_lastPathSort = b.template at(p); p += 8; np->_vProto = b.template at(p); p += 2; np->_vMajor = b.template at(p); p += 2; @@ -581,7 +569,6 @@ private: uint64_t _lastAnnouncedTo; uint64_t _lastPathConfirmationSent; uint64_t _lastDirectPathPushSent; - uint64_t _lastDirectPathPushReceived; uint64_t _lastPathSort; uint16_t _vProto; uint16_t _vMajor; diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 1b70f17cb..81d193694 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -123,16 +123,16 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // For all peers for whom we forgot an address, send a packet indirectly if // they are still considered alive so that we will re-establish direct links. - SharedPtr sn(RR->topology->getBestRoot()); - if (sn) { - RemotePath *snp = sn->getBestPath(now); - if (snp) { + SharedPtr r(RR->topology->getBestRoot()); + if (r) { + RemotePath *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)) { - TRACE("sending indirect NOP to %s via %s(%s) to re-establish link",(*p)->address().toString().c_str(),sn->address().toString().c_str(),snp->address().toString().c_str()); + TRACE("sending indirect NOP to %s via %s to re-establish link",(*p)->address().toString().c_str(),r->address().toString().c_str()); Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP); outp.armor((*p)->key(),true); - snp->send(RR,outp.data(),outp.size(),now); + rp->send(RR,outp.data(),outp.size(),now); } } } diff --git a/node/Topology.cpp b/node/Topology.cpp index 7bb4b4491..09668ef55 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -254,68 +254,8 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou return *bestOverall; } - /* - unsigned int l,bestLatency = 65536; - uint64_t lds,ldr; - - // First look for a best root by comparing latencies, but exclude - // root servers that have not responded to direct messages in order to - // try to exclude any that are dead or unreachable. - for(std::vector< SharedPtr >::const_iterator sn(_rootPeers.begin());sn!=_rootPeers.end();) { - // Skip explicitly avoided relays - for(unsigned int i=0;iaddress()) - goto keep_searching_for_roots; - } - - // Skip possibly comatose or unreachable relays - lds = (*sn)->lastDirectSend(); - ldr = (*sn)->lastDirectReceive(); - if ((lds)&&(lds > ldr)&&((lds - ldr) > ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD)) - goto keep_searching_for_roots; - - if ((*sn)->hasActiveDirectPath(now)) { - l = (*sn)->latency(); - if (bestRoot) { - if ((l)&&(l < bestLatency)) { - bestLatency = l; - bestRoot = *sn; - } - } else { - if (l) - bestLatency = l; - bestRoot = *sn; - } - } - -keep_searching_for_roots: - ++sn; - } - - if (bestRoot) { - bestRoot->use(now); - return bestRoot; - } else if (strictAvoid) - return SharedPtr(); - - // If we have nothing from above, just pick one without avoidance criteria. - for(std::vector< SharedPtr >::const_iterator sn=_rootPeers.begin();sn!=_rootPeers.end();++sn) { - if ((*sn)->hasActiveDirectPath(now)) { - unsigned int l = (*sn)->latency(); - if (bestRoot) { - if ((l)&&(l < bestLatency)) { - bestLatency = l; - bestRoot = *sn; - } - } else { - if (l) - bestLatency = l; - bestRoot = *sn; - } - } - } - */ } + return SharedPtr(); } From 0ffbd05c0e14088ddb7eaecba7f19541651e859b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 12:21:57 -0700 Subject: [PATCH 139/195] --wtf; prevent roots from TCP fallback --- node/Node.cpp | 4 ++-- service/OneService.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/node/Node.cpp b/node/Node.cpp index 2b2989039..87871e20d 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 729812edb..7a473b67e 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, From cfe166ef359c0d92b1521e6c127e2b92238c0731 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 12:29:01 -0700 Subject: [PATCH 140/195] Tweak some size limits. --- include/ZeroTierOne.h | 2 +- node/Cluster.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 7af4f7601..7371b9f0a 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -141,7 +141,7 @@ extern "C" { /** * Maximum allowed cluster message length in bytes */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1444 * 6) +#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1444 * 4) /** * A null/empty sockaddr (all zero) to signify an unspecified socket address diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 7d0c6a089..bb7d3b397 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -57,7 +57,7 @@ /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 50 namespace ZeroTier { From 7295fcfa86aafdca76ac0210ca59ad9a70a2d67a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 12:50:23 -0700 Subject: [PATCH 141/195] Merge Phy<> from netcon. --- osdep/Http.cpp | 8 +++ osdep/Phy.hpp | 123 ++++++++++++++++++----------------------- selftest.cpp | 4 +- service/OneService.cpp | 4 +- 4 files changed, 65 insertions(+), 74 deletions(-) diff --git a/osdep/Http.cpp b/osdep/Http.cpp index 0eb7c4c6d..6d812a142 100644 --- a/osdep/Http.cpp +++ b/osdep/Http.cpp @@ -100,6 +100,14 @@ struct HttpPhyHandler phy->setNotifyWritable(sock,false); } + inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} +#ifdef __UNIX_LIKE__ + inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {} + 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) {} +#endif // __UNIX_LIKE__ + http_parser parser; std::string currentHeaderField; std::string currentHeaderValue; diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index 6737034e3..94130fafa 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -78,11 +78,6 @@ #define ZT_PHY_MAX_INTERCEPTS ZT_PHY_MAX_SOCKETS #define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage -#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) -#define ZT_PHY_HAVE_EVENTFD 1 -#include -#endif - #endif // Windows or not namespace ZeroTier { @@ -109,6 +104,7 @@ typedef void PhySocket; * phyOnTcpClose(PhySocket *sock,void **uptr) * phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) * phyOnTcpWritable(PhySocket *sock,void **uptr) + * phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) * * On Linux/OSX/Unix only (not required/used on Windows or elsewhere): * @@ -116,9 +112,6 @@ typedef void PhySocket; * phyOnUnixClose(PhySocket *sock,void **uptr) * phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) * phyOnUnixWritable(PhySocket *sock,void **uptr) - * phyOnSocketPairEndpointClose(PhySocket *sock,void **uptr) - * phyOnSocketPairEndpointData(PhySocket *sock,void **uptr,void *data,unsigned long len) - * phyOnSocketPairEndpointWritable(PhySocket *sock,void **uptr) * * These templates typically refer to function objects. Templates are used to * avoid the call overhead of indirection, which is surprisingly high for high @@ -154,11 +147,10 @@ private: ZT_PHY_SOCKET_TCP_OUT_CONNECTED = 0x02, ZT_PHY_SOCKET_TCP_IN = 0x03, ZT_PHY_SOCKET_TCP_LISTEN = 0x04, - ZT_PHY_SOCKET_RAW = 0x05, - ZT_PHY_SOCKET_UDP = 0x06, + ZT_PHY_SOCKET_UDP = 0x05, + ZT_PHY_SOCKET_FD = 0x06, ZT_PHY_SOCKET_UNIX_IN = 0x07, - ZT_PHY_SOCKET_UNIX_LISTEN = 0x08, - ZT_PHY_SOCKET_PAIR_ENDPOINT = 0x09 + ZT_PHY_SOCKET_UNIX_LISTEN = 0x08 }; struct PhySocketImpl @@ -277,57 +269,47 @@ public: */ inline unsigned long maxCount() const throw() { return ZT_PHY_MAX_SOCKETS; } -#ifdef __UNIX_LIKE__ /** - * Create a two-way socket pair + * Wrap a raw file descriptor in a PhySocket structure * - * This uses socketpair() to create a local domain pair. The returned - * PhySocket holds the local side of the socket pair, while the - * supplied fd variable is set to the descriptor for the remote side. + * This can be used to select/poll on a raw file descriptor as part of this + * class's I/O loop. By default the fd is set for read notification but + * this can be controlled with setNotifyReadable(). When any detected + * condition is present, the phyOnFileDescriptorActivity() callback is + * called with one or both of its arguments 'true'. * - * The local side is set to O_NONBLOCK to work with our poll loop, but - * the remote descriptor is left untouched. It's up to the caller to - * set any required fcntl(), ioctl(), or setsockopt() settings there. - * It's also up to the caller to close the remote descriptor when - * done, if necessary. + * The Phy<>::close() method *must* be called when you're done with this + * file descriptor to remove it from the select/poll set, but unlike other + * types of sockets Phy<> does not actually close the underlying fd or + * otherwise manage its life cycle. There is also no close notification + * callback for this fd, since Phy<> doesn't actually perform reading or + * writing or detect error conditions. This is only useful for adding a + * file descriptor to Phy<> to select/poll on it. * - * @param remoteSocketDescriptor Result parameter set to remote end of socket pair's socket FD - * @param uptr Pointer to associate with local side of socket pair - * @return PhySocket for local side of socket pair + * @param fd Raw file descriptor + * @param uptr User pointer to supply to callbacks + * @return PhySocket wrapping fd or NULL on failure (out of memory or too many sockets) */ - inline PhySocket *createSocketPair(ZT_PHY_SOCKFD_TYPE &remoteSocketDescriptor,void *uptr = (void *)0) + inline PhySocket *wrapSocket(ZT_PHY_SOCKFD_TYPE fd,void *uptr = (void *)0) { if (_socks.size() >= ZT_PHY_MAX_SOCKETS) return (PhySocket *)0; - - int fd[2]; fd[0] = -1; fd[1] = -1; - if ((::socketpair(PF_LOCAL,SOCK_STREAM,0,fd) != 0)||(fd[0] <= 0)||(fd[1] <= 0)) - return (PhySocket *)0; - fcntl(fd[0],F_SETFL,O_NONBLOCK); - try { _socks.push_back(PhySocketImpl()); } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(fd[0]); - ZT_PHY_CLOSE_SOCKET(fd[1]); return (PhySocket *)0; } PhySocketImpl &sws = _socks.back(); - - if ((long)fd[0] > _nfds) - _nfds = (long)fd[0]; - FD_SET(fd[0],&_readfds); - sws.type = ZT_PHY_SOCKET_PAIR_ENDPOINT; - sws.sock = fd[0]; + if ((long)fd > _nfds) + _nfds = (long)fd; + FD_SET(fd,&_readfds); + sws.type = ZT_PHY_SOCKET_FD; + sws.sock = fd; sws.uptr = uptr; memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); // no sockaddr for this socket type, leave saddr null - - remoteSocketDescriptor = fd[1]; - return (PhySocket *)&sws; } -#endif // __UNIX_LIKE__ /** * Bind a UDP socket @@ -787,6 +769,26 @@ public: } } + /** + * Set whether we want to be notified that a socket is readable + * + * This is primarily for raw sockets added with wrapSocket(). It could be + * used with others, but doing so would essentially lock them and prevent + * data from being read from them until this is set to 'true' again. + * + * @param sock Socket to modify + * @param notifyReadable True if socket should be monitored for readability + */ + inline const void setNotifyReadable(PhySocket *sock,bool notifyReadable) + { + PhySocketImpl &sws = *(reinterpret_cast(sock)); + if (notifyReadable) { + FD_SET(sws.sock,&_readfds); + } else { + FD_CLR(sws.sock,&_readfds); + } + } + /** * Wait for activity and handle one or more events * @@ -936,7 +938,7 @@ public: } if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) { try { - _handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr)); + //_handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr)); } catch ( ... ) {} } #endif // __UNIX_LIKE__ @@ -971,25 +973,15 @@ public: #endif // __UNIX_LIKE__ break; - case ZT_PHY_SOCKET_PAIR_ENDPOINT: { -#ifdef __UNIX_LIKE__ - ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable - if (FD_ISSET(sock,&rfds)) { - long n = (long)::read(sock,buf,sizeof(buf)); - if (n <= 0) { - this->close((PhySocket *)&(*s),true); - } else { - try { - _handler->phyOnSocketPairEndpointData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } - } - if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) { + case ZT_PHY_SOCKET_FD: { + ZT_PHY_SOCKFD_TYPE sock = s->sock; + const bool readable = ((FD_ISSET(sock,&rfds))&&(FD_ISSET(sock,&_readfds))); + const bool writable = ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))); + if ((readable)||(writable)) { try { - _handler->phyOnSocketPairEndpointWritable((PhySocket *)&(*s),&(s->uptr)); + _handler->phyOnFileDescriptorActivity((PhySocket *)&(*s),&(s->uptr),readable,writable); } catch ( ... ) {} } -#endif // __UNIX_LIKE__ } break; default: @@ -1021,7 +1013,8 @@ public: FD_CLR(sws.sock,&_exceptfds); #endif - ZT_PHY_CLOSE_SOCKET(sws.sock); + if (sws.type != ZT_PHY_SOCKET_FD) + ZT_PHY_CLOSE_SOCKET(sws.sock); #ifdef __UNIX_LIKE__ if (sws.type == ZT_PHY_SOCKET_UNIX_LISTEN) @@ -1048,12 +1041,6 @@ public: } catch ( ... ) {} #endif // __UNIX_LIKE__ break; - case ZT_PHY_SOCKET_PAIR_ENDPOINT: -#ifdef __UNIX_LIKE__ - try { - _handler->phyOnSocketPairEndpointClose(sock,&(sws.uptr)); - } catch ( ... ) {} -#endif // __UNIX_LIKE__ default: break; } diff --git a/selftest.cpp b/selftest.cpp index 9c357dc4c..fa5eec0fb 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -837,9 +837,7 @@ 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 phyOnSocketPairEndpointClose(PhySocket *sock,void **uptr) {} - inline void phyOnSocketPairEndpointData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} - inline void phyOnSocketPairEndpointWritable(PhySocket *sock,void **uptr) {} + inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} #endif // __UNIX_LIKE__ }; static int testPhy() diff --git a/service/OneService.cpp b/service/OneService.cpp index 7a473b67e..1765b5c44 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1085,9 +1085,7 @@ public: 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 phyOnSocketPairEndpointClose(PhySocket *sock,void **uptr) {} - inline void phyOnSocketPairEndpointData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} - inline void phyOnSocketPairEndpointWritable(PhySocket *sock,void **uptr) {} + inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc) { From 40e0a34a5c22276e5546dc7835eec87282ac9484 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 13:04:08 -0700 Subject: [PATCH 142/195] Add set buffer sizes code to Phy<> --- osdep/Phy.hpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index 94130fafa..8dde279ce 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -655,6 +655,36 @@ public: return (PhySocket *)&sws; } + /** + * Try to set buffer sizes as close to the given value as possible + * + * This will try the specified value and then lower values in 16K increments + * until one works. + * + * @param sock Socket + * @param bufferSize Desired buffer sizes + */ + inline void setBufferSizes(const PhySocket *sock,int bufferSize) + { + PhySocketImpl &sws = *(reinterpret_cast(sock)); + if (bufferSize > 0) { + int bs = bufferSize; + while (bs >= 65536) { + int tmpbs = bs; + if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) + break; + bs -= 16384; + } + bs = bufferSize; + while (bs >= 65536) { + int tmpbs = bs; + if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) + break; + bs -= 16384; + } + } + } + /** * Attempt to send data to a stream socket (non-blocking) * From f692cec763d67caae54a4f47446657c390563319 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 14:04:12 -0700 Subject: [PATCH 143/195] Change how cluster relays packets -- just PROXY_UNITE and then send packet via normal ZeroTier front plane -- more efficient and eliminates fragmentation issues. --- include/ZeroTierOne.h | 2 +- node/Cluster.cpp | 189 +++++++++++++++++++++--------------------- node/Cluster.hpp | 22 +++-- node/Switch.cpp | 37 +++++---- node/Switch.hpp | 8 +- 5 files changed, 130 insertions(+), 128 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 7371b9f0a..3457634bb 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -141,7 +141,7 @@ extern "C" { /** * Maximum allowed cluster message length in bytes */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1444 * 4) +#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48) /** * A null/empty sockaddr (all zero) to signify an unspecified socket address diff --git a/node/Cluster.cpp b/node/Cluster.cpp index eef02bc79..c18663bc7 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -250,102 +250,87 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } } break; - case STATE_MESSAGE_RELAY: { + case STATE_MESSAGE_PROXY_UNITE: { + const Address localPeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; + const Address remotePeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; const unsigned int numRemotePeerPaths = dmsg[ptr++]; InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max for(unsigned int i=0;i(ptr); ptr += 2; - const void *packet = (const void *)dmsg.field(ptr,packetLen); ptr += packetLen; - if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address - const Address destinationAddress(reinterpret_cast(packet) + 8,ZT_ADDRESS_LENGTH); - TRACE("[%u] relay %u bytes to %s (%u remote paths included)",(unsigned int)fromMemberId,packetLen,destinationAddress.toString().c_str(),numRemotePeerPaths); + TRACE("[%u] requested proxy unite between local peer %s and remote peer %s",(unsigned int)fromMemberId,localPeerAddress.toString().c_str(),remotePeerAddress.toString().c_str()); - SharedPtr destinationPeer(RR->topology->getPeer(destinationAddress)); - if (destinationPeer) { - if ( - (destinationPeer->send(RR,packet,packetLen,RR->node->now()))&& - (numRemotePeerPaths > 0)&& - (packetLen >= 18)&& - (reinterpret_cast(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) - ) { - // If remote peer paths were sent with this relayed packet, we do - // RENDEZVOUS. It's handled here for cluster-relayed packets since - // we don't have both Peer records so this is a different path. + SharedPtr localPeer(RR->topology->getPeer(localPeerAddress)); + if ((localPeer)&&(numRemotePeerPaths > 0)) { + InetAddress bestLocalV4,bestLocalV6; + localPeer->getBestActiveAddresses(RR->node->now(),bestLocalV4,bestLocalV6); - const Address remotePeerAddress(reinterpret_cast(packet) + 13,ZT_ADDRESS_LENGTH); - - InetAddress bestDestV4,bestDestV6; - destinationPeer->getBestActiveAddresses(RR->node->now(),bestDestV4,bestDestV6); - InetAddress bestRemoteV4,bestRemoteV6; - for(unsigned int i=0;iidentity.address(),Packet::VERB_RENDEZVOUS); - rendezvousForDest.append((uint8_t)0); - remotePeerAddress.appendTo(rendezvousForDest); - - Buffer<2048> rendezvousForOtherEnd; - remotePeerAddress.appendTo(rendezvousForOtherEnd); - rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS); - const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size(); - rendezvousForOtherEnd.addSize(2); // space for actual packet payload length - rendezvousForOtherEnd.append((uint8_t)0); // flags == 0 - destinationAddress.appendTo(rendezvousForOtherEnd); - - bool haveMatch = false; - if ((bestDestV6)&&(bestRemoteV6)) { - haveMatch = true; - - rendezvousForDest.append((uint16_t)bestRemoteV6.port()); - rendezvousForDest.append((uint8_t)16); - rendezvousForDest.append(bestRemoteV6.rawIpData(),16); - - rendezvousForOtherEnd.append((uint16_t)bestDestV6.port()); - rendezvousForOtherEnd.append((uint8_t)16); - rendezvousForOtherEnd.append(bestDestV6.rawIpData(),16); - rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16)); - } else if ((bestDestV4)&&(bestRemoteV4)) { - haveMatch = true; - - rendezvousForDest.append((uint16_t)bestRemoteV4.port()); - rendezvousForDest.append((uint8_t)4); - rendezvousForDest.append(bestRemoteV4.rawIpData(),4); - - rendezvousForOtherEnd.append((uint16_t)bestDestV4.port()); - rendezvousForOtherEnd.append((uint8_t)4); - rendezvousForOtherEnd.append(bestDestV4.rawIpData(),4); - rendezvousForOtherEnd.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4)); - } - - if (haveMatch) { - _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size()); - RR->sw->send(rendezvousForDest,true,0); - } + InetAddress bestRemoteV4,bestRemoteV6; + for(unsigned int i=0;iidentity.address(),Packet::VERB_RENDEZVOUS); + rendezvousForLocal.append((uint8_t)0); + remotePeerAddress.appendTo(rendezvousForLocal); + + Buffer<2048> rendezvousForRemote; + remotePeerAddress.appendTo(rendezvousForRemote); + rendezvousForRemote.append((uint8_t)Packet::VERB_RENDEZVOUS); + const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForRemote.size(); + rendezvousForRemote.addSize(2); // space for actual packet payload length + rendezvousForRemote.append((uint8_t)0); // flags == 0 + localPeerAddress.appendTo(rendezvousForRemote); + + bool haveMatch = false; + if ((bestLocalV6)&&(bestRemoteV6)) { + haveMatch = true; + + rendezvousForLocal.append((uint16_t)bestRemoteV6.port()); + rendezvousForLocal.append((uint8_t)16); + rendezvousForLocal.append(bestRemoteV6.rawIpData(),16); + + rendezvousForRemote.append((uint16_t)bestLocalV6.port()); + rendezvousForRemote.append((uint8_t)16); + rendezvousForRemote.append(bestLocalV6.rawIpData(),16); + rendezvousForRemote.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16)); + } else if ((bestLocalV4)&&(bestRemoteV4)) { + haveMatch = true; + + rendezvousForLocal.append((uint16_t)bestRemoteV4.port()); + rendezvousForLocal.append((uint8_t)4); + rendezvousForLocal.append(bestRemoteV4.rawIpData(),4); + + rendezvousForRemote.append((uint16_t)bestLocalV4.port()); + rendezvousForRemote.append((uint8_t)4); + rendezvousForRemote.append(bestLocalV4.rawIpData(),4); + rendezvousForRemote.setAt(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4)); + } + + if (haveMatch) { + _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size()); + RR->sw->send(rendezvousForLocal,true,0); + } } } break; case STATE_MESSAGE_PROXY_SEND: { - const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; const Packet::Verb verb = (Packet::Verb)dmsg[ptr++]; const unsigned int len = dmsg.at(ptr); ptr += 2; Packet outp(rcpt,RR->identity.address(),verb); - outp.append(dmsg.field(ptr,len),len); + outp.append(dmsg.field(ptr,len),len); ptr += len; RR->sw->send(outp,true,0); TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len); } break; @@ -364,13 +349,13 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } } -bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len) +bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite) { if (len > 16384) // sanity check return false; uint64_t mostRecentTimestamp = 0; - uint16_t canHasPeer = 0; + unsigned int canHasPeer = 0; { // Anyone got this peer? Mutex::Lock _l2(_peerAffinities_m); @@ -387,25 +372,37 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee const uint64_t now = RR->node->now(); if ((now - mostRecentTimestamp) < ZT_PEER_ACTIVITY_TIMEOUT) { - Buffer<16384> buf; + Buffer<2048> buf; - InetAddress v4,v6; - if (fromPeerAddress) { - SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); - if (fromPeer) - fromPeer->getBestActiveAddresses(now,v4,v6); + if (unite) { + InetAddress v4,v6; + if (fromPeerAddress) { + SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); + if (fromPeer) + fromPeer->getBestActiveAddresses(now,v4,v6); + } + uint8_t addrCount = 0; + if (v4) + ++addrCount; + if (v6) + ++addrCount; + if (addrCount) { + toPeerAddress.appendTo(buf); + fromPeerAddress.appendTo(buf); + buf.append(addrCount); + if (v4) + v4.serialize(buf); + if (v6) + v6.serialize(buf); + } } - buf.append((uint8_t)( (v4) ? ((v6) ? 2 : 1) : ((v6) ? 1 : 0) )); - if (v4) - v4.serialize(buf); - if (v6) - v6.serialize(buf); - buf.append((uint16_t)len); - buf.append(data,len); { Mutex::Lock _l2(_members[canHasPeer].lock); - _send(canHasPeer,STATE_MESSAGE_RELAY,buf.data(),buf.size()); + if (buf.size() > 0) + _send(canHasPeer,STATE_MESSAGE_PROXY_UNITE,buf.data(),buf.size()); + if (_members[canHasPeer].zeroTierPhysicalEndpoints.size() > 0) + RR->node->putPacket(InetAddress(),_members[canHasPeer].zeroTierPhysicalEndpoints.front(),data,len); } TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer); diff --git a/node/Cluster.hpp b/node/Cluster.hpp index bb7d3b397..282d81200 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -57,7 +57,7 @@ /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 50 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 namespace ZeroTier { @@ -136,13 +136,18 @@ public: STATE_MESSAGE_COM = 4, /** - * Relay a packet to a peer: - * <[1] 8-bit number of sending peer active path addresses> - * <[...] series of serialized InetAddresses of sending peer's paths> - * <[2] 16-bit packet length> - * <[...] packet or packet fragment> + * Request that VERB_RENDEZVOUS be sent to a peer that we have: + * <[5] ZeroTier address of peer on recipient's side> + * <[5] ZeroTier address of peer on sender's side> + * <[1] 8-bit number of sender's peer's active path addresses> + * <[...] series of serialized InetAddresses of sender's peer's paths> + * + * This requests that we perform NAT-t introduction between a peer that + * we have and one on the sender's side. The sender furnishes contact + * info for its peer, and we send VERB_RENDEZVOUS to both sides: to ours + * directly and with PROXY_SEND to theirs. */ - STATE_MESSAGE_RELAY = 5, + STATE_MESSAGE_PROXY_UNITE = 5, /** * Request that a cluster member send a packet to a locally-known peer: @@ -211,9 +216,10 @@ public: * @param toPeerAddress Destination peer address * @param data Packet or packet fragment data * @param len Length of packet or fragment + * @param unite If true, also request proxy unite across cluster * @return True if this data was sent via another cluster member, false if none have this peer */ - bool sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len); + bool sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite); /** * Advertise to the cluster that we have this peer diff --git a/node/Switch.cpp b/node/Switch.cpp index 709ed802a..772eaf023 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -303,11 +303,10 @@ void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid) } } -bool Switch::unite(const Address &p1,const Address &p2,bool force) +bool Switch::unite(const Address &p1,const Address &p2) { if ((p1 == RR->identity.address())||(p2 == RR->identity.address())) return false; - SharedPtr p1p = RR->topology->getPeer(p1); if (!p1p) return false; @@ -317,14 +316,6 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) const uint64_t now = RR->node->now(); - { - Mutex::Lock _l(_lastUniteAttempt_m); - uint64_t &luts = _lastUniteAttempt[_LastUniteKey(p1,p2)]; - if (((now - luts) < ZT_MIN_UNITE_INTERVAL)&&(!force)) - return false; - luts = now; - } - std::pair cg(Peer::findCommonGround(*p1p,*p2p,now)); if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope())) return false; @@ -571,7 +562,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet SharedPtr relayTo = RR->topology->getPeer(destination); if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) { #ifdef ZT_ENABLE_CLUSTER - if ((RR->cluster)&&(RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size()))) + if ((RR->cluster)&&(RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false))) return; // sent by way of another member of this cluster #endif @@ -634,7 +625,8 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len) { - SharedPtr packet(new IncomingPacket(data,len,localAddr,fromAddr,RR->node->now())); + const uint64_t now = RR->node->now(); + SharedPtr packet(new IncomingPacket(data,len,localAddr,fromAddr,now)); Address source(packet->source()); Address destination(packet->destination()); @@ -652,17 +644,18 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr packet->incrementHops(); SharedPtr relayTo = RR->topology->getPeer(destination); - if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) { - unite(source,destination,false); + if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),now)))) { + if (_shouldTryUnite(now,source,destination)) + unite(source,destination); } else { #ifdef ZT_ENABLE_CLUSTER - if ((RR->cluster)&&(RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size()))) + if ((RR->cluster)&&(RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size(),_shouldTryUnite(now,source,destination)))) return; // sent by way of another member of this cluster #endif relayTo = RR->topology->getBestRoot(&source,1,true); if (relayTo) - relayTo->send(RR,packet->data(),packet->size(),RR->node->now()); + relayTo->send(RR,packet->data(),packet->size(),now); } } else { TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str()); @@ -677,7 +670,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr if (!dq.creationTime) { // If we have no other fragments yet, create an entry and save the head - dq.creationTime = RR->node->now(); + dq.creationTime = now; dq.frag0 = packet; dq.totalFragments = 0; // 0 == unknown, waiting for Packet::Fragment dq.haveFragments = 1; // head is first bit (left to right) @@ -805,4 +798,14 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) return false; } +bool Switch::_shouldTryUnite(const uint64_t now,const Address &p1,const Address &p2) +{ + Mutex::Lock _l(_lastUniteAttempt_m); + uint64_t &luts = _lastUniteAttempt[_LastUniteKey(p1,p2)]; + if ((now - luts) < ZT_MIN_UNITE_INTERVAL) + return false; + luts = now; + return true; +} + } // namespace ZeroTier diff --git a/node/Switch.hpp b/node/Switch.hpp index 3bdc0c47c..42e87ca56 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -127,15 +127,10 @@ public: * This only works if both peers are known, with known working direct * links to this peer. The best link for each peer is sent to the other. * - * A rate limiter is in effect via the _lastUniteAttempt map. If force - * is true, a unite attempt is made even if one has been made less than - * ZT_MIN_UNITE_INTERVAL milliseconds ago. - * * @param p1 One of two peers (order doesn't matter) * @param p2 Second of pair - * @param force If true, send now regardless of interval */ - bool unite(const Address &p1,const Address &p2,bool force); + bool unite(const Address &p1,const Address &p2); /** * Attempt NAT traversal to peer at a given physical address @@ -185,6 +180,7 @@ private: void _handleRemotePacketHead(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len); Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid); + bool _shouldTryUnite(const uint64_t now,const Address &p1,const Address &p2); const RuntimeEnvironment *const RR; uint64_t _lastBeaconResponse; From 40976c02a42b8e9078519f92a7c7412b8464e9bc Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 14:37:38 -0700 Subject: [PATCH 144/195] Forget paths to peers if we are handing them off. --- node/Cluster.cpp | 14 ++++---- node/Cluster.hpp | 15 +++------ node/Peer.cpp | 88 +++++++++++++++++++++++++----------------------- 3 files changed, 56 insertions(+), 61 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index c18663bc7..73ff58463 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -563,17 +563,14 @@ void Cluster::removeMember(uint16_t memberId) _memberIds = newMemberIds; } -InetAddress Cluster::findBetterEndpoint(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload) +bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload) { - if (!peerPhysicalAddress) // sanity check - return InetAddress(); - 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) { TRACE("no geolocation data for %s (geo-lookup is lazy/async so it may work next time)",peerPhysicalAddress.toIpString().c_str()); - return InetAddress(); + return false; } // Find member closest to this peer @@ -603,14 +600,15 @@ InetAddress Cluster::findBetterEndpoint(const Address &peerAddress,const InetAdd for(std::vector::const_iterator a(best.begin());a!=best.end();++a) { if (a->ss_family == peerPhysicalAddress.ss_family) { TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str()); - return *a; + redirectTo = *a; + return true; } } TRACE("%s at [%d,%d,%d] is %f from us, no better endpoints found",peerAddress.toString().c_str(),px,py,pz,currentDistance); - return InetAddress(); + return false; } else { // TODO: pick based on load if no location info? - return InetAddress(); + return false; } } diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 282d81200..45395b0f0 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -264,22 +264,15 @@ public: void removeMember(uint16_t memberId); /** - * Find a better cluster endpoint for this peer - * - * If this endpoint appears to be the best, a NULL/0 InetAddres is returned. - * Otherwise the InetAddress of a better endpoint is returned and the peer - * can then then be told to contact us there. - * - * Redirection is only done within the same address family, so the returned - * endpoint will always be the same ss_family as the supplied physical - * address. + * Find a better cluster endpoint for this peer (if any) * + * @param redirectTo InetAddress to be set to a better endpoint (if there is one) * @param peerAddress Address of 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 InetAddress or NULL if there does not seem to be a better endpoint + * @return True if redirectTo was set to a new address, false if redirectTo was not modified */ - InetAddress findBetterEndpoint(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); + bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); /** * Fill out ZT_ClusterStatus structure (from core API) diff --git a/node/Peer.cpp b/node/Peer.cpp index d5367b17c..009e2be58 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -81,47 +81,49 @@ void Peer::received( Packet::Verb inReVerb) { #ifdef ZT_ENABLE_CLUSTER - bool redirected = false; - if ((RR->cluster)&&(hops == 0)&&(verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS)) { - InetAddress redirectTo(RR->cluster->findBetterEndpoint(_id.address(),remoteAddr,false)); - if ((redirectTo.ss_family == AF_INET)||(redirectTo.ss_family == AF_INET6)) { - if (_vProto >= 5) { - // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS. - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); - outp.append((uint16_t)1); // count == 1 - outp.append((uint8_t)0); // no flags - outp.append((uint16_t)0); // no extensions - if (redirectTo.ss_family == AF_INET) { - outp.append((uint8_t)4); - outp.append((uint8_t)6); - outp.append(redirectTo.rawIpData(),4); + InetAddress redirectTo; + if ((RR->cluster)&&(hops == 0)) { + // Note: findBetterEndpoint() is first since we still want to check + // for a better endpoint even if we don't actually send a redirect. + if ( (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) && (verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS) ) { + if ((redirectTo.ss_family == AF_INET)||(redirectTo.ss_family == AF_INET6)) { + if (_vProto >= 5) { + // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); + outp.append((uint16_t)1); // count == 1 + outp.append((uint8_t)0); // no flags + outp.append((uint16_t)0); // no extensions + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append((uint8_t)6); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)6); + outp.append((uint8_t)18); + outp.append(redirectTo.rawIpData(),16); + } + outp.append((uint16_t)redirectTo.port()); + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } else { - outp.append((uint8_t)6); - outp.append((uint8_t)18); - outp.append(redirectTo.rawIpData(),16); + // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); // no flags + RR->identity.address().appendTo(outp); + outp.append((uint16_t)redirectTo.port()); + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)16); + outp.append(redirectTo.rawIpData(),16); + } + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } - outp.append((uint16_t)redirectTo.port()); - outp.armor(_key,true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); - } else { - // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere. - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); // no flags - RR->identity.address().appendTo(outp); - outp.append((uint16_t)redirectTo.port()); - if (redirectTo.ss_family == AF_INET) { - outp.append((uint8_t)4); - outp.append(redirectTo.rawIpData(),4); - } else { - outp.append((uint8_t)16); - outp.append(redirectTo.rawIpData(),16); - } - outp.armor(_key,true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } - redirected = true; } } #endif @@ -140,11 +142,13 @@ void Peer::received( _lastMulticastFrame = now; #ifdef ZT_ENABLE_CLUSTER - // If we're in cluster mode and have sent the peer a better endpoint, stop - // here and don't confirm paths, replicate multicast info, etc. The new - // endpoint should do that. - if (redirected) + // If we're in cluster mode and there's a better endpoint, stop here and don't + // learn or confirm paths. Also reset any existing paths, since they should + // go there and no longer talk to us here. + if (redirectTo) { + _numPaths = 0; return; + } #endif if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { From 16bc3e03982286232e1df2da17d8b2fc3c5a5c55 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 15:00:16 -0700 Subject: [PATCH 145/195] 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 diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 3457634bb..01c8bcdeb 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 73ff58463..07ca0ba13 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 2514cd64b..7015535a4 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 6e6cd628a..e43d7d886 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 cd30e386d..afbe10740 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 87871e20d..82cda66d8 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 4094a79e9..48c5ead8f 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 000000000..e2475751c --- /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 6fa3c52e6..99f6590b2 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 009e2be58..ebdb60264 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 04b541af0..aa75b3f48 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 8b37621a8..000000000 --- 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 81d193694..b4841544b 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 772eaf023..2f72f57af 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 6dd5ea30e..540072d5d 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 1765b5c44..4e3f24c79 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; From 218ef07d8e7fcc361aa0875f755eea7b5171f02c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 15:01:11 -0700 Subject: [PATCH 146/195] Build fix in TRACE mode. --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.cpp b/node/Peer.cpp index ebdb60264..e56c1ecaa 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -288,7 +288,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now, #ifdef ZT_TRACE { std::string ps; - for(std::vector::const_iterator p(dps.begin());p!=dps.end();++p) { + for(std::vector::const_iterator p(dps.begin());p!=dps.end();++p) { if (ps.length() > 0) ps.push_back(','); ps.append(p->toString()); From 6399f6f0940b6f20819d021a0dc3dcf0d289f002 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 15:02:15 -0700 Subject: [PATCH 147/195] This no longer has to be quite so fast. --- node/Cluster.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 45395b0f0..bab56785a 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -57,7 +57,7 @@ /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 250 namespace ZeroTier { From cc6080fe3898ddd1419050ee3a2c45cc87dd140b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 15:57:26 -0700 Subject: [PATCH 148/195] (1) No need to confirm if we are a root (small optimization), (2) Refactor peer affinity tracking. --- node/Cluster.cpp | 132 ++++++++++++++++++++--------------------------- node/Cluster.hpp | 29 ++++------- node/Peer.cpp | 70 ++++++++++++------------- 3 files changed, 99 insertions(+), 132 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 07ca0ba13..b2f3d5854 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -82,7 +82,8 @@ Cluster::Cluster( _z(z), _id(id), _zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints), - _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]) + _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]), + _peerAffinities(65536) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -214,19 +215,12 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) ptr += id.deserialize(dmsg,ptr); if (id) { RR->topology->saveIdentity(id); - - { // Add or update peer affinity entry - _PeerAffinity pa(id.address(),fromMemberId,RR->node->now()); + { Mutex::Lock _l2(_peerAffinities_m); - std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) - if ((i != _peerAffinities.end())&&(i->key == pa.key)) { - i->timestamp = pa.timestamp; - } else { - _peerAffinities.push_back(pa); - std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now - } + _PA &pa = _peerAffinities[id.address()]; + pa.ts = RR->node->now(); + pa.mid = fromMemberId; } - TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str()); } } catch ( ... ) { @@ -355,83 +349,66 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee if (len > 16384) // sanity check return false; - uint64_t mostRecentTimestamp = 0; + const uint64_t now = RR->node->now(); unsigned int canHasPeer = 0; { // Anyone got this peer? Mutex::Lock _l2(_peerAffinities_m); - std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),_PeerAffinity(toPeerAddress,0,0))); // O(log(n)) - while ((i != _peerAffinities.end())&&(i->address() == toPeerAddress)) { - const uint16_t mid = i->clusterMemberId(); - if ((mid != _id)&&(i->timestamp > mostRecentTimestamp)) { - mostRecentTimestamp = i->timestamp; - canHasPeer = mid; - } - ++i; - } + _PA *pa = _peerAffinities.get(toPeerAddress); + if ((pa)&&(pa->mid != _id)&&((now - pa->ts) < ZT_PEER_ACTIVITY_TIMEOUT)) + canHasPeer = pa->mid; + else return false; } - const uint64_t now = RR->node->now(); - if ((now - mostRecentTimestamp) < ZT_PEER_ACTIVITY_TIMEOUT) { - Buffer<2048> buf; - - if (unite) { - InetAddress v4,v6; - if (fromPeerAddress) { - SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); - if (fromPeer) - fromPeer->getBestActiveAddresses(now,v4,v6); - } - uint8_t addrCount = 0; + Buffer<2048> buf; + if (unite) { + InetAddress v4,v6; + if (fromPeerAddress) { + SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); + if (fromPeer) + fromPeer->getBestActiveAddresses(now,v4,v6); + } + uint8_t addrCount = 0; + if (v4) + ++addrCount; + if (v6) + ++addrCount; + if (addrCount) { + toPeerAddress.appendTo(buf); + fromPeerAddress.appendTo(buf); + buf.append(addrCount); if (v4) - ++addrCount; + v4.serialize(buf); if (v6) - ++addrCount; - if (addrCount) { - toPeerAddress.appendTo(buf); - fromPeerAddress.appendTo(buf); - buf.append(addrCount); - if (v4) - v4.serialize(buf); - if (v6) - v6.serialize(buf); - } + v6.serialize(buf); } - - { - Mutex::Lock _l2(_members[canHasPeer].lock); - if (buf.size() > 0) - _send(canHasPeer,STATE_MESSAGE_PROXY_UNITE,buf.data(),buf.size()); - if (_members[canHasPeer].zeroTierPhysicalEndpoints.size() > 0) - RR->node->putPacket(InetAddress(),_members[canHasPeer].zeroTierPhysicalEndpoints.front(),data,len); - } - - TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer); - return true; - } else { - TRACE("sendViaCluster(): unable to relay %u bytes from %s to %s since no cluster members seem to have it!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str()); - return false; } + { + Mutex::Lock _l2(_members[canHasPeer].lock); + if (buf.size() > 0) + _send(canHasPeer,STATE_MESSAGE_PROXY_UNITE,buf.data(),buf.size()); + if (_members[canHasPeer].zeroTierPhysicalEndpoints.size() > 0) + RR->node->putPacket(InetAddress(),_members[canHasPeer].zeroTierPhysicalEndpoints.front(),data,len); + } + + TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer); + + return true; } void Cluster::replicateHavePeer(const Identity &peerId) { + const uint64_t now = RR->node->now(); { // Use peer affinity table to track our own last announce time for peers - _PeerAffinity pa(peerId.address(),_id,RR->node->now()); Mutex::Lock _l2(_peerAffinities_m); - std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n)) - if ((i != _peerAffinities.end())&&(i->key == pa.key)) { - if ((pa.timestamp - i->timestamp) >= ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) { - i->timestamp = pa.timestamp; - // continue to announcement - } else { - // we've already announced this peer recently, so skip - return; - } + _PA &pa = _peerAffinities[peerId.address()]; + if (pa.mid != _id) { + pa.ts = now; + pa.mid = _id; + } else if ((now - pa.ts) >= ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) { + return; } else { - _peerAffinities.push_back(pa); - std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now - // continue to announcement + pa.ts = now; } } @@ -598,6 +575,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr } } + // Suggestion redirection if a closer member was found for(std::vector::const_iterator a(best.begin());a!=best.end();++a) { if (a->ss_family == peerPhysicalAddress.ss_family) { TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str()); @@ -661,10 +639,12 @@ void Cluster::status(ZT_ClusterStatus &status) const { Mutex::Lock _l2(_peerAffinities_m); - for(std::vector<_PeerAffinity>::const_iterator pi(_peerAffinities.begin());pi!=_peerAffinities.end();++pi) { - unsigned int mid = pi->clusterMemberId(); - if ((ms[mid])&&(mid != _id)&&((now - pi->timestamp) < ZT_PEER_ACTIVITY_TIMEOUT)) - ++ms[mid]->peers; + Address *k = (Address *)0; + _PA *v = (_PA *)0; + Hashtable< Address,_PA >::Iterator i(const_cast(this)->_peerAffinities); + while (i.next(k,v)) { + if ( (ms[v->mid]) && (v->mid != _id) && ((now - v->ts) < ZT_PEER_ACTIVITY_TIMEOUT) ) + ++ms[v->mid]->peers; } } } diff --git a/node/Cluster.hpp b/node/Cluster.hpp index bab56785a..42a26c7ff 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -52,7 +52,7 @@ /** * How often should we announce that we have a peer? */ -#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD 60000 +#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD 30000 /** * Desired period between doPeriodicTasks() in milliseconds @@ -285,7 +285,7 @@ private: void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len); void _flush(uint16_t memberId); - // These are initialized in the constructor and remain static + // These are initialized in the constructor and remain immutable uint16_t _masterSecret[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; const RuntimeEnvironment *RR; @@ -298,6 +298,7 @@ private: const int32_t _z; const uint16_t _id; const std::vector _zeroTierPhysicalEndpoints; + // end immutable fields struct _Member { @@ -330,30 +331,18 @@ private: _Member() { this->clear(); } ~_Member() { Utils::burn(key,sizeof(key)); } }; - - _Member *const _members; // cluster IDs can be from 0 to 65535 (16-bit) + _Member *const _members; std::vector _memberIds; Mutex _memberIds_m; - // Record tracking which members have which peers and how recently they claimed this -- also used to track our last claimed time - struct _PeerAffinity + struct _PA { - _PeerAffinity(const Address &a,uint16_t mid,uint64_t ts) : - key((a.toInt() << 16) | (uint64_t)mid), - timestamp(ts) {} - - uint64_t key; - uint64_t timestamp; - - inline Address address() const throw() { return Address(key >> 16); } - inline uint16_t clusterMemberId() const throw() { return (uint16_t)(key & 0xffff); } - - inline bool operator<(const _PeerAffinity &pi) const throw() { return (key < pi.key); } + _PA() : ts(0),mid(0xffff) {} + uint64_t ts; + uint16_t mid; }; - - // A memory-efficient packed map of _PeerAffinity records searchable with std::binary_search() and std::lower_bound() - std::vector<_PeerAffinity> _peerAffinities; + Hashtable< Address,_PA > _peerAffinities; Mutex _peerAffinities_m; }; diff --git a/node/Peer.cpp b/node/Peer.cpp index e56c1ecaa..99e2156e5 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -86,43 +86,41 @@ void Peer::received( // Note: findBetterEndpoint() is first since we still want to check // for a better endpoint even if we don't actually send a redirect. if ( (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) && (verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS) ) { - if ((redirectTo.ss_family == AF_INET)||(redirectTo.ss_family == AF_INET6)) { - if (_vProto >= 5) { - // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS. - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); - outp.append((uint16_t)1); // count == 1 - outp.append((uint8_t)0); // no flags - outp.append((uint16_t)0); // no extensions - if (redirectTo.ss_family == AF_INET) { - outp.append((uint8_t)4); - outp.append((uint8_t)6); - outp.append(redirectTo.rawIpData(),4); - } else { - outp.append((uint8_t)6); - outp.append((uint8_t)18); - outp.append(redirectTo.rawIpData(),16); - } - outp.append((uint16_t)redirectTo.port()); - outp.armor(_key,true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); + if (_vProto >= 5) { + // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); + outp.append((uint16_t)1); // count == 1 + outp.append((uint8_t)0); // no flags + outp.append((uint16_t)0); // no extensions + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append((uint8_t)6); + outp.append(redirectTo.rawIpData(),4); } else { - // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere. - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); // no flags - RR->identity.address().appendTo(outp); - outp.append((uint16_t)redirectTo.port()); - if (redirectTo.ss_family == AF_INET) { - outp.append((uint8_t)4); - outp.append(redirectTo.rawIpData(),4); - } else { - outp.append((uint8_t)16); - outp.append(redirectTo.rawIpData(),16); - } - outp.armor(_key,true); - RR->antiRec->logOutgoingZT(outp.data(),outp.size()); - RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); + outp.append((uint8_t)6); + outp.append((uint8_t)18); + outp.append(redirectTo.rawIpData(),16); } + outp.append((uint16_t)redirectTo.port()); + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); + } else { + // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere. + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); // no flags + RR->identity.address().appendTo(outp); + outp.append((uint16_t)redirectTo.port()); + if (redirectTo.ss_family == AF_INET) { + outp.append((uint8_t)4); + outp.append(redirectTo.rawIpData(),4); + } else { + outp.append((uint8_t)16); + outp.append(redirectTo.rawIpData(),16); + } + outp.armor(_key,true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); + RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size()); } } } @@ -167,7 +165,7 @@ void Peer::received( } if (!pathIsConfirmed) { - if (verb == Packet::VERB_OK) { + if ((verb == Packet::VERB_OK)||(RR->topology->amRoot())) { Path *slot = (Path *)0; if (np < ZT_MAX_PEER_NETWORK_PATHS) { From cc1b275ad97bf186f21b487aa57d7893bee3c956 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 16:47:13 -0700 Subject: [PATCH 149/195] Replicate peer endpoints and forget paths if we have them -- this allows two clusters to talk to each other, whereas forgetting all paths does not. --- node/Cluster.cpp | 45 +++++++++++++++++++++++++++------------------ node/Cluster.hpp | 7 ++++++- node/Constants.hpp | 4 ++-- node/Peer.cpp | 11 ++++------- node/Peer.hpp | 19 +++++++++++++++++++ 5 files changed, 58 insertions(+), 28 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index b2f3d5854..0797d83d0 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -210,22 +210,30 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } break; case STATE_MESSAGE_HAVE_PEER: { - try { - Identity id; - ptr += id.deserialize(dmsg,ptr); - if (id) { - RR->topology->saveIdentity(id); - { - Mutex::Lock _l2(_peerAffinities_m); - _PA &pa = _peerAffinities[id.address()]; - pa.ts = RR->node->now(); - pa.mid = fromMemberId; - } - TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str()); - } - } catch ( ... ) { - // ignore invalid identities - } + Identity id; + InetAddress physicalAddress; + ptr += id.deserialize(dmsg,ptr); + ptr += physicalAddress.deserialize(dmsg,ptr); + if (id) { + // Forget any paths that we have to this peer at its address + if (physicalAddress) { + SharedPtr myPeerRecord(RR->topology->getPeer(id.address())); + if (myPeerRecord) + myPeerRecord->removePathByAddress(physicalAddress); + } + + // Always save identity to update file time + RR->topology->saveIdentity(id); + + // Set peer affinity to its new home + { + Mutex::Lock _l2(_peerAffinities_m); + _PA &pa = _peerAffinities[id.address()]; + 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; case STATE_MESSAGE_MULTICAST_LIKE: { @@ -396,7 +404,7 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee return true; } -void Cluster::replicateHavePeer(const Identity &peerId) +void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress) { const uint64_t now = RR->node->now(); { // Use peer affinity table to track our own last announce time for peers @@ -405,7 +413,7 @@ void Cluster::replicateHavePeer(const Identity &peerId) if (pa.mid != _id) { pa.ts = now; pa.mid = _id; - } else if ((now - pa.ts) >= ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) { + } else if ((now - pa.ts) < ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) { return; } else { pa.ts = now; @@ -415,6 +423,7 @@ void Cluster::replicateHavePeer(const Identity &peerId) // announcement Buffer<4096> buf; peerId.serialize(buf,false); + physicalAddress.serialize(buf); { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 42a26c7ff..c3367d574 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -117,6 +117,10 @@ public: /** * Cluster member has this peer: * <[...] binary serialized peer identity> + * <[...] 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, @@ -225,8 +229,9 @@ 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); + void replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress); /** * Advertise a multicast LIKE to the cluster diff --git a/node/Constants.hpp b/node/Constants.hpp index bef1183a8..4b06db449 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -317,7 +317,7 @@ /** * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ -#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000 +#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000 /** * Interval between direct path pushes in milliseconds @@ -350,7 +350,7 @@ /** * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) */ -#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 2 +#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 4 /** * A test pseudo-network-ID that can be joined diff --git a/node/Peer.cpp b/node/Peer.cpp index 99e2156e5..99eb32c71 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -140,13 +140,10 @@ void Peer::received( _lastMulticastFrame = now; #ifdef ZT_ENABLE_CLUSTER - // If we're in cluster mode and there's a better endpoint, stop here and don't - // learn or confirm paths. Also reset any existing paths, since they should - // go there and no longer talk to us here. - if (redirectTo) { - _numPaths = 0; + // If we think this peer belongs elsewhere, don't learn this path or + // do other connection init stuff. + if (redirectTo) return; - } #endif if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { @@ -206,7 +203,7 @@ void Peer::received( #ifdef ZT_ENABLE_CLUSTER if ((RR->cluster)&&(pathIsConfirmed)) - RR->cluster->replicateHavePeer(_id); + RR->cluster->replicateHavePeer(_id,remoteAddr); #endif if (needMulticastGroupAnnounce) { diff --git a/node/Peer.hpp b/node/Peer.hpp index aa75b3f48..69343f20c 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -412,6 +412,25 @@ public: */ void clean(const RuntimeEnvironment *RR,uint64_t now); + /** + * Remove all paths with this remote address + * + * @param addr Remote address to remove + */ + inline void removePathByAddress(const InetAddress &addr) + { + Mutex::Lock _l(_lock); + unsigned int np = _numPaths; + unsigned int x = 0; + unsigned int y = 0; + while (x < np) { + if (_paths[x].address() != addr) + _paths[y++] = _paths[x]; + ++x; + } + _numPaths = y; + } + /** * Find a common set of addresses by which two peers can link, if any * From 4221552c0b3283d106a7c3a44959a02fefd31af6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 16:52:44 -0700 Subject: [PATCH 150/195] Use getPeerNoCache() in Cluster to avoid keeping all peers cached everywhere. --- node/Cluster.cpp | 5 +++-- node/Topology.hpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 0797d83d0..0535f9ee3 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -210,6 +210,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } break; case STATE_MESSAGE_HAVE_PEER: { + const uint64_t now = RR->node->now(); Identity id; InetAddress physicalAddress; ptr += id.deserialize(dmsg,ptr); @@ -217,7 +218,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) if (id) { // Forget any paths that we have to this peer at its address if (physicalAddress) { - SharedPtr myPeerRecord(RR->topology->getPeer(id.address())); + SharedPtr myPeerRecord(RR->topology->getPeerNoCache(id.address(),now)); if (myPeerRecord) myPeerRecord->removePathByAddress(physicalAddress); } @@ -229,7 +230,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) { Mutex::Lock _l2(_peerAffinities_m); _PA &pa = _peerAffinities[id.address()]; - pa.ts = RR->node->now(); + pa.ts = now; pa.mid = fromMemberId; } TRACE("[%u] has %s @ %s",(unsigned int)fromMemberId,id.address().toString().c_str(),physicalAddress.toString().c_str()); diff --git a/node/Topology.hpp b/node/Topology.hpp index ee9827b95..16836e075 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -78,6 +78,23 @@ public: */ SharedPtr getPeer(const Address &zta); + /** + * Get a peer only if it is presently in memory (no disk cache) + * + * @param zta ZeroTier address + * @param now Current time + */ + inline SharedPtr getPeerNoCache(const Address &zta,const uint64_t now) + { + Mutex::Lock _l(_lock); + const SharedPtr *ap = _peers.get(zta); + if (ap) { + (*ap)->use(now); + return *ap; + } + return SharedPtr(); + } + /** * Get the identity of a peer * From 51fcc753549e4f7c18efb889a841c4dd4fb9e6cf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 17:36:47 -0700 Subject: [PATCH 151/195] Some cleanup, and use getPeerNoCache() exclusively in Cluster. --- node/Cluster.cpp | 42 ++++++++++++++++++++++++++++++------------ node/Cluster.hpp | 3 ++- node/Path.hpp | 8 ++++++++ 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 0535f9ee3..9b0348221 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -83,7 +83,8 @@ Cluster::Cluster( _id(id), _zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints), _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]), - _peerAffinities(65536) + _peerAffinities(65536), + _lastCleanedPeerAffinities(0) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -247,11 +248,13 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) } break; case STATE_MESSAGE_COM: { + /* not currently used so not decoded yet CertificateOfMembership com; ptr += com.deserialize(dmsg,ptr); if (com) { TRACE("[%u] COM for %s on %.16llu rev %llu",(unsigned int)fromMemberId,com.issuedTo().toString().c_str(),com.networkId(),com.revision()); } + */ } break; case STATE_MESSAGE_PROXY_UNITE: { @@ -262,12 +265,13 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) for(unsigned int i=0;i localPeer(RR->topology->getPeer(localPeerAddress)); + const uint64_t now = RR->node->now(); + SharedPtr localPeer(RR->topology->getPeerNoCache(localPeerAddress,now)); if ((localPeer)&&(numRemotePeerPaths > 0)) { InetAddress bestLocalV4,bestLocalV6; - localPeer->getBestActiveAddresses(RR->node->now(),bestLocalV4,bestLocalV6); + localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6); InetAddress bestRemoteV4,bestRemoteV6; for(unsigned int i=0;i buf; + Buffer<1024> buf; if (unite) { InetAddress v4,v6; if (fromPeerAddress) { - SharedPtr fromPeer(RR->topology->getPeer(fromPeerAddress)); + SharedPtr fromPeer(RR->topology->getPeerNoCache(fromPeerAddress,now)); if (fromPeer) fromPeer->getBestActiveAddresses(now,v4,v6); } @@ -408,7 +412,7 @@ bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress) { const uint64_t now = RR->node->now(); - { // Use peer affinity table to track our own last announce time for peers + { Mutex::Lock _l2(_peerAffinities_m); _PA &pa = _peerAffinities[peerId.address()]; if (pa.mid != _id) { @@ -436,7 +440,7 @@ void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physic void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group) { - Buffer<2048> buf; + Buffer<1024> buf; buf.append((uint64_t)nwid); peerAddress.appendTo(buf); group.mac().appendTo(buf); @@ -453,7 +457,7 @@ void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,co void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembership &com) { - Buffer<2048> buf; + Buffer<4096> buf; com.serialize(buf); TRACE("replicating %s COM for %.16llx to all members",com.issuedTo().toString().c_str(),com.networkId()); { @@ -502,6 +506,20 @@ void Cluster::doPeriodicTasks() _flush(*mid); // does nothing if nothing to flush } } + + { + if ((now - _lastCleanedPeerAffinities) >= (ZT_PEER_ACTIVITY_TIMEOUT * 10)) { + _lastCleanedPeerAffinities = now; + Address *k = (Address *)0; + _PA *v = (_PA *)0; + Mutex::Lock _l(_peerAffinities_m); + Hashtable< Address,_PA >::Iterator i(_peerAffinities); + while (i.next(k,v)) { + if ((now - v->ts) >= (ZT_PEER_ACTIVITY_TIMEOUT * 10)) + _peerAffinities.erase(*k); + } + } + } } void Cluster::addMember(uint16_t memberId) @@ -563,7 +581,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr // Find member closest to this peer const uint64_t now = RR->node->now(); - std::vector best; // initial "best" is for peer to stay put + std::vector best; const double currentDistance = _dist3d(_x,_y,_z,px,py,pz); double bestDistance = (offload ? 2147483648.0 : currentDistance); unsigned int bestMember = _id; @@ -575,7 +593,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr // 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); + const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz); if (mdist < bestDistance) { bestDistance = mdist; bestMember = *mid; @@ -585,7 +603,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr } } - // Suggestion redirection if a closer member was found + // Redirect to a closer member if it has a ZeroTier endpoint address in the same ss_family for(std::vector::const_iterator a(best.begin());a!=best.end();++a) { if (a->ss_family == peerPhysicalAddress.ss_family) { TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str()); diff --git a/node/Cluster.hpp b/node/Cluster.hpp index c3367d574..cc9edd1d1 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -47,7 +47,7 @@ /** * Timeout for cluster members being considered "alive" */ -#define ZT_CLUSTER_TIMEOUT 10000 +#define ZT_CLUSTER_TIMEOUT 20000 /** * How often should we announce that we have a peer? @@ -349,6 +349,7 @@ private: }; Hashtable< Address,_PA > _peerAffinities; Mutex _peerAffinities_m; + uint64_t _lastCleanedPeerAffinities; }; } // namespace ZeroTier diff --git a/node/Path.hpp b/node/Path.hpp index 99f6590b2..2b05b8126 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -95,6 +95,14 @@ public: { } + inline Path &operator=(const Path &p) + throw() + { + if (this != &p) + memcpy(this,&p,sizeof(Path)); + return *this; + } + /** * Called when a packet is sent to this remote path * From 88b100e5d0db5df16622fa48899cf652e09b3e91 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 17:59:17 -0700 Subject: [PATCH 152/195] More cleanup. --- node/IncomingPacket.cpp | 49 +++++++++++++++++++++++++++-------------- node/InetAddress.hpp | 11 ++++++--- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7015535a4..b1a9af3b0 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -45,6 +45,7 @@ #include "World.hpp" #include "Cluster.hpp" #include "Node.hpp" +#include "AntiRecursion.hpp" namespace ZeroTier { @@ -122,7 +123,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr case Packet::ERROR_OBJ_NOT_FOUND: if (inReVerb == Packet::VERB_WHOIS) { - if (RR->topology->isRoot(peer->identity())) + if (RR->topology->isUpstream(peer->identity())) RR->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH)); } else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); @@ -155,6 +156,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); nconf->com().serialize(outp); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -202,13 +204,13 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) const uint64_t timestamp = at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); Identity id; - InetAddress destAddr; + InetAddress externalSurfaceAddress; 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); + ptr += externalSurfaceAddress.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); @@ -281,11 +283,8 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // VALID -- if we made it here, packet passed identity and authenticity checks! - peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); - peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); - - if (destAddr) - RR->sa->iam(id.address(),_remoteAddress,destAddr,RR->topology->isRoot(id),RR->node->now()); + if (externalSurfaceAddress) + RR->sa->iam(id.address(),_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now()); Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_HELLO); @@ -308,7 +307,11 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + + peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); } catch ( ... ) { TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -332,12 +335,17 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); + if (vProto < ZT_PROTO_VERSION_MIN) { + TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const bool trusted = RR->topology->isRoot(peer->identity()); - InetAddress destAddr; + InetAddress externalSurfaceAddress; unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2; if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field - ptr += destAddr.deserialize(*this,ptr); + ptr += externalSurfaceAddress.deserialize(*this,ptr); if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root World worldUpdate; const unsigned int worldLen = at(ptr); ptr += 2; @@ -348,18 +356,13 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p } } - if (vProto < ZT_PROTO_VERSION_MIN) { - TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((destAddr) ? destAddr.toString().c_str() : "(none)")); peer->addDirectLatencyMeasurment(latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); - if (destAddr) - RR->sa->iam(peer->address(),_remoteAddress,destAddr,trusted,RR->node->now()); + if (externalSurfaceAddress) + RR->sa->iam(peer->address(),_remoteAddress,externalSurfaceAddress,trusted,RR->node->now()); } break; case Packet::VERB_WHOIS: { @@ -443,6 +446,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr outp.append(packetId()); queried.serialize(outp,false); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); @@ -451,6 +455,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); outp.append(payload(),ZT_ADDRESS_LENGTH); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } else { @@ -608,6 +613,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr outp.append((unsigned char)Packet::VERB_ECHO); outp.append((uint64_t)pid); outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP); } catch ( ... ) { @@ -693,6 +699,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length()); } else { + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -705,6 +712,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } break; @@ -715,6 +723,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } break; @@ -737,6 +746,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } catch ( ... ) { @@ -781,6 +791,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar outp.append((uint32_t)mg.adi()); if (RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit)) { outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -873,6 +884,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share outp.append((unsigned char)0x02); // flag 0x02 = contains gather results if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) { outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -1173,6 +1185,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const outp.append((uint16_t)sizeof(result)); outp.append(result,sizeof(result)); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); @@ -1180,6 +1193,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const outp.append(pid); outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } break; @@ -1285,6 +1299,7 @@ void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index ecafcf51f..fcbed4b15 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -346,14 +346,19 @@ struct InetAddress : public sockaddr_storage } /** + * Performs an IP-only comparison or, if that is impossible, a memcmp() + * * @param a InetAddress to compare again * @return True if only IP portions are equal (false for non-IP or null addresses) */ inline bool ipsEqual(const InetAddress &a) const { - switch(ss_family) { - case AF_INET: return (reinterpret_cast(this)->sin_addr.s_addr == reinterpret_cast(&a)->sin_addr.s_addr); - case AF_INET6: return (memcmp(reinterpret_cast(this)->sin6_addr.s6_addr,reinterpret_cast(&a)->sin6_addr.s6_addr,16) == 0); + if (ss_family == a.ss_family) { + if (ss_family == AF_INET) + return (reinterpret_cast(this)->sin_addr.s_addr == reinterpret_cast(&a)->sin_addr.s_addr); + if (ss_family == AF_INET6) + return (memcmp(reinterpret_cast(this)->sin6_addr.s6_addr,reinterpret_cast(&a)->sin6_addr.s6_addr,16) == 0); + return (memcmp(this,&a,sizeof(InetAddress)) == 0); } return false; } From cdc99bfee10ac58a8dab1aabcb85e69f3862b7ad Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 18:18:26 -0700 Subject: [PATCH 153/195] Add a circuit breaker for VERB_PUSH_DIRECT_PATHS. --- node/Constants.hpp | 26 ++++++++++++++++++++------ node/IncomingPacket.cpp | 5 +++++ node/Peer.cpp | 2 ++ node/Peer.hpp | 31 +++++++++++++++++++++++++++++-- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index 4b06db449..53cc64c7e 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -319,11 +319,6 @@ */ #define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000 -/** - * Interval between direct path pushes in milliseconds - */ -#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 - /** * How long (max) to remember network certificates of membership? * @@ -347,10 +342,29 @@ */ #define ZT_MAX_BRIDGE_SPAM 16 +/** + * Interval between direct path pushes in milliseconds + */ +#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 + /** * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) */ -#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 4 +#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 5 + +/** + * Time horizon for push direct paths cutoff + */ +#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000 + +/** + * Maximum number of direct path pushes within cutoff time + * + * This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses + * per CUTOFF_TIME milliseconds per peer to prevent this from being + * useful for DOS amplification attacks. + */ +#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5 /** * A test pseudo-network-ID that can be joined diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index b1a9af3b0..e985b34c4 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -901,6 +901,11 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); + if (!peer->shouldRespondToDirectPathPush(now)) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const Path *currentBest = peer->getBestPath(now); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); diff --git a/node/Peer.cpp b/node/Peer.cpp index 99eb32c71..976c7c449 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -55,6 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastAnnouncedTo(0), _lastPathConfirmationSent(0), _lastDirectPathPushSent(0), + _lastDirectPathPushReceive(0), _lastPathSort(0), _vProto(0), _vMajor(0), @@ -63,6 +64,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _id(peerIdentity), _numPaths(0), _latency(0), + _directPathPushCutoffCount(0), _networkComs(4), _lastPushedComs(4) { diff --git a/node/Peer.hpp b/node/Peer.hpp index 69343f20c..d9ef5fcba 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -431,6 +431,27 @@ public: _numPaths = y; } + /** + * Update direct path push stats and return true if we should respond + * + * This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly + * useful as a DDOS amplification attack vector. Otherwise a malicious peer + * could send loads of these and cause others to bombard arbitrary IPs with + * traffic. + * + * @param now Current time + * @return True if we should respond + */ + inline bool shouldRespondToDirectPathPush(const uint64_t now) + { + Mutex::Lock _l(_lock); + if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) + ++_directPathPushCutoffCount; + else _directPathPushCutoffCount = 0; + _lastDirectPathPushReceive = now; + return (_directPathPushCutoffCount >= ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); + } + /** * Find a common set of addresses by which two peers can link, if any * @@ -459,7 +480,7 @@ public: const unsigned int recSizePos = b.size(); b.addSize(4); // space for uint32_t field length - b.append((uint16_t)0); // version of serialized Peer data + b.append((uint16_t)1); // version of serialized Peer data _id.serialize(b,false); @@ -470,12 +491,14 @@ public: b.append((uint64_t)_lastAnnouncedTo); b.append((uint64_t)_lastPathConfirmationSent); b.append((uint64_t)_lastDirectPathPushSent); + b.append((uint64_t)_lastDirectPathPushReceive); b.append((uint64_t)_lastPathSort); b.append((uint16_t)_vProto); b.append((uint16_t)_vMajor); b.append((uint16_t)_vMinor); b.append((uint16_t)_vRevision); b.append((uint32_t)_latency); + b.append((uint16_t)_directPathPushCutoffCount); b.append((uint16_t)_numPaths); for(unsigned int i=0;i<_numPaths;++i) @@ -521,7 +544,7 @@ public: const unsigned int recSize = b.template at(p); p += 4; if ((p + recSize) > b.size()) return SharedPtr(); // size invalid - if (b.template at(p) != 0) + if (b.template at(p) != 1) return SharedPtr(); // version mismatch p += 2; @@ -539,12 +562,14 @@ public: np->_lastAnnouncedTo = b.template at(p); p += 8; np->_lastPathConfirmationSent = b.template at(p); p += 8; np->_lastDirectPathPushSent = b.template at(p); p += 8; + np->_lastDirectPathPushReceive = b.template at(p); p += 8; np->_lastPathSort = b.template at(p); p += 8; np->_vProto = b.template at(p); p += 2; np->_vMajor = b.template at(p); p += 2; np->_vMinor = b.template at(p); p += 2; np->_vRevision = b.template at(p); p += 2; np->_latency = b.template at(p); p += 4; + np->_directPathPushCutoffCount = b.template at(p); p += 2; const unsigned int numPaths = b.template at(p); p += 2; for(unsigned int i=0;i Date: Wed, 28 Oct 2015 09:11:30 -0700 Subject: [PATCH 154/195] Clean up PUSH_DIRECT_PATH limits a bit more and make them a bit smarter. --- node/Constants.hpp | 10 +++++----- node/IncomingPacket.cpp | 25 +++++++++++++++---------- node/InetAddress.hpp | 8 +++++++- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index 53cc64c7e..5a4d4a4de 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -347,11 +347,6 @@ */ #define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 -/** - * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) - */ -#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 5 - /** * Time horizon for push direct paths cutoff */ @@ -366,6 +361,11 @@ */ #define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5 +/** + * Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6) + */ +#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 1 + /** * A test pseudo-network-ID that can be joined * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index e985b34c4..f06eb30c3 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -901,16 +901,19 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); + + // First, subject this to a rate limit if (!peer->shouldRespondToDirectPathPush(now)) { TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str()); return true; } - const Path *currentBest = peer->getBestPath(now); + // Second, limit addresses by scope and type + uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6 + memset(countPerScope,0,sizeof(countPerScope)); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; - unsigned int v4Count = 0,v6Count = 0; while (count--) { // if ptr overflows Buffer will throw // TODO: some flags are not yet implemented @@ -925,20 +928,22 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha case 4: { 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) { - if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,now); + 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); + } else { + TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } } } 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) { - if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,now); + 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); + } else { + TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } } } break; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index fcbed4b15..5e5eb06ee 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -42,6 +42,11 @@ namespace ZeroTier { +/** + * Maximum integer value of enum IpScope + */ +#define ZT_INETADDRESS_MAX_SCOPE 7 + /** * Extends sockaddr_storage with friendly C++ methods * @@ -66,7 +71,8 @@ struct InetAddress : public sockaddr_storage * IP address scope * * Note that these values are in ascending order of path preference and - * MUST remain that way or Path must be changed to reflect. + * MUST remain that way or Path must be changed to reflect. Also be sure + * to change ZT_INETADDRESS_MAX_SCOPE if the max changes. */ enum IpScope { From c1b0329969d3601fe80ef3298837edac5bdbbed2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 09:32:07 -0700 Subject: [PATCH 155/195] Only check IP equality to detect external surface changes (should prevent some spurious resets under symmetric NATs), and simplify some logic. --- node/SelfAwareness.cpp | 50 ++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index b4841544b..856892ffa 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -36,6 +36,7 @@ #include "Topology.hpp" #include "Packet.hpp" #include "Peer.hpp" +#include "Switch.hpp" // Entry timeout -- make it fairly long since this is just to prevent stale buildup #define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000 @@ -65,7 +66,8 @@ private: }; SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) : - RR(renv) + RR(renv), + _phy(32) { } @@ -77,35 +79,35 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi { const InetAddress::IpScope scope = myPhysicalAddress.ipScope(); + // This would be weird, e.g. a public IP talking to 10.0.0.1, so just ignore it. + // If your network is this weird it's probably not reliable information. + if (scope != reporterPhysicalAddress.ipScope()) + return; + + // Some scopes we ignore, and global scope IPs are only used for this + // mechanism if they come from someone we trust (e.g. a root). switch(scope) { case InetAddress::IP_SCOPE_NONE: case InetAddress::IP_SCOPE_LOOPBACK: case InetAddress::IP_SCOPE_MULTICAST: return; case InetAddress::IP_SCOPE_GLOBAL: - if ((!trusted)||(scope != reporterPhysicalAddress.ipScope())) + if (!trusted) return; break; default: - if (scope != reporterPhysicalAddress.ipScope()) - return; break; } Mutex::Lock _l(_phy_m); - PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,reporterPhysicalAddress,scope)]; - if ((now - entry.ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT) { + if ( ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) { entry.mySurface = myPhysicalAddress; entry.ts = now; - TRACE("learned physical address %s for scope %u as seen from %s(%s) (replaced )",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str()); - } else if (entry.mySurface != myPhysicalAddress) { - entry.mySurface = myPhysicalAddress; - entry.ts = now; - TRACE("learned physical address %s for scope %u as seen from %s(%s) (replaced %s, resetting all in scope)",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str()); + TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str()); - // Erase all entries in this scope that were not reported by this remote address to prevent 'thrashing' + // Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing' // due to multiple reports of endpoint change. // Don't use 'entry' after this since hash table gets modified. { @@ -118,26 +120,22 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi } } + // Reset all paths within this scope _ResetWithinScope rset(RR,now,(InetAddress::IpScope)scope); RR->topology->eachPeer<_ResetWithinScope &>(rset); - // For all peers for whom we forgot an address, send a packet indirectly if - // they are still considered alive so that we will re-establish direct links. - SharedPtr r(RR->topology->getBestRoot()); - if (r) { - 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)) { - TRACE("sending indirect NOP to %s via %s to re-establish link",(*p)->address().toString().c_str(),r->address().toString().c_str()); - Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP); - outp.armor((*p)->key(),true); - rp->send(RR,outp.data(),outp.size(),now); - } - } + // Send a NOP to all peers for whom we forgot a path. This will cause direct + // 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)) { + TRACE("sending indirect NOP to %s via %s to re-establish link",(*p)->address().toString().c_str(),r->address().toString().c_str()); + Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP); + RR->sw->send(outp,true,0); } } } else { + entry.mySurface = myPhysicalAddress; entry.ts = now; } } From fdc3e103ccc3207c4a00b8476d8635772c6c6dc4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 09:38:33 -0700 Subject: [PATCH 156/195] Cleanup and docs. --- node/InetAddress.hpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 5e5eb06ee..c4d5cfda0 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -52,8 +52,8 @@ namespace ZeroTier { * * This is basically a "mixin" for sockaddr_storage. It adds methods and * operators, but does not modify the structure. This can be cast to/from - * sockaddr_storage and used interchangeably. Don't change this as it's - * used in a few places. + * sockaddr_storage and used interchangeably. DO NOT change this by e.g. + * adding non-static fields, since much code depends on this identity. */ struct InetAddress : public sockaddr_storage { @@ -326,7 +326,7 @@ struct InetAddress : public sockaddr_storage inline bool isV6() const throw() { return (ss_family == AF_INET6); } /** - * @return pointer to raw IP address bytes + * @return pointer to raw address bytes or NULL if not available */ inline const void *rawIpData() const throw() @@ -338,19 +338,6 @@ struct InetAddress : public sockaddr_storage } } - /** - * @return pointer to raw IP address bytes - */ - inline void *rawIpData() - throw() - { - switch(ss_family) { - case AF_INET: return (void *)&(reinterpret_cast(this)->sin_addr.s_addr); - case AF_INET6: return (void *)(reinterpret_cast(this)->sin6_addr.s6_addr); - default: return 0; - } - } - /** * Performs an IP-only comparison or, if that is impossible, a memcmp() * From 938d0a970b84c6a50465bb6ebb64798e4ffec202 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 10:01:32 -0700 Subject: [PATCH 157/195] TRACE build fixes. --- node/IncomingPacket.cpp | 2 +- node/SelfAwareness.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f06eb30c3..b0d65159b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -356,7 +356,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p } } - TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((destAddr) ? destAddr.toString().c_str() : "(none)")); + TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((externalSurfaceAddress) ? externalSurfaceAddress.toString().c_str() : "(none)")); peer->addDirectLatencyMeasurment(latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 856892ffa..d8eca0718 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -129,7 +129,6 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // other relay. for(std::vector< SharedPtr >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) { if ((*p)->alive(now)) { - TRACE("sending indirect NOP to %s via %s to re-establish link",(*p)->address().toString().c_str(),r->address().toString().c_str()); Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP); RR->sw->send(outp,true,0); } From 0fd15d9cf3a7bf6e85247533e83edade69cd01d0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 10:38:37 -0700 Subject: [PATCH 158/195] Fix inverted sense bug. --- node/Peer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Peer.hpp b/node/Peer.hpp index d9ef5fcba..39acffd97 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -449,7 +449,7 @@ public: ++_directPathPushCutoffCount; else _directPathPushCutoffCount = 0; _lastDirectPathPushReceive = now; - return (_directPathPushCutoffCount >= ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); + return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); } /** From 0034efafe4f141d06d07464e7df2e151f4304294 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 11:08:15 -0700 Subject: [PATCH 159/195] On semi-undocumented test net, assign a RFC4193 IPv6 address too. Will be useful for our at-scale tests. --- node/NetworkConfig.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index cd32600f1..35e238377 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -55,6 +55,9 @@ SharedPtr NetworkConfig::createTestNetworkConfig(const Address &s if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0 nc->_staticIps.push_back(InetAddress(Utils::hton(ip),8)); + // Assign an RFC4193-compliant IPv6 address -- will never collide + nc->_staticIps.push_back(InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt())); + return nc; } From c6a918d9962dcf2354483b709b8bf0fffbbc3983 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 12:50:48 -0700 Subject: [PATCH 160/195] HTTP test code. --- tests/http/README.md | 5 + tests/http/agent.js | 224 ++++++++++++++++++++++++++++++++++++++++ tests/http/package.json | 16 +++ tests/http/server.js | 48 +++++++++ 4 files changed, 293 insertions(+) create mode 100644 tests/http/README.md create mode 100644 tests/http/agent.js create mode 100644 tests/http/package.json create mode 100644 tests/http/server.js diff --git a/tests/http/README.md b/tests/http/README.md new file mode 100644 index 000000000..ae7f08f10 --- /dev/null +++ b/tests/http/README.md @@ -0,0 +1,5 @@ +HTTP one-to-all test +====== + +This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. + diff --git a/tests/http/agent.js b/tests/http/agent.js new file mode 100644 index 000000000..14964d873 --- /dev/null +++ b/tests/http/agent.js @@ -0,0 +1,224 @@ +// --------------------------------------------------------------------------- +// Customizable parameters: + +// How frequently in ms to run tests +//var RUN_TEST_EVERY = (60 * 5 * 1000); +var RUN_TEST_EVERY = 1000; + +// Maximum test duration in milliseconds (must be less than RUN_TEST_EVERY) +var TEST_DURATION = (60 * 1000); + +// Where should I contact to register and query a list of other nodes? +var SERVER_HOST = '127.0.0.1'; +var SERVER_PORT = 18080; + +// Which port should agents use for their HTTP? +var AGENT_PORT = 18888; + +// Payload size in bytes +var PAYLOAD_SIZE = 4096; + +// --------------------------------------------------------------------------- + +var ipaddr = require('ipaddr.js'); +var os = require('os'); +var http = require('http'); +var async = require('async'); + +var express = require('express'); +var app = express(); + +// Find our ZeroTier-assigned RFC4193 IPv6 address +var thisAgentId = null; +var interfaces = os.networkInterfaces(); +if (!interfaces) { + console.error('FATAL: os.networkInterfaces() failed.'); + process.exit(1); +} +for(var ifname in interfaces) { + var ifaddrs = interfaces[ifname]; + if (Array.isArray(ifaddrs)) { + for(var i=0;i Date: Wed, 28 Oct 2015 13:14:53 -0700 Subject: [PATCH 161/195] HTTP test works! --- tests/http/agent.js | 16 +++++++++------- tests/http/server.js | 14 +++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/http/agent.js b/tests/http/agent.js index 14964d873..348374111 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -9,14 +9,14 @@ var RUN_TEST_EVERY = 1000; var TEST_DURATION = (60 * 1000); // Where should I contact to register and query a list of other nodes? -var SERVER_HOST = '127.0.0.1'; +var SERVER_HOST = '174.136.102.178'; var SERVER_PORT = 18080; // Which port should agents use for their HTTP? var AGENT_PORT = 18888; // Payload size in bytes -var PAYLOAD_SIZE = 4096; +var PAYLOAD_SIZE = 100000; // --------------------------------------------------------------------------- @@ -66,9 +66,9 @@ if (thisAgentId === null) { //console.log(thisAgentId); // Create a random (and therefore not very compressable) payload -var payload = ''; -while (payload.length < PAYLOAD_SIZE) { - payload += String.fromCharCode(Math.round(Math.random() * 255.0)); +var payload = new Buffer(PAYLOAD_SIZE); +for(var xx=0;xx Date: Wed, 28 Oct 2015 13:35:52 -0700 Subject: [PATCH 162/195] Basic Dockerfile for building test agents. --- .gitignore | 5 ++--- tests/http/Dockerfile | 23 +++++++++++++++++++++++ tests/http/docker-main.sh | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/http/Dockerfile create mode 100644 tests/http/docker-main.sh diff --git a/.gitignore b/.gitignore index 06b06b7d3..89ab049fe 100755 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ Thumbs.db /world/mkworld /world/*.c25519 -# Miscellaneous file types that we don't want to check in +# Miscellaneous temporaries, build files, etc. *.log *.opensdf *.user @@ -50,10 +50,9 @@ Thumbs.db *.autosave *.tmp node_modules - -# cluster-geo stuff cluster-geo/cluster-geo/config.js cluster-geo/cluster-geo/cache.* +tests/http/zerotier-one # MacGap wrapper build files /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/* diff --git a/tests/http/Dockerfile b/tests/http/Dockerfile new file mode 100644 index 000000000..02578cd53 --- /dev/null +++ b/tests/http/Dockerfile @@ -0,0 +1,23 @@ +FROM centos:latest + +MAINTAINER https://www.zerotier.com/ + +EXPOSE 9993/udp + +RUN yum -y update && yum -y install epel-release && yum -y install nodejs npm && yum clean all + +RUN mkdir -p /var/lib/zerotier-one +RUN mkdir -p /var/lib/zerotier-one/networks.d +RUN touch /var/lib/zerotier-one/networks.d/ffffffffffffffff.conf + +ADD package.json / +RUN npm install + +ADD zerotier-one / +RUN chmod a+x /zerotier-one + +ADD agent.js / +ADD main.sh / +RUN chmod a+x /docker-main.sh + +CMD ["./docker-main.sh"] diff --git a/tests/http/docker-main.sh b/tests/http/docker-main.sh new file mode 100644 index 000000000..947ccf47a --- /dev/null +++ b/tests/http/docker-main.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin + +/zerotier-one -d +exec node --harmony /agent.js From 07c1b4ddee1b6ba1a4399dc62c8e3c6f5367afa0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 14:16:58 -0700 Subject: [PATCH 163/195] test stuff --- tests/http/Dockerfile | 2 +- tests/http/agent.js | 148 +++++++++++++++++++------------------- tests/http/docker-main.sh | 7 +- tests/http/server.js | 6 +- 4 files changed, 83 insertions(+), 80 deletions(-) mode change 100644 => 100755 tests/http/docker-main.sh diff --git a/tests/http/Dockerfile b/tests/http/Dockerfile index 02578cd53..7bba2fc07 100644 --- a/tests/http/Dockerfile +++ b/tests/http/Dockerfile @@ -17,7 +17,7 @@ ADD zerotier-one / RUN chmod a+x /zerotier-one ADD agent.js / -ADD main.sh / +ADD docker-main.sh / RUN chmod a+x /docker-main.sh CMD ["./docker-main.sh"] diff --git a/tests/http/agent.js b/tests/http/agent.js index 348374111..465a2e28e 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -1,14 +1,13 @@ // --------------------------------------------------------------------------- // Customizable parameters: -// How frequently in ms to run tests -//var RUN_TEST_EVERY = (60 * 5 * 1000); -var RUN_TEST_EVERY = 1000; +// Maximum test duration in milliseconds +var TEST_DURATION = (30 * 1000); -// Maximum test duration in milliseconds (must be less than RUN_TEST_EVERY) -var TEST_DURATION = (60 * 1000); +// Interval between tests (should be several times longer than TEST_DURATION) +var TEST_INTERVAL = (60 * 2 * 1000); -// Where should I contact to register and query a list of other nodes? +// Where should I contact to register and query a list of other test agents? var SERVER_HOST = '174.136.102.178'; var SERVER_PORT = 18080; @@ -71,8 +70,29 @@ for(var xx=0;xx>agent.out 2>&1 diff --git a/tests/http/server.js b/tests/http/server.js index a58756bc1..6211b4eea 100644 --- a/tests/http/server.js +++ b/tests/http/server.js @@ -26,7 +26,8 @@ app.get('/:agentId',function(req,res) { return res.status(200).send(JSON.stringify(Object.keys(knownAgents))); }); -app.post('/:agentId',function(req,res) { +app.post('/:testNumber/:agentId',function(req,res) { + var testNumber = req.params.testNumber; var agentId = req.params.agentId; if ((!agentId)||(agentId.length !== 32)) return res.status(404).send(''); @@ -40,8 +41,9 @@ app.post('/:agentId',function(req,res) { } result = { agentId: agentId, + testNumber: testNumber, receiveTime: receiveTime, - result: resultData + results: resultData }; var nows = receiveTime.toString(16); From 9653531242dd5f66e331fc716c4aacd1aece30c5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 14:18:58 -0700 Subject: [PATCH 164/195] . --- tests/http/agent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/agent.js b/tests/http/agent.js index 465a2e28e..53b2e9f9c 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -204,7 +204,7 @@ function doTestsAndReport() }); } }); -} +}; // Agents just serve up a test payload app.get('/',function(req,res) { From 4c24e0cfb0bb9b61a4e19ac81b89dc1cbce6ea99 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 14:24:54 -0700 Subject: [PATCH 165/195] More tweaks to tests... just about ready to run at scale. --- .gitignore | 1 + tests/http/agent.js | 1 + tests/http/server.js | 10 +++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 89ab049fe..87e387a6d 100755 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ node_modules cluster-geo/cluster-geo/config.js cluster-geo/cluster-geo/cache.* tests/http/zerotier-one +tests/http/result_* # MacGap wrapper build files /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/* diff --git a/tests/http/agent.js b/tests/http/agent.js index 53b2e9f9c..061c7ba70 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -217,6 +217,7 @@ var expressServer = app.listen(AGENT_PORT,agentIdToIp(thisAgentId),function () { console.error('FATAL: unable to contact or query server: '+err.toString()); process.exit(1); } + doTestsAndReport(); setInterval(doTestsAndReport,TEST_INTERVAL); }); }); diff --git a/tests/http/server.js b/tests/http/server.js index 6211b4eea..69ccf527e 100644 --- a/tests/http/server.js +++ b/tests/http/server.js @@ -41,15 +41,15 @@ app.post('/:testNumber/:agentId',function(req,res) { } result = { agentId: agentId, - testNumber: testNumber, + testNumber: parseInt(testNumber), receiveTime: receiveTime, results: resultData }; - var nows = receiveTime.toString(16); - while (nows.length < 16) - nows = '0' + nows; - fs.writeFile('result_'+agentId+'_'+nows,JSON.stringify(result),function(err) { + testNumber = testNumber.toString(); + while (testNumber.length < 10) + testNumber = '0' + testNumber; + fs.writeFile('result_'+testNumber+'_'+agentId,JSON.stringify(result),function(err) { console.log(result); }); From 68d6d3c4ff4d3921408f7d9bab3e31e0e028a871 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 14:29:08 -0700 Subject: [PATCH 166/195] Fix bug in peer count. --- node/Cluster.cpp | 2 +- node/Topology.cpp | 4 ++-- node/Topology.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 9b0348221..e95f6acca 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -634,7 +634,7 @@ void Cluster::status(ZT_ClusterStatus &status) const ms[_id]->x = _x; ms[_id]->y = _y; ms[_id]->z = _z; - ms[_id]->peers = RR->topology->countAlive(); + ms[_id]->peers = RR->topology->countActive(); for(std::vector::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) { if (ms[_id]->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check break; diff --git a/node/Topology.cpp b/node/Topology.cpp index 09668ef55..9027eff16 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -308,7 +308,7 @@ void Topology::clean(uint64_t now) } } -unsigned long Topology::countAlive() const +unsigned long Topology::countActive() const { const uint64_t now = RR->node->now(); unsigned long cnt = 0; @@ -317,7 +317,7 @@ unsigned long Topology::countAlive() const Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { - if ((*p)->alive(now)) + if ((*p)->hasActiveDirectPath(now)) ++cnt; } return cnt; diff --git a/node/Topology.hpp b/node/Topology.hpp index 16836e075..b9f063c85 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -210,9 +210,9 @@ public: void clean(uint64_t now); /** - * @return Number of 'alive' peers + * @return Number of peers with active direct paths */ - unsigned long countAlive() const; + unsigned long countActive() const; /** * Apply a function or function object to all peers From 1f5ef968cff51721d97587cb1c5402c988c92d5e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 15:08:00 -0700 Subject: [PATCH 167/195] Test need a more recent version of NodeJS so update Dockerfile. --- tests/http/Dockerfile | 3 ++- tests/http/agent.js | 2 +- tests/http/docker-main.sh | 3 +++ tests/http/nodesource-el.repo | 6 ++++++ 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/http/nodesource-el.repo diff --git a/tests/http/Dockerfile b/tests/http/Dockerfile index 7bba2fc07..e19b3feef 100644 --- a/tests/http/Dockerfile +++ b/tests/http/Dockerfile @@ -4,7 +4,8 @@ MAINTAINER https://www.zerotier.com/ EXPOSE 9993/udp -RUN yum -y update && yum -y install epel-release && yum -y install nodejs npm && yum clean all +ADD nodesource-el.repo /etc/yum.repos.d/nodesource-el.repo +RUN yum -y update && yum install -y nodejs && yum clean all RUN mkdir -p /var/lib/zerotier-one RUN mkdir -p /var/lib/zerotier-one/networks.d diff --git a/tests/http/agent.js b/tests/http/agent.js index 061c7ba70..8b1cf5121 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -211,7 +211,7 @@ app.get('/',function(req,res) { return res.status(200).send(payload); }); -var expressServer = app.listen(AGENT_PORT,agentIdToIp(thisAgentId),function () { +var expressServer = app.listen(AGENT_PORT,function () { registerAndGetPeers(function(err,peers) { if (err) { console.error('FATAL: unable to contact or query server: '+err.toString()); diff --git a/tests/http/docker-main.sh b/tests/http/docker-main.sh index ad80af0ce..720236687 100755 --- a/tests/http/docker-main.sh +++ b/tests/http/docker-main.sh @@ -8,4 +8,7 @@ while [ ! -d "/proc/sys/net/ipv6/conf/zt0" ]; do sleep 0.25 done +sleep 2 + exec node --harmony /agent.js >>agent.out 2>&1 +#exec node --harmony /agent.js diff --git a/tests/http/nodesource-el.repo b/tests/http/nodesource-el.repo new file mode 100644 index 000000000..b785d3d07 --- /dev/null +++ b/tests/http/nodesource-el.repo @@ -0,0 +1,6 @@ +[nodesource] +name=Node.js Packages for Enterprise Linux 7 - $basearch +baseurl=https://rpm.nodesource.com/pub_4.x/el/7/$basearch +failovermethod=priority +enabled=1 +gpgcheck=0 From cabb8752cb9d9c85dc2aa2cac22a4ad101614577 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 15:28:05 -0700 Subject: [PATCH 168/195] docs --- tests/http/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/http/README.md b/tests/http/README.md index ae7f08f10..7852ac167 100644 --- a/tests/http/README.md +++ b/tests/http/README.md @@ -3,3 +3,6 @@ HTTP one-to-all test This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. +A Dockerfile is also included which will build a simple Docker image that runs the agent. The image must be launched with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. + +Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. From e3d811b04b7fb04981d65a85d9042e2bd31798b7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 15:55:40 -0700 Subject: [PATCH 169/195] docs --- tests/http/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/http/README.md b/tests/http/README.md index 7852ac167..ab4827ecc 100644 --- a/tests/http/README.md +++ b/tests/http/README.md @@ -3,6 +3,8 @@ HTTP one-to-all test This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. -A Dockerfile is also included which will build a simple Docker image that runs the agent. The image must be launched with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. - Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. + +A Dockerfile is also included which will build a simple Docker image that runs the agent. The image must be launched with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. You can run a bunch with a command like: + + for ((n=0;n<10;n++)); do docker run --device=/dev/net/tun --privileged -d zerotier/http-test; done From 883c84bdb95b0374e4f4ea2238b2288787547897 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 29 Oct 2015 09:39:36 -0700 Subject: [PATCH 170/195] Tweak some timings, and remove some dead code. --- node/Cluster.hpp | 2 +- node/Peer.hpp | 31 ------------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index cc9edd1d1..0d8c0f155 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -57,7 +57,7 @@ /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 250 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 namespace ZeroTier { diff --git a/node/Peer.hpp b/node/Peer.hpp index 39acffd97..e5db3bde5 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -205,32 +205,6 @@ public: return pp; } - /** - * @return Time of last direct packet receive for any path - */ - inline uint64_t lastDirectReceive() const - throw() - { - Mutex::Lock _l(_lock); - uint64_t x = 0; - for(unsigned int p=0,np=_numPaths;p Date: Thu, 29 Oct 2015 09:42:15 -0700 Subject: [PATCH 171/195] 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(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 01c8bcdeb..e9b38c524 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 82cda66d8..42180e990 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 48c5ead8f..9b85b8326 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 2b05b8126..c01829903 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 976c7c449..9d0d78e5a 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 4e3f24c79..8c8ff1ed1 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; From 9f0f0197fe5a79ed04647b110485a530ed06ed8e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 29 Oct 2015 09:44:25 -0700 Subject: [PATCH 172/195] More dead code removal. --- node/Path.hpp | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/node/Path.hpp b/node/Path.hpp index c01829903..39a18c430 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -49,37 +49,12 @@ class RuntimeEnvironment; class Path { public: - /** - * Path trust category - * - * Note that this is NOT peer trust and has nothing to do with root server - * designations or other trust metrics. This indicates how much we trust - * this path to be secure and/or private. A trust level of normal means - * encrypt and authenticate all traffic. Privacy trust means we can send - * traffic in the clear. Ultimate trust means we don't even need - * authentication. Generally a private path would be a hard-wired local - * LAN, while an ultimate trust path would be a physically isolated private - * server backplane. - * - * Nearly all paths will be normal trust. The other levels are for high - * performance local SDN use only. - * - * These values MUST match ZT_LocalInterfaceAddressTrust in ZeroTierOne.h - */ - enum Trust // NOTE: max 255 - { - TRUST_NORMAL = 0, - TRUST_PRIVACY = 10, - TRUST_ULTIMATE = 20 - }; - Path() : _lastSend(0), _lastReceived(0), _addr(), _localAddress(), - _ipScope(InetAddress::IP_SCOPE_NONE), - _flags(0) + _ipScope(InetAddress::IP_SCOPE_NONE) { } @@ -88,8 +63,7 @@ public: _lastReceived(0), _addr(addr), _localAddress(localAddress), - _ipScope(addr.ipScope()), - _flags(0) + _ipScope(addr.ipScope()) { } @@ -241,7 +215,6 @@ public: b.append((uint64_t)_lastReceived); _addr.serialize(b); _localAddress.serialize(b); - b.append((uint16_t)_flags); } template @@ -255,7 +228,6 @@ public: p += _addr.deserialize(b,p); p += _localAddress.deserialize(b,p); _ipScope = _addr.ipScope(); - _flags = b.template at(p); p += 2; return (p - startAt); } @@ -265,7 +237,6 @@ private: InetAddress _addr; InetAddress _localAddress; InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often - uint16_t _flags; }; } // namespace ZeroTier From d6c0d176ee3bc71f25105503e807a7fe0d45674a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 29 Oct 2015 10:10:09 -0700 Subject: [PATCH 173/195] Periodically re-announce peers that we have. --- node/Cluster.cpp | 53 ++++++++++++++++++++++++++++++++++-------------- node/Cluster.hpp | 11 +++++++--- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index e95f6acca..93b69a08a 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -84,7 +84,8 @@ Cluster::Cluster( _zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints), _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]), _peerAffinities(65536), - _lastCleanedPeerAffinities(0) + _lastCleanedPeerAffinities(0), + _lastCheckedPeersForAnnounce(0) { uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)]; @@ -328,6 +329,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len) if (haveMatch) { _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size()); + _flush(fromMemberId); // we want this to go ASAP, since with port restricted cone NATs success can be timing-sensitive RR->sw->send(rendezvousForLocal,true,0); } } @@ -469,10 +471,45 @@ void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembers } } +struct _ClusterAnnouncePeers +{ + _ClusterAnnouncePeers(const uint64_t now_,Cluster *parent_) : now(now_),parent(parent_) {} + const uint64_t now; + Cluster *const parent; + inline void operator()(const Topology &t,const SharedPtr &peer) + { + Path *p = peer->getBestPath(now); + if (p) + parent->replicateHavePeer(peer->identity(),p->address()); + } +}; void Cluster::doPeriodicTasks() { const uint64_t now = RR->node->now(); + // Erase old peer affinity entries just to control table size + if ((now - _lastCleanedPeerAffinities) >= (ZT_PEER_ACTIVITY_TIMEOUT * 5)) { + _lastCleanedPeerAffinities = now; + Address *k = (Address *)0; + _PA *v = (_PA *)0; + Mutex::Lock _l(_peerAffinities_m); + Hashtable< Address,_PA >::Iterator i(_peerAffinities); + while (i.next(k,v)) { + if ((now - v->ts) >= (ZT_PEER_ACTIVITY_TIMEOUT * 5)) + _peerAffinities.erase(*k); + } + } + + // Announce peers that we have active direct paths to -- note that we forget paths + // that other cluster members claim they have, which prevents us from fighting + // with other cluster members (route flapping) over specific paths. + if ((now - _lastCheckedPeersForAnnounce) >= (ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD / 4)) { + _lastCheckedPeersForAnnounce = now; + _ClusterAnnouncePeers func(now,this); + RR->topology->eachPeer<_ClusterAnnouncePeers &>(func); + } + + // Flush outgoing packet send queue every doPeriodicTasks() { Mutex::Lock _l(_memberIds_m); for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { @@ -506,20 +543,6 @@ void Cluster::doPeriodicTasks() _flush(*mid); // does nothing if nothing to flush } } - - { - if ((now - _lastCleanedPeerAffinities) >= (ZT_PEER_ACTIVITY_TIMEOUT * 10)) { - _lastCleanedPeerAffinities = now; - Address *k = (Address *)0; - _PA *v = (_PA *)0; - Mutex::Lock _l(_peerAffinities_m); - Hashtable< Address,_PA >::Iterator i(_peerAffinities); - while (i.next(k,v)) { - if ((now - v->ts) >= (ZT_PEER_ACTIVITY_TIMEOUT * 10)) - _peerAffinities.erase(*k); - } - } - } } void Cluster::addMember(uint16_t memberId) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index 0d8c0f155..7d7a1ced4 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -46,18 +46,21 @@ /** * Timeout for cluster members being considered "alive" + * + * A cluster member is considered dead and will no longer have peers + * redirected to it if we have not heard a heartbeat in this long. */ -#define ZT_CLUSTER_TIMEOUT 20000 +#define ZT_CLUSTER_TIMEOUT 10000 /** * How often should we announce that we have a peer? */ -#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD 30000 +#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD ((ZT_PEER_ACTIVITY_TIMEOUT / 2) - 1000) /** * Desired period between doPeriodicTasks() in milliseconds */ -#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100 +#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 250 namespace ZeroTier { @@ -349,7 +352,9 @@ private: }; Hashtable< Address,_PA > _peerAffinities; Mutex _peerAffinities_m; + uint64_t _lastCleanedPeerAffinities; + uint64_t _lastCheckedPeersForAnnounce; }; } // namespace ZeroTier From e2fc20876d1f171b1d3f1bfabb9fc23fb8455a5a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 29 Oct 2015 18:23:41 -0700 Subject: [PATCH 174/195] docs --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 248065047..00efed368 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,10 @@ If you're interested, there's a [technical deep dive about NAT traversal on our If a firewall between you and the Internet blocks ZeroTier's UDP traffic, you will fall back to last-resort TCP tunneling to rootservers over port 443 (https impersonation). This will work almost anywhere but is *very slow* compared to UDP or direct peer to peer connectivity. +### Contributing + +There are three main branches: **edge**, **test**, and **master**. Other branches may be for specific features, tests, or use cases. In general **edge** is "bleeding" and may or may not work, while **test** should be relatively stable and **master** is the latest tagged release. Pull requests should generally be done against **test** or **edge**, since pull requests against **master** may be working against a branch that is somewhat out of date. + ### License The ZeroTier source code is open source and is licensed under the GNU GPL v3 (not LGPL). If you'd like to embed it in a closed-source commercial product or appliance, please e-mail [contact@zerotier.com](mailto:contact@zerotier.com) to discuss commercial licensing. Otherwise it can be used for free. From 80e62ad29180800fd1669aa68515876f0e8add54 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 10:55:05 -0700 Subject: [PATCH 175/195] docs --- tests/http/README.md | 4 ++-- tests/http/agent.js | 2 ++ tests/http/server.js | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/http/README.md b/tests/http/README.md index ab4827ecc..58d4a9892 100644 --- a/tests/http/README.md +++ b/tests/http/README.md @@ -3,8 +3,8 @@ HTTP one-to-all test This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. -Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. +Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. You will also want to edit the Dockerfile if you want to build containers and change the network ID to the network you want to run tests over. -A Dockerfile is also included which will build a simple Docker image that runs the agent. The image must be launched with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. You can run a bunch with a command like: +The Dockerfile builds an image that launches the agent. The image must be "docker run" with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. (Unfortunately CAP_NET_ADMIN may not work due to a bug in Docker and/or Linux.) You can run a bunch with a command like: for ((n=0;n<10;n++)); do docker run --device=/dev/net/tun --privileged -d zerotier/http-test; done diff --git a/tests/http/agent.js b/tests/http/agent.js index 8b1cf5121..8a8b785e5 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -1,3 +1,5 @@ +// ZeroTier distributed HTTP test agent + // --------------------------------------------------------------------------- // Customizable parameters: diff --git a/tests/http/server.js b/tests/http/server.js index 69ccf527e..30d8339a3 100644 --- a/tests/http/server.js +++ b/tests/http/server.js @@ -1,3 +1,5 @@ +// ZeroTier distributed HTTP test coordinator and result-reporting server + // --------------------------------------------------------------------------- // Customizable parameters: From 5bfa29ddedaa918fb0d5912537391f1ca4b0ba07 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 11:09:40 -0700 Subject: [PATCH 176/195] Make antirec tail len slightly shorter, better performance and still plenty long enough. --- node/AntiRecursion.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp index c5aa92d80..4bb24bf3b 100644 --- a/node/AntiRecursion.hpp +++ b/node/AntiRecursion.hpp @@ -35,7 +35,7 @@ namespace ZeroTier { -#define ZT_ANTIRECURSION_TAIL_LEN 256 +#define ZT_ANTIRECURSION_TAIL_LEN 128 /** * Filter to prevent recursion (ZeroTier-over-ZeroTier) From b6725c44150af306130d38a2875ab31a640607c8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 11:48:33 -0700 Subject: [PATCH 177/195] Optimize AntiRecursion. --- node/AntiRecursion.hpp | 66 +++++++++++++++++++++++++++--------------- node/Constants.hpp | 5 ---- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp index 4bb24bf3b..8629d19a9 100644 --- a/node/AntiRecursion.hpp +++ b/node/AntiRecursion.hpp @@ -35,28 +35,28 @@ namespace ZeroTier { -#define ZT_ANTIRECURSION_TAIL_LEN 128 +/** + * Size of anti-recursion history + */ +#define ZT_ANTIRECURSION_HISTORY_SIZE 16 /** * Filter to prevent recursion (ZeroTier-over-ZeroTier) * * This works by logging ZeroTier packets that we send. It's then invoked - * again against packets read from local Ethernet taps. If the last N + * again against packets read from local Ethernet taps. If the last 32 * bytes representing the ZeroTier packet match in the tap frame, then * the frame is a re-injection of a frame that we sent and is rejected. * * This means that ZeroTier packets simply will not traverse ZeroTier * networks, which would cause all sorts of weird problems. * - * NOTE: this is applied to low-level packets before they are sent to - * SocketManager and/or sockets, not to fully assembled packets before - * (possible) fragmentation. + * This is highly optimized code since it's checked for every packet. */ class AntiRecursion { public: AntiRecursion() - throw() { memset(_history,0,sizeof(_history)); _ptr = 0; @@ -68,13 +68,20 @@ public: * @param data ZT packet data * @param len Length of packet */ - inline void logOutgoingZT(const void *data,unsigned int len) - throw() + inline void logOutgoingZT(const void *const data,const unsigned int len) { - ArItem *i = &(_history[_ptr++ % ZT_ANTIRECURSION_HISTORY_SIZE]); - const unsigned int tl = (len > ZT_ANTIRECURSION_TAIL_LEN) ? ZT_ANTIRECURSION_TAIL_LEN : len; - memcpy(i->tail,((const unsigned char *)data) + (len - tl),tl); - i->len = tl; + if (len < 32) + return; +#ifdef ZT_NO_TYPE_PUNNING + memcpy(_history[++_ptr % ZT_ANTIRECURSION_HISTORY_SIZE].tail,reinterpret_cast(data) + (len - 32),32); +#else + uint64_t *t = _history[++_ptr % ZT_ANTIRECURSION_HISTORY_SIZE].tail; + const uint64_t *p = reinterpret_cast(reinterpret_cast(data) + (len - 32)); + *(t++) = *(p++); + *(t++) = *(p++); + *(t++) = *(p++); + *t = *p; +#endif } /** @@ -84,25 +91,36 @@ public: * @param len Length of frame * @return True if frame is OK to be passed, false if it's a ZT frame that we sent */ - inline bool checkEthernetFrame(const void *data,unsigned int len) - throw() + inline bool checkEthernetFrame(const void *const data,const unsigned int len) const { - for(unsigned int h=0;hlen > 0)&&(len >= i->len)&&(!memcmp(((const unsigned char *)data) + (len - i->len),i->tail,i->len))) + if (len < 32) + return true; + const uint8_t *const pp = reinterpret_cast(data) + (len - 32); + const _ArItem *i = _history; + const _ArItem *const end = i + ZT_ANTIRECURSION_HISTORY_SIZE; + while (i != end) { +#ifdef ZT_NO_TYPE_PUNNING + if (!memcmp(pp,i->tail,32)) return false; +#else + const uint64_t *t = i->tail; + const uint64_t *p = reinterpret_cast(pp); + uint64_t bits = *(t++) ^ *(p++); + bits |= *(t++) ^ *(p++); + bits |= *(t++) ^ *(p++); + bits |= *t ^ *p; + if (!bits) + return false; +#endif + ++i; } return true; } private: - struct ArItem - { - unsigned char tail[ZT_ANTIRECURSION_TAIL_LEN]; - unsigned int len; - }; - ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE]; - volatile unsigned int _ptr; + struct _ArItem { uint64_t tail[4]; }; + _ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE]; + volatile unsigned long _ptr; }; } // namespace ZeroTier diff --git a/node/Constants.hpp b/node/Constants.hpp index 5a4d4a4de..1d5fa6f42 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -309,11 +309,6 @@ */ #define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000 -/** - * Size of anti-recursion history (see AntiRecursion.hpp) - */ -#define ZT_ANTIRECURSION_HISTORY_SIZE 16 - /** * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ From b845dd1b88b966c7931524721d8369e6db240ed7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 12:38:12 -0700 Subject: [PATCH 178/195] Set contact IP for real test. --- tests/http/agent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/agent.js b/tests/http/agent.js index 8a8b785e5..d0c33917b 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -10,7 +10,7 @@ var TEST_DURATION = (30 * 1000); var TEST_INTERVAL = (60 * 2 * 1000); // Where should I contact to register and query a list of other test agents? -var SERVER_HOST = '174.136.102.178'; +var SERVER_HOST = '104.238.141.145'; var SERVER_PORT = 18080; // Which port should agents use for their HTTP? From f808138a942893188537bf82a064932a2523d34d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 13:05:34 -0700 Subject: [PATCH 179/195] docs and stuff --- tests/http/README.md | 4 +++- tests/http/run-a-big-test.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100755 tests/http/run-a-big-test.sh diff --git a/tests/http/README.md b/tests/http/README.md index 58d4a9892..23a956054 100644 --- a/tests/http/README.md +++ b/tests/http/README.md @@ -1,10 +1,12 @@ HTTP one-to-all test ====== -This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. +*This is really internal use code. You're free to test it out but expect to do some editing/tweaking to make it work. We used this to run some massive scale tests of our new geo-cluster-based root server infrastructure prior to taking it live.* Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. You will also want to edit the Dockerfile if you want to build containers and change the network ID to the network you want to run tests over. +This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. + The Dockerfile builds an image that launches the agent. The image must be "docker run" with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. (Unfortunately CAP_NET_ADMIN may not work due to a bug in Docker and/or Linux.) You can run a bunch with a command like: for ((n=0;n<10;n++)); do docker run --device=/dev/net/tun --privileged -d zerotier/http-test; done diff --git a/tests/http/run-a-big-test.sh b/tests/http/run-a-big-test.sh new file mode 100755 index 000000000..1c125345d --- /dev/null +++ b/tests/http/run-a-big-test.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits +NUM_CONTAINERS=100 +CONTAINER_IMAGE=zerotier/http-test + +# +# This script is designed to be run on Docker hosts to run NUM_CONTAINERS +# +# It can then be run on each Docker host via pssh or similar to run very +# large scale tests. +# + +export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin + +# Kill and clean up old test containers if any -- note that this kills all containers on the system! +docker ps -q | xargs -n 1 docker kill +docker ps -aq | xargs -n 1 docker rm + +# Pull latest if needed -- change this to your image name and/or where to pull it from +docker pull $CONTAINER_IMAGE + +# Run NUM_CONTAINERS +for ((n=0;n<$NUM_CONTAINERS;n++)); do + docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE +done + +exit 0 From f974517f64aac6b527fefa8f3b30088b804d2ae2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 13:06:30 -0700 Subject: [PATCH 180/195] Save zerotier output in containers. --- tests/http/docker-main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/http/docker-main.sh b/tests/http/docker-main.sh index 720236687..f9e11de57 100755 --- a/tests/http/docker-main.sh +++ b/tests/http/docker-main.sh @@ -2,7 +2,7 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin -/zerotier-one -d +/zerotier-one -d >>zerotier-one.out 2>&1 while [ ! -d "/proc/sys/net/ipv6/conf/zt0" ]; do sleep 0.25 From 377ccff600859da2d0e7ecd65a38953bd471d04d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 13:39:28 -0700 Subject: [PATCH 181/195] getPeer() had a small potential to be unsafe. --- node/Topology.cpp | 38 +++++++++++++++++++++----------------- node/Topology.hpp | 2 +- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index 9027eff16..031c0b1b6 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -146,26 +146,30 @@ SharedPtr Topology::getPeer(const Address &zta) return SharedPtr(); } - Mutex::Lock _l(_lock); - - SharedPtr &ap = _peers[zta]; - - if (ap) { - ap->use(RR->node->now()); - return ap; + { + Mutex::Lock _l(_lock); + const SharedPtr *const ap = _peers.get(zta); + if (ap) { + (*ap)->use(RR->node->now()); + return *ap; + } } - Identity id(_getIdentity(zta)); - if (id) { - try { - ap = SharedPtr(new Peer(RR->identity,id)); - ap->use(RR->node->now()); - return ap; - } catch ( ... ) {} // invalid identity? - } + try { + Identity id(_getIdentity(zta)); + if (id) { + SharedPtr np(new Peer(RR->identity,id)); + { + Mutex::Lock _l(_lock); + SharedPtr &ap = _peers[zta]; + if (!ap) + ap.swap(np); + ap->use(RR->node->now()); + return ap; + } + } + } catch ( ... ) {} // invalid identity on disk? - // If we get here it means we read an invalid cache identity or had some other error - _peers.erase(zta); return SharedPtr(); } diff --git a/node/Topology.hpp b/node/Topology.hpp index b9f063c85..d6f453aca 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -87,7 +87,7 @@ public: inline SharedPtr getPeerNoCache(const Address &zta,const uint64_t now) { Mutex::Lock _l(_lock); - const SharedPtr *ap = _peers.get(zta); + const SharedPtr *const ap = _peers.get(zta); if (ap) { (*ap)->use(now); return *ap; From d8dbbf7484f5df16a3e36b35da31383cc9589081 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 14:11:10 -0700 Subject: [PATCH 182/195] Add some debug code in TRACE mode to catch a bug. --- node/Topology.cpp | 12 +++++++----- node/Topology.hpp | 11 +++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/node/Topology.cpp b/node/Topology.cpp index 031c0b1b6..5e086116b 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -120,10 +120,12 @@ Topology::~Topology() SharedPtr Topology::addPeer(const SharedPtr &peer) { - if (peer->address() == RR->identity.address()) { - TRACE("BUG: addPeer() caught and ignored attempt to add peer for self"); - throw std::logic_error("cannot add peer for self"); +#ifdef ZT_TRACE + if ((!peer)||(peer->address() == RR->identity.address())) { + TRACE("BUG: addPeer() caught and ignored attempt to add peer for self or add a NULL peer"); + abort(); } +#endif SharedPtr np; { @@ -133,6 +135,7 @@ SharedPtr Topology::addPeer(const SharedPtr &peer) hp = peer; np = hp; } + np->use(RR->node->now()); saveIdentity(np->identity()); @@ -321,8 +324,7 @@ unsigned long Topology::countActive() const Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { - if ((*p)->hasActiveDirectPath(now)) - ++cnt; + cnt += (unsigned long)((*p)->hasActiveDirectPath(now)); } return cnt; } diff --git a/node/Topology.hpp b/node/Topology.hpp index d6f453aca..999338666 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -234,8 +234,15 @@ public: Hashtable< Address,SharedPtr >::Iterator i(_peers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) - f(*this,*p); + while (i.next(a,p)) { +#ifdef ZT_TRACE + if (!(*p)) { + ZT_TRACE("eachPeer() caught NULL peer for %s",a->toString().c_str()); + abort(); + } +#endif + f(*this,*((const SharedPtr *)p)); + } } /** From 2fbb5d0bbf206c6c87c30fc9b7cdf9b7975a5c0a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 14:11:45 -0700 Subject: [PATCH 183/195] . --- node/Topology.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Topology.hpp b/node/Topology.hpp index 999338666..f74b130f5 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -237,7 +237,7 @@ public: while (i.next(a,p)) { #ifdef ZT_TRACE if (!(*p)) { - ZT_TRACE("eachPeer() caught NULL peer for %s",a->toString().c_str()); + TRACE("eachPeer() caught NULL peer for %s",a->toString().c_str()); abort(); } #endif From 641b0dec44d641f6b21c67d5807418d9c89e4033 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 14:12:20 -0700 Subject: [PATCH 184/195] . --- node/Topology.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Topology.hpp b/node/Topology.hpp index f74b130f5..f48a89b2f 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -237,7 +237,7 @@ public: while (i.next(a,p)) { #ifdef ZT_TRACE if (!(*p)) { - TRACE("eachPeer() caught NULL peer for %s",a->toString().c_str()); + fprintf(stderr,"eachPeer() caught NULL peer for %s",a->toString().c_str()); abort(); } #endif From 7382c328daa33b5ff21baf78ee022f23092cda3b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 14:23:28 -0700 Subject: [PATCH 185/195] Null pointer bug appears fixed... testing again at large scale. --- node/Topology.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Topology.hpp b/node/Topology.hpp index f48a89b2f..f7804c293 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -237,7 +237,7 @@ public: while (i.next(a,p)) { #ifdef ZT_TRACE if (!(*p)) { - fprintf(stderr,"eachPeer() caught NULL peer for %s",a->toString().c_str()); + fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL",a->toString().c_str()); abort(); } #endif From 1b4cc4af5c7c0e47f73f3728ca36dc665d0e3224 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 30 Oct 2015 15:54:40 -0700 Subject: [PATCH 186/195] Fix evil bug, and instrument/assert on some other stuff, and a bit of cleanup. --- node/Cluster.cpp | 2 +- node/Hashtable.hpp | 2 -- node/Topology.cpp | 33 ++++++++++++++++++--------------- node/Topology.hpp | 16 ++-------------- selftest.cpp | 28 +++++++++++++++++++++++++--- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 93b69a08a..d0daae437 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -476,7 +476,7 @@ struct _ClusterAnnouncePeers _ClusterAnnouncePeers(const uint64_t now_,Cluster *parent_) : now(now_),parent(parent_) {} const uint64_t now; Cluster *const parent; - inline void operator()(const Topology &t,const SharedPtr &peer) + inline void operator()(const Topology &t,const SharedPtr &peer) const { Path *p = peer->getBestPath(now); if (p) diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index 1d8d9e5d2..e3512fefd 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -322,7 +322,6 @@ public: b->next = _t[bidx]; _t[bidx] = b; ++_s; - return b->v; } @@ -351,7 +350,6 @@ public: b->next = _t[bidx]; _t[bidx] = b; ++_s; - return b->v; } diff --git a/node/Topology.cpp b/node/Topology.cpp index 5e086116b..b8bb55f29 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -65,7 +65,7 @@ Topology::Topology(const RuntimeEnvironment *renv) : if (!p) break; // stop if invalid records if (p->address() != RR->identity.address()) - _peers[p->address()] = p; + _peers.set(p->address(),p); } catch ( ... ) { break; // stop if invalid records } @@ -122,7 +122,9 @@ SharedPtr Topology::addPeer(const SharedPtr &peer) { #ifdef ZT_TRACE if ((!peer)||(peer->address() == RR->identity.address())) { - TRACE("BUG: addPeer() caught and ignored attempt to add peer for self or add a NULL peer"); + if (!peer) + fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add NULL peer"ZT_EOL_S); + else fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add peer for self"ZT_EOL_S); abort(); } #endif @@ -171,7 +173,10 @@ SharedPtr Topology::getPeer(const Address &zta) return ap; } } - } catch ( ... ) {} // invalid identity on disk? + } catch ( ... ) { + fprintf(stderr,"EXCEPTION in getPeer() part 2\n"); + abort(); + } // invalid identity on disk? return SharedPtr(); } @@ -180,9 +185,9 @@ Identity Topology::getIdentity(const Address &zta) { { Mutex::Lock _l(_lock); - SharedPtr &ap = _peers[zta]; + const SharedPtr *const ap = _peers.get(zta); if (ap) - return ap->identity(); + return (*ap)->identity(); } return _getIdentity(zta); } @@ -207,18 +212,16 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou * causes packets searching for a route to pretty much literally * circumnavigate the globe rather than bouncing between just two. */ - if (_rootAddresses.size() > 1) { // gotta be one other than me for this to work - for(unsigned long p=0;p<_rootAddresses.size();++p) { - if (_rootAddresses[p] == RR->identity.address()) { - for(unsigned long q=1;q<_rootAddresses.size();++q) { - SharedPtr *nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]); - if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) { - (*nextsn)->use(now); - return *nextsn; - } + for(unsigned long p=0;p<_rootAddresses.size();++p) { + if (_rootAddresses[p] == RR->identity.address()) { + for(unsigned long q=1;q<_rootAddresses.size();++q) { + const SharedPtr *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]); + if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) { + (*nextsn)->use(now); + return *nextsn; } - break; } + break; } } diff --git a/node/Topology.hpp b/node/Topology.hpp index f7804c293..4c1a2ab37 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -113,24 +113,12 @@ public: */ void saveIdentity(const Identity &id); - /** - * @return Vector of peers that are root servers - */ - inline std::vector< SharedPtr > rootPeers() const - { - Mutex::Lock _l(_lock); - return _rootPeers; - } - /** * Get the current favorite root server * * @return Root server with lowest latency or NULL if none */ - inline SharedPtr getBestRoot() - { - return getBestRoot((const Address *)0,0,false); - } + inline SharedPtr getBestRoot() { return getBestRoot((const Address *)0,0,false); } /** * Get the best root server, avoiding root servers listed in an array @@ -237,7 +225,7 @@ public: while (i.next(a,p)) { #ifdef ZT_TRACE if (!(*p)) { - fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL",a->toString().c_str()); + fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL"ZT_EOL_S,a->toString().c_str()); abort(); } #endif diff --git a/selftest.cpp b/selftest.cpp index fa5eec0fb..0787925f8 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -593,23 +593,44 @@ static int testOther() std::cout << "[other] Testing Hashtable... "; std::cout.flush(); { Hashtable ht; - Hashtable ht2; std::map ref; // assume std::map works correctly :) for(int x=0;x<2;++x) { - for(int i=0;i<25000;++i) { + for(int i=0;i<77777;++i) { uint64_t k = rand(); while ((k == 0)||(ref.count(k) > 0)) ++k; std::string v("!"); for(int j=0;j<(int)(k % 64);++j) v.push_back("0123456789"[rand() % 10]); - ht.set(k,v); ref[k] = v; + ht.set(0xffffffffffffffffULL,v); + std::string &vref = ht[k]; + vref = v; + ht.erase(0xffffffffffffffffULL); } if (ht.size() != ref.size()) { std::cout << "FAILED! (size mismatch, original)" << std::endl; return -1; } + { + Hashtable::Iterator i(ht); + uint64_t *k = (uint64_t *)0; + std::string *v = (std::string *)0; + while(i.next(k,v)) { + if (ref.find(*k)->second != *v) { + std::cout << "FAILED! (data mismatch!)" << std::endl; + return -1; + } + } + } + for(std::map::const_iterator i(ref.begin());i!=ref.end();++i) { + if (ht[i->first] != i->second) { + std::cout << "FAILED! (data mismatch!)" << std::endl; + return -1; + } + } + + Hashtable ht2; ht2 = ht; Hashtable ht3(ht2); if (ht2.size() != ref.size()) { @@ -620,6 +641,7 @@ static int testOther() std::cout << "FAILED! (size mismatch, copied)" << std::endl; return -1; } + for(std::map::iterator i(ref.begin());i!=ref.end();++i) { std::string *v = ht.get(i->first); if (!v) { From f1b6427e63d71b6be79e55bd1abf44ec519e2d11 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 09:32:56 -0800 Subject: [PATCH 187/195] Decided to make this 1.1.0 (semantic versioning increment is warranted), and add a legacy hack for older clients working with clusters. --- node/IncomingPacket.cpp | 31 ++++++++++++++++++++++++++++++- node/Packet.hpp | 7 ++++--- version.h | 4 ++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index b0d65159b..32229ba65 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -294,7 +294,36 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - _remoteAddress.serialize(outp); + if (protoVersion >= 5) { + _remoteAddress.serialize(outp); + } else { + /* LEGACY COMPATIBILITY HACK: + * + * For a while now (since 1.0.3), ZeroTier has recognized changes in + * its network environment empirically by examining its external network + * address as reported by trusted peers. In versions prior to 1.1.0 + * (protocol version < 5), they did this by saving a snapshot of this + * information (in SelfAwareness.hpp) keyed by reporting device ID and + * address type. + * + * This causes problems when clustering is combined with symmetric NAT. + * Symmetric NAT remaps ports, so different endpoints in a cluster will + * report back different exterior addresses. Since the old code keys + * this by device ID and not sending physical address and compares the + * entire address including port, it constantly thinks its external + * surface is changing and resets connections when talking to a cluster. + * + * In new code we key by sending physical address and device and we also + * take the more conservative position of only interpreting changes in + * IP address (neglecting port) as a change in network topology that + * necessitates a reset. But we can make older clients work here by + * nulling out the port field. Since this info is only used for empirical + * detection of link changes, it doesn't break anything else. + */ + InetAddress tmpa(_remoteAddress); + tmpa.setPort(0); + tmpa.serialize(outp); + } if ((worldId != ZT_WORLD_ID_NULL)&&(RR->topology->worldTimestamp() > worldTimestamp)&&(worldId == RR->topology->worldId())) { World w(RR->topology->world()); diff --git a/node/Packet.hpp b/node/Packet.hpp index 985d25d0e..63c49ce33 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -57,10 +57,11 @@ * + New crypto completely changes key agreement cipher * 4 - 0.6.0 ... 1.0.6 * + New identity format based on hashcash design - * 5 - 1.0.6 ... CURRENT + * 5 - 1.1.0 ... CURRENT * + Supports circuit test, proof of work, and echo - * + Supports in-band world (root definition) updates - * + Otherwise backward compatible with 4 + * + Supports in-band world (root server definition) updates + * + Clustering! (Though this will work with protocol v4 clients.) + * + Otherwise backward compatible with protocol v4 */ #define ZT_PROTO_VERSION 5 diff --git a/version.h b/version.h index 4c36cb5e9..e27738776 100644 --- a/version.h +++ b/version.h @@ -36,11 +36,11 @@ /** * Minor version */ -#define ZEROTIER_ONE_VERSION_MINOR 0 +#define ZEROTIER_ONE_VERSION_MINOR 1 /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 6 +#define ZEROTIER_ONE_VERSION_REVISION 0 #endif From 29249db5d295b72cd28f73550ff7727b34fd5c9a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 11:37:32 -0800 Subject: [PATCH 188/195] Big test stuff. --- tests/http/big-test-hosts | 4 +++ .../{run-a-big-test.sh => big-test-kill.sh} | 14 +++++---- tests/http/big-test-start.sh | 30 +++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 tests/http/big-test-hosts rename tests/http/{run-a-big-test.sh => big-test-kill.sh} (63%) create mode 100755 tests/http/big-test-start.sh diff --git a/tests/http/big-test-hosts b/tests/http/big-test-hosts new file mode 100644 index 000000000..27c0c6565 --- /dev/null +++ b/tests/http/big-test-hosts @@ -0,0 +1,4 @@ +root@104.156.246.48 +root@104.156.252.136 +root@46.101.72.130 +root@188.166.240.16 diff --git a/tests/http/run-a-big-test.sh b/tests/http/big-test-kill.sh similarity index 63% rename from tests/http/run-a-big-test.sh rename to tests/http/big-test-kill.sh index 1c125345d..fbb34c104 100755 --- a/tests/http/run-a-big-test.sh +++ b/tests/http/big-test-kill.sh @@ -14,15 +14,17 @@ CONTAINER_IMAGE=zerotier/http-test export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin # Kill and clean up old test containers if any -- note that this kills all containers on the system! -docker ps -q | xargs -n 1 docker kill -docker ps -aq | xargs -n 1 docker rm +#docker ps -q | xargs -n 1 docker kill +#docker ps -aq | xargs -n 1 docker rm # Pull latest if needed -- change this to your image name and/or where to pull it from -docker pull $CONTAINER_IMAGE +#docker pull $CONTAINER_IMAGE # Run NUM_CONTAINERS -for ((n=0;n<$NUM_CONTAINERS;n++)); do - docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE -done +#for ((n=0;n<$NUM_CONTAINERS;n++)); do +# docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE +#done + +pssh -h big-test-hosts -i -p 256 "docker ps -q | xargs -r -n 128 docker kill && docker ps -aq | xargs -r -P 16 -n 1 docker rm" exit 0 diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh new file mode 100755 index 000000000..79b6f93a3 --- /dev/null +++ b/tests/http/big-test-start.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits +NUM_CONTAINERS=100 +CONTAINER_IMAGE=zerotier/http-test + +# +# This script is designed to be run on Docker hosts to run NUM_CONTAINERS +# +# It can then be run on each Docker host via pssh or similar to run very +# large scale tests. +# + +export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin + +# Kill and clean up old test containers if any -- note that this kills all containers on the system! +#docker ps -q | xargs -n 1 docker kill +#docker ps -aq | xargs -n 1 docker rm + +# Pull latest if needed -- change this to your image name and/or where to pull it from +#docker pull $CONTAINER_IMAGE + +# Run NUM_CONTAINERS +#for ((n=0;n<$NUM_CONTAINERS;n++)); do +# docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE +#done + +pssh -o big-test-out -h big-test-hosts -i -p 256 "docker pull $CONTAINER_IMAGE && for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; done" + +exit 0 From e53ef9642e0201880672699ca12edd50d103be9e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 12:31:34 -0800 Subject: [PATCH 189/195] test stuff. --- tests/http/agent.js | 26 ++++--- tests/http/big-test-hosts | 2 - tests/http/big-test-kill.sh | 14 +--- tests/http/big-test-out/root@104.156.246.48 | 67 +++++++++++++++++ tests/http/big-test-out/root@104.156.252.136 | 75 ++++++++++++++++++++ tests/http/big-test-out/root@188.166.240.16 | 0 tests/http/big-test-out/root@46.101.72.130 | 0 tests/http/big-test-ready.sh | 30 ++++++++ tests/http/big-test-start.sh | 4 +- 9 files changed, 193 insertions(+), 25 deletions(-) create mode 100644 tests/http/big-test-out/root@104.156.246.48 create mode 100644 tests/http/big-test-out/root@104.156.252.136 create mode 100644 tests/http/big-test-out/root@188.166.240.16 create mode 100644 tests/http/big-test-out/root@46.101.72.130 create mode 100755 tests/http/big-test-ready.sh diff --git a/tests/http/agent.js b/tests/http/agent.js index d0c33917b..e11fed60c 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -214,12 +214,22 @@ app.get('/',function(req,res) { }); var expressServer = app.listen(AGENT_PORT,function () { - registerAndGetPeers(function(err,peers) { - if (err) { - console.error('FATAL: unable to contact or query server: '+err.toString()); - process.exit(1); - } - doTestsAndReport(); - setInterval(doTestsAndReport,TEST_INTERVAL); - }); + var serverUp = false; + async.whilst( + function() { return (!serverUp); }, + function(nextTry) { + registerAndGetPeers(function(err,peers) { + if ((err)||(!peers)) { + setTimeout(nextTry,1000); + } else { + serverUp = true; + return nextTry(null); + } + }); + }, + function(err) { + console.log('Server up, starting!'); + doTestsAndReport(); + setInterval(doTestsAndReport,TEST_INTERVAL); + }); }); diff --git a/tests/http/big-test-hosts b/tests/http/big-test-hosts index 27c0c6565..93b6f23f3 100644 --- a/tests/http/big-test-hosts +++ b/tests/http/big-test-hosts @@ -1,4 +1,2 @@ root@104.156.246.48 root@104.156.252.136 -root@46.101.72.130 -root@188.166.240.16 diff --git a/tests/http/big-test-kill.sh b/tests/http/big-test-kill.sh index fbb34c104..917a7791c 100755 --- a/tests/http/big-test-kill.sh +++ b/tests/http/big-test-kill.sh @@ -13,18 +13,6 @@ CONTAINER_IMAGE=zerotier/http-test export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin -# Kill and clean up old test containers if any -- note that this kills all containers on the system! -#docker ps -q | xargs -n 1 docker kill -#docker ps -aq | xargs -n 1 docker rm - -# Pull latest if needed -- change this to your image name and/or where to pull it from -#docker pull $CONTAINER_IMAGE - -# Run NUM_CONTAINERS -#for ((n=0;n<$NUM_CONTAINERS;n++)); do -# docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE -#done - -pssh -h big-test-hosts -i -p 256 "docker ps -q | xargs -r -n 128 docker kill && docker ps -aq | xargs -r -P 16 -n 1 docker rm" +pssh -h big-test-hosts -i -t 128 -p 256 "docker ps -q | xargs -r docker kill && docker ps -aq | xargs -r docker rm" exit 0 diff --git a/tests/http/big-test-out/root@104.156.246.48 b/tests/http/big-test-out/root@104.156.246.48 new file mode 100644 index 000000000..afcda19f2 --- /dev/null +++ b/tests/http/big-test-out/root@104.156.246.48 @@ -0,0 +1,67 @@ +5fc1676570b10196c5e261dac6ff28998c4f66c706b131d6423532a38b8b7a15 +894bbfa612f772aa0170d5c5ddb9362a9edb0f5fbe22c81591b50dfa0a0eadeb +bae9bf7c37806adbbdfe9e01b4e62034953d01ecae55e0cd450a21b1415f5c00 +26e83eccbd6083f2c7a8532786fd1ec5c96d6a51bb3508ec4fd1919a1883630a +90e95ac0cd3b4ba6ffa33fe946a1f12a2cdba61168770af9fca7b42df45c9530 +88a11bbc89c0b3579de7faf4db4dbe3f0a5a073aa49fd7a5482eff35c93fab4e +50be80765f09000085d6c0895958e6d3794f672f562ff5bc27634b15c77b7583 +99630786ef067f4c053f7bca96e2869f14c9ceb9f1d07b7635945c143700b950 +8422d4cb09ed44e34488163a96910ece8f1dd39c280daf3d369d17ce2ac70766 +e0a0af014418076adc351827fabbd9ef6da7404f2d6184f20d601736c4685154 +387cd6dbbd8e8b17d9ccf943fd13dcaddf27c178834a9630e357407a42733fb8 +b06b75cb0c5748321ab4501e1055dd5ff8457e81502548954702ead843011388 +59d874f5e36f15b62e8540b86d552950c72f086d90667780b6d82b5595326fce +69dc1347f8671ba4bbe1da12f26e8f67b0980f1e2ad73bc0b77cc06e5cbf1b06 +68ef4b43d3f5559b5b0c82ba2f396a5b6dcb6001a67efd3a3b3b2a415c2b7b61 +3ecc9f45151f95a194d8274a88f433f83540f5397523de7a86db714cd9155bb2 +3b24b66b1dcd5e8ce1fa24d33cb2eb3cc55f3a157602a09bca4942089e25790f +ba34465f6cedd2f306022cc9259ef4e43819959f51f980a8cb94ea33a29c7e34 +4e9a701f18a0ac42ee24f3bf2dd6dc442706a0bf4b6288336447b03752640852 +5865df2182165576a0825644d9b7537314c9fd4323cb2023260382acc2f9d7fd +f1498fda2dd6d1f1fdfc95d539d6ee511ca8baad65b1a1b44d76309e84015550 +7d9232ecda50856096523dc2d0c1fae46481053067d8bfe024e1dfccf8f9c0fc +173b47f327a3c21187e25d2d02dbde49760182a40e00cf4a64746c00537a3ff6 +9221fdb92693a29f720a5c41f09791c35f2b220459a2e4bdcc24fd5e3af4ad85 +ef8def0260ba3ff21aa292bb482bedf94b17fd9306e529f6feae5cab04e1bdc2 +7392179b9d51cad626b6cf78b00c355b280ddccccd7013e11a8cecbdd2db1c15 +fc31766af9265f2c496ee3847971ee0f5249aca8ad0cd214620643c7347436b2 +7f8bd6d1fde6948a09b132b4b2c3919ca11c932bfdf8a279c4cbe140daf24287 +ce9596c1d8dd42929820e4d3a56e8b1f8eb2d8b67474c525bbed1b888143cc14 +67c9de4f3d88daa89796e8a03306d2c5f2bcc4c409eef89e67c6ffc6a6282060 +705c1973e4a9ef77ffe6319375e585aec1d762a031c8b93ba9883af98e377590 +c7fdcc10eed007a30da7c7bbb8767d0b6a7287fadd67d935651f0bb265a71e1b +5102e854fe72cdcd91b228d520a9380a36a273df7949de054355e8f99bd14e95 +77c37e503ccaa1350bf2fe16daaef486ec223348bc8f678b6f6d6bb477ae10c1 +fd97792d7bd61449163fa4e953212446e11cf02b27a45a2073a1de5148da63e8 +7d5b84b290f727713ad02f0817856c9b891996bdcc6ad4d0994005608cf9bbb7 +104c41c9cce7934f0e205cfc90b65f5ba89ca696cab41771a12b4384af7f6805 +75e1751a7e290a34faddea54c98e870b4a1cd5bb37c810cafabda8d3ba1faa5f +2293a17e82ff54e04218c5aafd904079d15d71e47496249dc125f90f0960039f +3f3563782349fddb61cdc638cf5f54030f726d9759cb104253f5b8b04ae8e2b0 +2328da71617e1abc0e2335e5974a70f45f8abaedba641292ad4d87e2f27a6b83 +00e79b478925c7b866f7d669ee73af1a7b377fb8bf22a04f2bd5356f256f59ba +2960f757cca294c32abd51f072798f5457a1552de52bd42ca9233cd075da086f +efbc248c9a3ce7b7a52724b67f14e27b02f5f37da295bc905d4f8fabe847cd00 +a068d735478e065236e840e50697a078d77aad9b82f906555279e5dee074db5d +c4a366870dd1f3d1fb776b25f009d9079a6f7d0d83b03cd178b237b412d8dce6 +20340c7bd3bd9d32d6ecb7a451c377fb239bfe2d2c976886e2bd59746ed180ee +8c522ecbabb9580528794d94f82bdef2982f0458b677598f236acc14e819e480 +8dfcc3407b050a39d82af263eec6e332bd69cb848ffa660344644ea10e3c2221 +74e06d9deba29982f9dccdb841163715dd419dee6a54eaf3ac987cd3458a1a2a +725d990ca2ea34dc3e9acc02480cc8009a9c2016414c9c9cca3c7b135bd384f4 +120d7eac6a5bd761ee6acfb751f48bf7075c0316123326ce6bbb6ce3fe05c3a7 +91aa44d2650ff308877d9c81357619c51c0c0d05dc9f4c899df9ce460673b2aa +3c203cd73c6be2606397357154789a94fbfe8670271825f75bbfc6c97fa0e048 +f9018c8390a472798c7a39bfe834cd01bb62bb4b0882dfe1108bf43334a3bd0e +cdf8afae641e0423b2a7a1ff92cca80c7db478ff8deb1d81032808ac84415921 +9a5364df1df5460f6a9ed15e020b8bd283c47464556a7c5896707cee00c01a14 +df9365b8816e9d67f484adf94eef53aab236a92a588a1f3f650fd36b8073f7bb +bd0172f67fa20716502e2bee82387de7f426e3cdb19d5ac6ce9dcc177f919cde +90dd259c03b11625c09b8db614f45759e67edf07fe350681d273bfd988b45443 +9f69d376248b6851aa7837a7d09a1b9eda917601049e5942796815069d09a80f +1a065bee20e8f4c6a91bb92ec9cca6ca16e8eb434798ed433a5248c48d91f596 +fb5a6a9397546a97ccdc4252603d5e774d8430195b06ec74926c48cf372b9906 +51cf0867773bb298140eb09c88d69587aae3a6da3e63275d32f3d32d98a737d7 +70574f3e616413ce90b045e0e9fd92353766e216eabf8139556fa61efea9c3b4 +ccd21257994f9eaf309d06b2fc5652b14ea80796a79face304fcc8fd1da53423 +7d8417be656f17fbfb779a9803b2de045e2f496f75ea5f1ea69e223572bde2a5 diff --git a/tests/http/big-test-out/root@104.156.252.136 b/tests/http/big-test-out/root@104.156.252.136 new file mode 100644 index 000000000..c11311b49 --- /dev/null +++ b/tests/http/big-test-out/root@104.156.252.136 @@ -0,0 +1,75 @@ +5ea6049a0b92070494f40a5ccecccaf788a5aafccee7c2eada9b9eb8731bc002 +798f8beecd2e3fbb50df49b7ef57cdd1e8e00c0680046b3c2d53a3554f956fda +dd20da6340210e1b7612d8922aaa4b045e84da32f264add073a65a15f676a9e4 +0479f14d0aa68e835c07dc5ca413febd9da19b6554fd8bdba7e319e5f4661f80 +df6747868f90cfa069f5f9f954626b7392cd99026e43e6d6c83ab7c16d5cbdc6 +c24799f74233b1bc7d7d936d57699b955000c640531f3db38be8196a87eb262c +46b00a65d527738c0bfad924051bd2117563e0c6ad74b803b662e74720d8d085 +dc14b9428032771388d30c6002bb5cba05131972cab53360f088c51769786c47 +e7dc364aeccf60bafa5a42787cc6de231612782252f57b9f03ebae3a309b2352 +4e8e578a8948ab384525646a17c2e0cb9f2b9ef67fd0c489ad6aa2bffbfabddc +cc626d978e32dfe14782011218ba265ae4e69886f44335a2c402001dc0c4c3c7 +36a148254d34f954906a810ba4a8644a4433e8847d3cc30e091b1f63723f0590 +1bdd06b691fa06b3af77d2868b78f2a01b91026b4f483fb278cb8872a9150987 +d6d5709039708a515b295519a4007c3b49361ef67465ddca2dfba9a473b9c37c +f8a3a0b3dd5ce42bd87cdba28df7469071f948fb28151955cfa75abe0455a000 +f7eb37fecb17571091f05f1bfc66f6fba731ff934988a529813f4751451401af +1c2f2c77c429095b1dad53032684df672f351489ed6b7e00e1097f7dc1c0ba97 +361dd9cc6764facd2aca0b462e64a2400f6b41e124c4d9be71466d801763270a +3f5f49ce854439bc672c4b0cf4c1e1b9a978e8f2db14709977ddcfd39b7d6bb3 +5221bec1aec2c9f08ddec548a24b0700e3d0c0568d10caa753564c914b35c95f +ebdcc27c264d326619262f82b5d7dfcbf102d720ad9bab4428b9725118bf627a +bc01c863316a29c8f119da7cce1c891185c43d385521d46b06186a89cb6cff9d +47fb8b119e3d098f22cc6f4a7867f2016f244cc8b114aa66630dadbd4bfb2a0c +4ca2402d762adbd7ce860c3f3d072e948bb33afb7c2830ad51ae9d3fb2c714df +328e4fda6dd1befcfda64d5c89c458fcc9386d88375218cb5197d479d3d292d0 +099636c67de66af06ee8493ccd55b588cf8bbacf67352877f37a077a2166117a +624e0c032e4b8a78af1387c0199c5b02da68e0795e9b6397ba8bdd5ebcf7daa4 +265e038f17e4bc3c99737bf4ac98364c98a12d9d22a28b6a9302ce5a83c7d4f8 +de198984870126801aa20f25c459ea8f89bb7a4782614e91659b820c42a33c93 +ad48788c3f91a1292fb28dde84750f94e27ef150b2ebb52b7807f3d0c7986c4b +34ab2f912eae5e75bbde977b8bb2952e552e36c83f29c2cd64d7ac3165aa9726 +951efa9c57da7eb2a1f6578927f809ab0c9feac2aa4326ea42f182e3ad74e600 +8b8193d9ebd89c36a728a3fb89282854bfb89b27172d93952747126c22ea6a97 +79a78a9f96f2c961b2d06dfd484a495ddc3809228243c608ecd51715a228e528 +4301dfc330137391585d36ddc4b54971999a1da96b1c12aa43221dd92cc32e79 +f956660447a893adbc0fc4382fe67ad2a7bb8591a647a130faa9e17aa380328a +0cee22f18107559eff4f4eba20320c349d70d82b72197ae380fc514e6241730c +d74601767fec3cff13062ad1393fad9a88277bbc6710c4fce6d78c21d001bfa4 +c1c46fd958b7806bf0c22c77c997317b7e4dffd7243d0a5919d4922fafd841a2 +a07e0f4e1cac4a84e3ca922b8c59a37ed8049096d9154be76e6d2d9094ba3714 +6861c27e7584e83a792ce710d004f7dc22213cc732b23f0a2025606cc5e9e325 +ee334626da5143ddf49561522483eb689663d031ba6cf9891204b709b279b28b +3be351d25381f07b85c7f5c2bb08b80b5bbed80c348515187d727ccbf293b13b +268da41127aa3b7768bbf6075baad5397f0af4f0ff16f5b8dff2cad9c3019750 +74cc47af92d6cf6b2315f62e0261d4461c6297c5ffc19b50a97784cd6271acfc +8e52beef1bd61be4d223c460b589d32d1a64d5525406ab179d1962fccd734309 +9471871b2f1ec2331c1f9855b408d212ba868965f99aeee91ddd0b7dd76b2985 +16eb9e7e24b61ade0939345118d5d14032ae496de3b5fc702c0c1356662b8a80 +f503549eb9c03c8dc7e54559bf076e1f7eaf7c8599c84722062851597a4c91c1 +d9dce304b504003c97ffdf9d076ff348343d7d0ce50070038d049b71239bc70e +2ed388e1730860b7605527213d7d61bbc8f29703a2f586d127b37e7ed8eca708 +d20988195a901d19597bf4ed2b136c3d2aed2d169093409a5d3ea8daf1f983af +a9be22365634b68f0ff5ba9228550eba9b3923319eff07f5cf5785eec85fa11e +99b512fe14b21569c81d358b161976c9dce45c608cd1a03da6f6063dbabb6c41 +88f76143cb289e8daf8edbc183a4b760c0d86efd1cc9da3933e59603bf92c539 +62054c1a23221461f1c06b94148614754f7da0cd7145eb85290bb11bdf7b9af8 +8dffffd5b63672f8749160dbff55c489f1f1f6c41ce01e086f2d9aa8bc6150ad +f86a0512be3ec599065dec982545ecb96bdb0d82dda7285c58b3c3b7666988c9 +773786ea8094e7f013702ef721a2dde7657dcb6c44927f41f5acb32161957d83 +ef981b569d0c98e79bfc5bb387cf00457c4254e13f1ec625d0149b06ef92d5e1 +77841624d9fbba73cf722327f531ea36cfa42be59b4709c3b4ca4e7e453ac7fa +f90ada865c766ef7e0e47d0a677b64183aa1e612f14f3f1f996d411b7197ea3e +11c278763c1f2ff8d9b2ee5e3100a40538adcc1b74f6ca7d9668fcc8c1ab2f9a +4a72c59eda21b43136cb5f308298aba39235bbd227668c2c9f3830fbab7b4a34 +f12a3d9308eb7800a1436b3258d899aae3b643f2c78648258f126266f9707032 +ce651585240975fc5c954c526203a048c6e4326e2d16feb083ca3a3ff4ad682a +43aa6c22cb636983ff1b6e169057b0d7a70ad75754ef1b3a3bc7f49461c84cdb +7e71c166a6f285c4c501b2125713e698575d1987f1819b76d4fabbbf246eae6c +d38478eec0109a5b76da1a6e3de982382cf01bbc871b651d3e258330642cbe27 +c4c6ce60a0b1c4f2553ecb0f551de2c7df2e2d2cf2101e80af48b29e03cfefd5 +38905fb0f59281f1d4aa80894654e56df76653f3d3545a883e37c80053e72977 +1a6ee044aa753748035975b12885e73504ffde1bf129a7ff992f012d9cff111b +f0d37e0a5ccb7871a30b143fad68a4e07624aa2a153e295022868e68e34ff770 +ee9d0ef6b557bd5e7c8fba9e087a428a98ee5350ec86785205db6ea10493b21c +53ca280c12ba4d5be4ea78144fb2d411ecd9910f5105d04537d4bec362865c40 diff --git a/tests/http/big-test-out/root@188.166.240.16 b/tests/http/big-test-out/root@188.166.240.16 new file mode 100644 index 000000000..e69de29bb diff --git a/tests/http/big-test-out/root@46.101.72.130 b/tests/http/big-test-out/root@46.101.72.130 new file mode 100644 index 000000000..e69de29bb diff --git a/tests/http/big-test-ready.sh b/tests/http/big-test-ready.sh new file mode 100755 index 000000000..391ca2a1b --- /dev/null +++ b/tests/http/big-test-ready.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits +NUM_CONTAINERS=100 +CONTAINER_IMAGE=zerotier/http-test + +# +# This script is designed to be run on Docker hosts to run NUM_CONTAINERS +# +# It can then be run on each Docker host via pssh or similar to run very +# large scale tests. +# + +export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin + +# Kill and clean up old test containers if any -- note that this kills all containers on the system! +#docker ps -q | xargs -n 1 docker kill +#docker ps -aq | xargs -n 1 docker rm + +# Pull latest if needed -- change this to your image name and/or where to pull it from +#docker pull $CONTAINER_IMAGE + +# Run NUM_CONTAINERS +#for ((n=0;n<$NUM_CONTAINERS;n++)); do +# 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" + +exit 0 diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh index 79b6f93a3..a4c7e6c12 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=64 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 -o big-test-out -h big-test-hosts -i -p 256 "docker pull $CONTAINER_IMAGE && for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; done" +pssh -o big-test-out -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; done" exit 0 From fd3916a49e7923e95c47c70afb8696f110b79951 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 13:17:11 -0800 Subject: [PATCH 190/195] More test stuff... make it more granular and less batch based. --- tests/http/agent.js | 132 ++++++++++++------- tests/http/big-test-kill.sh | 2 +- tests/http/big-test-out/root@104.156.246.48 | 67 ---------- tests/http/big-test-out/root@104.156.252.136 | 75 ----------- tests/http/big-test-out/root@188.166.240.16 | 0 tests/http/big-test-out/root@46.101.72.130 | 0 tests/http/big-test-start.sh | 4 +- tests/http/server.js | 42 ++---- 8 files changed, 101 insertions(+), 221 deletions(-) delete mode 100644 tests/http/big-test-out/root@104.156.246.48 delete mode 100644 tests/http/big-test-out/root@104.156.252.136 delete mode 100644 tests/http/big-test-out/root@188.166.240.16 delete mode 100644 tests/http/big-test-out/root@46.101.72.130 diff --git a/tests/http/agent.js b/tests/http/agent.js index e11fed60c..1d4a43200 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -3,21 +3,23 @@ // --------------------------------------------------------------------------- // Customizable parameters: -// Maximum test duration in milliseconds -var TEST_DURATION = (30 * 1000); +// Maximum interval between test attempts +//var TEST_INTERVAL_MAX = (60 * 1 * 1000); +var TEST_INTERVAL_MAX = 1000; -// Interval between tests (should be several times longer than TEST_DURATION) -var TEST_INTERVAL = (60 * 2 * 1000); +// Test timeout in ms +var TEST_TIMEOUT = 30000; // Where should I contact to register and query a list of other test agents? -var SERVER_HOST = '104.238.141.145'; +var SERVER_HOST = '127.0.0.1'; +//var SERVER_HOST = '104.238.141.145'; var SERVER_PORT = 18080; // Which port should agents use for their HTTP? var AGENT_PORT = 18888; // Payload size in bytes -var PAYLOAD_SIZE = 100000; +var PAYLOAD_SIZE = 10000; // --------------------------------------------------------------------------- @@ -72,9 +74,6 @@ for(var xx=0;xx 0) { + + var target = allOtherAgents[Math.floor(Math.random() * allOtherAgents.length)]; + + var testRequest = null; + var timeoutId = null; + timeoutId = setTimeout(function() { + if (testRequest !== null) + testRequest.abort(); + timeoutId = null; + }); + var startTime = Date.now(); + + testRequest = http.get({ + host: agentIdToIp(target), + port: AGENT_PORT, + path: '/' + },function(res) { + var bytes = 0; + res.on('data',function(chunk) { bytes += chunk.length; }); + res.on('end',function() { + lastTestResult = { + source: thisAgentId, + target: target, + time: (Date.now() - startTime), + bytes: bytes, + timedOut: (timeoutId === null), + error: null + }; + if (timeoutId !== null) + clearTimeout(timeoutId); + return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1); + }); + }).on('error',function(e) { + lastTestResult = { + source: thisAgentId, + target: target, + time: (Date.now() - startTime), + bytes: 0, + timedOut: (timeoutId === null), + error: e.toString() + }; + if (timeoutId !== null) + clearTimeout(timeoutId); + return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1); + }); + + } else { + return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1); + } + }); }).on('error',function(e) { - return callback(e,null); + console.log('POST failed: '+e.toString()); + return setTimeout(doTest,1000); }); + if (lastTestResult !== null) { + submit.write(JSON.stringify(lastTestResult)); + lastTestResult = null; + } + submit.end(); }; +/* function performTestOnAllPeers(peers,callback) { var allResults = {}; @@ -191,11 +251,10 @@ function doTestsAndReport() console.error('WARNING: skipping test: unable to contact or query server: '+err.toString()); } else { performTestOnAllPeers(peers,function(results) { - ++testNumber; var submit = http.request({ host: SERVER_HOST, port: SERVER_PORT, - path: '/'+testNumber+'/'+thisAgentId, + path: '/'+thisAgentId, method: 'POST' },function(res) { }).on('error',function(e) { @@ -207,29 +266,12 @@ function doTestsAndReport() } }); }; +*/ // Agents just serve up a test payload -app.get('/',function(req,res) { - return res.status(200).send(payload); -}); +app.get('/',function(req,res) { return res.status(200).send(payload); }); var expressServer = app.listen(AGENT_PORT,function () { - var serverUp = false; - async.whilst( - function() { return (!serverUp); }, - function(nextTry) { - registerAndGetPeers(function(err,peers) { - if ((err)||(!peers)) { - setTimeout(nextTry,1000); - } else { - serverUp = true; - return nextTry(null); - } - }); - }, - function(err) { - console.log('Server up, starting!'); - doTestsAndReport(); - setInterval(doTestsAndReport,TEST_INTERVAL); - }); + // Start timeout-based loop + doTest(); }); diff --git a/tests/http/big-test-kill.sh b/tests/http/big-test-kill.sh index 917a7791c..4a764d1fb 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 -q | xargs -r docker kill && docker ps -aq | xargs -r docker rm" +pssh -h big-test-hosts -i -t 128 -p 256 "docker ps -aq | xargs -r docker rm -f" exit 0 diff --git a/tests/http/big-test-out/root@104.156.246.48 b/tests/http/big-test-out/root@104.156.246.48 deleted file mode 100644 index afcda19f2..000000000 --- a/tests/http/big-test-out/root@104.156.246.48 +++ /dev/null @@ -1,67 +0,0 @@ -5fc1676570b10196c5e261dac6ff28998c4f66c706b131d6423532a38b8b7a15 -894bbfa612f772aa0170d5c5ddb9362a9edb0f5fbe22c81591b50dfa0a0eadeb -bae9bf7c37806adbbdfe9e01b4e62034953d01ecae55e0cd450a21b1415f5c00 -26e83eccbd6083f2c7a8532786fd1ec5c96d6a51bb3508ec4fd1919a1883630a -90e95ac0cd3b4ba6ffa33fe946a1f12a2cdba61168770af9fca7b42df45c9530 -88a11bbc89c0b3579de7faf4db4dbe3f0a5a073aa49fd7a5482eff35c93fab4e -50be80765f09000085d6c0895958e6d3794f672f562ff5bc27634b15c77b7583 -99630786ef067f4c053f7bca96e2869f14c9ceb9f1d07b7635945c143700b950 -8422d4cb09ed44e34488163a96910ece8f1dd39c280daf3d369d17ce2ac70766 -e0a0af014418076adc351827fabbd9ef6da7404f2d6184f20d601736c4685154 -387cd6dbbd8e8b17d9ccf943fd13dcaddf27c178834a9630e357407a42733fb8 -b06b75cb0c5748321ab4501e1055dd5ff8457e81502548954702ead843011388 -59d874f5e36f15b62e8540b86d552950c72f086d90667780b6d82b5595326fce -69dc1347f8671ba4bbe1da12f26e8f67b0980f1e2ad73bc0b77cc06e5cbf1b06 -68ef4b43d3f5559b5b0c82ba2f396a5b6dcb6001a67efd3a3b3b2a415c2b7b61 -3ecc9f45151f95a194d8274a88f433f83540f5397523de7a86db714cd9155bb2 -3b24b66b1dcd5e8ce1fa24d33cb2eb3cc55f3a157602a09bca4942089e25790f -ba34465f6cedd2f306022cc9259ef4e43819959f51f980a8cb94ea33a29c7e34 -4e9a701f18a0ac42ee24f3bf2dd6dc442706a0bf4b6288336447b03752640852 -5865df2182165576a0825644d9b7537314c9fd4323cb2023260382acc2f9d7fd -f1498fda2dd6d1f1fdfc95d539d6ee511ca8baad65b1a1b44d76309e84015550 -7d9232ecda50856096523dc2d0c1fae46481053067d8bfe024e1dfccf8f9c0fc -173b47f327a3c21187e25d2d02dbde49760182a40e00cf4a64746c00537a3ff6 -9221fdb92693a29f720a5c41f09791c35f2b220459a2e4bdcc24fd5e3af4ad85 -ef8def0260ba3ff21aa292bb482bedf94b17fd9306e529f6feae5cab04e1bdc2 -7392179b9d51cad626b6cf78b00c355b280ddccccd7013e11a8cecbdd2db1c15 -fc31766af9265f2c496ee3847971ee0f5249aca8ad0cd214620643c7347436b2 -7f8bd6d1fde6948a09b132b4b2c3919ca11c932bfdf8a279c4cbe140daf24287 -ce9596c1d8dd42929820e4d3a56e8b1f8eb2d8b67474c525bbed1b888143cc14 -67c9de4f3d88daa89796e8a03306d2c5f2bcc4c409eef89e67c6ffc6a6282060 -705c1973e4a9ef77ffe6319375e585aec1d762a031c8b93ba9883af98e377590 -c7fdcc10eed007a30da7c7bbb8767d0b6a7287fadd67d935651f0bb265a71e1b -5102e854fe72cdcd91b228d520a9380a36a273df7949de054355e8f99bd14e95 -77c37e503ccaa1350bf2fe16daaef486ec223348bc8f678b6f6d6bb477ae10c1 -fd97792d7bd61449163fa4e953212446e11cf02b27a45a2073a1de5148da63e8 -7d5b84b290f727713ad02f0817856c9b891996bdcc6ad4d0994005608cf9bbb7 -104c41c9cce7934f0e205cfc90b65f5ba89ca696cab41771a12b4384af7f6805 -75e1751a7e290a34faddea54c98e870b4a1cd5bb37c810cafabda8d3ba1faa5f -2293a17e82ff54e04218c5aafd904079d15d71e47496249dc125f90f0960039f -3f3563782349fddb61cdc638cf5f54030f726d9759cb104253f5b8b04ae8e2b0 -2328da71617e1abc0e2335e5974a70f45f8abaedba641292ad4d87e2f27a6b83 -00e79b478925c7b866f7d669ee73af1a7b377fb8bf22a04f2bd5356f256f59ba -2960f757cca294c32abd51f072798f5457a1552de52bd42ca9233cd075da086f -efbc248c9a3ce7b7a52724b67f14e27b02f5f37da295bc905d4f8fabe847cd00 -a068d735478e065236e840e50697a078d77aad9b82f906555279e5dee074db5d -c4a366870dd1f3d1fb776b25f009d9079a6f7d0d83b03cd178b237b412d8dce6 -20340c7bd3bd9d32d6ecb7a451c377fb239bfe2d2c976886e2bd59746ed180ee -8c522ecbabb9580528794d94f82bdef2982f0458b677598f236acc14e819e480 -8dfcc3407b050a39d82af263eec6e332bd69cb848ffa660344644ea10e3c2221 -74e06d9deba29982f9dccdb841163715dd419dee6a54eaf3ac987cd3458a1a2a -725d990ca2ea34dc3e9acc02480cc8009a9c2016414c9c9cca3c7b135bd384f4 -120d7eac6a5bd761ee6acfb751f48bf7075c0316123326ce6bbb6ce3fe05c3a7 -91aa44d2650ff308877d9c81357619c51c0c0d05dc9f4c899df9ce460673b2aa -3c203cd73c6be2606397357154789a94fbfe8670271825f75bbfc6c97fa0e048 -f9018c8390a472798c7a39bfe834cd01bb62bb4b0882dfe1108bf43334a3bd0e -cdf8afae641e0423b2a7a1ff92cca80c7db478ff8deb1d81032808ac84415921 -9a5364df1df5460f6a9ed15e020b8bd283c47464556a7c5896707cee00c01a14 -df9365b8816e9d67f484adf94eef53aab236a92a588a1f3f650fd36b8073f7bb -bd0172f67fa20716502e2bee82387de7f426e3cdb19d5ac6ce9dcc177f919cde -90dd259c03b11625c09b8db614f45759e67edf07fe350681d273bfd988b45443 -9f69d376248b6851aa7837a7d09a1b9eda917601049e5942796815069d09a80f -1a065bee20e8f4c6a91bb92ec9cca6ca16e8eb434798ed433a5248c48d91f596 -fb5a6a9397546a97ccdc4252603d5e774d8430195b06ec74926c48cf372b9906 -51cf0867773bb298140eb09c88d69587aae3a6da3e63275d32f3d32d98a737d7 -70574f3e616413ce90b045e0e9fd92353766e216eabf8139556fa61efea9c3b4 -ccd21257994f9eaf309d06b2fc5652b14ea80796a79face304fcc8fd1da53423 -7d8417be656f17fbfb779a9803b2de045e2f496f75ea5f1ea69e223572bde2a5 diff --git a/tests/http/big-test-out/root@104.156.252.136 b/tests/http/big-test-out/root@104.156.252.136 deleted file mode 100644 index c11311b49..000000000 --- a/tests/http/big-test-out/root@104.156.252.136 +++ /dev/null @@ -1,75 +0,0 @@ -5ea6049a0b92070494f40a5ccecccaf788a5aafccee7c2eada9b9eb8731bc002 -798f8beecd2e3fbb50df49b7ef57cdd1e8e00c0680046b3c2d53a3554f956fda -dd20da6340210e1b7612d8922aaa4b045e84da32f264add073a65a15f676a9e4 -0479f14d0aa68e835c07dc5ca413febd9da19b6554fd8bdba7e319e5f4661f80 -df6747868f90cfa069f5f9f954626b7392cd99026e43e6d6c83ab7c16d5cbdc6 -c24799f74233b1bc7d7d936d57699b955000c640531f3db38be8196a87eb262c -46b00a65d527738c0bfad924051bd2117563e0c6ad74b803b662e74720d8d085 -dc14b9428032771388d30c6002bb5cba05131972cab53360f088c51769786c47 -e7dc364aeccf60bafa5a42787cc6de231612782252f57b9f03ebae3a309b2352 -4e8e578a8948ab384525646a17c2e0cb9f2b9ef67fd0c489ad6aa2bffbfabddc -cc626d978e32dfe14782011218ba265ae4e69886f44335a2c402001dc0c4c3c7 -36a148254d34f954906a810ba4a8644a4433e8847d3cc30e091b1f63723f0590 -1bdd06b691fa06b3af77d2868b78f2a01b91026b4f483fb278cb8872a9150987 -d6d5709039708a515b295519a4007c3b49361ef67465ddca2dfba9a473b9c37c -f8a3a0b3dd5ce42bd87cdba28df7469071f948fb28151955cfa75abe0455a000 -f7eb37fecb17571091f05f1bfc66f6fba731ff934988a529813f4751451401af -1c2f2c77c429095b1dad53032684df672f351489ed6b7e00e1097f7dc1c0ba97 -361dd9cc6764facd2aca0b462e64a2400f6b41e124c4d9be71466d801763270a -3f5f49ce854439bc672c4b0cf4c1e1b9a978e8f2db14709977ddcfd39b7d6bb3 -5221bec1aec2c9f08ddec548a24b0700e3d0c0568d10caa753564c914b35c95f -ebdcc27c264d326619262f82b5d7dfcbf102d720ad9bab4428b9725118bf627a -bc01c863316a29c8f119da7cce1c891185c43d385521d46b06186a89cb6cff9d -47fb8b119e3d098f22cc6f4a7867f2016f244cc8b114aa66630dadbd4bfb2a0c -4ca2402d762adbd7ce860c3f3d072e948bb33afb7c2830ad51ae9d3fb2c714df -328e4fda6dd1befcfda64d5c89c458fcc9386d88375218cb5197d479d3d292d0 -099636c67de66af06ee8493ccd55b588cf8bbacf67352877f37a077a2166117a -624e0c032e4b8a78af1387c0199c5b02da68e0795e9b6397ba8bdd5ebcf7daa4 -265e038f17e4bc3c99737bf4ac98364c98a12d9d22a28b6a9302ce5a83c7d4f8 -de198984870126801aa20f25c459ea8f89bb7a4782614e91659b820c42a33c93 -ad48788c3f91a1292fb28dde84750f94e27ef150b2ebb52b7807f3d0c7986c4b -34ab2f912eae5e75bbde977b8bb2952e552e36c83f29c2cd64d7ac3165aa9726 -951efa9c57da7eb2a1f6578927f809ab0c9feac2aa4326ea42f182e3ad74e600 -8b8193d9ebd89c36a728a3fb89282854bfb89b27172d93952747126c22ea6a97 -79a78a9f96f2c961b2d06dfd484a495ddc3809228243c608ecd51715a228e528 -4301dfc330137391585d36ddc4b54971999a1da96b1c12aa43221dd92cc32e79 -f956660447a893adbc0fc4382fe67ad2a7bb8591a647a130faa9e17aa380328a -0cee22f18107559eff4f4eba20320c349d70d82b72197ae380fc514e6241730c -d74601767fec3cff13062ad1393fad9a88277bbc6710c4fce6d78c21d001bfa4 -c1c46fd958b7806bf0c22c77c997317b7e4dffd7243d0a5919d4922fafd841a2 -a07e0f4e1cac4a84e3ca922b8c59a37ed8049096d9154be76e6d2d9094ba3714 -6861c27e7584e83a792ce710d004f7dc22213cc732b23f0a2025606cc5e9e325 -ee334626da5143ddf49561522483eb689663d031ba6cf9891204b709b279b28b -3be351d25381f07b85c7f5c2bb08b80b5bbed80c348515187d727ccbf293b13b -268da41127aa3b7768bbf6075baad5397f0af4f0ff16f5b8dff2cad9c3019750 -74cc47af92d6cf6b2315f62e0261d4461c6297c5ffc19b50a97784cd6271acfc -8e52beef1bd61be4d223c460b589d32d1a64d5525406ab179d1962fccd734309 -9471871b2f1ec2331c1f9855b408d212ba868965f99aeee91ddd0b7dd76b2985 -16eb9e7e24b61ade0939345118d5d14032ae496de3b5fc702c0c1356662b8a80 -f503549eb9c03c8dc7e54559bf076e1f7eaf7c8599c84722062851597a4c91c1 -d9dce304b504003c97ffdf9d076ff348343d7d0ce50070038d049b71239bc70e -2ed388e1730860b7605527213d7d61bbc8f29703a2f586d127b37e7ed8eca708 -d20988195a901d19597bf4ed2b136c3d2aed2d169093409a5d3ea8daf1f983af -a9be22365634b68f0ff5ba9228550eba9b3923319eff07f5cf5785eec85fa11e -99b512fe14b21569c81d358b161976c9dce45c608cd1a03da6f6063dbabb6c41 -88f76143cb289e8daf8edbc183a4b760c0d86efd1cc9da3933e59603bf92c539 -62054c1a23221461f1c06b94148614754f7da0cd7145eb85290bb11bdf7b9af8 -8dffffd5b63672f8749160dbff55c489f1f1f6c41ce01e086f2d9aa8bc6150ad -f86a0512be3ec599065dec982545ecb96bdb0d82dda7285c58b3c3b7666988c9 -773786ea8094e7f013702ef721a2dde7657dcb6c44927f41f5acb32161957d83 -ef981b569d0c98e79bfc5bb387cf00457c4254e13f1ec625d0149b06ef92d5e1 -77841624d9fbba73cf722327f531ea36cfa42be59b4709c3b4ca4e7e453ac7fa -f90ada865c766ef7e0e47d0a677b64183aa1e612f14f3f1f996d411b7197ea3e -11c278763c1f2ff8d9b2ee5e3100a40538adcc1b74f6ca7d9668fcc8c1ab2f9a -4a72c59eda21b43136cb5f308298aba39235bbd227668c2c9f3830fbab7b4a34 -f12a3d9308eb7800a1436b3258d899aae3b643f2c78648258f126266f9707032 -ce651585240975fc5c954c526203a048c6e4326e2d16feb083ca3a3ff4ad682a -43aa6c22cb636983ff1b6e169057b0d7a70ad75754ef1b3a3bc7f49461c84cdb -7e71c166a6f285c4c501b2125713e698575d1987f1819b76d4fabbbf246eae6c -d38478eec0109a5b76da1a6e3de982382cf01bbc871b651d3e258330642cbe27 -c4c6ce60a0b1c4f2553ecb0f551de2c7df2e2d2cf2101e80af48b29e03cfefd5 -38905fb0f59281f1d4aa80894654e56df76653f3d3545a883e37c80053e72977 -1a6ee044aa753748035975b12885e73504ffde1bf129a7ff992f012d9cff111b -f0d37e0a5ccb7871a30b143fad68a4e07624aa2a153e295022868e68e34ff770 -ee9d0ef6b557bd5e7c8fba9e087a428a98ee5350ec86785205db6ea10493b21c -53ca280c12ba4d5be4ea78144fb2d411ecd9910f5105d04537d4bec362865c40 diff --git a/tests/http/big-test-out/root@188.166.240.16 b/tests/http/big-test-out/root@188.166.240.16 deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/http/big-test-out/root@46.101.72.130 b/tests/http/big-test-out/root@46.101.72.130 deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh index a4c7e6c12..a5e71ef14 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=64 +NUM_CONTAINERS=100 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 -o big-test-out -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; 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" exit 0 diff --git a/tests/http/server.js b/tests/http/server.js index 30d8339a3..57109392f 100644 --- a/tests/http/server.js +++ b/tests/http/server.js @@ -20,44 +20,24 @@ app.use(function(req,res,next) { var knownAgents = {}; -app.get('/:agentId',function(req,res) { +app.post('/:agentId',function(req,res) { var agentId = req.params.agentId; if ((!agentId)||(agentId.length !== 32)) return res.status(404).send(''); + + if (req.rawBody) { + var receiveTime = Date.now(); + var resultData = null; + try { + resultData = JSON.parse(req.rawBody); + console.log(resultData.source+','+resultData.target+','+resultData.time+','+resultData.bytes+','+resultData.timedOut+',"'+((resultData.error) ? resultData.error : '')+'"'); + } catch (e) {} + } + knownAgents[agentId] = Date.now(); return res.status(200).send(JSON.stringify(Object.keys(knownAgents))); }); -app.post('/:testNumber/:agentId',function(req,res) { - var testNumber = req.params.testNumber; - var agentId = req.params.agentId; - if ((!agentId)||(agentId.length !== 32)) - return res.status(404).send(''); - - var receiveTime = Date.now(); - var resultData = null; - try { - resultData = JSON.parse(req.rawBody); - } catch (e) { - resultData = req.rawBody; - } - result = { - agentId: agentId, - testNumber: parseInt(testNumber), - receiveTime: receiveTime, - results: resultData - }; - - testNumber = testNumber.toString(); - while (testNumber.length < 10) - testNumber = '0' + testNumber; - fs.writeFile('result_'+testNumber+'_'+agentId,JSON.stringify(result),function(err) { - console.log(result); - }); - - return res.status(200).send(''); -}); - var expressServer = app.listen(SERVER_PORT,function () { console.log('LISTENING ON '+SERVER_PORT); console.log(''); From ab27a91b07278146975087e873577bed43793554 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 13:53:27 -0800 Subject: [PATCH 191/195] . --- tests/http/agent.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/http/agent.js b/tests/http/agent.js index 1d4a43200..bc7c475e6 100644 --- a/tests/http/agent.js +++ b/tests/http/agent.js @@ -4,15 +4,13 @@ // Customizable parameters: // Maximum interval between test attempts -//var TEST_INTERVAL_MAX = (60 * 1 * 1000); -var TEST_INTERVAL_MAX = 1000; +var TEST_INTERVAL_MAX = 60000; // Test timeout in ms var TEST_TIMEOUT = 30000; // Where should I contact to register and query a list of other test agents? -var SERVER_HOST = '127.0.0.1'; -//var SERVER_HOST = '104.238.141.145'; +var SERVER_HOST = '104.238.141.145'; var SERVER_PORT = 18080; // Which port should agents use for their HTTP? @@ -118,9 +116,11 @@ function doTest() } catch (e) {} } - if (allOtherAgents.length > 0) { + if (allOtherAgents.length > 1) { var target = allOtherAgents[Math.floor(Math.random() * allOtherAgents.length)]; + while (target === thisAgentId) + target = allOtherAgents[Math.floor(Math.random() * allOtherAgents.length)]; var testRequest = null; var timeoutId = null; @@ -128,7 +128,7 @@ function doTest() if (testRequest !== null) testRequest.abort(); timeoutId = null; - }); + },TEST_TIMEOUT); var startTime = Date.now(); testRequest = http.get({ @@ -166,7 +166,7 @@ function doTest() }); } else { - return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1); + return setTimeout(doTest,1000); } }); From 60ce886605c0298fc22dbce48beb106a96bd35e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 15:15:20 -0800 Subject: [PATCH 192/195] Tweak some timings for better reliability. --- node/Cluster.cpp | 6 +- node/Cluster.hpp | 8 +- node/Constants.hpp | 24 +--- node/Multicaster.cpp | 216 ++++++++++++++++++----------------- 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, 129 insertions(+), 146 deletions(-) diff --git a/node/Cluster.cpp b/node/Cluster.cpp index d0daae437..e9e31edeb 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 7d7a1ced4..f1caa436d 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 1d5fa6f42..bb62484d0 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 e43d7d886..01e6b7999 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)]; + 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()]; + 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; + // 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; } } - 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; - } - } - } else { - unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1; + if (gs.members.size() >= limit) { + // Skip queue if we already have enough members to complete the send operation + OutboundMulticast out; - 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()); + 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); - 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()); - } + 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; } - - 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; - } - gs.txQueue.push_back(OutboundMulticast()); - OutboundMulticast &out = gs.txQueue.back(); + 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; - out.init( - RR, - now, - nwid, - com, - limit, - gatherLimit, - src, - mg, - etherType, - data, - len); + 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()); - unsigned int count = 0; + 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()); + } + } - 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; + 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; + } + + 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 42180e990..74acc869b 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 4a764d1fb..59f367884 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 391ca2a1b..aa540bba2 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 a5e71ef14..43166c6eb 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 From 7fbe2f7adf3575f3a21fc1ab3a5a2a036e18e6e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 15:38:53 -0800 Subject: [PATCH 193/195] Tweak some more timings for better reliability. --- node/Cluster.hpp | 2 +- node/Constants.hpp | 12 ++++++------ 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, 22 insertions(+), 19 deletions(-) diff --git a/node/Cluster.hpp b/node/Cluster.hpp index f1caa436d..ee2209998 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 bb62484d0..552688a62 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -266,16 +266,16 @@ */ #define ZT_PEER_DIRECT_PING_DELAY 60000 +/** + * Timeout for overall peer activity (measured from last receive) + */ +#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 4) + ZT_PING_CHECK_INVERVAL) + /** * Delay between requests for updated network autoconf information */ #define ZT_NETWORK_AUTOCONF_DELAY 60000 -/** - * Timeout for overall peer activity (measured from last receive) - */ -#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 * @@ -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 74acc869b..82cb7ddbc 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 e5db3bde5..ad4c67463 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 d8eca0718..ce75eb03e 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 2f72f57af..120ce7a4d 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 4c1a2ab37..a0c28b0fe 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 43166c6eb..f300ac612 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 From 00dcb0f22c6c4ee9e983510cff783c678afd43fa Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 15:39:09 -0800 Subject: [PATCH 194/195] . --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 87e387a6d..28d7ec5d6 100755 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ cluster-geo/cluster-geo/config.js cluster-geo/cluster-geo/cache.* tests/http/zerotier-one tests/http/result_* +tests/http/big-test-out # MacGap wrapper build files /ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/* From 4e9d4304761f93a1764d3ec2d2b0c38140decad8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 2 Nov 2015 16:03:28 -0800 Subject: [PATCH 195/195] Make root and relay selection somewhat more robust. --- node/Peer.hpp | 26 ++++++++++++++++++++++---- node/Switch.cpp | 7 +++++-- node/Topology.cpp | 37 +++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/node/Peer.hpp b/node/Peer.hpp index ad4c67463..a70d98688 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -236,15 +236,33 @@ public: 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) + * @return Latency in milliseconds or 0 if unknown */ - inline unsigned int latency() const - throw() + inline unsigned int latency() const { return _latency; } + + /** + * This computes a quality score for relays and root servers + * + * If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they + * receive the worst possible quality (max unsigned int). Otherwise the + * quality is a product of latency and the number of potential missed + * pings. This causes roots and relays to switch over a bit faster if they + * fail. + * + * @return Relay quality score computed from latency and other factors, lower is better + */ + inline unsigned int relayQuality(const uint64_t now) const { + const uint64_t tsr = now - _lastReceive; + if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) + return (~(unsigned int)0); unsigned int l = _latency; - return std::min(l,(unsigned int)65535); + if (!l) + l = 0xffff; + return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1)); } + /** * Update latency with a new direct measurment * diff --git a/node/Switch.cpp b/node/Switch.cpp index 120ce7a4d..b7a9c5227 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -741,12 +741,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (!viaPath) { // See if this network has a preferred relay (if packet has an associated network) if (nconf) { - unsigned int latency = ~((unsigned int)0); + unsigned int bestq = ~((unsigned int)0); for(std::vector< std::pair >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { if (r->first != peer->address()) { SharedPtr rp(RR->topology->getPeer(r->first)); - if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) + const unsigned int q = rp->relayQuality(now); + if ((rp)&&(q < bestq)) { // SUBTILE: < == don't use these if they are nil quality (unsigned int max), instead use a root + bestq = q; rp.swap(relay); + } } } } diff --git a/node/Topology.cpp b/node/Topology.cpp index b8bb55f29..bea97ab9f 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -227,33 +227,30 @@ SharedPtr Topology::getBestRoot(const Address *avoid,unsigned int avoidCou } else { /* If I am not a root server, the best root server is the active one with - * the lowest latency. */ + * the lowest quality score. (lower == better) */ - unsigned int bestLatencyOverall = ~((unsigned int)0); - unsigned int bestLatencyNotAvoid = ~((unsigned int)0); + unsigned int bestQualityOverall = ~((unsigned int)0); + unsigned int bestQualityNotAvoid = ~((unsigned int)0); const SharedPtr *bestOverall = (const SharedPtr *)0; const SharedPtr *bestNotAvoid = (const SharedPtr *)0; for(std::vector< SharedPtr >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) { - if ((*r)->hasActiveDirectPath(now)) { - bool avoiding = false; - for(unsigned int i=0;iaddress()) { - avoiding = true; - break; - } - } - unsigned int l = (*r)->latency(); - if (!l) l = ~l; // zero latency indicates no measurment, so make this 'max' - if (l <= bestLatencyOverall) { - bestLatencyOverall = l; - bestOverall = &(*r); - } - if ((!avoiding)&&(l <= bestLatencyNotAvoid)) { - bestLatencyNotAvoid = l; - bestNotAvoid = &(*r); + bool avoiding = false; + for(unsigned int i=0;iaddress()) { + avoiding = true; + break; } } + const unsigned int q = (*r)->relayQuality(now); + if (q <= bestQualityOverall) { + bestQualityOverall = q; + bestOverall = &(*r); + } + if ((!avoiding)&&(q <= bestQualityNotAvoid)) { + bestQualityNotAvoid = q; + bestNotAvoid = &(*r); + } } if (bestNotAvoid) {