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/DB.cpp | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 controller/DB.cpp (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp new file mode 100644 index 00000000..4a5688e3 --- /dev/null +++ b/controller/DB.cpp @@ -0,0 +1,314 @@ +/* + * 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 "DB.hpp" +#include "EmbeddedNetworkController.hpp" + +#include +#include +#include + +using json = nlohmann::json; + +namespace ZeroTier { + +DB::DB(EmbeddedNetworkController *const nc,const Address &myAddress,const char *path) : + _controller(nc), + _myAddress(myAddress), + _path((path) ? path : "") +{ + { + char tmp[32]; + _myAddress.toString(tmp); + _myAddressStr = tmp; + } +} + +DB::~DB() +{ +} + +bool DB::get(const uint64_t networkId,nlohmann::json &network) +{ + waitForReady(); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + network = nw->config; + } + return true; +} + +bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member) +{ + waitForReady(); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + network = nw->config; + auto m = nw->members.find(memberId); + if (m == nw->members.end()) + return false; + member = m->second; + } + return true; +} + +bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info) +{ + waitForReady(); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + network = nw->config; + _fillSummaryInfo(nw,info); + auto m = nw->members.find(memberId); + if (m == nw->members.end()) + return false; + member = m->second; + } + return true; +} + +bool DB::get(const uint64_t networkId,nlohmann::json &network,std::vector &members) +{ + waitForReady(); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + network = nw->config; + for(auto m=nw->members.begin();m!=nw->members.end();++m) + members.push_back(m->second); + } + return true; +} + +bool DB::summary(const uint64_t networkId,NetworkSummaryInfo &info) +{ + waitForReady(); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + _fillSummaryInfo(nw,info); + } + return true; +} + +void DB::networks(std::vector &networks) +{ + waitForReady(); + std::lock_guard l(_networks_l); + networks.reserve(_networks.size() + 1); + for(auto n=_networks.begin();n!=_networks.end();++n) + networks.push_back(n->first); +} + +void DB::_memberChanged(nlohmann::json &old,nlohmann::json &member,bool push) +{ + uint64_t memberId = 0; + uint64_t networkId = 0; + bool isAuth = false; + bool wasAuth = false; + std::shared_ptr<_Network> nw; + + if (old.is_object()) { + json &config = old["config"]; + if (config.is_object()) { + memberId = OSUtils::jsonIntHex(config["id"],0ULL); + networkId = OSUtils::jsonIntHex(config["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(config["activeBridge"],false)) + nw->activeBridgeMembers.erase(memberId); + wasAuth = OSUtils::jsonBool(config["authorized"],false); + if (wasAuth) + nw->authorizedMembers.erase(memberId); + json &ips = config["ipAssignments"]; + if (ips.is_array()) { + for(unsigned long i=0;iallocatedIps.erase(ipa); + } + } + } + } + } + } + } + + if (member.is_object()) { + json &config = member["config"]; + if (config.is_object()) { + if (!nw) { + memberId = OSUtils::jsonIntHex(config["id"],0ULL); + networkId = OSUtils::jsonIntHex(config["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] = config; + + if (OSUtils::jsonBool(config["activeBridge"],false)) + nw->activeBridgeMembers.insert(memberId); + isAuth = OSUtils::jsonBool(config["authorized"],false); + if (isAuth) + nw->authorizedMembers.insert(memberId); + json &ips = config["ipAssignments"]; + if (ips.is_array()) { + for(unsigned long i=0;iallocatedIps.insert(ipa); + } + } + } + + if (!isAuth) { + const int64_t ldt = (int64_t)OSUtils::jsonInt(config["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 ((push)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId))) + _controller->onNetworkMemberDeauthorize(networkId,memberId); +} + +void DB::_networkChanged(nlohmann::json &old,nlohmann::json &network,bool push) +{ + if (network.is_object()) { + json &config = network["config"]; + if (config.is_object()) { + const std::string ids = config["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 = config; + } + 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); + } + } +} + +void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info) +{ + for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab) + info.activeBridges.push_back(Address(*ab)); + for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip) + info.allocatedIps.push_back(*ip); + info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size(); + info.totalMemberCount = (unsigned long)nw->members.size(); + info.mostRecentDeauthTime = nw->mostRecentDeauthTime; +} + +} // namespace ZeroTier -- cgit v1.2.3 From 9f85371073f488cde73d9ab7c840fc15ff54399d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Nov 2017 15:23:16 -0800 Subject: cleanup --- controller/DB.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 4a5688e3..2f9a4a89 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -32,11 +32,9 @@ DB::DB(EmbeddedNetworkController *const nc,const Address &myAddress,const char * _myAddress(myAddress), _path((path) ? path : "") { - { - char tmp[32]; - _myAddress.toString(tmp); - _myAddressStr = tmp; - } + char tmp[32]; + _myAddress.toString(tmp); + _myAddressStr = tmp; } DB::~DB() -- 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/DB.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 9bab49d2f91fbb1d19c75e868bc04959ef9f135e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 9 Jan 2018 12:39:25 -0800 Subject: Move DB stuff into Db. --- controller/DB.cpp | 70 ++++++++++++++++++++++++++++++++ controller/DB.hpp | 20 +++++++++ controller/EmbeddedNetworkController.cpp | 14 +++---- controller/EmbeddedNetworkController.hpp | 67 ------------------------------ 4 files changed, 97 insertions(+), 74 deletions(-) (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 2f09205b..688fede2 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -27,6 +27,76 @@ using json = nlohmann::json; namespace ZeroTier { +void DB::initNetwork(nlohmann::json &network) +{ + if (!network.count("private")) network["private"] = true; + if (!network.count("creationTime")) network["creationTime"] = OSUtils::now(); + if (!network.count("name")) network["name"] = ""; + if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32; + if (!network.count("enableBroadcast")) network["enableBroadcast"] = true; + if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}}; + if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}}; + if (!network.count("authTokens")) network["authTokens"] = {{}}; + if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array(); + 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("mtu")) network["mtu"] = ZT_DEFAULT_MTU; + if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); + if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0; + if (!network.count("rules")) { + // If unspecified, rules are set to allow anything and behave like a flat L2 segment + network["rules"] = {{ + { "not",false }, + { "or", false }, + { "type","ACTION_ACCEPT" } + }}; + } + network["objtype"] = "network"; +} + +void DB::initMember(nlohmann::json &member) +{ + if (!member.count("authorized")) member["authorized"] = false; + if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array(); + if (!member.count("activeBridge")) member["activeBridge"] = false; + if (!member.count("tags")) member["tags"] = nlohmann::json::array(); + if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array(); + if (!member.count("creationTime")) member["creationTime"] = OSUtils::now(); + if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false; + if (!member.count("revision")) member["revision"] = 0ULL; + if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL; + if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL; + if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json(); + if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json(); + if (!member.count("vMajor")) member["vMajor"] = -1; + if (!member.count("vMinor")) member["vMinor"] = -1; + if (!member.count("vRev")) member["vRev"] = -1; + if (!member.count("vProto")) member["vProto"] = -1; + if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json(); + if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0; + member["objtype"] = "member"; +} + +void DB::cleanNetwork(nlohmann::json &network) +{ + network.erase("clock"); + network.erase("authorizedMemberCount"); + network.erase("activeMemberCount"); + network.erase("totalMemberCount"); + network.erase("lastModified"); +} + +void DB::cleanMember(nlohmann::json &member) +{ + member.erase("clock"); + member.erase("physicalAddr"); + member.erase("recentLog"); + member.erase("lastModified"); + member.erase("lastRequestMetaData"); +} + DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) : _controller(nc), _myId(myId), diff --git a/controller/DB.hpp b/controller/DB.hpp index 4c7a16b2..abd1483e 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -58,6 +58,26 @@ public: int64_t mostRecentDeauthTime; }; + /** + * Ensure that all network fields are present + */ + static void initNetwork(nlohmann::json &network); + + /** + * Ensure that all member fields are present + */ + static void initMember(nlohmann::json &member); + + /** + * Remove old and temporary network fields + */ + static void cleanNetwork(nlohmann::json &network); + + /** + * Remove old and temporary member fields + */ + static void cleanMember(nlohmann::json &member); + DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path); virtual ~DB(); diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 2032f097..37eff0a4 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -648,7 +648,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json member,network; _db->get(nwid,network,address,member); json origMember(member); // for detecting changes - _initMember(member); + DB::initMember(member); try { if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false); @@ -734,7 +734,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( member["address"] = addrs; // legacy member["nwid"] = nwids; - _cleanMember(member); + DB::cleanMember(member); _db->save(&origMember,member); responseBody = OSUtils::jsonDump(member); responseContentType = "application/json"; @@ -767,7 +767,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json network; _db->get(nwid,network); json origNetwork(network); // for detecting changes - _initNetwork(network); + DB::initNetwork(network); try { if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],""); @@ -981,7 +981,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( network["id"] = nwids; network["nwid"] = nwids; // legacy - _cleanNetwork(network); + DB::cleanNetwork(network); _db->save(&origNetwork,network); responseBody = OSUtils::jsonDump(network); @@ -1183,7 +1183,7 @@ void EmbeddedNetworkController::_request( } origMember = member; const bool newMember = ((!member.is_object())||(member.size() == 0)); - _initMember(member); + DB::initMember(member); { const std::string haveIdStr(OSUtils::jsonString(member["identity"],"")); @@ -1281,7 +1281,7 @@ void EmbeddedNetworkController::_request( } } else { // If they are not authorized, STOP! - _cleanMember(member); + DB::cleanMember(member); _db->save(&origMember,member); _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; @@ -1646,7 +1646,7 @@ void EmbeddedNetworkController::_request( return; } - _cleanMember(member); + DB::cleanMember(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/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 5864100d..e9b4764a 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -105,73 +105,6 @@ private: void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary &metaData); void _startThreads(); - // These init objects with default and static/informational fields - inline void _initMember(nlohmann::json &member) - { - if (!member.count("authorized")) member["authorized"] = false; - if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array(); - if (!member.count("activeBridge")) member["activeBridge"] = false; - if (!member.count("tags")) member["tags"] = nlohmann::json::array(); - if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array(); - if (!member.count("creationTime")) member["creationTime"] = OSUtils::now(); - if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false; - if (!member.count("revision")) member["revision"] = 0ULL; - if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL; - if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL; - if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json(); - if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json(); - if (!member.count("vMajor")) member["vMajor"] = -1; - if (!member.count("vMinor")) member["vMinor"] = -1; - if (!member.count("vRev")) member["vRev"] = -1; - if (!member.count("vProto")) member["vProto"] = -1; - if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json(); - if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0; - member["objtype"] = "member"; - } - inline void _initNetwork(nlohmann::json &network) - { - if (!network.count("private")) network["private"] = true; - if (!network.count("creationTime")) network["creationTime"] = OSUtils::now(); - if (!network.count("name")) network["name"] = ""; - if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32; - if (!network.count("enableBroadcast")) network["enableBroadcast"] = true; - if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}}; - if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}}; - if (!network.count("authTokens")) network["authTokens"] = {{}}; - if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array(); - 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("mtu")) network["mtu"] = ZT_DEFAULT_MTU; - if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); - if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0; - if (!network.count("rules")) { - // If unspecified, rules are set to allow anything and behave like a flat L2 segment - network["rules"] = {{ - { "not",false }, - { "or", false }, - { "type","ACTION_ACCEPT" } - }}; - } - network["objtype"] = "network"; - } - inline void _cleanNetwork(nlohmann::json &network) - { - network.erase("clock"); - network.erase("authorizedMemberCount"); - network.erase("activeMemberCount"); - network.erase("totalMemberCount"); - network.erase("lastModified"); - } - inline void _cleanMember(nlohmann::json &member) - { - member.erase("clock"); - member.erase("physicalAddr"); - member.erase("recentLog"); - member.erase("lastModified"); - member.erase("lastRequestMetaData"); - } - struct _RQEntry { uint64_t nwid; -- 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/DB.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 4e689998f9b9e2c1de9000c8ca7bfa519f5ceb77 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 12 Jan 2018 10:38:19 -0800 Subject: Sanity checks on array sizes and fix a bug in IPv4 auto-assign. --- controller/DB.cpp | 2 + controller/EmbeddedNetworkController.cpp | 115 ++++++++++++++++++++----------- controller/RethinkDB.cpp | 11 +-- controller/RethinkDB.hpp | 2 +- 4 files changed, 82 insertions(+), 48 deletions(-) (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 7b792eb4..70472e57 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -373,8 +373,10 @@ void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo { for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab) info.activeBridges.push_back(Address(*ab)); + std::sort(info.activeBridges.begin(),info.activeBridges.end()); for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip) info.allocatedIps.push_back(*ip); + std::sort(info.allocatedIps.begin(),info.allocatedIps.end()); info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size(); info.totalMemberCount = (unsigned long)nw->members.size(); info.mostRecentDeauthTime = nw->mostRecentDeauthTime; diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index e9cb41fc..3945c8c7 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -53,6 +53,9 @@ using json = nlohmann::json; // Min duration between requests for an address/nwid combo to prevent floods #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000 +// Global maximum size of arrays in JSON objects +#define ZT_CONTROLLER_MAX_ARRAY_SIZE 16384 + namespace ZeroTier { namespace { @@ -686,6 +689,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { char tmpip[64]; mipa.push_back(ip.toIpString(tmpip)); + if (mipa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; } } member["ipAssignments"] = mipa; @@ -707,6 +712,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( ta.push_back(t->first); ta.push_back(t->second); mtagsa.push_back(ta); + if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; } member["tags"] = mtagsa; } @@ -718,6 +725,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json mcaps = json::array(); for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; } std::sort(mcaps.begin(),mcaps.end()); mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end()); @@ -850,6 +859,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( tmp["via"] = v.toIpString(tmp2); else tmp["via"] = json(); nrts.push_back(tmp); + if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; } } } @@ -873,6 +884,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( tmp["ipRangeStart"] = f.toIpString(tmp2); tmp["ipRangeEnd"] = t.toIpString(tmp2); nipp.push_back(tmp); + if (nipp.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; } } } @@ -888,8 +901,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &rule = rules[i]; if (rule.is_object()) { ZT_VirtualNetworkRule ztr; - if (_parseRule(rule,ztr)) + if (_parseRule(rule,ztr)) { nrules.push_back(_renderRule(ztr)); + if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } } } network["rules"] = nrules; @@ -929,8 +945,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &rule = rules[i]; if (rule.is_object()) { ZT_VirtualNetworkRule ztr; - if (_parseRule(rule,ztr)) + if (_parseRule(rule,ztr)) { nrules.push_back(_renderRule(ztr)); + if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } } } } @@ -941,8 +960,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } json ncapsa = json::array(); - for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) + for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) { ncapsa.push_back(c->second); + if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } network["capabilities"] = ncapsa; } } @@ -966,8 +988,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } json ntagsa = json::array(); - for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t) + for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t) { ntagsa.push_back(t->second); + if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } network["tags"] = ntagsa; } } @@ -1304,7 +1329,7 @@ void EmbeddedNetworkController::_request( } } - std::auto_ptr nc(new NetworkConfig()); + std::unique_ptr nc(new NetworkConfig()); nc->networkId = nwid; nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; @@ -1483,29 +1508,29 @@ void EmbeddedNetworkController::_request( json ipAssignments = member["ipAssignments"]; // we want to make a copy if (ipAssignments.is_array()) { for(unsigned long i=0;irouteCount;++rk) { - if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip)) ) - routedNetmaskBits = reinterpret_cast(&(nc->routes[rk].target))->netmaskBits(); - } + if (ipAssignments[i].is_string()) { + const std::string ips = ipAssignments[i]; + InetAddress ip(ips.c_str()); + + // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from + // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source + // of user error / poor UX. + int routedNetmaskBits = -1; + for(unsigned int rk=0;rkrouteCount;++rk) { + if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip)) ) + routedNetmaskBits = reinterpret_cast(&(nc->routes[rk].target))->netmaskBits(); + } - if (routedNetmaskBits > 0) { - if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) { - ip.setPort(routedNetmaskBits); - nc->staticIps[nc->staticIpCount++] = ip; + if (routedNetmaskBits >= 0) { + if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) { + ip.setPort(routedNetmaskBits); + nc->staticIps[nc->staticIpCount++] = ip; + } + if (ip.ss_family == AF_INET) + haveManagedIpv4AutoAssignment = true; + else if (ip.ss_family == AF_INET6) + haveManagedIpv6AutoAssignment = true; } - if (ip.ss_family == AF_INET) - haveManagedIpv4AutoAssignment = true; - else if (ip.ss_family == AF_INET6) - haveManagedIpv6AutoAssignment = true; } } } else { @@ -1559,13 +1584,16 @@ void EmbeddedNetworkController::_request( // If it's routed, then try to claim and assign it and if successful end loop if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) { char tmpip[64]; - ipAssignments.push_back(ip6.toIpString(tmpip)); - member["ipAssignments"] = ipAssignments; - ip6.setPort((unsigned int)routedNetmaskBits); - if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) - nc->staticIps[nc->staticIpCount++] = ip6; - haveManagedIpv6AutoAssignment = true; - break; + const std::string ipStr(ip6.toIpString(tmpip)); + if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) { + ipAssignments.push_back(ipStr); + member["ipAssignments"] = ipAssignments; + ip6.setPort((unsigned int)routedNetmaskBits); + if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) + nc->staticIps[nc->staticIpCount++] = ip6; + haveManagedIpv6AutoAssignment = true; + break; + } } } } @@ -1612,16 +1640,19 @@ void EmbeddedNetworkController::_request( const InetAddress ip4(Utils::hton(ip),0); if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) { char tmpip[64]; - ipAssignments.push_back(ip4.toIpString(tmpip)); - member["ipAssignments"] = ipAssignments; - if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) { - struct sockaddr_in *const v4ip = reinterpret_cast(&(nc->staticIps[nc->staticIpCount++])); - v4ip->sin_family = AF_INET; - v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits); - v4ip->sin_addr.s_addr = Utils::hton(ip); + const std::string ipStr(ip4.toIpString(tmpip)); + if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) { + ipAssignments.push_back(ipStr); + member["ipAssignments"] = ipAssignments; + if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) { + struct sockaddr_in *const v4ip = reinterpret_cast(&(nc->staticIps[nc->staticIpCount++])); + v4ip->sin_family = AF_INET; + v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits); + v4ip->sin_addr.s_addr = Utils::hton(ip); + } + haveManagedIpv4AutoAssignment = true; + break; } - haveManagedIpv4AutoAssignment = true; - break; } } } diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp index 3f730efc..a3bdfbc9 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.cpp @@ -212,6 +212,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co delete config; if (!table) continue; + const std::string jdump(OSUtils::jsonDump(record,-1)); while (_run == 1) { try { @@ -223,7 +224,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb); } else { //printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str()); - R::db(this->_db).table(table).insert(R::Datum::from_json(OSUtils::jsonDump(record,-1)),R::optargs("conflict","update","return_changes",false)).run(*rdb); + R::db(this->_db).table(table).insert(R::Datum::from_json(jdump),R::optargs("conflict","update","return_changes",false)).run(*rdb); } break; } else { @@ -231,13 +232,13 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co rdb.reset(); } } catch (std::exception &e) { - fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what()); + fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what(),jdump.c_str()); rdb.reset(); } catch (R::Error &e) { - fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str()); + fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str(),jdump.c_str()); rdb.reset(); } catch ( ... ) { - fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt()); + fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),jdump.c_str()); rdb.reset(); } std::this_thread::sleep_for(std::chrono::milliseconds(250)); @@ -280,7 +281,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co tmpobj["ts"] = i->second.first; tmpobj["phy"] = i->second.second.toIpString(tmp2); batch.emplace_back(tmpobj); - if (batch.size() >= 256) { + if (batch.size() >= 1024) { R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb); batch.clear(); } diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp index ab2611c0..01b46a47 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -23,7 +23,7 @@ #include "DB.hpp" -#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 8 +#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 4 namespace ZeroTier { -- 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/DB.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 From 574b24c0826cea575b77f36aa0238d7a26aeac7b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Mar 2018 22:41:42 -0800 Subject: docs --- controller/DB.cpp | 2 +- controller/DB.hpp | 2 +- controller/EmbeddedNetworkController.cpp | 2 +- controller/EmbeddedNetworkController.hpp | 2 +- controller/FileDB.hpp | 2 +- controller/RethinkDB.cpp | 2 +- controller/RethinkDB.hpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 680b4120..64311be7 100644 --- a/controller/DB.cpp +++ b/controller/DB.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/DB.hpp b/controller/DB.hpp index 86626009..4757bb40 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -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/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index f88f8cff..9a07b285 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.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/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 1dda9f45..417005a4 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -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/FileDB.hpp b/controller/FileDB.hpp index b02da8cb..1e275a36 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -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 279d6ec2..f6c8a59c 100644 --- a/controller/RethinkDB.cpp +++ b/controller/RethinkDB.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.hpp b/controller/RethinkDB.hpp index 01b46a47..b1049ac3 100644 --- a/controller/RethinkDB.hpp +++ b/controller/RethinkDB.hpp @@ -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 -- cgit v1.2.3 From bbdb2aa67252372ce7fc72c86415a93bba69ffb9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 8 Mar 2018 23:53:57 -0800 Subject: Some work on IPv4 enabled ad-hoc networks. --- controller/DB.cpp | 1 - node/Network.cpp | 146 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 51 deletions(-) (limited to 'controller/DB.cpp') diff --git a/controller/DB.cpp b/controller/DB.cpp index 64311be7..b2e8878a 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -41,7 +41,6 @@ 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("mtu")) network["mtu"] = ZT_DEFAULT_MTU; if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0; diff --git a/node/Network.cpp b/node/Network.cpp index c12df6c1..d6f66e65 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1072,15 +1072,89 @@ void Network::requestConfiguration(void *tPtr) if (_destroyed) return; - /* ZeroTier addresses can't begin with 0xff, so this is used to mark controllerless - * network IDs. Controllerless network IDs only support unicast IPv6 using the 6plane - * addressing scheme and have the following format: 0xffSSSSEEEE000000 where SSSS - * is the 16-bit starting IP port range allowed and EEEE is the 16-bit ending IP port - * range allowed. Remaining digits are reserved for future use and must be zero. */ if ((_id >> 56) == 0xff) { - const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff); - const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff); - if (((_id & 0xffffff) == 0)&&(endPortRange >= startPortRange)) { + if ((_id & 0xffffff) == 0) { + const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff); + const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff); + if (endPortRange >= startPortRange) { + NetworkConfig *const nconf = new NetworkConfig(); + + nconf->networkId = _id; + nconf->timestamp = RR->node->now(); + nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + nconf->revision = 1; + nconf->issuedTo = RR->identity.address(); + nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; + nconf->mtu = ZT_DEFAULT_MTU; + nconf->multicastLimit = 0; + nconf->staticIpCount = 1; + nconf->ruleCount = 14; + nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt()); + + // Drop everything but IPv6 + nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT + nconf->rules[0].v.etherType = 0x86dd; // IPv6 + nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP; + + // Allow ICMPv6 + nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; + nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6 + nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; + + // Allow destination ports within range + nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; + nconf->rules[4].v.ipProtocol = 0x11; // UDP + nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR + nconf->rules[5].v.ipProtocol = 0x06; // TCP + nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE; + nconf->rules[6].v.port[0] = startPortRange; + nconf->rules[6].v.port[1] = endPortRange; + nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; + + // Allow non-SYN TCP packets to permit non-connection-initiating traffic + nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT + nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN; + nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; + + // Also allow SYN+ACK which are replies to SYN + nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; + nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN; + nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; + nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK; + nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; + + nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP; + + nconf->type = ZT_NETWORK_TYPE_PUBLIC; + + nconf->name[0] = 'a'; + nconf->name[1] = 'd'; + nconf->name[2] = 'h'; + nconf->name[3] = 'o'; + nconf->name[4] = 'c'; + nconf->name[5] = '-'; + Utils::hex((uint16_t)startPortRange,nconf->name + 6); + nconf->name[10] = '-'; + Utils::hex((uint16_t)endPortRange,nconf->name + 11); + nconf->name[15] = (char)0; + + this->setConfiguration(tPtr,*nconf,false); + delete nconf; + } else { + this->setNotFound(); + } + } else if ((_id & 0xff) == 0x01) { + // ffAA__________01 + const uint64_t myAddress = RR->identity.address().toInt(); + uint8_t ipv4[4]; + ipv4[0] = (uint8_t)((_id >> 48) & 0xff); + ipv4[1] = (uint8_t)((myAddress >> 16) & 0xff); + ipv4[2] = (uint8_t)((myAddress >> 8) & 0xff); + ipv4[3] = (uint8_t)(myAddress & 0xff); + + char v4ascii[24]; + Utils::decimal(ipv4[0],v4ascii); + NetworkConfig *const nconf = new NetworkConfig(); nconf->networkId = _id; @@ -1091,43 +1165,12 @@ void Network::requestConfiguration(void *tPtr) nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; nconf->mtu = ZT_DEFAULT_MTU; nconf->multicastLimit = 0; - nconf->staticIpCount = 1; + nconf->staticIpCount = 2; nconf->ruleCount = 14; - nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt()); - - // Drop everything but IPv6 - nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT - nconf->rules[0].v.etherType = 0x86dd; // IPv6 - nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP; - - // Allow ICMPv6 - nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; - nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6 - nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - - // Allow destination ports within range - nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; - nconf->rules[4].v.ipProtocol = 0x11; // UDP - nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR - nconf->rules[5].v.ipProtocol = 0x06; // TCP - nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE; - nconf->rules[6].v.port[0] = startPortRange; - nconf->rules[6].v.port[1] = endPortRange; - nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - - // Allow non-SYN TCP packets to permit non-connection-initiating traffic - nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT - nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN; - nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - - // Also allow SYN+ACK which are replies to SYN - nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; - nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN; - nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; - nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK; - nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - - nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP; + nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,myAddress); + nconf->staticIps[1].set(ipv4,4,8); + + nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; nconf->type = ZT_NETWORK_TYPE_PUBLIC; @@ -1137,15 +1180,18 @@ void Network::requestConfiguration(void *tPtr) nconf->name[3] = 'o'; nconf->name[4] = 'c'; nconf->name[5] = '-'; - Utils::hex((uint16_t)startPortRange,nconf->name + 6); - nconf->name[10] = '-'; - Utils::hex((uint16_t)endPortRange,nconf->name + 11); - nconf->name[15] = (char)0; + unsigned long nn = 6; + while ((nconf->name[nn] = v4ascii[nn - 6])) ++nn; + nconf->name[nn++] = '.'; + nconf->name[nn++] = '0'; + nconf->name[nn++] = '.'; + nconf->name[nn++] = '0'; + nconf->name[nn++] = '.'; + nconf->name[nn++] = '0'; + nconf->name[nn++] = (char)0; this->setConfiguration(tPtr,*nconf,false); delete nconf; - } else { - this->setNotFound(); } return; } -- cgit v1.2.3