From 1613f42d0082cf6438ad0c62d89405ab82625f98 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Nov 2017 14:44:46 -0800 Subject: Re-integrate in-filesystem DB into new controller DB structure. --- controller/FileDB.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 controller/FileDB.cpp (limited to 'controller/FileDB.cpp') diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp new file mode 100644 index 00000000..b48d5e87 --- /dev/null +++ b/controller/FileDB.cpp @@ -0,0 +1,129 @@ +/* + * 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 . + */ + +#include "FileDB.hpp" + +namespace ZeroTier +{ + +FileDB::FileDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path) : + DB(nc,myAddress,path), + _networksPath(_path + ZT_PATH_SEPARATOR_S + "network") +{ + OSUtils::mkdir(_path.c_str()); + OSUtils::lockDownFile(_path.c_str(),true); + + std::vector networks(OSUtils::listDirectory(_networksPath.c_str(),false)); + std::string buf; + for(auto n=networks.begin();n!=networks.end();++n) { + buf.clear(); + if ((n->length() == 21)&&(OSUtils::readFile((_networksPath + ZT_PATH_SEPARATOR_S + *n).c_str(),buf))) { + try { + nlohmann::json network(OSUtils::jsonParse(buf)); + const std::string nwids = network["id"]; + if (nwids.length() == 16) { + nlohmann::json nullJson; + _networkChanged(nullJson,network,false); + std::string membersPath(_networksPath + ZT_PATH_SEPARATOR_S + nwids + ZT_PATH_SEPARATOR_S "member"); + std::vector members(OSUtils::listDirectory(membersPath.c_str(),false)); + for(auto m=members.begin();m!=members.end();++m) { + buf.clear(); + if ((m->length() == 15)&&(OSUtils::readFile((membersPath + ZT_PATH_SEPARATOR_S + *m).c_str(),buf))) { + try { + nlohmann::json member(OSUtils::jsonParse(buf)); + const std::string addrs = member["id"]; + if (addrs.length() == 10) { + nlohmann::json nullJson2; + _memberChanged(nullJson2,member,false); + } + } catch ( ... ) {} + } + } + } + } catch ( ... ) {} + } + } +} + +FileDB::~FileDB() +{ +} + +bool FileDB::waitForReady() +{ + return true; +} + +void FileDB::save(const nlohmann::json &record) +{ + char p1[16384],p2[16384]; + try { + nlohmann::json rec(record); + const std::string objtype = rec["objtype"]; + if (objtype == "network") { + const uint64_t nwid = OSUtils::jsonIntHex(rec["id"],0ULL); + if (nwid) { + nlohmann::json old; + get(nwid,old); + + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json.new",_networksPath.c_str(),nwid); + OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) + fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); + OSUtils::rename(p1,p2); + + _networkChanged(old,rec,true); + } + } else if (objtype == "member") { + const uint64_t id = OSUtils::jsonIntHex(rec["id"],0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(rec["nwid"],0ULL); + if ((id)&&(nwid)) { + nlohmann::json network,old; + get(nwid,network,id,old); + + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json.new",_networksPath.c_str(),nwid); + OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json",_networksPath.c_str(),nwid); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) + fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); + OSUtils::rename(p1,p2); + + _memberChanged(old,rec,true); + } + } else if (objtype == "trace") { + const std::string id = rec["id"]; + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "trace" ZT_PATH_SEPARATOR_S "%s.json",_path.c_str(),id.c_str()); + OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1)); + } + } catch ( ... ) {} // drop invalid records missing fields +} + +void FileDB::eraseNetwork(const uint64_t networkId) +{ + nlohmann::json network,nullJson; + get(networkId,network); + char p[16384]; + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),networkId); + OSUtils::rm(p); + _networkChanged(network,nullJson,true); +} + +void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId) +{ +} + +} // namespace ZeroTier -- cgit v1.2.3 From 4166d8ca35ded34180d60b56105a853dd6b02ff4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 8 Nov 2017 11:06:14 -0800 Subject: Fix a deadlock and some more work on RethinkDB (for central) integration. --- controller/DB.hpp | 4 +- controller/EmbeddedNetworkController.cpp | 31 +++---------- controller/FileDB.cpp | 7 ++- controller/FileDB.hpp | 4 +- controller/RethinkDB.cpp | 74 ++++++++++++++++++++++++++++++-- controller/RethinkDB.hpp | 10 ++++- node/Peer.cpp | 59 ++++--------------------- 7 files changed, 106 insertions(+), 83 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/DB.hpp b/controller/DB.hpp index dfc8ac95..fe06c24d 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -78,12 +78,14 @@ public: void networks(std::vector &networks); - virtual void save(const nlohmann::json &record) = 0; + virtual void save(nlohmann::json *orig,nlohmann::json &record) = 0; virtual void eraseNetwork(const uint64_t networkId) = 0; virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0; + virtual void nodeIsOnline(const uint64_t memberId) = 0; + protected: struct _Network { diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 5707e6e0..a2795d96 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -734,12 +734,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( member["nwid"] = nwids; _removeMemberNonPersistedFields(member); - if (member != origMember) { - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - _db->save(member); - } - + _db->save(&origMember,member); _addMemberNonPersistedFields(nwid,address,member,now); responseBody = OSUtils::jsonDump(member); responseContentType = "application/json"; @@ -986,12 +981,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( network["nwid"] = nwids; // legacy _removeNetworkNonPersistedFields(network); - if (network != origNetwork) { - json &revj = network["revision"]; - network["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - _db->save(network); - } - + _db->save(&origNetwork,network); ControllerDB::NetworkSummaryInfo ns; _db->summary(nwid,ns); _addNetworkNonPersistedFields(nwid,network,now,ns); @@ -1116,7 +1106,7 @@ void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) d["objtype"] = "trace"; d["ts"] = now; d["nodeId"] = Utils::hex10(rt.origin,tmp); - _db->save(d); + _db->save((nlohmann::json *)0,d); } catch ( ... ) { // drop invalid trace messages if an error occurs } @@ -1185,6 +1175,8 @@ void EmbeddedNetworkController::_request( ms.lastRequestTime = now; } + _db->nodeIsOnline(identity.address().toInt()); + Utils::hex(nwid,nwids); _db->get(nwid,network,identity.address().toInt(),member,ns); if ((!network.is_object())||(network.size() == 0)) { @@ -1299,11 +1291,7 @@ void EmbeddedNetworkController::_request( } else { // If they are not authorized, STOP! _removeMemberNonPersistedFields(member); - if (origMember != member) { - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - _db->save(member); - } + _db->save(&origMember,member); _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; } @@ -1666,12 +1654,7 @@ void EmbeddedNetworkController::_request( } _removeMemberNonPersistedFields(member); - if (member != origMember) { - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - _db->save(member); - } - + _db->save(&origMember,member); _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index b48d5e87..646fa2fe 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -69,7 +69,7 @@ bool FileDB::waitForReady() return true; } -void FileDB::save(const nlohmann::json &record) +void FileDB::save(nlohmann::json *orig,nlohmann::json &record) { char p1[16384],p2[16384]; try { @@ -126,4 +126,9 @@ void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId) { } +void FileDB::nodeIsOnline(const uint64_t memberId) +{ + // Nothing to do here right now in the filesystem store mode since we can just get this from the peer list +} + } // namespace ZeroTier diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index fe9869b9..76a47936 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -32,12 +32,14 @@ public: virtual bool waitForReady(); - virtual void save(const nlohmann::json &record); + virtual void save(nlohmann::json *orig,nlohmann::json &record); virtual void eraseNetwork(const uint64_t networkId); virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t memberId); + protected: std::string _networksPath; }; diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index d1012167..031bd516 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -215,6 +215,47 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddres }); } + _onlineNotificationThread = std::thread([this]() { + try { + std::unique_ptr rdb; + while (_run == 1) { + try { + if (!rdb) + rdb = R::connect(this->_host,this->_port,this->_auth); + if (rdb) { + std::lock_guard l(_lastOnline_l); + R::Array batch; + R::Object tmpobj; + for(auto i=_lastOnline.begin();i!=_lastOnline.end();++i) { + char nodeId[16]; + Utils::hex10(i->first,nodeId); + tmpobj["id"] = nodeId; + tmpobj["ts"] = i->second; + batch.emplace_back(tmpobj); + if (batch.size() >= 256) { + R::db(this->_db).table("NodeLastOnline").insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); + batch.clear(); + } + } + if (batch.size() > 0) + R::db(this->_db).table("NodeLastOnline").insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); + _lastOnline.clear(); + } + } catch (std::exception &e) { + fprintf(stderr,"ERROR: controller RethinkDB (node status update): %s" ZT_EOL_S,e.what()); + rdb.reset(); + } catch (R::Error &e) { + fprintf(stderr,"ERROR: controller RethinkDB (node status update): %s" ZT_EOL_S,e.message.c_str()); + rdb.reset(); + } catch ( ... ) { + fprintf(stderr,"ERROR: controller RethinkDB (node status update): unknown exception" ZT_EOL_S); + rdb.reset(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + } catch ( ... ) {} + }); + _heartbeatThread = std::thread([this]() { try { char tmp[1024]; @@ -251,9 +292,10 @@ RethinkDB::~RethinkDB() _membersDbWatcher.join(); _networksDbWatcher.join(); _heartbeatThread.join(); + _onlineNotificationThread.join(); } -void RethinkDB::waitForReady() +bool RethinkDB::waitForReady() { while (_ready > 0) { if (!_waitNoticePrinted) { @@ -263,12 +305,32 @@ void RethinkDB::waitForReady() _readyLock.lock(); _readyLock.unlock(); } + return true; } -void RethinkDB::save(const nlohmann::json &record) +void RethinkDB::save(nlohmann::json *orig,nlohmann::json &record) { + if (!record.is_object()) // sanity check + return; waitForReady(); - _commitQueue.post(new nlohmann::json(record)); + if (orig) { + if (*orig != record) { + nlohmann::json *q = new nlohmann::json(); + try { + record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1; + for(auto kv=record.begin();kv!=record.end();++kv) { + if ((kv.key() == "id")||(kv.key() == "nwid")||(kv.key() == "objtype")||((*q)[kv.key()] != kv.value())) + (*q)[kv.key()] = kv.value(); + } + } catch ( ... ) { + delete q; + throw; + } + } + } else { + record["revision"] = 1; + _commitQueue.post(new nlohmann::json(record)); + } } void RethinkDB::eraseNetwork(const uint64_t networkId) @@ -295,6 +357,12 @@ void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId) _commitQueue.post(tmp); } +void RethinkDB::nodeIsOnline(const uint64_t memberId) +{ + std::lock_guard l(_lastOnline_l); + _lastOnline[memberId] = OSUtils::now(); +} + } // namespace ZeroTier #endif // ZT_CONTROLLER_USE_RETHINKDB diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index 2309a25c..8c8b16a6 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -34,14 +34,16 @@ public: RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path); virtual ~RethinkDB(); - virtual void waitForReady(); + virtual bool waitForReady(); - virtual void save(const nlohmann::json &record); + virtual void save(nlohmann::json *orig,nlohmann::json &record); virtual void eraseNetwork(const uint64_t networkId); virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t memberId); + protected: std::string _host; std::string _db; @@ -56,6 +58,10 @@ protected: BlockingQueue< nlohmann::json * > _commitQueue; std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS]; + std::unordered_map< uint64_t,int64_t > _lastOnline; + mutable std::mutex _lastOnline_l; + std::thread _onlineNotificationThread; + std::thread _heartbeatThread; mutable std::mutex _readyLock; // locked until ready diff --git a/node/Peer.cpp b/node/Peer.cpp index a3682a97..2d562f12 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -78,54 +78,6 @@ void Peer::received( { const int64_t now = RR->node->now(); -/* -#ifdef ZT_ENABLE_CLUSTER - bool isClusterSuboptimalPath = false; - 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. - InetAddress redirectTo; - if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),path->address(),false)) ) { - 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)ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT); // flags: cluster redirect - 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,path->nextOutgoingCounter()); - path->send(RR,tPtr,outp.data(),outp.size(),now); - } 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,path->nextOutgoingCounter()); - path->send(RR,tPtr,outp.data(),outp.size(),now); - } - isClusterSuboptimalPath = true; - } - } -#endif -*/ - _lastReceive = now; switch (verb) { case Packet::VERB_FRAME: @@ -163,6 +115,7 @@ void Peer::received( } } + bool attemptToContact = false; if ((!havePath)&&(RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) { Mutex::Lock _l(_paths_m); @@ -201,13 +154,17 @@ void Peer::received( _paths[replacePath].p = path; _paths[replacePath].priority = 1; } else { - attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true,path->nextOutgoingCounter()); - path->sent(now); - RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); + attemptToContact = true; } } } } + + if (attemptToContact) { + attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true,path->nextOutgoingCounter()); + path->sent(now); + RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); + } } // If we have a trust relationship periodically push a message enumerating -- cgit v1.2.3 From c12b68a6b2f8597e374a4c1386c2b95f4291932e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 8 Nov 2017 11:32:01 -0800 Subject: More Central work. --- controller/DB.hpp | 2 +- controller/EmbeddedNetworkController.cpp | 2 +- controller/FileDB.cpp | 2 +- controller/FileDB.hpp | 2 +- controller/RethinkDB.cpp | 14 +++++++------- controller/RethinkDB.hpp | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/DB.hpp b/controller/DB.hpp index fe06c24d..fca41d70 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -84,7 +84,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0; - virtual void nodeIsOnline(const uint64_t memberId) = 0; + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId) = 0; protected: struct _Network diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index a2795d96..999319af 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1175,7 +1175,7 @@ void EmbeddedNetworkController::_request( ms.lastRequestTime = now; } - _db->nodeIsOnline(identity.address().toInt()); + _db->nodeIsOnline(nwid,identity.address().toInt()); Utils::hex(nwid,nwids); _db->get(nwid,network,identity.address().toInt(),member,ns); diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 646fa2fe..b9f36031 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -126,7 +126,7 @@ void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId) { } -void FileDB::nodeIsOnline(const uint64_t memberId) +void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId) { // Nothing to do here right now in the filesystem store mode since we can just get this from the peer list } diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index 76a47936..b438e80e 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -38,7 +38,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId); protected: std::string _networksPath; diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index 031bd516..2da55177 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -227,18 +227,18 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddres R::Array batch; R::Object tmpobj; for(auto i=_lastOnline.begin();i!=_lastOnline.end();++i) { - char nodeId[16]; - Utils::hex10(i->first,nodeId); - tmpobj["id"] = nodeId; + char tmp[64]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx-%.10llx",i->first.first,i->first.second); + tmpobj["id"] = tmp; tmpobj["ts"] = i->second; batch.emplace_back(tmpobj); if (batch.size() >= 256) { - R::db(this->_db).table("NodeLastOnline").insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); + R::db(this->_db).table("MemberLastRequest",R::optargs("read_mode","outdated")).insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); batch.clear(); } } if (batch.size() > 0) - R::db(this->_db).table("NodeLastOnline").insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); + R::db(this->_db).table("MemberLastRequest",R::optargs("read_mode","outdated")).insert(R::args(batch),R::optargs("conflict","update")).run(*rdb); _lastOnline.clear(); } } catch (std::exception &e) { @@ -357,10 +357,10 @@ void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId) _commitQueue.post(tmp); } -void RethinkDB::nodeIsOnline(const uint64_t memberId) +void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId) { std::lock_guard l(_lastOnline_l); - _lastOnline[memberId] = OSUtils::now(); + _lastOnline[std::pair(networkId,memberId)] = OSUtils::now(); } } // namespace ZeroTier diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index 8c8b16a6..6efa5624 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -42,7 +42,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId); protected: std::string _host; @@ -58,7 +58,7 @@ protected: BlockingQueue< nlohmann::json * > _commitQueue; std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS]; - std::unordered_map< uint64_t,int64_t > _lastOnline; + std::unordered_map< std::pair,int64_t > _lastOnline; mutable std::mutex _lastOnline_l; std::thread _onlineNotificationThread; -- cgit v1.2.3 From f7f658605dfc255c5bad88ed190f7b00b0dd81a2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 8 Nov 2017 20:19:46 -0500 Subject: Move more ephemeral stuff to a tiny MemberLastRequest table instead of the main Member table. --- controller/DB.hpp | 2 +- controller/EmbeddedNetworkController.cpp | 2 +- controller/FileDB.cpp | 2 +- controller/FileDB.hpp | 2 +- controller/RethinkDB.cpp | 12 ++++++++---- controller/RethinkDB.hpp | 4 ++-- 6 files changed, 14 insertions(+), 10 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/DB.hpp b/controller/DB.hpp index fca41d70..8731cb5c 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -84,7 +84,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0; - virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId) = 0; + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0; protected: struct _Network diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 999319af..0f82ff63 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1175,7 +1175,7 @@ void EmbeddedNetworkController::_request( ms.lastRequestTime = now; } - _db->nodeIsOnline(nwid,identity.address().toInt()); + _db->nodeIsOnline(nwid,identity.address().toInt(),fromAddr); Utils::hex(nwid,nwids); _db->get(nwid,network,identity.address().toInt(),member,ns); diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index b9f36031..728cec6b 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -126,7 +126,7 @@ void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId) { } -void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId) +void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) { // Nothing to do here right now in the filesystem store mode since we can just get this from the peer list } diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index b438e80e..e31b18e6 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -38,7 +38,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); protected: std::string _networksPath; diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index 6583f23c..cd968e26 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -227,10 +227,11 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddres R::Array batch; R::Object tmpobj; for(auto i=_lastOnline.begin();i!=_lastOnline.end();++i) { - char tmp[64]; + char tmp[64],tmp2[64]; OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx-%.10llx",i->first.first,i->first.second); tmpobj["id"] = tmp; - tmpobj["ts"] = i->second; + tmpobj["ts"] = i->second.first; + tmpobj["phy"] = i->second.second.toIpString(tmp2); batch.emplace_back(tmpobj); if (batch.size() >= 256) { R::db(this->_db).table("MemberLastRequest",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb); @@ -357,10 +358,13 @@ void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId) _commitQueue.post(tmp); } -void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId) +void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) { std::lock_guard l(_lastOnline_l); - _lastOnline[std::pair(networkId,memberId)] = OSUtils::now(); + std::pair &i = _lastOnline[std::pair(networkId,memberId)]; + i.first = OSUtils::now(); + if (physicalAddress) + i.second = physicalAddress; } } // namespace ZeroTier diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index 26987019..a69f462f 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -48,7 +48,7 @@ public: virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); protected: struct _PairHasher @@ -69,7 +69,7 @@ protected: BlockingQueue< nlohmann::json * > _commitQueue; std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS]; - std::unordered_map< std::pair,int64_t,_PairHasher > _lastOnline; + std::unordered_map< std::pair,std::pair,_PairHasher > _lastOnline; mutable std::mutex _lastOnline_l; std::thread _onlineNotificationThread; -- cgit v1.2.3 From 926ecf964047f7ab23ee2c086d826688c5228c96 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 5 Dec 2017 14:50:59 -0800 Subject: docs --- controller/EmbeddedNetworkController.cpp | 2 +- controller/FileDB.cpp | 9 +++++++-- controller/README.md | 8 +++++++- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 018f2215..c490055c 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1097,7 +1097,7 @@ void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) } const int64_t now = OSUtils::now(); - OSUtils::ztsnprintf(id,sizeof(id),"%.10llx-%.10llx-%.16llx-%.8lx",_signingId.address().toInt(),rt.origin,now,++idCounter); + OSUtils::ztsnprintf(id,sizeof(id),"%.10llx-%.16llx-%.10llx-%.4x",_signingId.address().toInt(),now,rt.origin,(unsigned int)(idCounter++ & 0xffff)); d["id"] = id; d["objtype"] = "trace"; d["ts"] = now; diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 728cec6b..3f8564fa 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -27,6 +27,9 @@ FileDB::FileDB(EmbeddedNetworkController *const nc,const Address &myAddress,cons { OSUtils::mkdir(_path.c_str()); OSUtils::lockDownFile(_path.c_str(),true); + OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "network").c_str()); + OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "network" + ZT_PATH_SEPARATOR_S + "member").c_str()); + OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "trace").c_str()); std::vector networks(OSUtils::listDirectory(_networksPath.c_str(),false)); std::string buf; @@ -106,8 +109,10 @@ void FileDB::save(nlohmann::json *orig,nlohmann::json &record) } } else if (objtype == "trace") { const std::string id = rec["id"]; - OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "trace" ZT_PATH_SEPARATOR_S "%s.json",_path.c_str(),id.c_str()); - OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1)); + if (id.length() > 0) { + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "trace" ZT_PATH_SEPARATOR_S "%s.json",_path.c_str(),id.c_str()); + OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1)); + } } } catch ( ... ) {} // drop invalid records missing fields } diff --git a/controller/README.md b/controller/README.md index a684ed9c..4521f22b 100644 --- a/controller/README.md +++ b/controller/README.md @@ -1,7 +1,7 @@ Network Controller Microservice ====== -Every ZeroTier virtual network has a *network controller*. This is our reference implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). Network controllers act as configuration servers and certificate authorities for the members of networks. Controllers are located on the network by simply parsing out the first 10 digits of a network's 16-digit network ID: these are the address of the controller. +Every ZeroTier virtual network has a *network controller*. This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). Network controllers act as configuration servers and certificate authorities for the members of networks. Controllers are located on the network by simply parsing out the first 10 digits of a network's 16-digit network ID: these are the address of the controller. As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets, allowing any device to create virtual networks without having to be rebuilt from source with special flags to enable this feature. While this does offer a convenient way to create ad-hoc networks or experiment, we recommend running a dedicated controller somewhere secure and stable for any "serious" use case. @@ -29,6 +29,12 @@ Since ZeroTier nodes are mobile and do not need static IPs, implementing high av ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it. +### About the RethinkDB Connector + +The default controller stores its data in the filesystem. There is also a direct RethinkDB connector that can be built on Linux with `make central-controller`. + +This is designed for use with ZeroTier Central. You are free to build it and use it but don't be surprised if it changes without warning. It shouldn't be considered stable for external use. + ### Network Controller API The controller API is hosted via the same JSON API endpoint that ZeroTier One uses for local control (usually at 127.0.0.1 port 9993). All controller options are routed under the `/controller` base path. -- cgit v1.2.3 From 8d9464c4140e5882b0fc9276388401514f29e62a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 7 Dec 2017 13:39:25 -0800 Subject: docs, and make RethinkDB controller DB driver upsert into the Controller DB and also update the hostname field. --- RELEASE-NOTES.md | 13 +++++++++++ controller/DB.cpp | 5 ++-- controller/DB.hpp | 5 ++-- controller/EmbeddedNetworkController.cpp | 4 ++-- controller/FileDB.cpp | 4 ++-- controller/FileDB.hpp | 2 +- controller/RethinkDB.cpp | 40 ++++++++++++++++++++++++++++---- controller/RethinkDB.hpp | 2 +- 8 files changed, 60 insertions(+), 15 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 195e8888..86522cea 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,19 @@ ZeroTier Release Notes ====== +# 2017-12-XX -- Version 1.2.6 + + * Network Hypervisor + * We've made some improvements to dead path detection and path selection. These also include changes under the hood to prepare for multi-path trunking and faster fail-over support. + * Platform-Specific Changes + * MacOS + * Installer now loads the kernel extension right away so that High Sierra users will see the prompt to authorize it. This is done in the "Security & Privacy" preference pane and must be done driectly on the console (not via remote desktop). + * Windows + * The Windows installer should now install the driver without requiring a special prompt in most cases. This should make it easier for our packages to be accepted into and updated in the Chocolatey repository and should make it easier to perform remote installs. + * The Windows official packages are now signed with an EV certificate (with hardware key) from DigiCert. + * The Windows UI now contains a preview of features to more deeply integrate it with ZeroTier Central. You can enter a ZeroTier Central API key and join networks, etc. from the UI itself. We'll be expanding this in the future and possibly changing it, so this is just a test to see how users respond. + * The `zerotier-idtool` command should now work on Windows. + # 2017-04-20 -- Version 1.2.4 * Managed routes are now only bifurcated for the default route. This is a change in behavior, though few people will probably notice. Bifurcating all managed routes was causing more trouble than it was worth for most users. diff --git a/controller/DB.cpp b/controller/DB.cpp index 2f9a4a89..2f09205b 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -27,9 +27,10 @@ using json = nlohmann::json; namespace ZeroTier { -DB::DB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path) : +DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : _controller(nc), - _myAddress(myAddress), + _myId(myId), + _myAddress(myId.address()), _path((path) ? path : "") { char tmp[32]; diff --git a/controller/DB.hpp b/controller/DB.hpp index 8731cb5c..4c7a16b2 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -20,7 +20,7 @@ #define ZT_CONTROLLER_DB_HPP #include "../node/Constants.hpp" -#include "../node/Address.hpp" +#include "../node/Identity.hpp" #include "../node/InetAddress.hpp" #include "../osdep/OSUtils.hpp" #include "../osdep/BlockingQueue.hpp" @@ -58,7 +58,7 @@ public: int64_t mostRecentDeauthTime; }; - DB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path); + DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path); virtual ~DB(); virtual bool waitForReady() = 0; @@ -104,6 +104,7 @@ protected: void _fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info); EmbeddedNetworkController *const _controller; + const Identity _myId; const Address _myAddress; const std::string _path; std::string _myAddressStr; diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index a3ce9208..d97a1ce2 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -477,10 +477,10 @@ void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender) _signingIdAddressString = signingId.address().toString(tmp); #ifdef ZT_CONTROLLER_USE_RETHINKDB if ((_path.length() > 10)&&(_path.substr(0,10) == "rethinkdb:")) - _db.reset(new RethinkDB(this,_signingId.address(),_path.c_str())); + _db.reset(new RethinkDB(this,_signingId,_path.c_str())); else // else use FileDB after endif #endif - _db.reset(new FileDB(this,_signingId.address(),_path.c_str())); + _db.reset(new FileDB(this,_signingId,_path.c_str())); _db->waitForReady(); } diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 3f8564fa..6b02f836 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -21,8 +21,8 @@ namespace ZeroTier { -FileDB::FileDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path) : - DB(nc,myAddress,path), +FileDB::FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : + DB(nc,myId,path), _networksPath(_path + ZT_PATH_SEPARATOR_S + "network") { OSUtils::mkdir(_path.c_str()); diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index e31b18e6..eeb1c541 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -27,7 +27,7 @@ namespace ZeroTier class FileDB : public DB { public: - FileDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path); + FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path); virtual ~FileDB(); virtual bool waitForReady(); diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index e6b58efd..b4f07f53 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -18,6 +18,8 @@ #ifdef ZT_CONTROLLER_USE_RETHINKDB +#include + #include "RethinkDB.hpp" #include "EmbeddedNetworkController.hpp" @@ -34,8 +36,8 @@ using json = nlohmann::json; namespace ZeroTier { -RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path) : - DB(nc,myAddress,path), +RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : + DB(nc,myId,path), _ready(2), // two tables need to be synchronized before we're ready, so this is ready when it reaches 0 _run(1), _waitNoticePrinted(false) @@ -317,16 +319,44 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddres _heartbeatThread = std::thread([this]() { try { - char tmp[1024]; + R::Object controllerRecord; std::unique_ptr rdb; + + { + char publicId[1024]; + char secretId[1024]; + char hostname[1024]; + this->_myId.toString(publicId,false); + this->_myId.toString(secretId,true); + if (gethostname(hostname,sizeof(hostname)) != 0) { + hostname[0] = (char)0; + } else { + for(int i=0;i_myAddressStr.c_str(); + controllerRecord["publicIdentity"] = publicId; + controllerRecord["secretIdentity"] = secretId; + if (hostname[0]) + controllerRecord["clusterHost"] = hostname; + controllerRecord["vMajor"] = ZEROTIER_ONE_VERSION_MAJOR; + controllerRecord["vMinor"] = ZEROTIER_ONE_VERSION_MINOR; + controllerRecord["vRev"] = ZEROTIER_ONE_VERSION_REVISION; + controllerRecord["vBuild"] = ZEROTIER_ONE_VERSION_BUILD; + } + while (_run == 1) { try { if (!rdb) rdb = R::connect(this->_host,this->_port,this->_auth); if (rdb) { - OSUtils::ztsnprintf(tmp,sizeof(tmp),"{\"id\":\"%s\",\"lastAlive\":%lld,\"version\":\"%d.%d.%d\"}",this->_myAddressStr.c_str(),(long long)OSUtils::now(),ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); + controllerRecord["lastAlive"] = OSUtils::now(); //printf("HEARTBEAT: %s" ZT_EOL_S,tmp); - R::db(this->_db).table("Controller").update(R::Datum::from_json(tmp)).run(*rdb); + R::db(this->_db).table("Controller",R::optargs("read_mode","outdated")).insert(controllerRecord,R::optargs("conflict","update")).run(*rdb); } } catch ( ... ) { rdb.reset(); diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index a69f462f..07f0abfb 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -37,7 +37,7 @@ namespace ZeroTier class RethinkDB : public DB { public: - RethinkDB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path); + RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path); virtual ~RethinkDB(); virtual bool waitForReady(); -- cgit v1.2.3 From dae728124ea5d5c1d2e68e90d6f6b0faf3f3693b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 7 Dec 2017 14:42:33 -0800 Subject: Fix to network path in conventional filesystem controller DB. --- controller/FileDB.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 6b02f836..b2f26e7c 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -28,7 +28,6 @@ FileDB::FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const ch OSUtils::mkdir(_path.c_str()); OSUtils::lockDownFile(_path.c_str(),true); OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "network").c_str()); - OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "network" + ZT_PATH_SEPARATOR_S + "member").c_str()); OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "trace").c_str()); std::vector networks(OSUtils::listDirectory(_networksPath.c_str(),false)); @@ -74,7 +73,7 @@ bool FileDB::waitForReady() void FileDB::save(nlohmann::json *orig,nlohmann::json &record) { - char p1[16384],p2[16384]; + char p1[4096],p2[4096],pb[4096]; try { nlohmann::json rec(record); const std::string objtype = rec["objtype"]; @@ -99,10 +98,14 @@ void FileDB::save(nlohmann::json *orig,nlohmann::json &record) nlohmann::json network,old; get(nwid,network,id,old); - OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json.new",_networksPath.c_str(),nwid); - OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json",_networksPath.c_str(),nwid); - if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) - fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); + OSUtils::ztsnprintf(pb,sizeof(pb),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)nwid); + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json.new",pb,(unsigned long long)id); + OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json",pb,(unsigned long long)id); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) { + OSUtils::mkdir(pb); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) + fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); + } OSUtils::rename(p1,p2); _memberChanged(old,rec,true); -- cgit v1.2.3 From a1992d76f2026c784c8a4f15bf1a4ef164ae74c6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 10 Jan 2018 14:31:28 -0800 Subject: Doc updates and other fixes. --- controller/DB.cpp | 2 +- controller/EmbeddedNetworkController.cpp | 2 +- controller/FileDB.cpp | 31 +++++++++++------- controller/README.md | 55 ++++++++++++++++---------------- 4 files changed, 48 insertions(+), 42 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 688fede2..7b792eb4 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -41,7 +41,7 @@ void DB::initNetwork(nlohmann::json &network) if (!network.count("tags")) network["tags"] = nlohmann::json::array(); if (!network.count("routes")) network["routes"] = nlohmann::json::array(); if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array(); - if (!network.count("anchors")) network["anchors"] = nlohmann::json::array(); + //if (!network.count("anchors")) network["anchors"] = nlohmann::json::array(); if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU; if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0; diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 37eff0a4..e9cb41fc 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -48,7 +48,7 @@ using json = nlohmann::json; // API version reported via JSON control plane -#define ZT_NETCONF_CONTROLLER_API_VERSION 3 +#define ZT_NETCONF_CONTROLLER_API_VERSION 4 // Min duration between requests for an address/nwid combo to prevent floods #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000 diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index b2f26e7c..6942a16e 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -75,25 +75,32 @@ void FileDB::save(nlohmann::json *orig,nlohmann::json &record) { char p1[4096],p2[4096],pb[4096]; try { - nlohmann::json rec(record); - const std::string objtype = rec["objtype"]; + if (orig) { + if (*orig != record) { + record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1; + } + } else { + record["revision"] = 1; + } + + const std::string objtype = record["objtype"]; if (objtype == "network") { - const uint64_t nwid = OSUtils::jsonIntHex(rec["id"],0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(record["id"],0ULL); if (nwid) { nlohmann::json old; get(nwid,old); OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json.new",_networksPath.c_str(),nwid); OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid); - if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); OSUtils::rename(p1,p2); - _networkChanged(old,rec,true); + _networkChanged(old,record,true); } } else if (objtype == "member") { - const uint64_t id = OSUtils::jsonIntHex(rec["id"],0ULL); - const uint64_t nwid = OSUtils::jsonIntHex(rec["nwid"],0ULL); + const uint64_t id = OSUtils::jsonIntHex(record["id"],0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"],0ULL); if ((id)&&(nwid)) { nlohmann::json network,old; get(nwid,network,id,old); @@ -101,20 +108,20 @@ void FileDB::save(nlohmann::json *orig,nlohmann::json &record) OSUtils::ztsnprintf(pb,sizeof(pb),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)nwid); OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json.new",pb,(unsigned long long)id); OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json",pb,(unsigned long long)id); - if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) { + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) { OSUtils::mkdir(pb); - if (!OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1))) + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); } OSUtils::rename(p1,p2); - _memberChanged(old,rec,true); + _memberChanged(old,record,true); } } else if (objtype == "trace") { - const std::string id = rec["id"]; + const std::string id = record["id"]; if (id.length() > 0) { OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "trace" ZT_PATH_SEPARATOR_S "%s.json",_path.c_str(),id.c_str()); - OSUtils::writeFile(p1,OSUtils::jsonDump(rec,-1)); + OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)); } } } catch ( ... ) {} // drop invalid records missing fields diff --git a/controller/README.md b/controller/README.md index 4521f22b..09eab834 100644 --- a/controller/README.md +++ b/controller/README.md @@ -1,49 +1,47 @@ Network Controller Microservice ====== -Every ZeroTier virtual network has a *network controller*. This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). Network controllers act as configuration servers and certificate authorities for the members of networks. Controllers are located on the network by simply parsing out the first 10 digits of a network's 16-digit network ID: these are the address of the controller. +Every ZeroTier virtual network has a *network controller* responsible for admitting members to the network, issuing certificates, and issuing default configuration information. -As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets, allowing any device to create virtual networks without having to be rebuilt from source with special flags to enable this feature. While this does offer a convenient way to create ad-hoc networks or experiment, we recommend running a dedicated controller somewhere secure and stable for any "serious" use case. +This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets. Controller data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, rsync'd, placed in `git`, etc. The files under `controller.d` should not be modified in place while the controller is running or data loss may result, and if they are edited directly take care not to save corrupt JSON since that can also lead to data loss when the controller is restarted. Going through the API is strongly preferred to directly modifying these files. -### Upgrading from Older (1.1.14 or earlier) Versions - -Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code. - -The migration tool is written in nodeJS and can be used like this: - - cd migrate-sqlite - npm install - node migrate.js - -Very old versions of nodeJS may have issues. We tested it with version 7. +See the API section below for information about controlling the controller. ### Scalability and Reliability Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend spreading large numbers of networks across many controllers for load balancing and fault tolerance reasons. Since the controller uses the filesystem as its data store we recommend fast filesystems and fast SSD drives for heavily loaded controllers. -Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Many modern orchestration tools have built-in support for this. It would also be possible in theory to run controllers on a replicated or distributed filesystem, but we haven't tested this yet. +Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Modern orchestration tools like Nomad and Kubernetes can be of help here. ### Dockerizing Controllers ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it. -### About the RethinkDB Connector +### RethinkDB Database Implementation + +The default controller stores its data in the filesystem in `controller.d` under ZeroTier's home folder. There's an alternative implementation that stores data in RethinkDB that can be built with `make central-controller`. Right now this is only guaranteed to build and run on Linux and is designed for use with [ZeroTier Central](https://my.zerotier.com/). You're welcome to use it but we don't "officially" support it for end-user use and it could change at any time. -The default controller stores its data in the filesystem. There is also a direct RethinkDB connector that can be built on Linux with `make central-controller`. +### Upgrading from Older (1.1.14 or earlier) Versions + +Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code. -This is designed for use with ZeroTier Central. You are free to build it and use it but don't be surprised if it changes without warning. It shouldn't be considered stable for external use. +The migration tool is written in nodeJS and can be used like this: + + cd migrate-sqlite + npm install + node migrate.js ### Network Controller API The controller API is hosted via the same JSON API endpoint that ZeroTier One uses for local control (usually at 127.0.0.1 port 9993). All controller options are routed under the `/controller` base path. -The controller microservice does not implement any fine-grained access control (authentication is via authtoken.secret, simply append the value from authtoken.secret file, into a new querystring parameter named "auth" - for example `/controller/network?auth=6hdmozf8k5ds39kabcdefabc`) or other complex mangement features. It just takes network and network member configurations and reponds to controller queries. We have an enterprise product called [ZeroTier Central](https://my.zerotier.com/) that we host as a service (and that companies can license to self-host) that does this. +The controller microservice itself does not implement any fine-grained access control. Access control is via the ZeroTier control interface itself and `authtoken.secret`. This can be sent as the `X-ZT1-Auth` HTTP header field or appended to the URL as `?auth=`. Take care when doing the latter that request URLs are not being logged. -All working network IDs on a controller must begin with the controller's ZeroTier address. The API will *allow* "foreign" networks to be added but the controller will have no way of doing anything with them since nobody will know to query it. (In the future we might support secondaries, which would make this relevant.) +While networks with any valid ID can be added to the controller's database, it will only actually work to control networks whose first 10 hex digits correspond with the network controller's ZeroTier ID. See [section 2.2.1 of the ZeroTier manual](https://zerotier.com/manual.shtml#2_2_1). -The JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrectly typed and unrecognized fields may result in ignored fields or a 400 (bad request) error. +The controller JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrect types may be ignored, set to default values, or set to undefined values. #### `/controller` @@ -84,30 +82,31 @@ Example: | Field | Type | Description | Writable | | --------------------- | ------------- | ------------------------------------------------- | -------- | | id | string | 16-digit network ID | no | -| nwid | string | 16-digit network ID (old, but still around) | no | +| nwid | string | 16-digit network ID (legacy) | no | +| objtype | string | Always "network" | no | | name | string | A short name for this network | YES | +| creationTime | integer | Time network record was created (ms since epoch) | no | | private | boolean | Is access control enabled? | YES | | enableBroadcast | boolean | Ethernet ff:ff:ff:ff:ff:ff allowed? | YES | | allowPassiveBridging | boolean | Allow any member to bridge (very experimental) | YES | | v4AssignMode | object | IPv4 management and assign options (see below) | YES | | v6AssignMode | object | IPv6 management and assign options (see below) | YES | +| mtu | integer | Network MTU (default: 2800) | YES | | multicastLimit | integer | Maximum recipients for a multicast packet | YES | | creationTime | integer | Time network was first created | no | | revision | integer | Network config revision counter | no | | routes | array[object] | Managed IPv4 and IPv6 routes; see below | YES | | ipAssignmentPools | array[object] | IP auto-assign ranges; see below | YES | | rules | array[object] | Traffic rules; see below | YES | - -Recent changes: - - * The `ipLocalRoutes` field appeared in older versions but is no longer present. Routes will now show up in `routes`. - * The `relays` field is gone since network preferred relays are gone. This capability is replaced by VL1 level federation ("federated roots"). - -Other important points: +| capabilities | array[object] | Array of capability objects (see below) | YES | +| tags | array[object] | Array of tag objects (see below) | YES | +| remoteTraceTarget | string | 10-digit ZeroTier ID of remote trace target | YES | +| remoteTraceLevel | integer | Remote trace verbosity level | YES | * Networks without rules won't carry any traffic. If you don't specify any on network creation an "accept anything" rule set will automatically be added. * Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members. * The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are otherwise not common. + * Changing the MTU can be disruptive and on some operating systems may require a leave/rejoin of the network or a restart of the ZeroTier service. **Auto-Assign Modes:** -- cgit v1.2.3 From f17cc1c6d80657fe8cd98891cf0bb1bbf3ac62bf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 10 Jan 2018 15:03:39 -0800 Subject: cleanup --- controller/FileDB.cpp | 9 +++++---- controller/FileDB.hpp | 5 +---- controller/RethinkDB.hpp | 4 ---- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 6942a16e..40b9f914 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -23,12 +23,13 @@ namespace ZeroTier FileDB::FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : DB(nc,myId,path), - _networksPath(_path + ZT_PATH_SEPARATOR_S + "network") + _networksPath(_path + ZT_PATH_SEPARATOR_S + "network"), + _tracePath(_path + ZT_PATH_SEPARATOR_S + "trace") { OSUtils::mkdir(_path.c_str()); OSUtils::lockDownFile(_path.c_str(),true); - OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "network").c_str()); - OSUtils::mkdir((_path + ZT_PATH_SEPARATOR + "trace").c_str()); + OSUtils::mkdir(_networksPath.c_str()); + OSUtils::mkdir(_tracePath.c_str()); std::vector networks(OSUtils::listDirectory(_networksPath.c_str(),false)); std::string buf; @@ -120,7 +121,7 @@ void FileDB::save(nlohmann::json *orig,nlohmann::json &record) } else if (objtype == "trace") { const std::string id = record["id"]; if (id.length() > 0) { - OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "trace" ZT_PATH_SEPARATOR_S "%s.json",_path.c_str(),id.c_str()); + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%s.json",_tracePath.c_str(),id.c_str()); OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)); } } diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index eeb1c541..b02da8cb 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -31,17 +31,14 @@ public: virtual ~FileDB(); virtual bool waitForReady(); - virtual void save(nlohmann::json *orig,nlohmann::json &record); - virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); protected: std::string _networksPath; + std::string _tracePath; }; } // namespace ZeroTier diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index 07f0abfb..bce8bdef 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -41,13 +41,9 @@ public: virtual ~RethinkDB(); virtual bool waitForReady(); - virtual void save(nlohmann::json *orig,nlohmann::json &record); - virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); protected: -- cgit v1.2.3 From 57b96af2c41ed63a12bfb41044f242fc55ab1358 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Mar 2018 22:33:08 -0800 Subject: Fix for FileDB _networkChanged / _memberChanged problem. --- controller/DB.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++++-- controller/DB.hpp | 4 +- controller/FileDB.cpp | 2 +- controller/RethinkDB.cpp | 9 ++-- 4 files changed, 138 insertions(+), 10 deletions(-) (limited to 'controller/FileDB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 70472e57..680b4120 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -222,7 +222,7 @@ void DB::networks(std::vector &networks) networks.push_back(n->first); } -void DB::_memberChanged(nlohmann::json &old,nlohmann::json &member,bool push) +void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push) { uint64_t memberId = 0; uint64_t networkId = 0; @@ -230,6 +230,102 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &member,bool push) bool wasAuth = false; std::shared_ptr<_Network> nw; + if (old.is_object()) { + memberId = OSUtils::jsonIntHex(old["id"],0ULL); + networkId = OSUtils::jsonIntHex(old["nwid"],0ULL); + if ((memberId)&&(networkId)) { + { + std::lock_guard l(_networks_l); + auto nw2 = _networks.find(networkId); + if (nw2 != _networks.end()) + nw = nw2->second; + } + if (nw) { + std::lock_guard l(nw->lock); + if (OSUtils::jsonBool(old["activeBridge"],false)) + nw->activeBridgeMembers.erase(memberId); + wasAuth = OSUtils::jsonBool(old["authorized"],false); + if (wasAuth) + nw->authorizedMembers.erase(memberId); + json &ips = old["ipAssignments"]; + if (ips.is_array()) { + for(unsigned long i=0;iallocatedIps.erase(ipa); + } + } + } + } + } + } + + if (memberConfig.is_object()) { + if (!nw) { + memberId = OSUtils::jsonIntHex(memberConfig["id"],0ULL); + networkId = OSUtils::jsonIntHex(memberConfig["nwid"],0ULL); + if ((!memberId)||(!networkId)) + return; + std::lock_guard l(_networks_l); + std::shared_ptr<_Network> &nw2 = _networks[networkId]; + if (!nw2) + nw2.reset(new _Network); + nw = nw2; + } + + { + std::lock_guard l(nw->lock); + + nw->members[memberId] = memberConfig; + + if (OSUtils::jsonBool(memberConfig["activeBridge"],false)) + nw->activeBridgeMembers.insert(memberId); + isAuth = OSUtils::jsonBool(memberConfig["authorized"],false); + if (isAuth) + nw->authorizedMembers.insert(memberId); + json &ips = memberConfig["ipAssignments"]; + if (ips.is_array()) { + for(unsigned long i=0;iallocatedIps.insert(ipa); + } + } + } + + if (!isAuth) { + const int64_t ldt = (int64_t)OSUtils::jsonInt(memberConfig["lastDeauthorizedTime"],0ULL); + if (ldt > nw->mostRecentDeauthTime) + nw->mostRecentDeauthTime = ldt; + } + } + + if (push) + _controller->onNetworkMemberUpdate(networkId,memberId); + } else if (memberId) { + if (nw) { + std::lock_guard l(nw->lock); + nw->members.erase(memberId); + } + if (networkId) { + std::lock_guard l(_networks_l); + auto er = _networkByMember.equal_range(memberId); + for(auto i=er.first;i!=er.second;++i) { + if (i->second == networkId) { + _networkByMember.erase(i); + break; + } + } + } + } + + /* if (old.is_object()) { json &config = old["config"]; if (config.is_object()) { @@ -330,16 +426,46 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &member,bool push) } } } + */ if ((push)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId))) _controller->onNetworkMemberDeauthorize(networkId,memberId); } -void DB::_networkChanged(nlohmann::json &old,nlohmann::json &network,bool push) +void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push) { + if (networkConfig.is_object()) { + const std::string ids = networkConfig["id"]; + const uint64_t id = Utils::hexStrToU64(ids.c_str()); + if (id) { + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + std::shared_ptr<_Network> &nw2 = _networks[id]; + if (!nw2) + nw2.reset(new _Network); + nw = nw2; + } + { + std::lock_guard l2(nw->lock); + nw->config = networkConfig; + } + if (push) + _controller->onNetworkUpdate(id); + } + } else if (old.is_object()) { + const std::string ids = old["id"]; + const uint64_t id = Utils::hexStrToU64(ids.c_str()); + if (id) { + std::lock_guard l(_networks_l); + _networks.erase(id); + } + } + + /* if (network.is_object()) { json &config = network["config"]; - if (config.is_object()) { + if (networkConfig.is_object()) { const std::string ids = config["id"]; const uint64_t id = Utils::hexStrToU64(ids.c_str()); if (id) { @@ -367,6 +493,7 @@ void DB::_networkChanged(nlohmann::json &old,nlohmann::json &network,bool push) _networks.erase(id); } } + */ } void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info) diff --git a/controller/DB.hpp b/controller/DB.hpp index abd1483e..86626009 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -119,8 +119,8 @@ protected: std::mutex lock; }; - void _memberChanged(nlohmann::json &old,nlohmann::json &member,bool push); - void _networkChanged(nlohmann::json &old,nlohmann::json &network,bool push); + void _memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push); + void _networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push); void _fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info); EmbeddedNetworkController *const _controller; diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 40b9f914..a7b59cbf 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. + * Copyright (C) 2011-2018 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 diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index a3bdfbc9..0948b012 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -138,10 +138,11 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co try { json &ov = tmp["old_val"]; json &nv = tmp["new_val"]; - if (ov.is_object()||nv.is_object()) { - //if (nv.is_object()) printf("NETWORK: %s" ZT_EOL_S,nv.dump().c_str()); - this->_networkChanged(ov,nv,(this->_ready <= 0)); - } + json oldConfig,newConfig; + if (ov.is_object()) oldConfig = ov["config"]; + if (nv.is_object()) newConfig = nv["config"]; + if (oldConfig.is_object()||newConfig.is_object()) + this->_networkChanged(oldConfig,newConfig,(this->_ready <= 0)); } catch ( ... ) {} // ignore bad records } } -- cgit v1.2.3