From 4868d21526d7cbced5786dc9359a6ded4200e284 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 7 Nov 2016 23:49:03 +0000 Subject: Bug fixes in controller refactor. --- controller/EmbeddedNetworkController.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 9c7611a4..d60bde98 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1073,10 +1073,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( responseContentType = "application/json"; return 400; } - } catch (std::exception &exc) { - responseBody = std::string("{ \"message\": \"body JSON is invalid: ") + exc.what() + "\" }"; - responseContentType = "application/json"; - return 400; } catch ( ... ) { responseBody = "{ \"message\": \"body JSON is invalid\" }"; responseContentType = "application/json"; @@ -1092,13 +1088,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); if (path.size() >= 3) { - json network; - { - Mutex::Lock _l(_db_m); - network = _db.get("network",nwids,0); - } - if (!network.size()) - return 404; if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { uint64_t address = Utils::hexStrToU64(path[3].c_str()); @@ -1110,8 +1099,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( Mutex::Lock _l(_db_m); member = _db.get("network",nwids,"member",Address(address).toString(),0); } - if (!member.size()) - return 404; _initMember(member); try { @@ -1283,8 +1270,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } network = _db.get("network",nwids,0); - if (!network.size()) - return 404; } _initNetwork(network); -- cgit v1.2.3 From 360c84e0351728b0c0479ceddba924997acb46e4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 8 Nov 2016 00:05:18 +0000 Subject: Minor fixes. --- controller/EmbeddedNetworkController.cpp | 6 ++++-- controller/JSONDB.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index d60bde98..85717e28 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1508,20 +1508,22 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( Mutex::Lock _l(_db_m); json member = _db.get("network",nwids,"member",Address(address).toString(),0); - if (!member.size()) - return 404; _db.erase("network",nwids,"member",Address(address).toString()); + if (!member.size()) + return 404; responseBody = member.dump(2); responseContentType = "application/json"; return 200; } } else { Mutex::Lock _l(_db_m); + std::string pfx("network/"); pfx.append(nwids); _db.filter(pfx,120000,[](const std::string &n,const json &obj) { return false; // delete }); + responseBody = network.dump(2); responseContentType = "application/json"; return 200; diff --git a/controller/JSONDB.hpp b/controller/JSONDB.hpp index a9a5e6ac..bd1ae5a5 100644 --- a/controller/JSONDB.hpp +++ b/controller/JSONDB.hpp @@ -79,7 +79,7 @@ public: { for(std::map::iterator i(_db.lower_bound(prefix));i!=_db.end();) { if ((i->first.length() >= prefix.length())&&(!memcmp(i->first.data(),prefix.data(),prefix.length()))) { - if (!func(i->first,get(i->second.obj,maxSinceCheck))) { + if (!func(i->first,get(i->first,maxSinceCheck))) { std::map::iterator i2(i); ++i2; this->erase(i->first); i = i2; -- cgit v1.2.3 From 8e76363ccf2cdc05b691337d07291cd27107e468 Mon Sep 17 00:00:00 2001 From: Tsukasa Hiiragi Date: Tue, 8 Nov 2016 16:50:32 +0200 Subject: Fix chown on /var/lib/zerotier-one --- osdep/LinuxDropPrivileges.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osdep/LinuxDropPrivileges.cpp b/osdep/LinuxDropPrivileges.cpp index dab85bd8..e2688e65 100644 --- a/osdep/LinuxDropPrivileges.cpp +++ b/osdep/LinuxDropPrivileges.cpp @@ -102,6 +102,8 @@ void dropPrivileges(std::string homeDir) { return; } + createOwnedHomedir(homeDir, targetUser); + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) { // Kernel has no support for ambient capabilities. notDropping(homeDir); @@ -113,8 +115,6 @@ void dropPrivileges(std::string homeDir) { return; } - createOwnedHomedir(homeDir, targetUser); - if (setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID)) < 0) { fprintf(stderr, "ERROR: failed to set capabilities (not running as real root?)\n"); exit(1); -- cgit v1.2.3 From 4f8feaa530b91f8009fcdfdc1d8d7fb06dc4e79f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 8 Nov 2016 10:23:25 -0800 Subject: update JSON API docs for OneService --- service/README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/service/README.md b/service/README.md index 75c437dd..8fe6a3af 100644 --- a/service/README.md +++ b/service/README.md @@ -25,6 +25,8 @@ A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP r FieldTypeDescriptionWritable addressstring10-digit hexadecimal ZeroTier address of this nodeno publicIdentitystringFull public ZeroTier identity of this nodeno +worldIdintegerFixed value representing the virtual data center of Earth.no +worldTimestampintegerTimestamp of the last root server topology change.no onlinebooleanDoes this node appear to have upstream network access?no tcpFallbackActivebooleanIs TCP fallback mode active?no versionMajorintegerZeroTier major versionno @@ -77,11 +79,21 @@ Most network settings are not writable, as they are defined by the network contr broadcastEnabledbooleanIs Ethernet broadcast (ff:ff:ff:ff:ff:ff) allowed?no portErrorintegerError code (if any) returned by underlying OS "tap" driverno netconfRevisionintegerNetwork configuration revision IDno -multicastSubscriptions[string]Multicast memberships as array of MAC/ADI tuplesno assignedAddresses[string]ZeroTier-managed IP address assignments as array of IP/netmask bits tuplesno +routes[route]ZeroTier-managed route assignments for a network. See below for a description of the route object.no portDeviceNamestringOS-specific network device name (if available)no +`route` objects + + + + + + + +
FieldTypeDescriptionWritable
targetstringTarget network / netmask bits, NULL, or 0.0.0.0/0 for default routeno
viastringGateway IP addressno
flagsintegerRoute flagsno
metricintegerRoute metric (not currently used)no
+ #### /peer * Purpose: Get all peers -- cgit v1.2.3 From 00e1b0ed1028e0da3ec4c56cacf72e99e91452db Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 8 Nov 2016 11:00:48 -0800 Subject: added docs for allowManaged, allowGlobal, allowDefault --- service/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/README.md b/service/README.md index 8fe6a3af..1c71fbdc 100644 --- a/service/README.md +++ b/service/README.md @@ -82,6 +82,9 @@ Most network settings are not writable, as they are defined by the network contr assignedAddresses[string]ZeroTier-managed IP address assignments as array of IP/netmask bits tuplesno routes[route]ZeroTier-managed route assignments for a network. See below for a description of the route object.no portDeviceNamestringOS-specific network device name (if available)no +allowManagedbooleanWhether ZeroTier-managed IP addresses are allowed.yes +allowGlobalbooleanWhether globally-reachable IP addresses are allowed to be assigned.yes +allowDefaultbooleanWhether a default route is allowed to be assigned for the network (route all traffic via ZeroTier)yes `route` objects -- cgit v1.2.3 From 4524899e4df849ab661a28676e5d0c44d239aa9f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 8 Nov 2016 12:41:27 -0800 Subject: Update LM time on members on request. --- controller/EmbeddedNetworkController.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 85717e28..c7114758 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -622,6 +622,9 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( member["recentLog"] = recentLog; } + // Update last modified time + member["lastModified"] = now; + // If they are not authorized, STOP! if (!authorizedBy) { Mutex::Lock _l(_db_m); -- cgit v1.2.3 From 3d948a930e935f126ab661c63e698283ff937380 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 8 Nov 2016 14:24:30 -0800 Subject: Send a blanket rule to old versions. New versions will still bidirecitonally enforce on the inbound side. --- controller/EmbeddedNetworkController.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index c7114758..2871df9b 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -673,12 +673,20 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( json &memberCapabilities = member["capabilities"]; json &memberTags = member["tags"]; - if (rules.is_array()) { - for(unsigned long i=0;i= ZT_MAX_NETWORK_RULES) - break; - if (_parseRule(rules[i],nc.rules[nc.ruleCount])) - ++nc.ruleCount; + if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) { + // Old versions with no rules engine support get an allow everything rule. + // Since rules are enforced bidirectionally, newer versions *will* still + // enforce rules on the inbound side. + nc.ruleCount = 1; + nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT; + } else { + if (rules.is_array()) { + for(unsigned long i=0;i= ZT_MAX_NETWORK_RULES) + break; + if (_parseRule(rules[i],nc.rules[nc.ruleCount])) + ++nc.ruleCount; + } } } -- cgit v1.2.3 From 1ebfca666d19dbc7def22c4a0f4e8071a3977357 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Nov 2016 12:34:20 -0800 Subject: Memo-ize some computed stuff to control CPU utilization. --- controller/EmbeddedNetworkController.cpp | 80 ++++++++++++++++++++------------ controller/EmbeddedNetworkController.hpp | 3 ++ 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 2871df9b..f91ba533 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1535,6 +1535,9 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( return false; // delete }); + Mutex::Lock _l2(_nmiCache_m); + _nmiCache.erase(nwid); + responseBody = network.dump(2); responseContentType = "application/json"; return 200; @@ -1614,43 +1617,60 @@ void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid char pfx[256]; Utils::snprintf(pfx,sizeof(pfx),"network/%.16llx/member",nwid); - Mutex::Lock _l(_db_m); - _db.filter(pfx,120000,[&nmi,&now](const std::string &n,const json &member) { - try { - if (_jB(member["authorized"],false)) { - ++nmi.authorizedMemberCount; - - if (member.count("recentLog")) { - const json &mlog = member["recentLog"]; - if ((mlog.is_array())&&(mlog.size() > 0)) { - const json &mlog1 = mlog[0]; - if (mlog1.is_object()) { - if ((now - _jI(mlog1["ts"],0ULL)) < ZT_NETCONF_NODE_ACTIVE_THRESHOLD) - ++nmi.activeMemberCount; + { + Mutex::Lock _l(_nmiCache_m); + std::map::iterator c(_nmiCache.find(nwid)); + if ((c != _nmiCache.end())&&((now - c->second.nmiTimestamp) < 1000)) { // a short duration cache but limits CPU use on big networks + nmi = c->second; + return; + } + } + + { + Mutex::Lock _l(_db_m); + _db.filter(pfx,120000,[&nmi,&now](const std::string &n,const json &member) { + try { + if (_jB(member["authorized"],false)) { + ++nmi.authorizedMemberCount; + + if (member.count("recentLog")) { + const json &mlog = member["recentLog"]; + if ((mlog.is_array())&&(mlog.size() > 0)) { + const json &mlog1 = mlog[0]; + if (mlog1.is_object()) { + if ((now - _jI(mlog1["ts"],0ULL)) < ZT_NETCONF_NODE_ACTIVE_THRESHOLD) + ++nmi.activeMemberCount; + } } } - } - if (_jB(member["activeBridge"],false)) { - nmi.activeBridges.insert(_jS(member["id"],"0000000000")); - } + if (_jB(member["activeBridge"],false)) { + nmi.activeBridges.insert(_jS(member["id"],"0000000000")); + } - if (member.count("ipAssignments")) { - const json &mips = member["ipAssignments"]; - if (mips.is_array()) { - for(unsigned long i=0;i _nmiCache; + Mutex _nmiCache_m; void _getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi); // These init objects with default and static/informational fields -- cgit v1.2.3 From eea712a1ae80994c32f250537ce14e52071312fb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Nov 2016 13:26:14 -0800 Subject: Field in wrong place fixed. --- controller/EmbeddedNetworkController.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 2a66c530..53d3be0f 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -115,7 +115,6 @@ private: 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("enableBroadcast")) member["enableBroadcast"] = true; member["objtype"] = "member"; } inline void _initNetwork(nlohmann::json &network) @@ -124,6 +123,7 @@ private: 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"] = nlohmann::json::array(); -- cgit v1.2.3 From c61ca1dea2001d8b77bf6b1f44da4246c3088e32 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Nov 2016 16:04:08 -0800 Subject: Keep connections up for netconf stuff as well as frames. --- include/ZeroTierOne.h | 10 ---------- node/Node.cpp | 2 -- node/Peer.cpp | 17 +++++++++++------ node/Peer.hpp | 20 ++------------------ service/ControlPlane.cpp | 4 ---- service/README.md | 2 -- 6 files changed, 13 insertions(+), 42 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 17112e90..d0fef1f1 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1018,16 +1018,6 @@ typedef struct */ uint64_t address; - /** - * Time we last received a unicast frame from this peer - */ - uint64_t lastUnicastFrame; - - /** - * Time we last received a multicast rame from this peer - */ - uint64_t lastMulticastFrame; - /** * Remote major version or -1 if not known */ diff --git a/node/Node.cpp b/node/Node.cpp index db9b8ea0..9314478f 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -405,8 +405,6 @@ ZT_PeerList *Node::peers() const for(std::vector< std::pair< Address,SharedPtr > >::iterator pi(peers.begin());pi!=peers.end();++pi) { ZT_Peer *p = &(pl->peers[pl->peerCount++]); p->address = pi->second->address().toInt(); - p->lastUnicastFrame = pi->second->lastUnicastFrame(); - p->lastMulticastFrame = pi->second->lastMulticastFrame(); if (pi->second->remoteVersionKnown()) { p->versionMajor = pi->second->remoteVersionMajor(); p->versionMinor = pi->second->remoteVersionMinor(); diff --git a/node/Peer.cpp b/node/Peer.cpp index 87882dad..94fb5298 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -42,8 +42,7 @@ static uint32_t _natKeepaliveBuf = 0; Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) : _lastReceive(0), - _lastUnicastFrame(0), - _lastMulticastFrame(0), + _lastNontrivialReceive(0), _lastDirectPathPushSent(0), _lastDirectPathPushReceive(0), _lastCredentialRequestSent(0), @@ -128,10 +127,16 @@ void Peer::received( #endif _lastReceive = now; - if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME)) - _lastUnicastFrame = now; - else if (verb == Packet::VERB_MULTICAST_FRAME) - _lastMulticastFrame = now; + switch (verb) { + case Packet::VERB_FRAME: + case Packet::VERB_EXT_FRAME: + case Packet::VERB_NETWORK_CONFIG_REQUEST: + case Packet::VERB_NETWORK_CONFIG: + case Packet::VERB_MULTICAST_FRAME: + _lastNontrivialReceive = now; + break; + default: break; + } if (trustEstablished) { _lastTrustEstablishedPacketReceived = now; diff --git a/node/Peer.hpp b/node/Peer.hpp index d0589ccf..be05aa3a 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -226,25 +226,10 @@ public: */ inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } - /** - * @return Time of most recent unicast frame received - */ - inline uint64_t lastUnicastFrame() const { return _lastUnicastFrame; } - - /** - * @return Time of most recent multicast frame received - */ - inline uint64_t lastMulticastFrame() const { return _lastMulticastFrame; } - - /** - * @return Time of most recent frame of any kind (unicast or multicast) - */ - inline uint64_t lastFrame() const { return std::max(_lastUnicastFrame,_lastMulticastFrame); } - /** * @return True if this peer has sent us real network traffic recently */ - inline uint64_t isActive(uint64_t now) const { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); } + inline uint64_t isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } /** * @return Latency in milliseconds or 0 if unknown @@ -469,8 +454,7 @@ private: uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH]; uint8_t _remoteClusterOptimal6[16]; uint64_t _lastReceive; // direct or indirect - uint64_t _lastUnicastFrame; - uint64_t _lastMulticastFrame; + uint64_t _lastNontrivialReceive; // frames, things like netconf, etc. uint64_t _lastDirectPathPushSent; uint64_t _lastDirectPathPushReceive; uint64_t _lastCredentialRequestSent; diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 5c135636..f14bae54 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -222,8 +222,6 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer) Utils::snprintf(json,sizeof(json), "%s{\n" "%s\t\"address\": \"%.10llx\",\n" - "%s\t\"lastUnicastFrame\": %llu,\n" - "%s\t\"lastMulticastFrame\": %llu,\n" "%s\t\"versionMajor\": %d,\n" "%s\t\"versionMinor\": %d,\n" "%s\t\"versionRev\": %d,\n" @@ -234,8 +232,6 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer) "%s}", prefix, prefix,peer->address, - prefix,peer->lastUnicastFrame, - prefix,peer->lastMulticastFrame, prefix,peer->versionMajor, prefix,peer->versionMinor, prefix,peer->versionRev, diff --git a/service/README.md b/service/README.md index 1c71fbdc..f487f2bc 100644 --- a/service/README.md +++ b/service/README.md @@ -114,8 +114,6 @@ Getting /peer returns an array of peer objects for all current peers. See below - - -- cgit v1.2.3 From e1c930f1b7a44d2a1c37d4b9b6f348c2ed7260fc Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 9 Nov 2016 16:33:01 -0800 Subject: update JNI wrapper to reflect removal of lastMulticastFrame and lastUnicastFrame from ZT_Peer struct --- java/jni/ZT_jniutils.cpp | 18 ------------------ java/src/com/zerotier/sdk/Peer.java | 16 ---------------- 2 files changed, 34 deletions(-) diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp index 27ca7374..6b882c6d 100644 --- a/java/jni/ZT_jniutils.cpp +++ b/java/jni/ZT_jniutils.cpp @@ -475,8 +475,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer) jclass peerClass = NULL; jfieldID addressField = NULL; - jfieldID lastUnicastFrameField = NULL; - jfieldID lastMulticastFrameField = NULL; jfieldID versionMajorField = NULL; jfieldID versionMinorField = NULL; jfieldID versionRevField = NULL; @@ -500,20 +498,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer) return NULL; } - lastUnicastFrameField = lookup.findField(peerClass, "lastUnicastFrame", "J"); - if(env->ExceptionCheck() || lastUnicastFrameField == NULL) - { - LOGE("Error finding lastUnicastFrame field of Peer object"); - return NULL; - } - - lastMulticastFrameField = lookup.findField(peerClass, "lastMulticastFrame", "J"); - if(env->ExceptionCheck() || lastMulticastFrameField == NULL) - { - LOGE("Error finding lastMulticastFrame field of Peer object"); - return NULL; - } - versionMajorField = lookup.findField(peerClass, "versionMajor", "I"); if(env->ExceptionCheck() || versionMajorField == NULL) { @@ -571,8 +555,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer) } env->SetLongField(peerObject, addressField, (jlong)peer.address); - env->SetLongField(peerObject, lastUnicastFrameField, (jlong)peer.lastUnicastFrame); - env->SetLongField(peerObject, lastMulticastFrameField, (jlong)peer.lastMulticastFrame); env->SetIntField(peerObject, versionMajorField, peer.versionMajor); env->SetIntField(peerObject, versionMinorField, peer.versionMinor); env->SetIntField(peerObject, versionRevField, peer.versionRev); diff --git a/java/src/com/zerotier/sdk/Peer.java b/java/src/com/zerotier/sdk/Peer.java index fb2d1065..eb3d7130 100644 --- a/java/src/com/zerotier/sdk/Peer.java +++ b/java/src/com/zerotier/sdk/Peer.java @@ -34,8 +34,6 @@ import java.util.ArrayList; */ public final class Peer { private long address; - private long lastUnicastFrame; - private long lastMulticastFrame; private int versionMajor; private int versionMinor; private int versionRev; @@ -52,20 +50,6 @@ public final class Peer { return address; } - /** - * Time we last received a unicast frame from this peer - */ - public final long lastUnicastFrame() { - return lastUnicastFrame; - } - - /** - * Time we last received a multicast rame from this peer - */ - public final long lastMulticastFrame() { - return lastMulticastFrame; - } - /** * Remote major version or -1 if not known */ -- cgit v1.2.3 From 5ebf5077f56442908d4a5ced8d969df9e7de8c4f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Nov 2016 17:11:10 -0800 Subject: Log last meta-data in controller, and ease up just a bit on keepalives. --- controller/EmbeddedNetworkController.cpp | 2 +- node/Constants.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index f91ba533..624c3145 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -622,8 +622,8 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( member["recentLog"] = recentLog; } - // Update last modified time member["lastModified"] = now; + member["lastRequestMetaData"] = metaData.data(); // If they are not authorized, STOP! if (!authorizedBy) { diff --git a/node/Constants.hpp b/node/Constants.hpp index b7042d5d..6400e289 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -256,12 +256,12 @@ /** * How frequently to send heartbeats over in-use paths */ -#define ZT_PATH_HEARTBEAT_PERIOD 10000 +#define ZT_PATH_HEARTBEAT_PERIOD 14000 /** * Paths are considered inactive if they have not received traffic in this long */ -#define ZT_PATH_ALIVE_TIMEOUT 25000 +#define ZT_PATH_ALIVE_TIMEOUT 45000 /** * Minimum time between attempts to check dead paths to see if they can be re-awakened -- cgit v1.2.3 From 226123ca08ffbb5f4e4f0699b92fb9db08576a66 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 11:54:47 -0800 Subject: Refactor controller to permit sending of pushes as well as just replies to config requests. --- controller/EmbeddedNetworkController.cpp | 77 +++++++++++++++-------- controller/EmbeddedNetworkController.hpp | 14 +++-- node/IncomingPacket.cpp | 84 +------------------------ node/Network.cpp | 105 +++++++++++++------------------ node/Network.hpp | 10 ++- node/NetworkController.hpp | 69 +++++++++++++------- node/Node.cpp | 85 +++++++++++++++++++++++++ node/Node.hpp | 8 ++- 8 files changed, 252 insertions(+), 200 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 624c3145..91b59215 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -470,11 +470,21 @@ EmbeddedNetworkController::~EmbeddedNetworkController() { } -NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,NetworkConfig &nc) +void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender) { - if (((!signingId)||(!signingId.hasPrivate()))||(signingId.address().toInt() != (nwid >> 24))) { - return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } + this->_sender = sender; + this->_signingId = signingId; +} + +void EmbeddedNetworkController::request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData) +{ + if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender)) + return; const uint64_t now = OSUtils::now(); @@ -483,7 +493,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( Mutex::Lock _l(_lastRequestTime_m); uint64_t &lrt = _lastRequestTime[std::pair(identity.address().toInt(),nwid)]; if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD) - return NetworkController::NETCONF_QUERY_IGNORE; + return; lrt = now; } @@ -496,8 +506,13 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( network = _db.get("network",nwids,0); member = _db.get("network",nwids,"member",identity.address().toString(),0); } - if (!network.size()) - return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; + + if (!network.size()) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND); + return; + } + + json origMember(member); // for detecting modification later _initMember(member); { @@ -507,10 +522,13 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( // a "collision" from being able to auth onto our network in place of an already // known member. try { - if (Identity(haveIdStr.c_str()) != identity) - return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + if (Identity(haveIdStr.c_str()) != identity) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; + } } catch ( ... ) { - return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; } } else { // If we do not yet know this member's identity, learn it. @@ -521,7 +539,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( // These are always the same, but make sure they are set member["id"] = identity.address().toString(); member["address"] = member["id"]; - member["nwid"] = network["id"]; + member["nwid"] = nwids; // Determine whether and how member is authorized const char *authorizedBy = (const char *)0; @@ -597,7 +615,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( } // Log this request - { + if (requestPacketId) { // only log if this is a request, not for generated pushes json rlEntry = json::object(); rlEntry["ts"] = now; rlEntry["authorized"] = (authorizedBy) ? true : false; @@ -620,22 +638,27 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( } } member["recentLog"] = recentLog; - } - member["lastModified"] = now; - member["lastRequestMetaData"] = metaData.data(); + // Also only do this on real requests + member["lastRequestMetaData"] = metaData.data(); + } // If they are not authorized, STOP! if (!authorizedBy) { - Mutex::Lock _l(_db_m); - _db.put("network",nwids,"member",identity.address().toString(),member); - return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + if (origMember != member) { + member["lastModified"] = now; + Mutex::Lock _l(_db_m); + _db.put("network",nwids,"member",identity.address().toString(),member); + } + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; } // ------------------------------------------------------------------------- // If we made it this far, they are authorized. // ------------------------------------------------------------------------- + NetworkConfig nc; _NetworkMemberInfo nmi; _getNetworkMemberInfo(now,nwid,nmi); @@ -661,8 +684,9 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( Utils::scopy(nc.name,sizeof(nc.name),_jS(network["name"],"").c_str()); nc.multicastLimit = (unsigned int)_jI(network["multicastLimit"],32ULL); - for(std::set
::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) + for(std::set
::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) { nc.addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); + } json &v4AssignMode = network["v4AssignMode"]; json &v6AssignMode = network["v6AssignMode"]; @@ -714,7 +738,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( } } nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); - if (nc.capabilities[nc.capabilityCount].sign(signingId,identity.address())) + if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address())) ++nc.capabilityCount; if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) break; @@ -733,7 +757,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) break; nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); - if (nc.tags[nc.tagCount].sign(signingId)) + if (nc.tags[nc.tagCount].sign(_signingId)) ++nc.tagCount; } } @@ -923,17 +947,20 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( } CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); - if (com.sign(signingId)) { + if (com.sign(_signingId)) { nc.com = com; } else { - return NETCONF_QUERY_INTERNAL_SERVER_ERROR; + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR); + return; } - { + if (member != origMember) { + member["lastModified"] = now; Mutex::Lock _l(_db_m); _db.put("network",nwids,"member",identity.address().toString(),member); } - return NetworkController::NETCONF_QUERY_OK; + + _sender->ncSendConfig(nwid,requestPacketId,identity.address(),nc,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 53d3be0f..79b919b9 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -52,13 +52,14 @@ public: EmbeddedNetworkController(Node *node,const char *dbPath); virtual ~EmbeddedNetworkController(); - virtual NetworkController::ResultCode doNetworkConfigRequest( + virtual void init(const Identity &signingId,Sender *sender); + + virtual void request( + uint64_t nwid, const InetAddress &fromAddr, - const Identity &signingId, + uint64_t requestPacketId, const Identity &identity, - uint64_t nwid, - const Dictionary &metaData, - NetworkConfig &nc); + const Dictionary &metaData); unsigned int handleControlPlaneHttpGET( const std::vector &path, @@ -157,6 +158,9 @@ private: Node *const _node; std::string _path; + NetworkController::Sender *_sender; + Identity _signingId; + struct _CircuitTestEntry { ZT_CircuitTest *test; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5afacd0e..bde5df71 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -865,92 +865,12 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons const uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); const unsigned int hopCount = hops(); const uint64_t requestPacketId = packetId(); - bool trustEstablished = false; if (RR->localNetworkController) { const unsigned int metaDataLength = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN); const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength); const Dictionary metaData(metaDataBytes,metaDataLength); - - NetworkConfig *netconf = new NetworkConfig(); - try { - switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) { - - case NetworkController::NETCONF_QUERY_OK: { - trustEstablished = true; - Dictionary *dconf = new Dictionary(); - try { - if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) { - uint64_t configUpdateId = RR->node->prng(); - if (!configUpdateId) ++configUpdateId; - const unsigned int totalSize = dconf->sizeBytes(); - unsigned int chunkIndex = 0; - while (chunkIndex < totalSize) { - const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256))); - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); - outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(requestPacketId); - - const unsigned int sigStart = outp.size(); - outp.append(nwid); - outp.append((uint16_t)chunkLen); - outp.append((const void *)(dconf->data() + chunkIndex),chunkLen); - - outp.append((uint8_t)0); // no flags - outp.append((uint64_t)configUpdateId); - outp.append((uint32_t)totalSize); - outp.append((uint32_t)chunkIndex); - - C25519::Signature sig(RR->identity.sign(reinterpret_cast(outp.data()) + sigStart,outp.size() - sigStart)); - outp.append((uint8_t)1); - outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN); - outp.append(sig.data,ZT_C25519_SIGNATURE_LEN); - - outp.compress(); - RR->sw->send(outp,true); - chunkIndex += chunkLen; - } - } - delete dconf; - } catch ( ... ) { - delete dconf; - throw; - } - } break; - - case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); - outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(requestPacketId); - outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); - outp.append(nwid); - outp.armor(peer->key(),true); - _path->send(RR,outp.data(),outp.size(),RR->node->now()); - } break; - - case NetworkController::NETCONF_QUERY_ACCESS_DENIED: { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); - outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(requestPacketId); - outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_); - outp.append(nwid); - outp.armor(peer->key(),true); - _path->send(RR,outp.data(),outp.size(),RR->node->now()); - } break; - - case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: - break; - case NetworkController::NETCONF_QUERY_IGNORE: - break; - default: - TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkController::doNetworkConfigRequest()"); - break; - } - delete netconf; - } catch ( ... ) { - delete netconf; - throw; - } + RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); @@ -961,7 +881,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons _path->send(RR,outp.data(),outp.size(),RR->node->now()); } - peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,trustEstablished); + peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false); } catch (std::exception &exc) { fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what()); TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what()); diff --git a/node/Network.cpp b/node/Network.cpp index c0e4b105..1f8e7ebf 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -599,7 +599,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) : if (conf.length()) { dconf->load(conf.c_str()); if (nconf->fromDictionary(*dconf)) { - this->_setConfiguration(*nconf,false); + this->setConfiguration(*nconf,false); _lastConfigUpdate = 0; // we still want to re-request a new config from the network gotConf = true; } @@ -1015,7 +1015,7 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr) } if (nc) { - this->_setConfiguration(*nc,true); + this->setConfiguration(*nc,true); delete nc; return configUpdateId; } else { @@ -1025,6 +1025,46 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr) return 0; } +int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk) +{ + // _lock is NOT locked when this is called + try { + if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id)) + return 0; + if (_config == nconf) + return 1; // OK config, but duplicate of what we already have + + ZT_VirtualNetworkConfig ctmp; + bool oldPortInitialized; + { + Mutex::Lock _l(_lock); + _config = nconf; + _lastConfigUpdate = RR->node->now(); + _netconfFailure = NETCONF_FAILURE_NONE; + oldPortInitialized = _portInitialized; + _portInitialized = true; + _externalConfig(&ctmp); + } + _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp); + + if (saveToDisk) { + Dictionary *d = new Dictionary(); + try { + char n[64]; + Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); + if (nconf.toDictionary(*d,false)) + RR->node->dataStorePut(n,(const void *)d->data(),d->sizeBytes(),true); + } catch ( ... ) {} + delete d; + } + + return 2; // OK and configuration has changed + } catch ( ... ) { + TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id); + } + return 0; +} + void Network::requestConfiguration() { const Address ctrl(controller()); @@ -1046,26 +1086,7 @@ void Network::requestConfiguration() if (ctrl == RR->identity.address()) { if (RR->localNetworkController) { - NetworkConfig *nconf = new NetworkConfig(); - try { - switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,*nconf)) { - case NetworkController::NETCONF_QUERY_OK: - this->_setConfiguration(*nconf,true); - break; - case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: - this->setNotFound(); - break; - case NetworkController::NETCONF_QUERY_ACCESS_DENIED: - this->setAccessDenied(); - break; - default: - this->setNotFound(); - break; - } - } catch ( ... ) { - this->setNotFound(); - } - delete nconf; + RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd); } else { this->setNotFound(); } @@ -1257,46 +1278,6 @@ ZT_VirtualNetworkStatus Network::_status() const } } -int Network::_setConfiguration(const NetworkConfig &nconf,bool saveToDisk) -{ - // _lock is NOT locked when this is called - try { - if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id)) - return 0; - if (_config == nconf) - return 1; // OK config, but duplicate of what we already have - - ZT_VirtualNetworkConfig ctmp; - bool oldPortInitialized; - { - Mutex::Lock _l(_lock); - _config = nconf; - _lastConfigUpdate = RR->node->now(); - _netconfFailure = NETCONF_FAILURE_NONE; - oldPortInitialized = _portInitialized; - _portInitialized = true; - _externalConfig(&ctmp); - } - _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp); - - if (saveToDisk) { - Dictionary *d = new Dictionary(); - try { - char n[64]; - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); - if (nconf.toDictionary(*d,false)) - RR->node->dataStorePut(n,(const void *)d->data(),d->sizeBytes(),true); - } catch ( ... ) {} - delete d; - } - - return 2; // OK and configuration has changed - } catch ( ... ) { - TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id); - } - return 0; -} - void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const { // assumes _lock is locked diff --git a/node/Network.hpp b/node/Network.hpp index 527d3048..1627be58 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -187,6 +187,15 @@ public: */ uint64_t handleConfigChunk(const Packet &chunk,unsigned int ptr); + /** + * Set network configuration + * + * @param nconf Network configuration + * @param saveToDisk Save to disk? Used during loading, should usually be true otherwise. + * @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new + */ + int setConfiguration(const NetworkConfig &nconf,bool saveToDisk); + /** * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this */ @@ -328,7 +337,6 @@ public: inline void **userPtr() throw() { return &_uPtr; } private: - int _setConfiguration(const NetworkConfig &nconf,bool saveToDisk); ZT_VirtualNetworkStatus _status() const; void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked bool _gate(const SharedPtr &peer); diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp index db95dd14..fc5db4af 100644 --- a/node/NetworkController.hpp +++ b/node/NetworkController.hpp @@ -27,7 +27,6 @@ namespace ZeroTier { -class RuntimeEnvironment; class Identity; class Address; struct InetAddress; @@ -38,45 +37,69 @@ struct InetAddress; class NetworkController { public: + enum ErrorCode + { + NC_ERROR_NONE = 0, + NC_ERROR_OBJECT_NOT_FOUND = 1, + NC_ERROR_ACCESS_DENIED = 2, + NC_ERROR_INTERNAL_SERVER_ERROR = 3 + }; + /** - * Return value of doNetworkConfigRequest + * Interface for sender used to send pushes and replies */ - enum ResultCode + class Sender { - NETCONF_QUERY_OK = 0, - NETCONF_QUERY_OBJECT_NOT_FOUND = 1, - NETCONF_QUERY_ACCESS_DENIED = 2, - NETCONF_QUERY_INTERNAL_SERVER_ERROR = 3, - NETCONF_QUERY_IGNORE = 4 + public: + /** + * Send a configuration to a remote peer + * + * @param nwid Network ID + * @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push) + * @param destination Destination peer Address + * @param nc Network configuration to send + * @param sendLegacyFormatConfig If true, send an old-format network config + */ + virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0; + + /** + * Send a network configuration request error + * + * @param nwid Network ID + * @param requestPacketId Request packet ID or 0 if none + * @param destination Destination peer Address + * @param errorCode Error code + */ + virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0; }; NetworkController() {} virtual ~NetworkController() {} /** - * Handle a network config request, sending replies if necessary - * - * This call is permitted to block, and may be called concurrently from more - * than one thread. Implementations must use locks if needed. + * Called when this is added to a Node to initialize and supply info * - * On internal server errors, the 'error' field in result can be filled in - * to indicate the error. + * @param signingId Identity for signing of network configurations, certs, etc. + * @param sender Sender implementation for sending replies or config pushes + */ + virtual void init(const Identity &signingId,Sender *sender) = 0; + + /** + * Handle a network configuration request * - * @param fromAddr Originating wire address or null address if packet is not direct (or from self) - * @param signingId Identity that should be used to sign results -- must include private key - * @param identity Originating peer ZeroTier identity * @param nwid 64-bit network ID + * @param fromAddr Originating wire address or null address if packet is not direct (or from self) + * @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request + * @param identity ZeroTier identity of originating peer * @param metaData Meta-data bundled with request (if any) - * @param nc NetworkConfig to fill with results * @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error */ - virtual NetworkController::ResultCode doNetworkConfigRequest( + virtual void request( + uint64_t nwid, const InetAddress &fromAddr, - const Identity &signingId, + uint64_t requestPacketId, const Identity &identity, - uint64_t nwid, - const Dictionary &metaData, - NetworkConfig &nc) = 0; + const Dictionary &metaData) = 0; }; } // namespace ZeroTier diff --git a/node/Node.cpp b/node/Node.cpp index 9314478f..69808bcf 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -490,6 +490,7 @@ void Node::clearLocalInterfaceAddresses() void Node::setNetconfMaster(void *networkControllerInstance) { RR->localNetworkController = reinterpret_cast(networkControllerInstance); + RR->localNetworkController->init(RR->identity,this); } ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) @@ -718,6 +719,90 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_ RR->topology->setTrustedPaths(reinterpret_cast(networks),ids,count); } +void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) +{ + if (destination == RR->identity.address()) { + SharedPtr n(network(nwid)); + if (!n) return; + n->setConfiguration(nc,true); + } else { + Dictionary *dconf = new Dictionary(); + try { + if (nc.toDictionary(*dconf,sendLegacyFormatConfig)) { + uint64_t configUpdateId = prng(); + if (!configUpdateId) ++configUpdateId; + + const unsigned int totalSize = dconf->sizeBytes(); + unsigned int chunkIndex = 0; + while (chunkIndex < totalSize) { + const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256))); + Packet outp(destination,RR->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(requestPacketId); + + const unsigned int sigStart = outp.size(); + outp.append(nwid); + outp.append((uint16_t)chunkLen); + outp.append((const void *)(dconf->data() + chunkIndex),chunkLen); + + outp.append((uint8_t)0); // no flags + outp.append((uint64_t)configUpdateId); + outp.append((uint32_t)totalSize); + outp.append((uint32_t)chunkIndex); + + C25519::Signature sig(RR->identity.sign(reinterpret_cast(outp.data()) + sigStart,outp.size() - sigStart)); + outp.append((uint8_t)1); + outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN); + outp.append(sig.data,ZT_C25519_SIGNATURE_LEN); + + outp.compress(); + RR->sw->send(outp,true); + chunkIndex += chunkLen; + } + } + delete dconf; + } catch ( ... ) { + delete dconf; + throw; + } + } +} + +void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) +{ + if (destination == RR->identity.address()) { + SharedPtr n(network(nwid)); + if (!n) return; + switch(errorCode) { + case NetworkController::NC_ERROR_OBJECT_NOT_FOUND: + case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR: + n->setNotFound(); + break; + case NetworkController::NC_ERROR_ACCESS_DENIED: + n->setAccessDenied(); + break; + + default: break; + } + } else if (requestPacketId) { + Packet outp(destination,RR->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(requestPacketId); + switch(errorCode) { + //case NetworkController::NC_ERROR_OBJECT_NOT_FOUND: + //case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR: + default: + outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); + break; + case NetworkController::NC_ERROR_ACCESS_DENIED: + outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_); + break; + } + outp.append(nwid); + RR->sw->send(outp,true); + } +} + } // namespace ZeroTier /****************************************************************************/ diff --git a/node/Node.hpp b/node/Node.hpp index 11462531..e616da3d 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -36,6 +36,7 @@ #include "Network.hpp" #include "Path.hpp" #include "Salsa20.hpp" +#include "NetworkController.hpp" #undef TRACE #ifdef ZT_TRACE @@ -55,7 +56,7 @@ namespace ZeroTier { * * The pointer returned by ZT_Node_new() is an instance of this class. */ -class Node +class Node : public NetworkController::Sender { public: Node( @@ -69,7 +70,7 @@ public: ZT_PathCheckFunction pathCheckFunction, ZT_EventCallback eventCallback); - ~Node(); + virtual ~Node(); // Public API Functions ---------------------------------------------------- @@ -282,6 +283,9 @@ public: return false; } + virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig); + virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); + private: inline SharedPtr _network(uint64_t nwid) const { -- cgit v1.2.3 From 12d32b9311d86a04353c8182e5d7cf4ec514d3df Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 11:57:45 -0800 Subject: Small fix to send pushes if not a reply. --- node/Node.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/node/Node.cpp b/node/Node.cpp index 69808bcf..c05a1850 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -736,9 +736,11 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de unsigned int chunkIndex = 0; while (chunkIndex < totalSize) { const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256))); - Packet outp(destination,RR->identity.address(),Packet::VERB_OK); - outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); - outp.append(requestPacketId); + Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG); + if (requestPacketId) { + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(requestPacketId); + } const unsigned int sigStart = outp.size(); outp.append(nwid); @@ -800,7 +802,7 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des } outp.append(nwid); RR->sw->send(outp,true); - } + } // else we can't send an ERROR() in response to nothing, so discard } } // namespace ZeroTier -- cgit v1.2.3 From 298e4a9f14e9697b2d780f791f9ec6ada4f974a6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 12:33:09 -0800 Subject: Also avoid sending tags and caps to old members since there is no point. --- controller/EmbeddedNetworkController.cpp | 80 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 91b59215..ed8ffa74 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -712,53 +712,53 @@ void EmbeddedNetworkController::request( ++nc.ruleCount; } } - } - if ((memberCapabilities.is_array())&&(memberCapabilities.size() > 0)&&(capabilities.is_array())) { - std::map< uint64_t,json * > capsById; - for(unsigned long i=0;i 0)&&(capabilities.is_array())) { + std::map< uint64_t,json * > capsById; + for(unsigned long i=0;iis_object())&&(cap->size() > 0)) { - ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; - unsigned int caprc = 0; - json &caprj = (*cap)["rules"]; - if ((caprj.is_array())&&(caprj.size() > 0)) { - for(unsigned long j=0;j= ZT_MAX_CAPABILITY_RULES) - break; - if (_parseRule(caprj[j],capr[caprc])) - ++caprc; + for(unsigned long i=0;iis_object())&&(cap->size() > 0)) { + ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; + unsigned int caprc = 0; + json &caprj = (*cap)["rules"]; + if ((caprj.is_array())&&(caprj.size() > 0)) { + for(unsigned long j=0;j= ZT_MAX_CAPABILITY_RULES) + break; + if (_parseRule(caprj[j],capr[caprc])) + ++caprc; + } } + nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); + if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address())) + ++nc.capabilityCount; + if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) + break; } - nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); - if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address())) - ++nc.capabilityCount; - if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) - break; } } - } - if (memberTags.is_array()) { - std::map< uint32_t,uint32_t > tagsById; - for(unsigned long i=0;i::const_iterator t(tagsById.begin());t!=tagsById.end();++t) { - if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) - break; - nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); - if (nc.tags[nc.tagCount].sign(_signingId)) - ++nc.tagCount; + if (memberTags.is_array()) { + std::map< uint32_t,uint32_t > tagsById; + for(unsigned long i=0;i::const_iterator t(tagsById.begin());t!=tagsById.end();++t) { + if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) + break; + nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); + if (nc.tags[nc.tagCount].sign(_signingId)) + ++nc.tagCount; + } } } -- cgit v1.2.3 From f0fcd222a12372de63a2c1b02e6c7304a5332a35 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 12:54:43 -0800 Subject: Actually push updates when things change. --- controller/EmbeddedNetworkController.cpp | 60 ++++++++++++++++++++++++-------- controller/EmbeddedNetworkController.hpp | 2 ++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index ed8ffa74..0fba8749 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -488,7 +488,6 @@ void EmbeddedNetworkController::request( const uint64_t now = OSUtils::now(); - // Check rate limit circuit breaker to prevent flooding { Mutex::Lock _l(_lastRequestTime_m); uint64_t &lrt = _lastRequestTime[std::pair(identity.address().toInt(),nwid)]; @@ -1137,12 +1136,12 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( Mutex::Lock _l(_db_m); member = _db.get("network",nwids,"member",Address(address).toString(),0); } + json origMember(member); // for detecting changes _initMember(member); try { if (b.count("activeBridge")) member["activeBridge"] = _jB(b["activeBridge"],false); if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = _jB(b["noAutoAssignIps"],false); - if ((b.count("identity"))&&(!member.count("identity"))) member["identity"] = _jS(b["identity"],""); // allow identity to be populated only if not already known if (b.count("authorized")) { const bool newAuth = _jB(b["authorized"],false); @@ -1214,13 +1213,16 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( member["id"] = addrs; member["address"] = addrs; // legacy member["nwid"] = nwids; - member["lastModified"] = now; - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - { - Mutex::Lock _l(_db_m); - _db.put("network",nwids,"member",Address(address).toString(),member); + if (member != origMember) { + member["lastModified"] = now; + json &revj = member["revision"]; + member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + { + Mutex::Lock _l(_db_m); + _db.put("network",nwids,"member",Address(address).toString(),member); + } + _pushMemberUpdate(now,nwid,member); } // Add non-persisted fields @@ -1309,6 +1311,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( network = _db.get("network",nwids,0); } + json origNetwork(network); // for detecting changes _initNetwork(network); try { @@ -1489,13 +1492,20 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( network["id"] = nwids; network["nwid"] = nwids; // legacy - json &rev = network["revision"]; - network["revision"] = (rev.is_number() ? ((uint64_t)rev + 1ULL) : 1ULL); - network["lastModified"] = now; - { - Mutex::Lock _l(_db_m); - _db.put("network",nwids,network); + if (network != origNetwork) { + json &revj = network["revision"]; + network["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + network["lastModified"] = now; + { + Mutex::Lock _l(_db_m); + _db.put("network",nwids,network); + } + std::string pfx("network/"); pfx.append(nwids); pfx.append("/member/"); + _db.filter(pfx,120000,[this,&now,&nwid](const std::string &n,const json &obj) { + _pushMemberUpdate(now,nwid,obj); + return true; // do not delete + }); } _NetworkMemberInfo nmi; @@ -1700,4 +1710,26 @@ void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid } } +void EmbeddedNetworkController::_pushMemberUpdate(uint64_t now,uint64_t nwid,const nlohmann::json &member) +{ + try { + const std::string idstr = member["identity"]; + const std::string mdstr = member["lastRequestMetaData"]; + if ((idstr.length() > 0)&&(mdstr.length() > 0)) { + const Identity id(idstr); + bool online; + { + Mutex::Lock _l(_lastRequestTime_m); + std::map,uint64_t>::iterator lrt(_lastRequestTime.find(std::pair(id.address().toInt(),nwid))); + online = ( (lrt != _lastRequestTime.end()) && ((now - lrt->second) < ZT_NETWORK_AUTOCONF_DELAY) ); + } + Dictionary *metaData = new Dictionary(mdstr.c_str()); + try { + this->request(nwid,InetAddress(),0,id,*metaData); + } catch ( ... ) {} + delete metaData; + } + } catch ( ... ) {} +} + } // namespace ZeroTier diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 79b919b9..620ef3e3 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -103,6 +103,8 @@ private: Mutex _nmiCache_m; void _getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi); + void _pushMemberUpdate(uint64_t now,uint64_t nwid,const nlohmann::json &member); + // These init objects with default and static/informational fields inline void _initMember(nlohmann::json &member) { -- cgit v1.2.3 From 1b10d3413a13ae3b3a4503e806f96130c4c50fff Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 13:08:43 -0800 Subject: Use circuit breaker only for requests. --- controller/EmbeddedNetworkController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 0fba8749..85072eef 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -488,7 +488,7 @@ void EmbeddedNetworkController::request( const uint64_t now = OSUtils::now(); - { + if (requestPacketId) { Mutex::Lock _l(_lastRequestTime_m); uint64_t &lrt = _lastRequestTime[std::pair(identity.address().toInt(),nwid)]; if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD) -- cgit v1.2.3 From e26bee45fb2ed78bc1984886da0ffc48d8f5a102 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Nov 2016 13:57:01 -0800 Subject: Multithreading in network controller. Threads are only started if controller is used. --- controller/EmbeddedNetworkController.cpp | 1433 +++++++++++++++--------------- controller/EmbeddedNetworkController.hpp | 27 + osdep/BlockingQueue.hpp | 64 ++ 3 files changed, 832 insertions(+), 692 deletions(-) create mode 100644 osdep/BlockingQueue.hpp diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 85072eef..7f885b4e 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -459,6 +459,7 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) } EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPath) : + _threadsStarted(false), _db(dbPath), _node(node) { @@ -468,6 +469,13 @@ EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPa EmbeddedNetworkController::~EmbeddedNetworkController() { + Mutex::Lock _l(_threads_m); + if (_threadsStarted) { + for(int i=0;i<(ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT*2);++i) + _queue.post((_RQEntry *)0); + for(int i=0;i> 24))||(!_sender)) return; - const uint64_t now = OSUtils::now(); - - if (requestPacketId) { - Mutex::Lock _l(_lastRequestTime_m); - uint64_t &lrt = _lastRequestTime[std::pair(identity.address().toInt(),nwid)]; - if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD) - return; - lrt = now; - } - - char nwids[24]; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid); - json network; - json member; { - Mutex::Lock _l(_db_m); - network = _db.get("network",nwids,0); - member = _db.get("network",nwids,"member",identity.address().toString(),0); + Mutex::Lock _l(_threads_m); + if (!_threadsStarted) { + for(int i=0;incSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND); - return; - } + _RQEntry *qe = new _RQEntry; + qe->nwid = nwid; + qe->requestPacketId = requestPacketId; + qe->fromAddr = fromAddr; + qe->identity = identity; + qe->metaData = metaData; + _queue.post(qe); +} - json origMember(member); // for detecting modification later - _initMember(member); +unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + if ((path.size() > 0)&&(path[0] == "network")) { - { - std::string haveIdStr(_jS(member["identity"],"")); - if (haveIdStr.length() > 0) { - // If we already know this member's identity perform a full compare. This prevents - // a "collision" from being able to auth onto our network in place of an already - // known member. - try { - if (Identity(haveIdStr.c_str()) != identity) { - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); - return; - } - } catch ( ... ) { - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); - return; + if ((path.size() >= 2)&&(path[1].length() == 16)) { + const uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + char nwids[24]; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); + + json network; + { + Mutex::Lock _l(_db_m); + network = _db.get("network",nwids,0); } - } else { - // If we do not yet know this member's identity, learn it. - member["identity"] = identity.toString(false); - } - } + if (!network.size()) + return 404; - // These are always the same, but make sure they are set - member["id"] = identity.address().toString(); - member["address"] = member["id"]; - member["nwid"] = nwids; + if (path.size() >= 3) { - // Determine whether and how member is authorized - const char *authorizedBy = (const char *)0; - if (_jB(member["authorized"],false)) { - authorizedBy = "memberIsAuthorized"; - } else if (!_jB(network["private"],true)) { - authorizedBy = "networkIsPublic"; - if (!member.count("authorized")) { - member["authorized"] = true; - json ah; - ah["a"] = true; - ah["by"] = authorizedBy; - ah["ts"] = now; - ah["ct"] = json(); - ah["c"] = json(); - member["authHistory"].push_back(ah); - member["lastModified"] = now; - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - } - } else { - char presentedAuth[512]; - if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) { - presentedAuth[511] = (char)0; // sanity check + if (path[2] == "member") { - // Check for bearer token presented by member - if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) { - const char *const presentedToken = presentedAuth + 6; + if (path.size() >= 4) { + const uint64_t address = Utils::hexStrToU64(path[3].c_str()); - json &authTokens = network["authTokens"]; - if (authTokens.is_array()) { - for(unsigned long i=0;i now))&&(tstr == presentedToken)) { - bool usable = (maxUses == 0); - if (!usable) { - uint64_t useCount = 0; - json &ahist = member["authHistory"]; - if (ahist.is_array()) { - for(unsigned long j=0;j 0) { + responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); + responseBody.append(_jS(member["id"],"")); + responseBody.append("\":"); + responseBody.append(_jS(member["revision"],"0")); } - } + return true; // never delete + }); + responseBody.push_back('}'); + responseContentType = "application/json"; + + return 200; } - } - } - } - } - // Log this request - if (requestPacketId) { // only log if this is a request, not for generated pushes - json rlEntry = json::object(); - rlEntry["ts"] = now; - rlEntry["authorized"] = (authorizedBy) ? true : false; - rlEntry["authorizedBy"] = (authorizedBy) ? authorizedBy : ""; - rlEntry["clientMajorVersion"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); - rlEntry["clientMinorVersion"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); - rlEntry["clientRevision"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); - rlEntry["clientProtocolVersion"] = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,0); - if (fromAddr) - rlEntry["fromAddr"] = fromAddr.toString(); + } else if ((path[2] == "test")&&(path.size() >= 4)) { - json recentLog = json::array(); - recentLog.push_back(rlEntry); - json &oldLog = member["recentLog"]; - if (oldLog.is_array()) { - for(unsigned long i=0;i= ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH) - break; - } - } - member["recentLog"] = recentLog; + Mutex::Lock _l(_circuitTests_m); + std::map< uint64_t,_CircuitTestEntry >::iterator cte(_circuitTests.find(Utils::hexStrToU64(path[3].c_str()))); + if ((cte != _circuitTests.end())&&(cte->second.test)) { - // Also only do this on real requests - member["lastRequestMetaData"] = metaData.data(); - } + responseBody = "["; + responseBody.append(cte->second.jsonResults); + responseBody.push_back(']'); + responseContentType = "application/json"; - // If they are not authorized, STOP! - if (!authorizedBy) { - if (origMember != member) { - member["lastModified"] = now; - Mutex::Lock _l(_db_m); - _db.put("network",nwids,"member",identity.address().toString(),member); - } - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); - return; - } + return 200; - // ------------------------------------------------------------------------- - // If we made it this far, they are authorized. - // ------------------------------------------------------------------------- + } // else 404 - NetworkConfig nc; - _NetworkMemberInfo nmi; - _getNetworkMemberInfo(now,nwid,nmi); + } // else 404 - // Compute credential TTL. This is the "moving window" for COM agreement and - // the global TTL for Capability and Tag objects. (The same value is used - // for both.) This is computed by reference to the last time we deauthorized - // a member, since within the time period since this event any temporal - // differences are not particularly relevant. - uint64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA; - if (now > nmi.mostRecentDeauthTime) - credentialtmd += (now - nmi.mostRecentDeauthTime); - if (credentialtmd > ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA) - credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + } else { - nc.networkId = nwid; - nc.type = _jB(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; - nc.timestamp = now; - nc.credentialTimeMaxDelta = credentialtmd; - nc.revision = _jI(network["revision"],0ULL); - nc.issuedTo = identity.address(); - if (_jB(network["enableBroadcast"],true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; - if (_jB(network["allowPassiveBridging"],false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; - Utils::scopy(nc.name,sizeof(nc.name),_jS(network["name"],"").c_str()); - nc.multicastLimit = (unsigned int)_jI(network["multicastLimit"],32ULL); + const uint64_t now = OSUtils::now(); + _NetworkMemberInfo nmi; + _getNetworkMemberInfo(now,nwid,nmi); + _addNetworkNonPersistedFields(network,now,nmi); + responseBody = network.dump(2); + responseContentType = "application/json"; + return 200; - for(std::set
::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) { - nc.addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); - } + } + } else if (path.size() == 1) { - json &v4AssignMode = network["v4AssignMode"]; - json &v6AssignMode = network["v6AssignMode"]; - json &ipAssignmentPools = network["ipAssignmentPools"]; - json &routes = network["routes"]; - json &rules = network["rules"]; - json &capabilities = network["capabilities"]; - json &memberCapabilities = member["capabilities"]; - json &memberTags = member["tags"]; - - if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) { - // Old versions with no rules engine support get an allow everything rule. - // Since rules are enforced bidirectionally, newer versions *will* still - // enforce rules on the inbound side. - nc.ruleCount = 1; - nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT; - } else { - if (rules.is_array()) { - for(unsigned long i=0;i= ZT_MAX_NETWORK_RULES) - break; - if (_parseRule(rules[i],nc.rules[nc.ruleCount])) - ++nc.ruleCount; - } - } - - if ((memberCapabilities.is_array())&&(memberCapabilities.size() > 0)&&(capabilities.is_array())) { - std::map< uint64_t,json * > capsById; - for(unsigned long i=0;i networkIds; + { + Mutex::Lock _l(_db_m); + _db.filter("network/",120000,[&networkIds](const std::string &n,const json &obj) { + if (n.length() == (16 + 8)) + networkIds.insert(n.substr(8)); + return true; // do not delete + }); } - for(unsigned long i=0;iis_object())&&(cap->size() > 0)) { - ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; - unsigned int caprc = 0; - json &caprj = (*cap)["rules"]; - if ((caprj.is_array())&&(caprj.size() > 0)) { - for(unsigned long j=0;j= ZT_MAX_CAPABILITY_RULES) - break; - if (_parseRule(caprj[j],capr[caprc])) - ++caprc; - } - } - nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); - if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address())) - ++nc.capabilityCount; - if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) - break; - } + responseBody.push_back('['); + for(std::set::iterator i(networkIds.begin());i!=networkIds.end();++i) { + responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); + responseBody.append(*i); + responseBody.append("\""); } - } + responseBody.push_back(']'); + responseContentType = "application/json"; + return 200; - if (memberTags.is_array()) { - std::map< uint32_t,uint32_t > tagsById; - for(unsigned long i=0;i::const_iterator t(tagsById.begin());t!=tagsById.end();++t) { - if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) - break; - nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); - if (nc.tags[nc.tagCount].sign(_signingId)) - ++nc.tagCount; - } - } - } + } // else 404 - if (routes.is_array()) { - for(unsigned long i=0;i= ZT_MAX_NETWORK_ROUTES) - break; - json &route = routes[i]; - json &target = route["target"]; - json &via = route["via"]; - if (target.is_string()) { - const InetAddress t(target.get()); - InetAddress v; - if (via.is_string()) v.fromString(via.get()); - if ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) { - ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]); - *(reinterpret_cast(&(r->target))) = t; - if (v.ss_family == t.ss_family) - *(reinterpret_cast(&(r->via))) = v; - ++nc.routeCount; - } - } - } - } + } else { - const bool noAutoAssignIps = _jB(member["noAutoAssignIps"],false); + char tmp[4096]; + Utils::snprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now()); + responseBody = tmp; + responseContentType = "application/json"; + return 200; - if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) { - if ((_jB(v6AssignMode["rfc4193"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { - nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()); - nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; - } - if ((_jB(v6AssignMode["6plane"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { - nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt()); - nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; - } } - bool haveManagedIpv4AutoAssignment = false; - bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count - json ipAssignments = member["ipAssignments"]; // we want to make a copy - if (ipAssignments.is_array()) { - for(unsigned long i=0;i(&(nc.routes[rk].target))->containsAddress(ip)) ) - routedNetmaskBits = reinterpret_cast(&(nc.routes[rk].target))->netmaskBits(); - } +unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + if (path.empty()) + return 404; - 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; - } + json b; + try { + b = json::parse(body); + if (!b.is_object()) { + responseBody = "{ \"message\": \"body is not a JSON object\" }"; + responseContentType = "application/json"; + return 400; } - } else { - ipAssignments = json::array(); + } catch ( ... ) { + responseBody = "{ \"message\": \"body JSON is invalid\" }"; + responseContentType = "application/json"; + return 400; } + const uint64_t now = OSUtils::now(); - if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(_jB(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) { - for(unsigned long p=0;((p s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) { - // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that. - xx[0] = Utils::hton(x[0]); - xx[1] = Utils::hton(x[1] + identity.address().toInt()); - } else { - // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway - Utils::getSecureRandom((void *)xx,16); - if ((e[0] > s[0])) - xx[0] %= (e[0] - s[0]); - else xx[0] = 0; - if ((e[1] > s[1])) - xx[1] %= (e[1] - s[1]); - else xx[1] = 0; - xx[0] = Utils::hton(x[0] + xx[0]); - xx[1] = Utils::hton(x[1] + xx[1]); - } + if ((path.size() >= 2)&&(path[1].length() == 16)) { + uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + char nwids[24]; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - InetAddress ip6((const void *)xx,16,0); + if (path.size() >= 3) { - // Check if this IP is within a local-to-Ethernet routed network - int routedNetmaskBits = 0; - for(unsigned int rk=0;rk(&(nc.routes[rk].target))->containsAddress(ip6)) ) - routedNetmaskBits = reinterpret_cast(&(nc.routes[rk].target))->netmaskBits(); - } + if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { + uint64_t address = Utils::hexStrToU64(path[3].c_str()); + char addrs[24]; + Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address); - // If it's routed, then try to claim and assign it and if successful end loop - if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip6))) { - ipAssignments.push_back(ip6.toIpString()); - member["ipAssignments"] = ipAssignments; - ip6.setPort((unsigned int)routedNetmaskBits); - if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) - nc.staticIps[nc.staticIpCount++] = ip6; - haveManagedIpv6AutoAssignment = true; - break; - } + json member; + { + Mutex::Lock _l(_db_m); + member = _db.get("network",nwids,"member",Address(address).toString(),0); } - } - } - } - } - - if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(_jB(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) { - for(unsigned long p=0;((p(&ipRangeStartIA)->sin_addr.s_addr)); - uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeEndIA)->sin_addr.s_addr)); - if ((ipRangeEnd < ipRangeStart)||(ipRangeStart == 0)) - continue; - uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - - // Start with the LSB of the member's address - uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); - - for(uint32_t k=ipRangeStart,trialCount=0;((k<=ipRangeEnd)&&(trialCount < 1000));++k,++trialCount) { - uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; - ++ipTrialCounter; - if ((ip & 0x000000ff) == 0x000000ff) - continue; // don't allow addresses that end in .255 - - // Check if this IP is within a local-to-Ethernet routed network - int routedNetmaskBits = -1; - for(unsigned int rk=0;rk(&(nc.routes[rk].target))->sin_addr.s_addr)); - int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast(&(nc.routes[rk].target))->sin_port)); - if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) { - routedNetmaskBits = targetBits; - break; - } - } - } - - // If it's routed, then try to claim and assign it and if successful end loop - const InetAddress ip4(Utils::hton(ip),0); - if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip4))) { - ipAssignments.push_back(ip4.toIpString()); - 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; - } - } - } - } - } - } - - CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); - if (com.sign(_signingId)) { - nc.com = com; - } else { - _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR); - return; - } - - if (member != origMember) { - member["lastModified"] = now; - Mutex::Lock _l(_db_m); - _db.put("network",nwids,"member",identity.address().toString(),member); - } - - _sender->ncSendConfig(nwid,requestPacketId,identity.address(),nc,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); -} - -unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( - const std::vector &path, - const std::map &urlArgs, - const std::map &headers, - const std::string &body, - std::string &responseBody, - std::string &responseContentType) -{ - if ((path.size() > 0)&&(path[0] == "network")) { - - if ((path.size() >= 2)&&(path[1].length() == 16)) { - const uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); - char nwids[24]; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - - json network; - { - Mutex::Lock _l(_db_m); - network = _db.get("network",nwids,0); - } - if (!network.size()) - return 404; - - if (path.size() >= 3) { - - if (path[2] == "member") { - - if (path.size() >= 4) { - const uint64_t address = Utils::hexStrToU64(path[3].c_str()); - - json member; - { - Mutex::Lock _l(_db_m); - member = _db.get("network",nwids,"member",Address(address).toString(),0); - } - if (!member.size()) - return 404; - - _addMemberNonPersistedFields(member,OSUtils::now()); - responseBody = member.dump(2); - responseContentType = "application/json"; - - return 200; - } else { - - Mutex::Lock _l(_db_m); - - responseBody = "{"; - std::string pfx(std::string("network/") + nwids + "member/"); - _db.filter(pfx,120000,[&responseBody](const std::string &n,const json &member) { - if (member.size() > 0) { - responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); - responseBody.append(_jS(member["id"],"")); - responseBody.append("\":"); - responseBody.append(_jS(member["revision"],"0")); - } - return true; // never delete - }); - responseBody.push_back('}'); - responseContentType = "application/json"; - - return 200; - } - - } else if ((path[2] == "test")&&(path.size() >= 4)) { - - Mutex::Lock _l(_circuitTests_m); - std::map< uint64_t,_CircuitTestEntry >::iterator cte(_circuitTests.find(Utils::hexStrToU64(path[3].c_str()))); - if ((cte != _circuitTests.end())&&(cte->second.test)) { - - responseBody = "["; - responseBody.append(cte->second.jsonResults); - responseBody.push_back(']'); - responseContentType = "application/json"; - - return 200; - - } // else 404 - - } // else 404 - - } else { - - const uint64_t now = OSUtils::now(); - _NetworkMemberInfo nmi; - _getNetworkMemberInfo(now,nwid,nmi); - _addNetworkNonPersistedFields(network,now,nmi); - responseBody = network.dump(2); - responseContentType = "application/json"; - return 200; - - } - } else if (path.size() == 1) { - - std::set networkIds; - { - Mutex::Lock _l(_db_m); - _db.filter("network/",120000,[&networkIds](const std::string &n,const json &obj) { - if (n.length() == (16 + 8)) - networkIds.insert(n.substr(8)); - return true; // do not delete - }); - } - - responseBody.push_back('['); - for(std::set::iterator i(networkIds.begin());i!=networkIds.end();++i) { - responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); - responseBody.append(*i); - responseBody.append("\""); - } - responseBody.push_back(']'); - responseContentType = "application/json"; - return 200; - - } // else 404 - - } else { - - char tmp[4096]; - Utils::snprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now()); - responseBody = tmp; - responseContentType = "application/json"; - return 200; - - } - - return 404; -} - -unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( - const std::vector &path, - const std::map &urlArgs, - const std::map &headers, - const std::string &body, - std::string &responseBody, - std::string &responseContentType) -{ - if (path.empty()) - return 404; - - json b; - try { - b = json::parse(body); - if (!b.is_object()) { - responseBody = "{ \"message\": \"body is not a JSON object\" }"; - responseContentType = "application/json"; - return 400; - } - } catch ( ... ) { - responseBody = "{ \"message\": \"body JSON is invalid\" }"; - responseContentType = "application/json"; - return 400; - } - const uint64_t now = OSUtils::now(); - - if (path[0] == "network") { - - if ((path.size() >= 2)&&(path[1].length() == 16)) { - uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); - char nwids[24]; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - - if (path.size() >= 3) { - - if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { - uint64_t address = Utils::hexStrToU64(path[3].c_str()); - char addrs[24]; - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address); - - json member; - { - Mutex::Lock _l(_db_m); - member = _db.get("network",nwids,"member",Address(address).toString(),0); - } - json origMember(member); // for detecting changes - _initMember(member); + json origMember(member); // for detecting changes + _initMember(member); try { if (b.count("activeBridge")) member["activeBridge"] = _jB(b["activeBridge"],false); @@ -1546,107 +1096,606 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( Mutex::Lock _l(_db_m); network = _db.get("network",nwids,0); } - if (!network.size()) - return 404; + if (!network.size()) + return 404; + + if (path.size() >= 3) { + if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { + const uint64_t address = Utils::hexStrToU64(path[3].c_str()); + + Mutex::Lock _l(_db_m); + + json member = _db.get("network",nwids,"member",Address(address).toString(),0); + _db.erase("network",nwids,"member",Address(address).toString()); + + if (!member.size()) + return 404; + responseBody = member.dump(2); + responseContentType = "application/json"; + return 200; + } + } else { + Mutex::Lock _l(_db_m); + + std::string pfx("network/"); pfx.append(nwids); + _db.filter(pfx,120000,[](const std::string &n,const json &obj) { + return false; // delete + }); + + Mutex::Lock _l2(_nmiCache_m); + _nmiCache.erase(nwid); + + responseBody = network.dump(2); + responseContentType = "application/json"; + return 200; + } + } // else 404 + + } // else 404 + + return 404; +} + +void EmbeddedNetworkController::threadMain() + throw() +{ + for(;;) { + _RQEntry *const qe = _queue.get(); + if (!qe) break; // enqueue a NULL to terminate threads + try { + _request(qe->nwid,qe->fromAddr,qe->requestPacketId,qe->identity,qe->metaData); + } catch ( ... ) {} + delete qe; + } +} + +void EmbeddedNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) +{ + char tmp[65535]; + EmbeddedNetworkController *const self = reinterpret_cast(test->ptr); + + if (!test) + return; + if (!report) + return; + + Mutex::Lock _l(self->_circuitTests_m); + std::map< uint64_t,_CircuitTestEntry >::iterator cte(self->_circuitTests.find(test->testId)); + + if (cte == self->_circuitTests.end()) { // sanity check: a circuit test we didn't launch? + self->_node->circuitTestEnd(test); + ::free((void *)test); + return; + } + + Utils::snprintf(tmp,sizeof(tmp), + "%s{\n" + "\t\"timestamp\": %llu," ZT_EOL_S + "\t\"testId\": \"%.16llx\"," ZT_EOL_S + "\t\"upstream\": \"%.10llx\"," ZT_EOL_S + "\t\"current\": \"%.10llx\"," ZT_EOL_S + "\t\"receivedTimestamp\": %llu," ZT_EOL_S + "\t\"sourcePacketId\": \"%.16llx\"," ZT_EOL_S + "\t\"flags\": %llu," ZT_EOL_S + "\t\"sourcePacketHopCount\": %u," ZT_EOL_S + "\t\"errorCode\": %u," ZT_EOL_S + "\t\"vendor\": %d," ZT_EOL_S + "\t\"protocolVersion\": %u," ZT_EOL_S + "\t\"majorVersion\": %u," ZT_EOL_S + "\t\"minorVersion\": %u," ZT_EOL_S + "\t\"revision\": %u," ZT_EOL_S + "\t\"platform\": %d," ZT_EOL_S + "\t\"architecture\": %d," ZT_EOL_S + "\t\"receivedOnLocalAddress\": \"%s\"," ZT_EOL_S + "\t\"receivedFromRemoteAddress\": \"%s\"" ZT_EOL_S + "}", + ((cte->second.jsonResults.length() > 0) ? ",\n" : ""), + (unsigned long long)report->timestamp, + (unsigned long long)test->testId, + (unsigned long long)report->upstream, + (unsigned long long)report->current, + (unsigned long long)OSUtils::now(), + (unsigned long long)report->sourcePacketId, + (unsigned long long)report->flags, + report->sourcePacketHopCount, + report->errorCode, + (int)report->vendor, + report->protocolVersion, + report->majorVersion, + report->minorVersion, + report->revision, + (int)report->platform, + (int)report->architecture, + reinterpret_cast(&(report->receivedOnLocalAddress))->toString().c_str(), + reinterpret_cast(&(report->receivedFromRemoteAddress))->toString().c_str()); + + cte->second.jsonResults.append(tmp); +} + +void EmbeddedNetworkController::_request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData) +{ + if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender)) + return; + + const uint64_t now = OSUtils::now(); + + if (requestPacketId) { + Mutex::Lock _l(_lastRequestTime_m); + uint64_t &lrt = _lastRequestTime[std::pair(identity.address().toInt(),nwid)]; + if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD) + return; + lrt = now; + } + + char nwids[24]; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid); + json network; + json member; + { + Mutex::Lock _l(_db_m); + network = _db.get("network",nwids,0); + member = _db.get("network",nwids,"member",identity.address().toString(),0); + } + + if (!network.size()) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND); + return; + } + + json origMember(member); // for detecting modification later + _initMember(member); + + { + std::string haveIdStr(_jS(member["identity"],"")); + if (haveIdStr.length() > 0) { + // If we already know this member's identity perform a full compare. This prevents + // a "collision" from being able to auth onto our network in place of an already + // known member. + try { + if (Identity(haveIdStr.c_str()) != identity) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; + } + } catch ( ... ) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; + } + } else { + // If we do not yet know this member's identity, learn it. + member["identity"] = identity.toString(false); + } + } + + // These are always the same, but make sure they are set + member["id"] = identity.address().toString(); + member["address"] = member["id"]; + member["nwid"] = nwids; + + // Determine whether and how member is authorized + const char *authorizedBy = (const char *)0; + if (_jB(member["authorized"],false)) { + authorizedBy = "memberIsAuthorized"; + } else if (!_jB(network["private"],true)) { + authorizedBy = "networkIsPublic"; + if (!member.count("authorized")) { + member["authorized"] = true; + json ah; + ah["a"] = true; + ah["by"] = authorizedBy; + ah["ts"] = now; + ah["ct"] = json(); + ah["c"] = json(); + member["authHistory"].push_back(ah); + member["lastModified"] = now; + json &revj = member["revision"]; + member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + } + } else { + char presentedAuth[512]; + if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) { + presentedAuth[511] = (char)0; // sanity check + + // Check for bearer token presented by member + if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) { + const char *const presentedToken = presentedAuth + 6; + + json &authTokens = network["authTokens"]; + if (authTokens.is_array()) { + for(unsigned long i=0;i now))&&(tstr == presentedToken)) { + bool usable = (maxUses == 0); + if (!usable) { + uint64_t useCount = 0; + json &ahist = member["authHistory"]; + if (ahist.is_array()) { + for(unsigned long j=0;j= ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH) + break; + } + } + member["recentLog"] = recentLog; + + // Also only do this on real requests + member["lastRequestMetaData"] = metaData.data(); + } + + // If they are not authorized, STOP! + if (!authorizedBy) { + if (origMember != member) { + member["lastModified"] = now; + Mutex::Lock _l(_db_m); + _db.put("network",nwids,"member",identity.address().toString(),member); + } + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); + return; + } + + // ------------------------------------------------------------------------- + // If we made it this far, they are authorized. + // ------------------------------------------------------------------------- + + NetworkConfig nc; + _NetworkMemberInfo nmi; + _getNetworkMemberInfo(now,nwid,nmi); + + // Compute credential TTL. This is the "moving window" for COM agreement and + // the global TTL for Capability and Tag objects. (The same value is used + // for both.) This is computed by reference to the last time we deauthorized + // a member, since within the time period since this event any temporal + // differences are not particularly relevant. + uint64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA; + if (now > nmi.mostRecentDeauthTime) + credentialtmd += (now - nmi.mostRecentDeauthTime); + if (credentialtmd > ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA) + credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + + nc.networkId = nwid; + nc.type = _jB(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; + nc.timestamp = now; + nc.credentialTimeMaxDelta = credentialtmd; + nc.revision = _jI(network["revision"],0ULL); + nc.issuedTo = identity.address(); + if (_jB(network["enableBroadcast"],true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; + if (_jB(network["allowPassiveBridging"],false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; + Utils::scopy(nc.name,sizeof(nc.name),_jS(network["name"],"").c_str()); + nc.multicastLimit = (unsigned int)_jI(network["multicastLimit"],32ULL); + + for(std::set
::const_iterator ab(nmi.activeBridges.begin());ab!=nmi.activeBridges.end();++ab) { + nc.addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); + } + + json &v4AssignMode = network["v4AssignMode"]; + json &v6AssignMode = network["v6AssignMode"]; + json &ipAssignmentPools = network["ipAssignmentPools"]; + json &routes = network["routes"]; + json &rules = network["rules"]; + json &capabilities = network["capabilities"]; + json &memberCapabilities = member["capabilities"]; + json &memberTags = member["tags"]; + + if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) { + // Old versions with no rules engine support get an allow everything rule. + // Since rules are enforced bidirectionally, newer versions *will* still + // enforce rules on the inbound side. + nc.ruleCount = 1; + nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT; + } else { + if (rules.is_array()) { + for(unsigned long i=0;i= ZT_MAX_NETWORK_RULES) + break; + if (_parseRule(rules[i],nc.rules[nc.ruleCount])) + ++nc.ruleCount; + } + } + + if ((memberCapabilities.is_array())&&(memberCapabilities.size() > 0)&&(capabilities.is_array())) { + std::map< uint64_t,json * > capsById; + for(unsigned long i=0;iis_object())&&(cap->size() > 0)) { + ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; + unsigned int caprc = 0; + json &caprj = (*cap)["rules"]; + if ((caprj.is_array())&&(caprj.size() > 0)) { + for(unsigned long j=0;j= ZT_MAX_CAPABILITY_RULES) + break; + if (_parseRule(caprj[j],capr[caprc])) + ++caprc; + } + } + nc.capabilities[nc.capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); + if (nc.capabilities[nc.capabilityCount].sign(_signingId,identity.address())) + ++nc.capabilityCount; + if (nc.capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) + break; + } + } + } + + if (memberTags.is_array()) { + std::map< uint32_t,uint32_t > tagsById; + for(unsigned long i=0;i::const_iterator t(tagsById.begin());t!=tagsById.end();++t) { + if (nc.tagCount >= ZT_MAX_NETWORK_TAGS) + break; + nc.tags[nc.tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); + if (nc.tags[nc.tagCount].sign(_signingId)) + ++nc.tagCount; + } + } + } + + if (routes.is_array()) { + for(unsigned long i=0;i= ZT_MAX_NETWORK_ROUTES) + break; + json &route = routes[i]; + json &target = route["target"]; + json &via = route["via"]; + if (target.is_string()) { + const InetAddress t(target.get()); + InetAddress v; + if (via.is_string()) v.fromString(via.get()); + if ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) { + ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]); + *(reinterpret_cast(&(r->target))) = t; + if (v.ss_family == t.ss_family) + *(reinterpret_cast(&(r->via))) = v; + ++nc.routeCount; + } + } + } + } - if (path.size() >= 3) { - if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { - const uint64_t address = Utils::hexStrToU64(path[3].c_str()); + const bool noAutoAssignIps = _jB(member["noAutoAssignIps"],false); - Mutex::Lock _l(_db_m); + if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) { + if ((_jB(v6AssignMode["rfc4193"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()); + nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; + } + if ((_jB(v6AssignMode["6plane"],false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt()); + nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; + } + } - json member = _db.get("network",nwids,"member",Address(address).toString(),0); - _db.erase("network",nwids,"member",Address(address).toString()); + bool haveManagedIpv4AutoAssignment = false; + bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count + json ipAssignments = member["ipAssignments"]; // we want to make a copy + if (ipAssignments.is_array()) { + for(unsigned long i=0;i(&(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; } - } else { - Mutex::Lock _l(_db_m); + if (ip.ss_family == AF_INET) + haveManagedIpv4AutoAssignment = true; + else if (ip.ss_family == AF_INET6) + haveManagedIpv6AutoAssignment = true; + } + } + } else { + ipAssignments = json::array(); + } - std::string pfx("network/"); pfx.append(nwids); - _db.filter(pfx,120000,[](const std::string &n,const json &obj) { - return false; // delete - }); + if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(_jB(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) { + for(unsigned long p=0;((p s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) { + // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that. + xx[0] = Utils::hton(x[0]); + xx[1] = Utils::hton(x[1] + identity.address().toInt()); + } else { + // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway + Utils::getSecureRandom((void *)xx,16); + if ((e[0] > s[0])) + xx[0] %= (e[0] - s[0]); + else xx[0] = 0; + if ((e[1] > s[1])) + xx[1] %= (e[1] - s[1]); + else xx[1] = 0; + xx[0] = Utils::hton(x[0] + xx[0]); + xx[1] = Utils::hton(x[1] + xx[1]); + } - responseBody = network.dump(2); - responseContentType = "application/json"; - return 200; + InetAddress ip6((const void *)xx,16,0); + + // Check if this IP is within a local-to-Ethernet routed network + int routedNetmaskBits = 0; + for(unsigned int rk=0;rk(&(nc.routes[rk].target))->containsAddress(ip6)) ) + routedNetmaskBits = reinterpret_cast(&(nc.routes[rk].target))->netmaskBits(); + } + + // If it's routed, then try to claim and assign it and if successful end loop + if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip6))) { + ipAssignments.push_back(ip6.toIpString()); + member["ipAssignments"] = ipAssignments; + ip6.setPort((unsigned int)routedNetmaskBits); + if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) + nc.staticIps[nc.staticIpCount++] = ip6; + haveManagedIpv6AutoAssignment = true; + break; + } + } + } } - } // else 404 + } + } - } // else 404 + if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(_jB(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) { + for(unsigned long p=0;((p(&ipRangeStartIA)->sin_addr.s_addr)); + uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeEndIA)->sin_addr.s_addr)); + if ((ipRangeEnd < ipRangeStart)||(ipRangeStart == 0)) + continue; + uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - return 404; -} + // Start with the LSB of the member's address + uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); -void EmbeddedNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report) -{ - char tmp[65535]; - EmbeddedNetworkController *const self = reinterpret_cast(test->ptr); + for(uint32_t k=ipRangeStart,trialCount=0;((k<=ipRangeEnd)&&(trialCount < 1000));++k,++trialCount) { + uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; + ++ipTrialCounter; + if ((ip & 0x000000ff) == 0x000000ff) + continue; // don't allow addresses that end in .255 - if (!test) - return; - if (!report) - return; + // Check if this IP is within a local-to-Ethernet routed network + int routedNetmaskBits = -1; + for(unsigned int rk=0;rk(&(nc.routes[rk].target))->sin_addr.s_addr)); + int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast(&(nc.routes[rk].target))->sin_port)); + if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) { + routedNetmaskBits = targetBits; + break; + } + } + } - Mutex::Lock _l(self->_circuitTests_m); - std::map< uint64_t,_CircuitTestEntry >::iterator cte(self->_circuitTests.find(test->testId)); + // If it's routed, then try to claim and assign it and if successful end loop + const InetAddress ip4(Utils::hton(ip),0); + if ((routedNetmaskBits > 0)&&(!nmi.allocatedIps.count(ip4))) { + ipAssignments.push_back(ip4.toIpString()); + 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; + } + } + } + } + } + } - if (cte == self->_circuitTests.end()) { // sanity check: a circuit test we didn't launch? - self->_node->circuitTestEnd(test); - ::free((void *)test); + CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); + if (com.sign(_signingId)) { + nc.com = com; + } else { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR); return; } - Utils::snprintf(tmp,sizeof(tmp), - "%s{\n" - "\t\"timestamp\": %llu," ZT_EOL_S - "\t\"testId\": \"%.16llx\"," ZT_EOL_S - "\t\"upstream\": \"%.10llx\"," ZT_EOL_S - "\t\"current\": \"%.10llx\"," ZT_EOL_S - "\t\"receivedTimestamp\": %llu," ZT_EOL_S - "\t\"sourcePacketId\": \"%.16llx\"," ZT_EOL_S - "\t\"flags\": %llu," ZT_EOL_S - "\t\"sourcePacketHopCount\": %u," ZT_EOL_S - "\t\"errorCode\": %u," ZT_EOL_S - "\t\"vendor\": %d," ZT_EOL_S - "\t\"protocolVersion\": %u," ZT_EOL_S - "\t\"majorVersion\": %u," ZT_EOL_S - "\t\"minorVersion\": %u," ZT_EOL_S - "\t\"revision\": %u," ZT_EOL_S - "\t\"platform\": %d," ZT_EOL_S - "\t\"architecture\": %d," ZT_EOL_S - "\t\"receivedOnLocalAddress\": \"%s\"," ZT_EOL_S - "\t\"receivedFromRemoteAddress\": \"%s\"" ZT_EOL_S - "}", - ((cte->second.jsonResults.length() > 0) ? ",\n" : ""), - (unsigned long long)report->timestamp, - (unsigned long long)test->testId, - (unsigned long long)report->upstream, - (unsigned long long)report->current, - (unsigned long long)OSUtils::now(), - (unsigned long long)report->sourcePacketId, - (unsigned long long)report->flags, - report->sourcePacketHopCount, - report->errorCode, - (int)report->vendor, - report->protocolVersion, - report->majorVersion, - report->minorVersion, - report->revision, - (int)report->platform, - (int)report->architecture, - reinterpret_cast(&(report->receivedOnLocalAddress))->toString().c_str(), - reinterpret_cast(&(report->receivedFromRemoteAddress))->toString().c_str()); + if (member != origMember) { + member["lastModified"] = now; + Mutex::Lock _l(_db_m); + _db.put("network",nwids,"member",identity.address().toString(),member); + } - cte->second.jsonResults.append(tmp); + _sender->ncSendConfig(nwid,requestPacketId,identity.address(),nc,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } void EmbeddedNetworkController::_getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi) diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 620ef3e3..0169b1d3 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -37,11 +37,15 @@ #include "../osdep/OSUtils.hpp" #include "../osdep/Thread.hpp" +#include "../osdep/BlockingQueue.hpp" #include "../ext/json/json.hpp" #include "JSONDB.hpp" +// Number of background threads to start -- not actually started until needed +#define ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT 2 + namespace ZeroTier { class Node; @@ -83,8 +87,31 @@ public: std::string &responseBody, std::string &responseContentType); + void threadMain() + throw(); + private: static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report); + void _request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData); + + struct _RQEntry + { + uint64_t nwid; + uint64_t requestPacketId; + InetAddress fromAddr; + Identity identity; + Dictionary metaData; + }; + BlockingQueue<_RQEntry *> _queue; + + Thread _threads[ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT]; + bool _threadsStarted; + Mutex _threads_m; // Gathers a bunch of statistics about members of a network, IP assignments, etc. that we need in various places // This does lock _networkMemberCache_m diff --git a/osdep/BlockingQueue.hpp b/osdep/BlockingQueue.hpp new file mode 100644 index 00000000..6172f4da --- /dev/null +++ b/osdep/BlockingQueue.hpp @@ -0,0 +1,64 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + */ + +#ifndef ZT_BLOCKINGQUEUE_HPP +#define ZT_BLOCKINGQUEUE_HPP + +#include +#include +#include + +namespace ZeroTier { + +/** + * Simple C++11 thread-safe queue + * + * Do not use in node/ since we have not gone C++11 there yet. + */ +template +class BlockingQueue +{ +public: + BlockingQueue(void) {} + + inline void post(T t) + { + std::lock_guard lock(m); + q.push(t); + c.notify_one(); + } + + inline T get(void) + { + std::unique_lock lock(m); + while(q.empty()) + c.wait(lock); + T val = q.front(); + q.pop(); + return val; + } + +private: + std::queue q; + mutable std::mutex m; + std::condition_variable c; +}; + +} // namespace ZeroTier + +#endif -- cgit v1.2.3 From ee5bd57d40f793baaf596a038f1446b29b1e86a4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 14 Nov 2016 15:29:36 -0800 Subject: We don't bind to non-local IP for TCP yet, but eliminate double check. --- service/OneService.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/service/OneService.cpp b/service/OneService.cpp index 30e6c938..fcbccd63 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1259,12 +1259,10 @@ public: inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) { - if ((!from)||(reinterpret_cast(from)->ipScope() != InetAddress::IP_SCOPE_LOOPBACK)) { - // Non-Loopback: deny (for now) + if (!from) { _phy.close(sockN,false); return; } else { - // Loopback == HTTP JSON API request TcpConnection *tc = new TcpConnection(); _tcpConnections.insert(tc); tc->type = TcpConnection::TCP_HTTP_INCOMING; -- cgit v1.2.3 From b6c99ba3efeb51c7f08c1a4b1392f3942258195d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 14 Nov 2016 15:47:06 -0800 Subject: Add (currently undocumented) option to allow management from certain networks. --- one.cpp | 11 ++++++++++- service/OneService.cpp | 37 +++++++++++++++++++++++-------------- service/OneService.hpp | 4 +++- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/one.cpp b/one.cpp index 55cc2e19..51cda0c7 100644 --- a/one.cpp +++ b/one.cpp @@ -973,6 +973,7 @@ int main(int argc,char **argv) std::string homeDir; unsigned int port = ZT_DEFAULT_PORT; bool skipRootCheck = false; + const char *allowManagementFrom = (const char *)0; for(int i=1;irun()) { case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done case OneService::ONE_NORMAL_TERMINATION: diff --git a/service/OneService.cpp b/service/OneService.cpp index fcbccd63..91063bad 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -483,6 +483,7 @@ public: const std::string _homePath; BackgroundResolver _tcpFallbackResolver; + InetAddress _allowManagementFrom; EmbeddedNetworkController *_controller; Phy _phy; Node *_node; @@ -570,7 +571,7 @@ public: // end member variables ---------------------------------------------------- - OneServiceImpl(const char *hp,unsigned int port) : + OneServiceImpl(const char *hp,unsigned int port,const char *allowManagementFrom) : _homePath((hp) ? hp : ".") ,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY) ,_controller((EmbeddedNetworkController *)0) @@ -595,6 +596,9 @@ public: #endif ,_run(true) { + if (allowManagementFrom) + _allowManagementFrom.fromString(allowManagementFrom); + _ports[0] = 0; _ports[1] = 0; _ports[2] = 0; @@ -614,7 +618,7 @@ public: struct sockaddr_in in4; memset(&in4,0,sizeof(in4)); in4.sin_family = AF_INET; - in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @127.0.0.1 + in4.sin_addr.s_addr = Utils::hton((uint32_t)((allowManagementFrom) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1 in4.sin_port = Utils::hton((uint16_t)port); _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this); @@ -622,7 +626,8 @@ public: memset((void *)&in6,0,sizeof(in6)); in6.sin6_family = AF_INET6; in6.sin6_port = in4.sin_port; - in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1 + if (!allowManagementFrom) + in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1 _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this); // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that @@ -1699,16 +1704,20 @@ public: std::string contentType("text/plain"); // default if not changed in handleRequest() unsigned int scode = 404; - try { - if (_controlPlane) - scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType); - else scode = 500; - } catch (std::exception &exc) { - fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what()); - scode = 500; - } catch ( ... ) { - fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S); - scode = 500; + if ( ((!_allowManagementFrom)&&(tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK)) || (_allowManagementFrom.containsAddress(tc->from)) ) { + try { + if (_controlPlane) + scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType); + else scode = 500; + } catch (std::exception &exc) { + fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what()); + scode = 500; + } catch ( ... ) { + fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S); + scode = 500; + } + } else { + scode = 401; } const char *scodestr; @@ -1973,7 +1982,7 @@ std::string OneService::autoUpdateUrl() return std::string(); } -OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } +OneService *OneService::newInstance(const char *hp,unsigned int port,const char *allowManagementFrom) { return new OneServiceImpl(hp,port,allowManagementFrom); } OneService::~OneService() {} } // namespace ZeroTier diff --git a/service/OneService.hpp b/service/OneService.hpp index 72ff7d84..553bfd5e 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -98,10 +98,12 @@ public: * * @param hp Home path * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port) + * @param allowManagementFrom If non-NULL, allow control from supplied IP/netmask */ static OneService *newInstance( const char *hp, - unsigned int port); + unsigned int port, + const char *allowManagementFrom = (const char *)0); virtual ~OneService(); -- cgit v1.2.3 From 4ad942522ba17d94ac68a450ff29d7f6159c2202 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 14 Nov 2016 15:57:46 -0800 Subject: Kill unnecessary check in another spot. --- service/ControlPlane.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index f14bae54..150bba1b 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -270,9 +270,6 @@ unsigned int ControlPlane::handleRequest( std::map urlArgs; Mutex::Lock _l(_lock); - if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6)))) - return 403; // Forbidden: we only allow access from localhost right now - /* Note: this is kind of restricted in what it'll take. It does not support * URL encoding, and /'s in URL args will screw it up. But the only URL args * it really uses in ?jsonp=funcionName, and otherwise it just takes simple -- cgit v1.2.3 From 5bd8968eb8fa1f5309a5437f14dc611068719582 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 15 Nov 2016 11:50:53 -0800 Subject: Add rules engine debugging switch to make-linux.mk --- make-linux.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/make-linux.mk b/make-linux.mk index 9dfd39bf..ceb97a8a 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -84,6 +84,10 @@ ifeq ($(ZT_TRACE),1) DEFS+=-DZT_TRACE endif +ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1) + DEFS+=-DZT_RULES_ENGINE_DEBUGGING +endif + ifeq ($(ZT_DEBUG),1) DEFS+=-DZT_TRACE override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS) -- cgit v1.2.3 From 15c6e2ec70b4c43e04e1d79d9743c535c6a530a0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 15 Nov 2016 14:06:25 -0800 Subject: Fix member deauthorization time threshold bug. --- controller/EmbeddedNetworkController.cpp | 50 +++++++++++++++++--------------- controller/EmbeddedNetworkController.hpp | 2 ++ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 7f885b4e..b2ca732a 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -697,6 +697,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( const bool newAuth = _jB(b["authorized"],false); if (newAuth != _jB(member["authorized"],false)) { member["authorized"] = newAuth; + member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now; + json ah; ah["a"] = newAuth; ah["by"] = "api"; @@ -1278,23 +1280,14 @@ void EmbeddedNetworkController::_request( // Determine whether and how member is authorized const char *authorizedBy = (const char *)0; + bool autoAuthorized = false; + json autoAuthCredentialType,autoAuthCredential; if (_jB(member["authorized"],false)) { authorizedBy = "memberIsAuthorized"; } else if (!_jB(network["private"],true)) { authorizedBy = "networkIsPublic"; - if (!member.count("authorized")) { - member["authorized"] = true; - json ah; - ah["a"] = true; - ah["by"] = authorizedBy; - ah["ts"] = now; - ah["ct"] = json(); - ah["c"] = json(); - member["authHistory"].push_back(ah); - member["lastModified"] = now; - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); - } + if (!member.count("authorized")) + autoAuthorized = true; } else { char presentedAuth[512]; if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) { @@ -1329,17 +1322,9 @@ void EmbeddedNetworkController::_request( } if (usable) { authorizedBy = "token"; - member["authorized"] = true; - json ah; - ah["a"] = true; - ah["by"] = authorizedBy; - ah["ts"] = now; - ah["ct"] = "token"; - ah["c"] = tstr; - member["authHistory"].push_back(ah); - member["lastModified"] = now; - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + autoAuthorized = true; + autoAuthCredentialType = "token"; + autoAuthCredential = tstr; } } } @@ -1349,6 +1334,23 @@ void EmbeddedNetworkController::_request( } } + // If we auto-authorized, update member record + if ((autoAuthorized)&&(authorizedBy)) { + member["authorized"] = true; + member["lastAuthorizedTime"] = now; + + json ah; + ah["a"] = true; + ah["by"] = authorizedBy; + ah["ts"] = now; + ah["ct"] = autoAuthCredentialType; + ah["c"] = autoAuthCredential; + member["authHistory"].push_back(ah); + + json &revj = member["revision"]; + member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + } + // Log this request if (requestPacketId) { // only log if this is a request, not for generated pushes json rlEntry = json::object(); diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 0169b1d3..cde6522d 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -145,6 +145,8 @@ private: 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; member["objtype"] = "member"; } inline void _initNetwork(nlohmann::json &network) -- cgit v1.2.3 From 07b2a3818ca389f45bff33606f729baf0260fdd9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 15 Nov 2016 14:26:05 -0800 Subject: Fix TTL scaling in cert. --- controller/EmbeddedNetworkController.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index b2ca732a..b78f847e 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1399,16 +1399,18 @@ void EmbeddedNetworkController::_request( _NetworkMemberInfo nmi; _getNetworkMemberInfo(now,nwid,nmi); - // Compute credential TTL. This is the "moving window" for COM agreement and - // the global TTL for Capability and Tag objects. (The same value is used - // for both.) This is computed by reference to the last time we deauthorized - // a member, since within the time period since this event any temporal - // differences are not particularly relevant. - uint64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA; - if (now > nmi.mostRecentDeauthTime) - credentialtmd += (now - nmi.mostRecentDeauthTime); - if (credentialtmd > ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA) - credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + uint64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + if (now > nmi.mostRecentDeauthTime) { + // If we recently de-authorized a member, shrink credential TTL/max delta to + // be below the threshold required to exclude it. Cap this to a min/max to + // prevent jitter or absurdly large values. + const uint64_t deauthWindow = now - nmi.mostRecentDeauthTime; + if (deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA) { + credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA; + } else if (deauthWindow < (ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL)) { + credentialtmd = deauthWindow - 5000ULL; + } + } nc.networkId = nwid; nc.type = _jB(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; -- cgit v1.2.3 From bab75186f5538ab9a188569793d24fb393e2e078 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 15 Nov 2016 15:51:25 -0800 Subject: make some fields in the network list selectable --- macui/ZeroTier One/ShowNetworksViewController.xib | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/macui/ZeroTier One/ShowNetworksViewController.xib b/macui/ZeroTier One/ShowNetworksViewController.xib index f26cb446..f210d721 100644 --- a/macui/ZeroTier One/ShowNetworksViewController.xib +++ b/macui/ZeroTier One/ShowNetworksViewController.xib @@ -1,8 +1,9 @@ - - + + - + + @@ -24,7 +25,7 @@ - + @@ -50,7 +51,7 @@ - + @@ -242,7 +243,7 @@ - + @@ -250,7 +251,7 @@ - + -- cgit v1.2.3 From 456c7ca66117c55444631ebd706afc9f26ab2cd4 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 15 Nov 2016 16:55:24 -0800 Subject: only changed items in the full network list are updated now --- macui/ZeroTier One/Network.h | 6 +++ macui/ZeroTier One/Network.m | 59 +++++++++++++++++++++++++ macui/ZeroTier One/ShowNetworksViewController.h | 2 +- macui/ZeroTier One/ShowNetworksViewController.m | 40 +++++++++++++++-- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/macui/ZeroTier One/Network.h b/macui/ZeroTier One/Network.h index 0f3c4964..957ff8d5 100644 --- a/macui/ZeroTier One/Network.h +++ b/macui/ZeroTier One/Network.h @@ -59,4 +59,10 @@ enum NetworkType { - (NSString*)statusString; - (NSString*)typeString; +- (BOOL)hasSameNetworkId:(UInt64)networkId; + +- (BOOL)isEqualToNetwork:(Network*)network; +- (BOOL)isEqual:(id)object; +- (NSUInteger)hash; + @end diff --git a/macui/ZeroTier One/Network.m b/macui/ZeroTier One/Network.m index 16efc6e3..8474acaa 100644 --- a/macui/ZeroTier One/Network.m +++ b/macui/ZeroTier One/Network.m @@ -275,4 +275,63 @@ NSString *NetworkAllowDefaultKey = @"allowDefault"; } } +- (BOOL)hasSameNetworkId:(UInt64)networkId +{ + return self.nwid == networkId; +} + +- (BOOL)isEqualToNetwork:(Network*)network +{ + return [self.assignedAddresses isEqualToArray:network.assignedAddresses] && + self.bridge == network.bridge && + self.broadcastEnabled == network.broadcastEnabled && + self.dhcp == network.dhcp && + [self.mac isEqualToString:network.mac] && + self.mtu == network.mtu && + self.netconfRevision == network.netconfRevision && + [self.name isEqualToString:network.name] && + self.nwid == network.nwid && + [self.portDeviceName isEqualToString:network.portDeviceName] && + self.status == network.status && + self.type == network.type && + self.allowManaged == network.allowManaged && + self.allowGlobal == network.allowGlobal && + self.allowDefault == network.allowDefault && + self.connected == network.connected; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[Network class]]) { + return NO; + } + + return [self isEqualToNetwork:object]; +} + +- (NSUInteger)hash +{ + return [self.assignedAddresses hash] ^ + self.bridge ^ + self.broadcastEnabled ^ + self.dhcp ^ + [self.mac hash] ^ + self.mtu ^ + self.netconfRevision ^ + [self.name hash] ^ + self.nwid ^ + [self.portDeviceName hash] ^ + self.portError ^ + self.status ^ + self.type ^ + self.allowManaged ^ + self.allowGlobal ^ + self.allowDefault ^ + self.connected; +} + @end diff --git a/macui/ZeroTier One/ShowNetworksViewController.h b/macui/ZeroTier One/ShowNetworksViewController.h index 5c251674..6138958d 100644 --- a/macui/ZeroTier One/ShowNetworksViewController.h +++ b/macui/ZeroTier One/ShowNetworksViewController.h @@ -23,7 +23,7 @@ @interface ShowNetworksViewController : NSViewController -@property (nonatomic) NSArray *networkList; +@property (nonatomic) NSMutableArray *networkList; @property (nonatomic) NetworkMonitor *netMonitor; @property (nonatomic) BOOL visible; diff --git a/macui/ZeroTier One/ShowNetworksViewController.m b/macui/ZeroTier One/ShowNetworksViewController.m index e3a1e52c..8ca32ed0 100644 --- a/macui/ZeroTier One/ShowNetworksViewController.m +++ b/macui/ZeroTier One/ShowNetworksViewController.m @@ -21,6 +21,17 @@ #import "NetworkInfoCell.h" #import "Network.h" +BOOL hasNetworkWithID(NSArray *list, UInt64 nwid) +{ + for(Network *n in list) { + if(n.nwid == nwid) { + return YES; + } + } + + return NO; +} + @interface ShowNetworksViewController () @end @@ -30,6 +41,8 @@ - (void)viewDidLoad { [super viewDidLoad]; + self.networkList = [NSMutableArray array]; + [self.tableView setDelegate:self]; [self.tableView setDataSource:self]; [self.tableView setBackgroundColor:[NSColor clearColor]]; @@ -50,9 +63,30 @@ } - (void)setNetworks:(NSArray *)list { - _networkList = list; - if(_visible) { - [_tableView reloadData]; + for (Network *n in list) { + if ([_networkList containsObject:n]) { + // don't need to do anything here. Already an identical object in the list + continue; + } + else { + // network not in the list based on equality. Did an object change? or is it a new item? + if (hasNetworkWithID(_networkList, n.nwid)) { + + for (int i = 0; i < [_networkList count]; ++i) { + Network *n2 = [_networkList objectAtIndex:i]; + if (n.nwid == n2.nwid) + { + [_networkList replaceObjectAtIndex:i withObject:n]; + [_tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:i] + columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + } + } + } + else { + [_networkList addObject:n]; + [_tableView reloadData]; + } + } } } -- cgit v1.2.3 From dc549b7f3c4852ed59c25af7b4d9a9fe494f4e7f Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 16 Nov 2016 14:50:03 -0800 Subject: add JSONDB.cpp --- windows/ZeroTierOne/ZeroTierOne.vcxproj | 3 ++- windows/ZeroTierOne/ZeroTierOne.vcxproj.filters | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index e5ea25bf..d19eee6c 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -20,6 +20,7 @@ + @@ -269,7 +270,7 @@ true - NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) false 4996 diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index 337b8cbb..1fa39abd 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -261,6 +261,9 @@ Source Files\controller + + Source Files\controller + -- cgit v1.2.3 From 14bf326de20ed70e752dcf2badf7d6d4623949d5 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 16 Nov 2016 14:50:25 -0800 Subject: make device ID selectable --- windows/WinUI/MainWindow.xaml | 4 +++- windows/WinUI/MainWindow.xaml.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/windows/WinUI/MainWindow.xaml b/windows/WinUI/MainWindow.xaml index 9d4a9fc1..f501b338 100644 --- a/windows/WinUI/MainWindow.xaml +++ b/windows/WinUI/MainWindow.xaml @@ -93,7 +93,9 @@ - + + + diff --git a/windows/WinUI/MainWindow.xaml.cs b/windows/WinUI/MainWindow.xaml.cs index 25534b46..e4a82f96 100644 --- a/windows/WinUI/MainWindow.xaml.cs +++ b/windows/WinUI/MainWindow.xaml.cs @@ -143,7 +143,7 @@ namespace WinUI networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { - this.networkId.Content = status.Address; + this.networkId.Text = status.Address; })); versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { @@ -160,7 +160,7 @@ namespace WinUI networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { - this.networkId.Content = ""; + this.networkId.Text = ""; })); versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { -- cgit v1.2.3 From 78a8ceda0efc026ebecb8ed7fe73c51db832552c Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 16 Nov 2016 15:11:28 -0800 Subject: IP addresses now selectable in Windows UI --- windows/WinUI/NetworkInfoView.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/WinUI/NetworkInfoView.xaml b/windows/WinUI/NetworkInfoView.xaml index aea85490..b3e8cae0 100644 --- a/windows/WinUI/NetworkInfoView.xaml +++ b/windows/WinUI/NetworkInfoView.xaml @@ -64,7 +64,7 @@ - + -- cgit v1.2.3 From 3c248ec61a732f539dcf0c9ea3d92ae8f42b62fe Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 16 Nov 2016 15:50:56 -0800 Subject: handle removing of networks we are no longer connected to from the UI --- windows/WinUI/NetworkInfoView.xaml.cs | 56 ++++++++++++++++++++--------- windows/WinUI/NetworksPage.xaml.cs | 67 +++++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 23 deletions(-) diff --git a/windows/WinUI/NetworkInfoView.xaml.cs b/windows/WinUI/NetworkInfoView.xaml.cs index 3ecc31b8..c9d6515f 100644 --- a/windows/WinUI/NetworkInfoView.xaml.cs +++ b/windows/WinUI/NetworkInfoView.xaml.cs @@ -21,7 +21,7 @@ namespace WinUI public partial class NetworkInfoView : UserControl { private APIHandler handler; - private ZeroTierNetwork network; + public ZeroTierNetwork network; public NetworkInfoView(APIHandler handler, ZeroTierNetwork network) { @@ -31,19 +31,41 @@ namespace WinUI this.network = network; UpdateNetworkData(); + + allowDefault.Checked += AllowDefault_CheckStateChanged; + allowDefault.Unchecked += AllowDefault_CheckStateChanged; + allowGlobal.Checked += AllowGlobal_CheckStateChanged; + allowGlobal.Unchecked += AllowGlobal_CheckStateChanged; + allowManaged.Checked += AllowManaged_CheckStateChanged; + allowManaged.Unchecked += AllowManaged_CheckStateChanged; } private void UpdateNetworkData() { - this.networkId.Text = network.NetworkId; - this.networkName.Text = network.NetworkName; - this.networkStatus.Text = network.NetworkStatus; - this.networkType.Text = network.NetworkType; - this.macAddress.Text = network.MacAddress; - this.mtu.Text = network.MTU.ToString(); + + if (this.networkId.Text != network.NetworkId) + this.networkId.Text = network.NetworkId; + + if (this.networkName.Text != network.NetworkName) + this.networkName.Text = network.NetworkName; + + if (this.networkStatus.Text != network.NetworkStatus) + this.networkStatus.Text = network.NetworkStatus; + + if (this.networkType.Text != network.NetworkType) + this.networkType.Text = network.NetworkType; + + if (this.macAddress.Text != network.MacAddress) + this.macAddress.Text = network.MacAddress; + + if (this.mtu.Text != network.MTU.ToString()) + this.mtu.Text = network.MTU.ToString(); + this.broadcastEnabled.Text = (network.BroadcastEnabled ? "ENABLED" : "DISABLED"); this.bridgingEnabled.Text = (network.Bridge ? "ENABLED" : "DISABLED"); - this.deviceName.Text = network.DeviceName; + + if (this.deviceName.Text != network.DeviceName) + this.deviceName.Text = network.DeviceName; string iplist = ""; for (int i = 0; i < network.AssignedAddresses.Length; ++i) @@ -53,19 +75,12 @@ namespace WinUI iplist += "\n"; } - this.managedIps.Text = iplist; + if (this.managedIps.Text != iplist) + this.managedIps.Text = iplist; this.allowDefault.IsChecked = network.AllowDefault; this.allowGlobal.IsChecked = network.AllowGlobal; this.allowManaged.IsChecked = network.AllowManaged; - - allowDefault.Checked += AllowDefault_CheckStateChanged; - allowDefault.Unchecked += AllowDefault_CheckStateChanged; - allowGlobal.Checked += AllowGlobal_CheckStateChanged; - allowGlobal.Unchecked += AllowGlobal_CheckStateChanged; - allowManaged.Checked += AllowManaged_CheckStateChanged; - allowManaged.Unchecked += AllowManaged_CheckStateChanged; - } public bool HasNetwork(ZeroTierNetwork network) @@ -76,6 +91,13 @@ namespace WinUI return false; } + public void SetNetworkInfo(ZeroTierNetwork network) + { + this.network = network; + + UpdateNetworkData(); + } + private void leaveButton_Click(object sender, RoutedEventArgs e) { handler.LeaveNetwork(network.NetworkId); diff --git a/windows/WinUI/NetworksPage.xaml.cs b/windows/WinUI/NetworksPage.xaml.cs index 5a0dc19d..04ea112f 100644 --- a/windows/WinUI/NetworksPage.xaml.cs +++ b/windows/WinUI/NetworksPage.xaml.cs @@ -34,19 +34,74 @@ namespace WinUI public void setNetworks(List networks) { - this.wrapPanel.Children.Clear(); if (networks == null) { + this.wrapPanel.Children.Clear(); return; } - for (int i = 0; i < networks.Count; ++i) + foreach (ZeroTierNetwork network in networks) { - this.wrapPanel.Children.Add( - new NetworkInfoView( - handler, - networks.ElementAt(i))); + NetworkInfoView view = ChildWithNetwork(network); + if (view != null) + { + view.SetNetworkInfo(network); + } + else + { + wrapPanel.Children.Add( + new NetworkInfoView( + handler, + network)); + } } + + // remove networks we're no longer joined to. + List tmpList = GetNetworksFromChildren(); + foreach (ZeroTierNetwork n in networks) + { + if (tmpList.Contains(n)) + { + tmpList.Remove(n); + } + } + + foreach (ZeroTierNetwork n in tmpList) + { + NetworkInfoView view = ChildWithNetwork(n); + if (view != null) + { + wrapPanel.Children.Remove(view); + } + } + } + + private NetworkInfoView ChildWithNetwork(ZeroTierNetwork network) + { + List list = wrapPanel.Children.OfType().ToList(); + + foreach (NetworkInfoView view in list) + { + if (view.HasNetwork(network)) + { + return view; + } + } + + return null; + } + + private List GetNetworksFromChildren() + { + List networks = new List(wrapPanel.Children.Count); + + List list = wrapPanel.Children.OfType().ToList(); + foreach (NetworkInfoView n in list) + { + networks.Add(n.network); + } + + return networks; } } } -- cgit v1.2.3
FieldTypeDescriptionWritable
addressstring10-digit hex ZeroTier addressno
lastUnicastFrameintegerTime of last unicast frame in ms since epochno
lastMulticastFrameintegerTime of last multicast frame in ms since epochno
versionMajorintegerMajor version of remote if knownno
versionMinorintegerMinor version of remote if knownno
versionRevintegerRevision of remote if knownno